Feed Rules

This guide will explain Feed Rules and how to implement them.


Feed Rules allow Feeds' administrators to add requirements or constraints that will be applied when any Account tries to Create a Post on the Feed, Edit a Post of the Feed, or Change the Post Rules of a Post of the Feed.

See the Rules concept section to learn more about them.

Existing Feed Rules

TokenGatedFeedRule

This rule can be applied to a Feed to require accounts to hold a certain balance of a token (both fungible and non-fungible are supported) in order to successfully Create a Post on the Feed.

The configuration requires to input the address of the token, the token standard used by the provided token (ERC-20, ERC-721, and ERC-1155 are supported), and the amount of tokens required to pass the gate. In case of using the ERC-1155 token standard, an additional token type ID is expected to be provided.

RestrictedSignersFeedRule

This rule can be applied to make a Feed to have restricted access. This means the rule applies a restriction that makes the Feed to only accept operations (i.e. Creating a Post, Editing a Post, Changing Post Rules) that are signed by an address that belongs to a set of trusted signers.

The rule configuration requires to input an array of addresses and boolean values indicating for each address if it should be added or removed from the list of trusted signers. Rules can be reconfigured, meaning signers can be dynamically added or removed over time.

In addition, an optional string can be provided for each address in order to label each of the signers.

The rule, when processing each operation, will require an EIP-712 signature that matches the details (function selector and parameters) of the operation being executed, signed by one of the configured trusted signers.

SimplePaymentFeedRule

This rule can be applied to a Feed to require an ERC-20 payment in order to Create a Post on it.

The configuration requires the ERC-20 token address, the amount of tokens required for the payment, and the recipient address where the tokens will be sent.

GroupGatedFeedRule

This rule can be applied to a Feed to require accounts to be a member of certain Group in order to successfully Create a Post on the Feed.

The configuration requires to input the address of the Group that will be used to check if the account is a member of it.

Building a Feed Rule

Let's illustrate the process with an example. We will build the GroupGatedFeedRule described above, a rule that requires accounts to be a member of a certain Group in order to Create a Post on the Feed.

To build a custom Feed Rule, you must implement the following IFeedRule interface:

interface IFeedRule {    function configure(bytes calldata data) external;
    function processCreatePost(        uint256 postId,        CreatePostParams calldata postParams,        bytes calldata data    ) external returns (bool);
    function processEditPost(        uint256 postId,        EditPostParams calldata editPostParams,        bytes calldata data    ) external returns (bool);
    function processPostRulesChanged(        uint256 postId,        RuleConfiguration[] calldata newPostRules,        bytes calldata data    ) external returns (bool);}

Each function of this interface must assume to be invoked by the Feed 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 Feed in order to update the rule configuration (i.e. reconfigure it).

It receives bytes that will be decoded into the required rule configuration parameters.

In our example, we need to decode an address parameter, which will represent the Group contract where the account trying to post must belong to. Let's define a storage mapping to store this configuration:

contract GroupGatedFeedRule is IFeedRule {    mapping(address => address) internal _groupGate;}

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

contract GroupGatedFeedRule is IFeedRule {    mapping(address => address) internal _groupGate;
    function configure(bytes calldata data) external override {        _groupGate[msg.sender] = abi.decode(data, (address));    }}

The configuration is stored in the mapping using the Feed contract address as the key, which is the msg.sender. So the same rule can be reused by different Feeds.

2

Implement the Process Create Post function

Next, implement the processCreatePost function. This function is invoked by the Feed contract every time a Post is trying to be created, so then our custom logic can be applied to shape under which conditions this operation can succeed.

The function receives the ID the Feed assigned to the Post (postId), the parameters of the Post (postParams, including things like author and contentURI), 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 Create Post operation, we will return true.

Groups follow the IGroup interface, which has the following function to query the membership ID of any account, returning zero if the account is not a member of the Group:

function getMembershipId(address account) external view returns (uint256);

Now let's code the processCreatePost function taking all the described above into account:

contract GroupGatedFeedRule is IFeedRule {    mapping(address => address) internal _groupGate;
    // . . .
    function processCreatePost(        uint256 postId,        CreatePostParams calldata postParams,        bytes calldata data    ) external override returns (bool) {        require(IGroup(_groupGate[msg.sender]).getMembershipId(postParams.author) != 0);        return true;    }

3

Implement the Process Edit Post function

Next, implement the processEditPost function. This function is invoked by the Feed contract every time a Post is trying to be edited, so then our custom logic can be applied to shape under which conditions this operation can succeed.

The function receives the ID the Feed assigned to the Post (postId), the parameters of the Post to edit (editPostParams), 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 not being applied to the Edit Post operation, we must implement the function to comply with the interface but just return false.

contract GroupGatedFeedRule is IFeedRule {
    // . . .
    function processEditPost(        uint256 postId,        EditPostParams calldata editPostParams,        bytes calldata data    ) external override returns (bool) {        return false;    }

4

Implement the Process Post Rule Changes function

Finally, implement the processPostRulesChanged function. This function is invoked by the Feed contract every time an account makes a change on the Post Rules of a Post it authors, so then our rule can define if this change must be accepted or not.

The function receives the ID the Feed assigned to the Post which rules are being changed (postId), the configuration of the rules being changed (newPostRules), 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, we do not want to apply the restriction to the Post Rules Changes operation, so we must implement the function to comply with the interface but just return false.

contract GroupGatedFeedRule is IFeedRule {
    // . . .
    function processPostRulesChanged(        uint256 postId,        RuleConfiguration[] calldata newPostRules,        bytes calldata data    ) external override returns (bool) {        return false;    }}

Now the GroupGatedFeedRule is ready to be applied into any Feed. See the full code below:

contract GroupGatedFeedRule is IFeedRule {    mapping(address => address) internal _groupGate;
    function configure(bytes calldata data) external override {        _groupGate[msg.sender] = abi.decode(data, (address));    }
    function processCreatePost(        uint256 postId,        CreatePostParams calldata postParams,        bytes calldata data    ) external override returns (bool) {        require(IGroup(_groupGate[msg.sender]).getMembershipId(postParams.author) != 0);        return true;    }
    function processEditPost(        uint256 postId,        EditPostParams calldata editPostParams,        bytes calldata data    ) external override returns (bool) {        return false;    }
    function processPostRulesChanged(        uint256 postId,        RuleConfiguration[] calldata newPostRules,        bytes calldata data    ) external override returns (bool) {        return false;    }}

Stay tuned for API integration of rules and more guides!