Skip to main content

Initialize a Tracker

You define your trackers in the policy.json file for your project. Below is an example policy to illustrate the examples that will follow.
policy.json
{
  "Policy": "Tracker Example Policy",
  "PolicyType": "open",
  "CallingFunctions": [
    {
      "Name": "Transfer",
      "FunctionSignature": "transfer(address to, uint256 amount)",
      "EncodedValues": "address to, uint256 amount, address from"
    }
  ],
  "ForeignCalls": [],
  "Trackers": [
    {
      "Name": "TradingVolume",
      "Type": "uint256",
      "InitialValue": "0"
    }
  ],
  "MappedTrackers": [],
  "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. The added rule limits total volume to 1 billion tokens. This rule by itself would simply prevent further trading once that limit is reached. We will add the additional trackers and rules create a fully functioning policy as the guide continues below.
policy.json
{
  "Policy": "Tracker Example Policy",
  "PolicyType": "open",
  "CallingFunctions": [
    {
      "Name": "Transfer",
      "FunctionSignature": "transfer(address to, uint256 amount)",
      "EncodedValues": "address to, uint256 amount, address from"
    }
  ],
  "ForeignCalls": [],
  "Trackers": [
    {
      "Name": "TradingVolume",
      "Type": "uint256",
      "InitialValue": "0"
    }
  ],
  "MappedTrackers": [],
  "Rules": [
    {
      "Name": "Limit Trading",
      "Description": "Limit Trading Volume to 1 Billion",
      "Condition": "(TR:TradingVolume + amount) < 1000000000",
      "PositiveEffects": ["TRU:TradingVolume += amount"],
      "NegativeEffects": ["revert(\"Trading Volume Max Reached\")"],
      "CallingFunction": "Transfer"
    }
  ]
}

Track User Trade Count

Next, we’ll add a mapped tracker, which is similar to a tracker, but allows storing a map of values instead of a single value. This rule condition ensures that the sender has not exceeded 10 transactions.
policy.json
{
  "Policy": "Tracker Example Policy",
  ...
  "MappedTrackers": [
    {
      "Name": "TradeCountPerUser",
      "KeyType": "address",
      "ValueType": "uint256",
      "InitialKeys": [],
      "InitialValues": []
    }
  ],
  "Rules": [
    {
      "Name": "Limit Trading",
      "Description": "Limit Trading Volume to 1 Billion",
      "Condition": "(TR:TradingVolume + amount) < 1000000000",
      "PositiveEffects": ["TRU:TradingVolume += amount"],
      "NegativeEffects": ["revert(\"Trading Volume Max Reached\")"],
      "CallingFunction": "Transfer"
    },
    {
      "Name": "Limit",
      "Description": "Limit Trade Count per User to 10",
      "Condition": "TR:TradeCountPerUser(GV:MSG_SENDER) < 10",
      "PositiveEffects": ["TRU:TradeCountPerUser(GV:MSG_SENDER) += 1"],
      "NegativeEffects": ["revert(\"Trading Transaction Max Reached\")"],
      "CallingFunction": "Transfer"
    }
  ]
}
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

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 billion in trading volume is reached the token will no longer be transferrable. Fixing this 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.
policy.json
{
  "Policy": "Tracker Example Policy",
  "PolicyType": "open",
  "CallingFunctions": [
    {
      "Name": "Transfer",
      "FunctionSignature": "transfer(address to, uint256 amount)",
      "EncodedValues": "address to, uint256 amount, address from"
    }
  ],
  "ForeignCalls": [],
  "Trackers": [
    {
      "Name": "TimeStamp",
      "Type": "uint256",
      "InitialValue": "0"
    },
    {
      "Name": "TradingVolume",
      "Type": "uint256",
      "InitialValue": "0"
    }
  ],
  "MappedTrackers": [
    {
      "Name": "UserTimeStamp",
      "KeyType": "address",
      "ValueType": "uint256",
      "InitialKeys": [],
      "InitialValues": []
    },
    {
      "Name": "TradeCountPerUser",
      "KeyType": "address",
      "ValueType": "uint256",
      "InitialKeys": [],
      "InitialValues": []
    }
  ],
  "Rules": [
    {
      "Name": "Time Window",
      "Description": "Rolling 24 Hour Reset",
      "Condition": "(GV:BLOCK_TIMESTAMP - TR:TimeStamp) >= 86400",
      "PositiveEffects": ["TRU:TimeStamp = GV:BLOCK_TIMESTAMP", "TRU:TradingVolume = 0"],
      "NegativeEffects": [],
      "CallingFunction": "Transfer"
    },
    {
      "Name": "Limit Trading",
      "Description": "Limit Trading Volume to 1 Billion",
      "Condition": "(TR:TradingVolume + amount) < 1000000000",
      "PositiveEffects": ["TRU:TradingVolume += amount"],
      "NegativeEffects": ["revert(\"Trading Volume Max Reached\")"],
      "CallingFunction": "Transfer"
    },
    {
      "Name": "Reset",
      "Description": "Reset Trade Count for User",
      "Condition": "(GV:BLOCK_TIMESTAMP - TR:UserTimeStamp(GV:MSG_SENDER)) >= 86400",
      "PositiveEffects": [
        "TRU:UserTimeStamp(GV:MSG_SENDER) = GV:BLOCK_TIMESTAMP",
        "TRU:TradeCountPerUser(GV:MSG_SENDER) = 0"
      ],
      "NegativeEffects": [],
      "CallingFunction": "Transfer"
    },
    {
      "Name": "Limit",
      "Description": "Limit Trade Count per User to 10",
      "Condition": "TR:TradeCountPerUser(GV:MSG_SENDER) < 10",
      "PositiveEffects": ["TRU:TradeCountPerUser(GV:MSG_SENDER) += 1"],
      "NegativeEffects": ["revert(\"Trading Transaction Max Reached\")"],
      "CallingFunction": "Transfer"
    }
  ]
}
You may have noticed the GV:BLOCK_TIMESTAMP variable above. The Rules Engine enables you to access the block data with the GV prefix. See the global variables for more info.