> ## Documentation Index
> Fetch the complete documentation index at: https://thrackle.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Tracker Usage

> Walkthrough of using Trackers

## Initialize a Tracker

As outlined in the [policy configuration](/v2/guides/policy-configuration) guide, you define your trackers in the `policy.json` file for your project. Below is an example policy to illustrate the examples that will follow.

```json policy.json
{
  "Policy": "Tracker Example Policy",
  "PolicyType": "open",
  "CallingFunctions": [
    {
      "name": "_update(address from, address to, uint256 amount)",
      "functionSignature": "_update(address from, address to, uint256 amount)",
      "encodedValues": "address from, address to, uint256 amount"
    }
  ],
  "ForeignCalls": [],
  "CallingFunctions": [
    {
      "name": "mint(uint256 amount)",
      "functionSignature": "mint(uint256 amount)",
      "encodedValues": "uint256 amount"
    }
  ],
  "Trackers": [
    {
      "name": "TradingVolume",
      "type": "uint256",
      "initialValue": 0
    }
  ],
  "Rules": [
    // purposefully empty for now
  ]
}
```

There are two ways that you can utilize trackers in your project.

1. Trackers can be used as input values in rule expressions.
2. Trackers can be updated as effects of rules.

Let's see how to accomplish these two usage options with standard and mapped trackers

## Usage in Rules

First, we need to add at least one rule to the policy that will use the named trackers.

### Tracker

This rule condition ensures that the total volume so far and the new `amount` does not exceed 1 billion tokens.

```json policy.json {12-18}
{
  "Policy": "Tracker Example Policy",
  "PolicyType": "open",
  "CallingFunctions": [
    {
      "name": "_update(address from, address to, uint256 amount)",
      "functionSignature": "_update(address from, address to, uint256 amount)",
      "encodedValues": "address from, address to, uint256 amount"
    }
  ],
  "ForeignCalls": [],
  "Trackers": [
    {
      "name": "TradingVolume",
      "type": "uint256",
      "initialValue": 0
    },
    {
      "name": "TradeCountPerUser",
      "type": "uint256",
      "initialValue": 0
    }
  ],
  "MappedTrackers": [],
  "Rules": [
    {
      "condition": "(TR:TradingVolume + amount) < 1_000_000_000 AND TR:TradeCountPerUser(from) < 10",
      "positiveEffects": [],
      "negativeEffects": ["revert(\"Trading Volume Max Reached\")"],
      "callingFunction": "_update(address from, address to, uint256 amount)"
    }
  ]
}
```

### Mapped Tracker

This rule condition ensures that the sender has not exceeded 10 transactions.

```json policy.json {12-18}
{
  "Policy": "Tracker Example Policy",
  "PolicyType": "open",
  "CallingFunctions": [
    {
      "name": "_update(address from, address to, uint256 amount)",
      "functionSignature": "_update(address from, address to, uint256 amount)",
      "encodedValues": "address from, address to, uint256 amount"
    }
  ],
  "ForeignCalls": [],
  "Trackers": [],
  "MappedTrackers": [
    {
      "name": "TradeCountPerUser",
      "keyType": "address",
      "valueType": "uint256",
      "initialKeys": ["0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e"],
      "initialValues": [0]
    }
  ],
  "Rules": [
    {
      "condition": "TR:TradeCountPerUser(from) < 10",
      "positiveEffects": ["TRU:TradeCountPerUser(from) += 1"],
      "negativeEffects": ["revert(\"Trading Transaction Max Reached\")"],
      "callingFunction": "_update(address from, address to, uint256 amount)"
    }
  ]
}
```

<Tip>
  Note that this rule is applied to the internal `_update` function. This ensures the policy is
  applied to both the `transfer` and `transferFrom` function calls.
</Tip>

## Tracker Update Effects

As of now, this rule will always succeed because the tracker value is never updated. Below we add a `positiveEffect` that updates the tracker value whenever a `transfer` call is successful.

### Tracker

```json policy.json {6}
//...
    "Rules": [
      {
        "condition": "(TR:TradingVolume + amount) < 1_000_000_000",
        "positiveEffects": [
          "TRU:TradingVolume += amount"
        ],
        "negativeEffects": ["revert(\"Trading Volume Max Reached\")"],
        "callingFunction": "_update(address from, address to, uint256 amount)"
      }
    ]
//...
```

### Mapped Tracker

```json policy.json {6}
//...
    "Rules": [
      {
        "condition": "TR:TradeCountPerUser(from) < 10",
        "positiveEffects": [
          "TRU:TradeCountPerUser(from) += 1"
        ],
        "negativeEffects": ["revert(\"Trading Transaction Max Reached\")"],
        "callingFunction": "_update(address from, address to, uint256 amount)"
      }
    ]
//...
```

<Tip>
  Notice that when referencing a tracker value in a rule condition or as a read value in an effect, you precede the name with `TR`
  as in `TR:TradingVolume`.

  When referencing in an update you must precede it with `TRU` as in
  `TRU:TradingVolume`
</Tip>

## Taking it Further

The rule defined thus far is a good start, but needs additional configuration to achieve the desired outcome.

First, as soon as `1_000_000_000` in trading volume is reached the token will no longer be transferrable. Fixing this will requires resetting the `TradingVolume` tracker when the defined duration is reached. To do this we need another tracker to record the timestamp and mark the beginning of a rolling 24 hour period. Then we test for that condition in a new rule and reset the tracker values when the condition is true.

```json policy.json {5-9, 17-23}
{
  "Policy": "Tracker Example Policy",
  "PolicyType": "open",
  "CallingFunctions": [
    {
      "name": "_update(address from, address to, uint256 amount)",
      "functionSignature": "_update(address from, address to, uint256 amount)",
      "encodedValues": "address from, address to, uint256 amount"
    }
  ],
  "ForeignCalls": [],
  "Trackers": [
    {
      "name": "TimeStamp",
      "type": "uint256",
      "initialValue": 0
    },
    {
      "name": "TradingVolume",
      "type": "uint256",
      "initialValue": 0
    }
  ],
  "MappedTrackers": [
    {
      "name": "UserTimeStamp",
      "keyType": "address",
      "valueType": "uint256",
      "initialKeys": ["0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e"],
      "initialValues": [0]
    },
    {
      "name": "TradeCountPerUser",
      "keyType": "address",
      "valueType": "uint256",
      "initialKeys": ["0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e"],
      "initialValues": [0]
    }
  ],
  "Rules": [
    {
      "condition": "(block.timestamp - TR:TimeStamp) >= 86400",
      "positiveEffects": ["TRU:TimeStamp = block.timestamp", "TRU:TradingVolume = 0"],
      "negativeEffects": [],
      "callingFunction": "_update(address from, address to, uint256 amount)"
    },
    {
      "condition": "(TR:TradingVolume + amount) < 1_000_000_000",
      "positiveEffects": ["TRU:TradingVolume += amount"],
      "negativeEffects": ["revert(\"Trading Volume Max Reached\")"],
      "callingFunction": "_update(address from, address to, uint256 amount)"
    },
    {
      "condition": "(block.timestamp - TR:UserTimeStamp(from)) >= 86400",
      "positiveEffects": ["TRU:UserTimeStamp(from) = block.timestamp", "TRU:TradeCountPerUser = 0"],
      "negativeEffects": [],
      "callingFunction": "_update(address from, address to, uint256 amount)"
    },
    {
      "condition": "TR:TradeCountPerUser(from) < 10",
      "positiveEffects": ["TRU:TradeCountPerUser(from) += 1"],
      "negativeEffects": ["revert(\"Trading Transaction Max Reached\")"],
      "callingFunction": "_update(address from, address to, uint256 amount)"
    }
  ]
}
```

<Note>
  The new rule above is leveraging the `block.timestamp` directly within the rule condition. This is
  not supported in the current version of the Rules Engine, but is anticipated. In the meantime,
  you'd need to directly pass the timestamp value as an extra argument to the modifier added to the
  `_update` function.

  Find more info about how to do this in the [contract integration guide](/v2/guides/contract-integration#passing-additional-values-to-the-rules-engine).
</Note>
