The scenario for this rule example is that an application has users that hold an NFT in their wallet associated with the project. A fungible token release is coming and includes an allocation to airdrop some of the fungible tokens to the existing NFT holders.

There will be three different token lockup periods depending on when the NFT was obtained, which are:

  • Early Adopter
  • Loyal Backer
  • New Ally

These categories will be defined in the rules engine as Tags. In this example, the tags will represent how long the wallet has been a supporter, but you can use tags for any arbitrary category you desire.

The rule that will be leveraged for this example is the Account Min/Max Token Balance rule, which relies on these tags to determine whether or not the tagged account can transfer their tokens (based on defined minimum required balance).

Get Started

This guide requires that you’ve already deployed the client contracts and is written with the expectation you’ll be working from a locally cloned copy of the Forte Rules Engine. Check the Installation Guide and Deploy App Manager Guide for more info.

Preparation

You will need to create the rule and then register and activate it for your deployed application manager. The command below relies on some environment variables being available in your shell. Refer to the environment variables guide for more info.

Determine the Tags

The addAccountMinMaxTokenBalance function that you must call requires that you first determine the bytes32 values of your tag names. You can do this with the cast command built into foundry like so:

cast from-utf8 "Early Adopter" | cast to-bytes32
cast from-utf8 "Loyal Backer" | cast to-bytes32
cast from-utf8 "New Ally" | cast to-bytes32

The resulting bytes32 values are:

# Early Adopter
0x4561726c792041646f7074657200000000000000000000000000000000000000
# Loyal Backer
0x4c6f79616c204261636b65720000000000000000000000000000000000000000
# New Ally
0x4e657720416c6c79000000000000000000000000000000000000000000000000

Decide the Minimum Balance per Tag

You can define a distinct minimum token balance for each tag type, which works well for the use case here. Since the ERC-20 token default includes 18 decimals, you must take this into account when setting up the minimums.

Tag NameToken AllocationWith all Decimals
Early Adopter1,0001_000_000_000_000_000_000_000
Loyal Backer500500_000_000_000_000_000_000
New Ally100100_000_000_000_000_000_000

If you wanted to airdrop the same amount to each token holder, regardless of tag type, then it would make sense to define the same minimum balance for each tag type. To acheive this, you’d pass an array with the same value three times to the min argument of the create function instead of three distinct values.

Define the Hold Times

Next, you need to decide the hold times associated with each tag. These values are defined in hours. The guide will proceed with the following values:

Tag NameHold TimeValue in hours
Early Adopter30 days720
Loyal Backer60 days1,440
New Ally90 days2,160

With these values figured out, you’re ready to add the rule!

Add the Rule to the Protocol

You will need to call the addAccountMinMaxTokenBalance function to create this rule, which requires the following arguments:

address _appManagerAddr,
bytes32[] calldata _accountTypes,
uint256[] calldata _min,
uint256[] calldata _max,
uint16[] calldata _periods,
uint64 _startTime

The above sections have determined the _accountTypes (tags), _min, and _periods values to be used in the function call. You should already know the _appManagerAddr and have it defined in your .env file. The $RULE_ADMIN_KEY is simply the private key of a wallet that has been granted the rule administrator privilege in your application.

Since this example rule is only about setting a minimum required balance you can pass an array of the max uint256 value for the _max argument. Finally, the protocol will accept 0 for the _startTime argument.

The command you need to run, with all of this in mind is outlined below:

cast send $RULE_PROCESSOR_DIAMOND \
    --private-key $RULE_ADMIN_KEY \
    --rpc-url $ETH_RPC_URL \
    "addAccountMinMaxTokenBalance(address,bytes32[],uint256[],uint256[],uint16[],uint64)" \
    $APPLICATION_APP_MANAGER \
    "[0x4561726c792041646f7074657200000000000000000000000000000000000000, 0x4c6f79616c204261636b65720000000000000000000000000000000000000000, 0x4e657720416c6c79000000000000000000000000000000000000000000000000]" \
    "[1000000000000000000000,500000000000000000000,100000000000000000000]" \
    "[115792089237316195423570985008687907853269984665640564039457584007913129639935,115792089237316195423570985008687907853269984665640564039457584007913129639935,115792089237316195423570985008687907853269984665640564039457584007913129639935]" \
    "[720,1440,2160]" \
    0

The output from that command should include a blockNumber, which can then be used to determine the rule ID that was assigned. First, check the output to determine the blockNumber and then export it as an environment variable like so:

export BLOCK_NUMBER=<block_number>

Then you can run this command to determine the assigned rule ID:

cast logs --from-block $BLOCK_NUMBER --address $RULE_PROCESSOR_DIAMOND

The last value in the returned topics array will contain the hex encoded value of the assigned rule ID.

What you’ve accomplished so far is to register this new rule with the main processor diamond on the blockchain network your application is running on. Now you must add this rule to your application manager.

Rules are re-usable in the protocol. For example, if the rule parameters defined above exactly matched the rule that you wanted to implement and your application is on Base Sepolia you could actually skip the above and simply register this existing rule ID with your application. That is unlikely, but it does enable you to create a well defined rule and then apply it to multiple tokens within your application once it exists.

Add the Rule to Your Application

Now that the rule is registered in the protocol you can add it to your specific application.

In this step, you only need to define the action types that you want the rule to be active for. Because of the way the rule was defined above, it only makes sense to apply it to burn, sell, and transfer, which are designated with [0,2,4].

Before calling the below command, make sure you have the environment variables available in your shell:

# determined when you configure your token handler
# see: /guides/client/deploy-token-handlers
APPLICATION_ERC20_HANDLER_ADDRESS
# private key of an account with rule admin privilege
RULE_ADMIN_KEY
# RPC URL to direct the command to
ETH_RPC_URL
# determined above
RULEID
cast send $APPLICATION_ERC20_HANDLER_ADDRESS \
    --private-key $RULE_ADMIN_KEY \
    --rpc-url $ETH_RPC_URL \
    "setAccountMinMaxTokenBalanceId(uint8[],uint32)" \
    "[0,2,4]" \
    $RULEID

Add Tags to Accounts

Finally, before you actually airdrop the token allocations to the existing NFT holders, you’ll need to add a tag to them so the protocol properly limits that wallet’s ability to transfer the airdropped tokens.

Consider an account with the public address 0x1234abcd... in your application that should be assigned the “Early Adopter” tag. Here is the command you would run:

cast send $APPLICATION_APP_MANAGER \
    --private-key $APP_ADMIN_PRIVATE_KEY \
    "addTag(address,bytes32)" \
    0x1234abcd... 0x4561726c792041646f7074657200000000000000000000000000000000000000

Obviously, the address 0x1234abcd... is an incomplete and example address. You must use the full address of a wallet related to a user of your project. The 0x4561726c792041646f707465720000... value after that represents the bytes32 value of the “Early Adopter” tag, which was determined above.

The entire process here would involve using a script to programmatically assign tags to the appropriate accounts in your application followed up by confirming those tags are correctly set to ensure the airdropped tokens are locked up per the expected rules.

Wrapping Up

You should now understand at a high level the specific steps required to enable this type of rule in your application. This guide purposefully did not cover the generalized steps of how you’d actually distribute the tokens after the rules are configured.

Lastly, this particular guide is only one example that illustrates one way you can leverage tag based rules to enforce minimum token balances for a specified period of time based on arbitrarily defined categories.

Let this be an inspiration for even more sophisticated use cases!

If you have questions about this guide and how the Forte Rules Engine can enable compliance and incentive alignment in your application send us a message to talk more!