Push Protocol: Conditional Gating

Push Protocol: Conditional Gating

Here in we will discuss about conditional gating and its role in Push Protocol's chat feature.

What do you mean by conditional gating??

Conditional gating empowers you to establish specific criteria governing the privileges and permissions of group members, encompassing aspects like entry and chat functionalities. This functionality is steered by the Push Chat rules engine, drawing inspiration from the JSON rules engine paradigm. This innovative system affords you the capability to craft robust, dynamic regulations that intricately shape communication dynamics within your community.

Where can we use it??

  • Establishing a token-gated group, wherein a user is required to possess a specified quantity of XX tokens for group entry.

  • Formulating an NFT-gated group, insisting that a user holds XX NFTs to gain entry.

  • Defining multi-chain conditions, allowing users to join the group based on possessing a combination of 5 tokens on Ethereum or 10 tokens on Polygon.

  • Setting conditions for sending messages, requiring users to have 1 token to join the group and 100 tokens to send a message.

  • Enforcing non-web3 conditions using Guild, where joining the group mandates following @pushprotocol on Twitter.

  • Crafting intricate game theories by amalgamating one or multiple conditions, such as requiring 1 token on any chain for group entry, but demanding 1000 tokens, 1 NFT, or 20 POAPs for sending messages.

Let's understand rules object

The inclusion of the rules objects in the create group API call is discretionary, providing the ability to conditionally control access or message sending within the group chat.

Creating a group API

// userAlice.chat.group.create(name, {options?})
const createdGroup = await userAlice.chat.group.create(name);

A diagram to understand the overview

Let's deep dive into this

  • The rules object comprises distinct permissions delineating the privileges within the group.

  • Each permission object encapsulates conditions that must be met.

  • The conditions object is an array encompassing one or more conditions.

  • The decider, operating within a specified namespace (any or all), determines whether all conditions or any one of them must be satisfied.

  • Within a condition, there exists an array of criteria. The namespace (any or all) within the criteria determines whether all criteria or any one of them must be fulfilled.

  • A criterion represents the atomic condition with attributes like type, category, subcategory, and data, serving as the basis for determining the fulfillment or failure of a specific condition.

In summary, you create a list of criteria, which is subsequently specified within a decider to articulate their logical operations. This configuration is then incorporated into the conditions of a particular permission. The collection of individual permissions is then linked to the rules of the group, empowering the Push Chat rule engine to forge dynamic communities infused with imaginative game theories.

To understand rules-based parameters more clearly, please refer to Push documentation.

Concepts are better understood with a use case

Let's take an example of a Token gated community

Problem statement
We want to create a group whose content is not visible to anyone outside our community of $PUSH. Furthermore, we want to make sure that the group can be viewed by anyone who has at least 1 $PUSH on Ethereum or on Polygon.

Solution
Let's dissect the problem statement into distinct requirements:

Visibility: Establishing a group with restricted visibility to members only involves setting up the private flag in the create group API.

Group Join Permission: This entails two criteria:

  • Criterion 1: The user must possess 1 $PUSH on Ethereum.

  • Criterion 2: The user must possess 1 $PUSH on Polygon.

// Push token on Ethereum Criteria
{
  "type": "PUSH", // define type that rules engine should go for
  "category": "ERC20", // define it's ERC20 token that you want to check, supports ERC721 as well
  "subcategory": "holder", // define if you are checking 'holder' or 'owner'
  "data": { 
    "contract": "eip155:1:0xf418588522d5dd018b425E472991E52EBBeEEEEE", // $PUSH address on ETH
    "comparison": ">=", // what comparison needs to pass
    "amount": 1, // amount that needs to passed
    "decimals": 18, // the decimals for the token
  }
}
// Push token on Polygon Criteria
{
  "type": "PUSH", // define type that rules engine should go for
  "category": "ERC20", // define it's ERC20 token that you want to check, supports ERC721 as well
  "subcategory": "holder", // define if you are checking 'holder' or 'owner'
  'data': { 
    "contract": "eip155:137:0x58001cC1A9E17A20935079aB40B1B8f4Fc19EFd1", // $PUSH address on ETH
    "comparison": ">=", // what comparison needs to pass
    "amount": 1, // amount that needs to passed
    "decimals": 18, // the decimals for the token
  }
}

Either of the criteria should be able to allow users to join the group, this means that the condition's namespace to use to combine these criteria would be any.

// decider object - 'any' since either condition should allow access
"any": [
  {
    "type": "PUSH", // define type that rules engine should go for
    "category": "ERC20", // define it's ERC20 token that you want to check, supports ERC721 as well
    "subcategory": "holder", // define if you are checking 'holder' or 'owner'
    "data": { 
      "contract": "eip155:137:0x58001cC1A9E17A20935079aB40B1B8f4Fc19EFd1", // $PUSH address on ETH
      "comparison": ">=", // what comparison needs to pass
      "amount": 1, // amount that needs to passed
      "decimals": 18, // the decimals for the token
    }
  },
  {
    "type": "PUSH", // define type that rules engine should go for
    "category": "ERC20", // define it's ERC20 token that you want to check, supports ERC721 as well
    "subcategory": "holder", // define if you are checking 'holder' or 'owner'
    "data": { 
      "contract": "eip155:137:0x58001cC1A9E17A20935079aB40B1B8f4Fc19EFd1", // $PUSH address on ETH
      "comparison": ">=", // what comparison needs to pass
      "amount": 1, // amount that needs to passed
      "decimals": 18, // the decimals for the token
    }
  }
]

Conditions only have one condition in entry so the namespace for conditions doesn't matter but to keep things clear, we will mark it as all.

  "rules": {
    "entry": { // permission object
      "conditions": { // conditions object
        "any": [ // conditions namespace decider - Either group owner / admin invites the user or the user has $PUSH on Ethereum or Polygon
          { // decider 1 - If admin or owner invites someone
            "any": [ 
              { // criteria 1
                "type": "PUSH",
                "category": "INVITE",
                "subcategory": "DEFAULT",
                "data": {
                    "inviterRoles": [
                        "ADMIN",
                        "OWNER"
                    ]
                }
              }
            ]
          },
          {
            "any": [ // decider 2 - If user has $PUSH on Ethereum or on Polygon
              { // criteria object
                "type": "PUSH", // define type that rules engine should go for
                "category": "ERC20", // define it's ERC20 token that you want to check, supports ERC721 as well
                "subcategory": "holder", // define if you are checking 'holder' or 'owner'
                "data": { 
                  "contract": "eip155:137:0x58001cC1A9E17A20935079aB40B1B8f4Fc19EFd1", // $PUSH address on ETH
                  "comparison": ">=", // what comparison needs to pass
                  "amount": 1, // amount that needs to passed
                  "decimals": 18, // the decimals for the token
                }
              },
              { // criteria object
                "type": "PUSH", // define type that rules engine should go for
                "category": "ERC20", // define it's ERC20 token that you want to check, supports ERC721 as well
                "subcategory": "holder", // define if you are checking 'holder' or 'owner'
                "data": { 
                  "contract": "eip155:137:0x58001cC1A9E17A20935079aB40B1B8f4Fc19EFd1", // $PUSH address on ETH
                  "comparison": ">=", // what comparison needs to pass
                  "amount": 1, // amount that needs to passed
                  "decimals": 18, // the decimals for the token
                }
              }
            ]
          }
        ]
      }
    }
    // since we are not defining chat permissions, it means that any user who is part of the group can chat
  }

Finally, pass this rules object into the group that we are creating using the create group API call.

  // Creating your token gated community
  const createTokenGatedGroup = await userAlice.chat.group.create('Push Community', {
    description: 'Token gated web3 native chat example', // provide short description of group
    image: 'data:image/png;base64,iVBORw0K...', // provide base64 encoded image
    members: [], // not needed, rules define this, can omit
    admins: [], // not needed as per problem statement, can omit
    private: true,
    "rules": {
      "entry": { // permission object
        "conditions": { // conditions object
          "any": [ // conditions namespace decider - Either group owner / admin invites the user or the user has $PUSH on Ethereum or Polygon
            { // decider 1 - If admin or owner invites someone
              "any": [ 
                { // criteria 1
                  "type": "PUSH",
                  "category": "INVITE",
                  "subcategory": "DEFAULT",
                  "data": {
                      "inviterRoles": [
                          "ADMIN",
                          "OWNER"
                      ]
                  }
                }
              ]
            },
            {
              "any": [ // decider 2 - If user has $PUSH on Ethereum or on Polygon
                { // criteria object
                  "type": "PUSH", // define type that rules engine should go for
                  "category": "ERC20", // define it's ERC20 token that you want to check, supports ERC721 as well
                  "subcategory": "holder", // define if you are checking 'holder' or 'owner'
                  "data": { 
                    "contract": "eip155:137:0x58001cC1A9E17A20935079aB40B1B8f4Fc19EFd1", // $PUSH address on ETH
                    "comparison": ">=", // what comparison needs to pass
                    "amount": 1, // amount that needs to passed
                    "decimals": 18, // the decimals for the token
                  }
                },
                { // criteria object
                  "type": "PUSH", // define type that rules engine should go for
                  "category": "ERC20", // define it's ERC20 token that you want to check, supports ERC721 as well
                  "subcategory": "holder", // define if you are checking 'holder' or 'owner'
                  "data": { 
                    "contract": "eip155:137:0x58001cC1A9E17A20935079aB40B1B8f4Fc19EFd1", // $PUSH address on ETH
                    "comparison": ">=", // what comparison needs to pass
                    "amount": 1, // amount that needs to passed
                    "decimals": 18, // the decimals for the token
                  }
                }
              ]
            }
          ]
        }
      }
      // since we are not defining chat permissions, it means that any user who is part of the group can chat
    }
  });

So this is it folks from my side. Wanna know more or have some doubts?? Just refer to the Push Protocol's official documentation.

Still not clear?? Then join Push's discord server or raise an issue on Push's GitHub.