Help & Support

Follow Rules

This guide explains how to use Follow Rules and how to implement custom ones.

Follow Rules allow accounts to add requirements or constraints that will be applied when another account tries to Follow them.

Using Follow Rules

Users have the option to apply Follow Rules to their accounts. These rules dictates the conditions under which another account can Follow them.

For Custom Graphs, these rules work in combination with Graph Rules set on the given graph.

This section presumes you are familiar with the process of following an Account on Lens.

Built-in Follow Rules

Simple Payment Follow Rule

The SimplePaymentFollowRule is a built-in rule that can be applied to an Account within the context of a specific Graph. It requires an ERC-20 payment from any Account attempting to follow it in order for the operation to succeed.

Token Gated Follow Rule

The TokenGatedFollowRule is a built-in rule that can be applied to an Account within the context of a specific Graph. It requires any other Account attempting to follow it to hold a certain balance of a token (both fungible and non-fungible are supported).

Update Follow Rules

You MUST be authenticated as Account Owner or Account Manager of the Account you intend to update the Follow Rules for.

1

Submit Update Request

First, prepare the update request with the new Follow Rules configuration.

Use the updateAccountFollowRules action to update the Follow Rules for the logged-in Account.

import { bigDecimal, evmAddress } from "@lens-protocol/client";import { updateAccountFollowRules } from "@lens-protocol/client/actions";
const result = await updateAccountFollowRules(sessionClient, {  toAdd: {    required: [      {        simplePaymentRule: {          cost: {            currency: evmAddress("0x5678…"),            value: bigDecimal("10.42"),          },          recipient: evmAddress("0x9012…"),        },      },    ],  },});
if (result.isErr()) {  return console.error(result.error);}

2

Handle Result

Then, handle the result using the adapter for the library of your choice:

import { handleOperationWith } from "@lens-protocol/client/viem";
// …
const result = await updateAccountFollowRules(sessionClient, {  // …}).andThen(handleOperationWith(walletClient));

See the Transaction Lifecycle guide for more information on how to determine the status of the transaction.

Building a Follow Rule

What follows is an example of how to build a rule based on an early iteration. It's provided for illustrative purposes only. Updated documentation on the final implementation will be provided soon.

Let's illustrate the process with an example. We will build a custom Follow Rule that once applied it will only accept Follows from accounts that you previously Followed in some Graph (particularly, the same Graph where the rule is applied can be used, so only the accounts that you Follow will be able to Follow you back).

To build a custom Follow Rule, you must implement the following IFollowRule interface:

interface IFollowRule {    function configure(        address account,        bytes calldata data    ) external;
    function processFollow(        address followerAccount,        address accountToFollow,        uint256 followId,        bytes calldata data    ) external returns (bool);}

Each function of this interface must assume to be invoked by the Graph contract.

A Lens dependency package with all relevant interfaces will be available soon.

1

Implement the Configure Function

First, implement the configure function. This function has the purpose of initializing any required state for the rule to work properly.

The configure function can be called multiple times by the same Graph, with the same account parameter, in order to update the rule configuration (i.e. reconfigure it).

It receives the account for which the rule is being configured for and some data bytes that will be decoded into any extra required rule configuration parameters.

In our example, we need to decode the data bytes into an address parameter, which will represent the Graph where we need to check if the account attempting the Follow is already being Followed by the account trying to be Followed. Let's define a storage mapping to store this configuration:

contract AlreadyFollowedFollowRule is IFollowRule {
  mapping(address => mapping(address => address)) internal _graphToCheck;}

Now let's code the configure function itself, decoding the address parameter and storing it in the mapping:

contract AlreadyFollowedFollowRule is IFollowRule {
  mapping(address => mapping(address => address)) internal _graphToCheck;
  function configure(     address account,     bytes calldata data  ) external override {      _graphToCheck[msg.sender][account] = abi.decode(data, (address));  }}

The configuration is stored in the mapping using the Graph contract address as the key, which is the msg.sender, and the account for which the rule is being configured for. So the same rule can be reused by different Graphs and accounts.

2

Implement the Process Follow function

Next, implement the processFollow function. This function is invoked by the Graph contract every time a Follow is executed, so then our custom logic can be applied to shape under which conditions this operation can succeed.

The function receives the account executing the follow (followerAccount), the account being followed (accountToFollow), the followId (only applies if Follows are tokenized, so you can use some specific Follow Token to Follow), and some data in case the rule requires additional information to work.

The function must revert in case of not meeting the requirements imposed by the rule. Otherwise, it must return a boolean that indicates if the rule is applying a restriction over this operation or not. In our example, given that a restriction is indeed being applied to the Follow operation, we will return true.

We first get the configured Graph where to perform the check:

contract AlreadyFollowedFollowRule is IFollowRule {
    mapping(address => mapping(address => address)) internal _graphToCheck;
    // . . .
    function processFollow(       address followerAccount,       address accountToFollow,       uint256 followId,       bytes calldata data      ) external override returns (bool)    {        // We get the configured Graph        IGraph graph = IGraph(_graphToCheck[msg.sender][accountToFollow]);    }}

Next we require that followerAccount is being followed by accountToFollow in the configured Graph.

The IGraph interface contains this function in order to check whether an account is following another account or not:

function isFollowing(        address followerAccount,        address targetAccount    ) external view returns (bool);

So, let's add the requirement check:

contract AlreadyFollowedFollowRule is IFollowRule {
    mapping(address => mapping(address => address)) internal _graphToCheck;
    // . . .
    function processFollow(       address followerAccount,       address accountToFollow,       uint256 followId,       bytes calldata data      ) external override returns (bool)    {        IGraph graph = IGraph(_graphToCheck[msg.sender][accountToFollow]);        // Require that `followerAccount` is being followed        // by `accountToFollow` in the configured Graph        require(graph.isFollowing(accountToFollow, followerAccount));    }}

Finally, we return true as the rule is being applied to the Follow operation:

contract AlreadyFollowedFollowRule is IFollowRule {
    mapping(address => mapping(address => address)) internal _graphToCheck;
    // . . .
    function processFollow(       address followerAccount,       address accountToFollow,       uint256 followId,       bytes calldata data      ) external override returns (bool)    {        IGraph graph = IGraph(_graphToCheck[msg.sender][accountToFollow]);        require(graph.isFollowing(accountToFollow, followerAccount));        // We return true to signal that the rule is being        // applied when executing a Follow        return true;    }}

Now the AlreadyFollowedFollowRule is ready to be applied into any account under any Graph. See the full code below:

contract AlreadyFollowedFollowRule is IFollowRule {
    mapping(address => mapping(address => address)) internal _graphToCheck;
    function configure(      address account,      bytes calldata data    ) external override {        _graphToCheck[msg.sender][account] = abi.decode(data, (address));    }
    function processFollow(       address followerAccount,       address accountToFollow,       uint256 followId,       bytes calldata data      ) external override returns (bool)    {        // We get the configured Graph        IGraph graph = IGraph(_graphToCheck[msg.sender][accountToFollow]);        // Require that `followerAccount` is being followed        // by `accountToFollow` in the configured Graph        require(graph.isFollowing(accountToFollow, followerAccount));        // We return true to signal that the rule is being        // applied when executing a Follow        return true;    }}

Stay tuned for API integration of rules and more guides!