Contract Action Templates

1. Description

Contract Action Templates enable admins of communities within Common to execute contract actions in a low code environment. Contract actions include changes or updates to the underlying smart contract such as proposals, token transfers, delegations etc. To use Contract Action Templates you will need to be an admin of a community on Common

The smart contracts currently supported for Contract Action Templates are:

  • Governor (Alpha/Bravo/OpenZeppelin)

  • ERC721/ERC1155

  • ERC20

  • AAVE

  • MolochDAO

How It Works

  • Navigate to the community you are an admin of using search or the sidebar. Within the community page, navigate to Contracts under Admin Capabilities

  • Click on Add Contract in the right top corner. This will prompt you to add the Contract Address for your smart contract. The Contract ABI file will be visible once you add a Contract Address that is valid and supported (refer list above for supported contract types)

  • Once you add a contract it will be available for community admins to connect to specific Contract Action Templates

  • Click on Enable action to bring up the Contract Action Form. You can either select from existing Contract Action Templates that are available or create a new Contract Action Template.

  • To create a new Contract Action Template, click on create new template. Add the name of the Contract Action this would execute, and add the relevant JSON blob. Refer to developer documentation on how to do this (link). Here is a list of contract action templates that you can create (link)

  • Select the Contract Action Template from the dropdown and fill out the details specific to the contract action that you want to execute

  • Select where you’d like this Contract Action Form available for the community to use

    • Create Sidebar (image)

    • Create Dropdown (image)

Types of templates

A non-exhaustive list of the types of templates that you can create using different contract types

Governor

  • Treasury Spend Proposal: A template that makes it easy to create a treasury spend proposal. Takes in as inputs a recipient, request amount, and other relevant metadata.

  • Vote Casting: Vote cast template takes in as inputs proposal id and decision (yes / no / abstain), enabling communities to vote on a proposal

  • Parameter Change: A template that makes it easy to update a parameter on the protocol owned by the Governor contract

ERC721/ERC1155

  • SendTo: Send and receive tokens

  • DelegateTo: This template allows you to delegate tokens to an address. This can be used if you want to delegate your votes to another member in the community

ERC20

  • DelegateTo: This template allows you to delegate tokens to an address. This can be used if you want to delegate your votes to another member in the community

  • SendTo: Send and receive tokens

AAVE

  • Treasury Spend Proposal: A template that makes it easy to create a treasury spend proposal. Takes in as inputs a recipient, request amount, and other relevant metadata.

  • Vote Casting: Vote cast template takes in as inputs proposal id and decision (yes / no / abstain), enabling communities to vote on a proposal

  • Parameter Change: A template that makes it easy to update a parameter on the protocol owned by the Governor contract

MolochDAO

  • Submit Proposal

  • Submit Vote

  • Proposal Processing

  • Rage Quit

  • Update Delegates

2. Using Contract Action Templates

Contract Action Template Schema

  • Schema v1.0

    const schema = {
      type: 'object',
      properties: {
        form_fields: {
          type: 'array',
          items: {
            oneOf: [
              { $ref: '#/$defs/divider' },
              { $ref: '#/$defs/text' },
              { $ref: '#/$defs/input' },
              { $ref: '#/$defs/dropdown' },
              { $ref: '#/$defs/function' },
            ],
          },
        },
        tx_template: {
          type: 'object',
          properties: {
            method: { type: 'string' },
            args: { type: 'object' },
          },
          required: ['method'],
          additionalProperties: false,
        },
      },
      required: ['form_fields', 'tx_template'],
      additionalProperties: false,
    
      /* component definitions */
      $defs: {
        /* shared formatter property, used in input and dropdown*/
        formatter: {
          type: 'string',
          enum: ['number', 'string', 'address', 'token'],
        },
        divider: {
          type: 'object',
          properties: {
            divider: {
              type: 'object',
              properties: {
                field_name: { type: 'string' },
              },
              required: ['field_name'],
              additionalProperties: false,
            },
          },
          required: ['divider'],
          additionalProperties: false,
        },
        text: {
          type: 'object',
          properties: {
            text: {
              type: 'object',
              properties: {
                field_name: { type: 'string' },
                field_value: { type: 'string' },
                field_type: {
                  type: 'string',
                  enum: ['h1', 'h2', 'h3', 'text'],
                },
              },
              required: ['field_name', 'field_value'],
              additionalProperties: false,
            },
          },
          required: ['text'],
          additionalProperties: false,
        },
        input: {
          type: 'object',
          properties: {
            input: {
              type: 'object',
              properties: {
                field_name: { type: 'string' },
                field_label: { type: 'string' },
                field_value: { type: 'string' },
                field_ref: { type: 'string' },
                formatter: { $ref: '#/$defs/formatter' },
              },
              required: ['field_name', 'field_label', 'field_ref'],
              additionalProperties: false,
            },
          },
          required: ['input'],
          additionalProperties: false,
        },
        method: {
          type: 'object',
          properties: {
            functionABI: {
              type: 'object',
              properties: {
                name: { type: 'string' },
                type: { type: 'string' },
                inputs: {
                  type: 'array',
                  items: {
                    type: 'object',
                    properties: {
                      name: { type: 'string' },
                      type: { type: 'string' },
                      required: ['name', 'type'],
                      additionalProperties: false,
                    },
                  },
                },
                required: ['name', 'type', 'inputs'],
                additionalProperties: false,
              },
            },
            paramRefs: { type: 'array', items: { type: 'string' } },
            form: {
              type: 'array',
              items: {
                oneOf: [
                  { $ref: '#/$defs/divider' },
                  { $ref: '#/$defs/text' },
                  { $ref: '#/$defs/input' },
                  { $ref: '#/$defs/dropdown' },
                ],
              },
            },
            required: ['functionABI', 'paramRefs', 'form'],
            additionalProperties: false,
          },
        },
        function: {
          type: 'object',
          properties: {
            function: {
              type: 'object',
              properties: {
                field_name: { type: 'string' },
                field_label: { type: 'string' },
                field_ref: { type: 'string' },
                tx_forms: { type: 'array', items: { $ref: '#/$defs/method' } },
              },
              required: ['field_name', 'field_label', 'field_ref', 'tx_forms'],
              additionalProperties: false,
            },
          },
          required: ['function'],
          additionalProperties: false,
        },
        dropdown: {
          type: 'object',
          properties: {
            dropdown: {
              type: 'object',
              properties: {
                field_name: { type: 'string' },
                field_label: { type: 'string' },
                field_value: { type: 'string' },
                field_ref: { type: 'string' },
                formatter: { $ref: '#/$defs/formatter' },
                field_options: {
                  type: 'array',
                  items: {
                    type: 'object',
                    properties: {
                      label: {
                        type: 'string',
                      },
                      value: {
                        type: 'string',
                      },
                    },
                  },
                },
              },
              required: ['field_name', 'field_label', 'field_ref', 'field_options'],
              additionalProperties: false,
            },
          },
          required: ['dropdown'],
          additionalProperties: false,
        },
      },
    };

Creating Contract Action Templates

  • What is “creating” a template?

    • Creating a new Contract Action Template (CAT) is the process of identifying a contract type (i.e. ERC20), building a JSON blob meeting the template schema (see above), and uploading it to Common via the Create Template UI.

    • After a Contract Action Template is created, it is available to be linked to ANY contract with the same ABI. For instance, if you create a CAT for an ERC20 contract, any ERC20 contract linked to a specific Common community will be able to use an instance of the template to build a Contract Action Form (CAF).

    • Creating a CAT does not mean that your community will have immediate access to the associated Contract Action Form. See the section below for details on linking the CAT to your community and generating usable forms.

  • Structure of Contract Action Templates

    • Contract Action Templates are JSON blobs that consist of two main fields: form_fields and tx_template

    • The form_fields is used to define the appearance and behavior of the Contract Action Form (the UI that will be rendered to users of your template). All subfields should be defined per our UI Registry (see below). The interactive fields you create will be assigned a “field-ref” so their values can be used to format a transaction.

    • The tx_template is used to define how these fields are formatted into a valid contract action. Here, the “field-refs” assigned in the prior section are used as inputs into a contract method call.

      • Within the tx_template field there are 3 main fields

        • method: The name of the method the template should call

        • args: An object with the methods arguments keys and “field-ref” values

        • tx_params: An object with transaction parameters(ie value, gas, etc)

      • Example

        "tx_template":{
              "method":"mint",
              "args":{
                 "Id":"$id-ref",
              },
              "tx_params":{
                 "value":"$value-ref"
              }
           }
  • UI Registry

    • Our v1.0 Contract Action Templates support a set of Common UI components that will be used to build your Contract Action Form. These elements are subfields of the form_field in your template. They include:

      • Text Fields

        • Description: A simple text field to provide written context in the form.

        • Shape:

          {
           "text": {
             "field_name": "<your-field-name>",
             "field_type": "<font-size>",
             "field_value": "<your-field-content>"
           }
          },
      • Text Inputs

        • Description: An input component to accept dynamic input from the user. Includes a “formatter” field to define a validation function on the input.

        • Shape:

          {
           "input": {
             "field_name": "<your-field-name>",
             "field_label": "<label-to-display>",
             "field_value": "<field-placeholder-value>",
             "field_ref": "<ref-name-for-parsing-input>",
             "formatter": "<formatter-for-validation>"
           }
          }
      • Dividers

        • Description: A simple divider component to break up the form visually.

        • Shape:

          {
           "divider":{
              "field_name":"divider"
           }
          },
      • Dropdowns

        • Description: A multi select dropdown to allow users to choose on option from a predefined set.

        • Shape:

          {
           "dropdown": {
             "field_name": "<your-field-name>",
             "field_label": "<label-to-display>",
             "field_ref": "<ref-name-for-parsing-input>",
          	 "field_options":["<list-of-options-for-dropdown>"]
           }
          }
      • Functions

        • Description: Functions are a special case for situations where function data needs to be encoded as a parameter for a parent functions(ie governance calldata)

        • Shape:

          {
            "function":{
               "field_name":"<your-field-name>",
               "field_label":"<label-to-display>",
               "field_ref": "<ref-name-for-parsing-input-to-parent-arg>",
               "tx_forms":[
                  {
                     "functionABI":"<individual-function-abi>",
                     "paramRefs":["<ref-name-for-each-nested-param>"],
                     "form":["<form-including-any-form_field-type>"]
                  }
               ]
            }
          }
    • Going forward we plan to support more UI components, but these are sufficient for most use cases!

  • Validating Contract Action Templates

    • When you attempt to create a new template, Common will validate your template before allowing you to upload. Templates that do not meet your schema definition will be flagged as invalid in the UI, and will not be successfully uploaded to the platform.

    • Be aware that your template may be valid according to our schema but invalid as a way to construct the specific contract method call you intend. This can result in a Contract Action Form that is not functional. Be sure to double check the contract method in question to ensure your inputs match the expected parameters.

  • Constraints / Limitations

    • Only one method call can be called per template

    • Methods added to function type fields are not validated against the target contract ABI.

      • Confirm encoded calldata before sending and run local test transactions

    • Payable methods can not currently be templated

  • Examples of Templates

    • ERC20, Send Template

      {
         "form_fields":[
            {
               "text": {
                 "field_name": "title",
                 "field_type": "h3",
                 "field_value": "Send Tokens"
               }
             },
      			 {
               "text": {
                 "field_name": "description",
                 "field_type": "h4",
                 "field_value": "This is an example template that facilitates the transfer of tokens between two addresses."
               }
             },
             {
               "input": {
                 "field_name": "Destination",
                 "field_label": "Address to send to",
                 "field_value": "0x123...",
                 "field_ref": "dst-ref",
                 "formatter": "address"
               }
             },
             {
               "input":{
                  "field_name": "Amount",
                  "field_label": "How many(in wei) tokens to send?",
                  "field_ref": "amount-ref",
                  "formatter": "number"
               }
             }
      
         ],
         "tx_template":{
            "method": "transfer",
            "args": {
               "dst": "$dst-ref",
               "rawAmount": "$amount-ref"
            }
         }
      }
    • ERC20Delegate, Delegation Template

      {
         "form_fields":[
            {
               "text": {
                 "field_name": "title",
                 "field_type": "h1",
                 "field_value": "Delegate Tokens"
               }
             },
      			 {
               "text": {
                 "field_name": "description",
                 "field_type": "h3",
                 "field_value": "This is an example template that allows users to delegate their voting power to another address"
               }
             },
             {
               "input": {
                 "field_name": "Delegatee",
                 "field_label": "Address to Delegate to",
                 "field_value": "0x123...",
                 "field_ref": "del-ref",
                 "formatter": "address"
               }
             }
         ],
         "tx_template":{
            "method": "delegate",
            "args": {
               "delegatee": "$del-ref"
            }
         }
      }
    • Delegate Cash, “Delegate To” Template

      {
         "form_fields":[
            {
               "text": {
                 "field_name": "title",
                 "field_type": "h1",
                 "field_value": "Delegate Hot Wallet via Delegate Cash"
               }
             },
      			 {
               "text": {
                 "field_name": "description",
                 "field_type": "h3",
                 "field_value": "This is an example template that allows users to use the delegate.cash protocol to delegate token usage to another wallet"
               }
             },
             {
               "input": {
                 "field_name": "Hot Wallet",
                 "field_label": "Address to approve for use of your ERC20 token",
                 "field_value": "0x123...",
                 "field_ref": "delegate-ref",
                 "formatter": "address"
               }
             },
             {
               "input": {
                 "field_name": "Token",
                 "field_label": "Token to Delegate",
                 "field_value": "0x123...",
                 "field_ref": "token-ref",
                 "formatter": "address"
               }
             }
         ],
         "tx_template":{
            "method": "delegateForToken",
            "args": {
               "delegate": "$delegate-ref",
               "contract_": "$token-ref", // Hard code the associated ERC20 address here
               "tokensId": "0", // Not needed unless NFT
               "value": true 
            }
         }
      }
    • Governor, Treasury Spend Template

      {
         "form_fields":[
            {
               "text":{
                  "field_name":"title",
                  "field_type":"h1",
                  "field_value":"New Treasury Spend Proposal"
               }
            },
      			{
               "text": {
                 "field_name": "description",
                 "field_type": "h3",
                 "field_value": "This is an example template that creates a governance proposal to transfer ERC20 tokens from a DAO treasury"
               }
             },
            {
               "input":{
                  "field_name":"name",
                  "field_label":"Enter proposal title",
                  "field_value":"example title",
                  "field_ref":"name-ref",
                  "formatter":"string"
               }
            },
            {
               "divider":{
                  "field_name":"divider"
               }
            },
            {
               "input":{
                  "field_name":"ERC20 Address",
                  "field_label":"Address of token to send",
                  "field_value":"['0x123...']",
                  "field_ref":"target-ref",
                  "formatter":"string"
               }
            },
            {
               "function":{
                  "field_name":"Calldata",
                  "field_label":"The transfer transactions to trigger",
                  "field_ref":"calldata-ref",
                  "tx_forms":[
                     {
                        "functionABI":{
                           "name":"transferFrom",
                           "type":"function",
                           "inputs":[
                              {
                                 "name":"src",
                                 "type":"address"
                              },
                              {
                                 "name":"dst",
                                 "type":"address"
                              },
                              {
                                 "name":"amount",
                                 "type":"uint256"
                              }
                           ]
                        },
                        "paramRefs":[
                           "$src-ref",
                           "$dst-ref",
                           "$amount-ref"
                        ],
                        "form":[
                           {
                              "input":{
                                 "field_name":"Source",
                                 "field_label":"The address to transfer tokens from(the treasury)",
                                 "field_ref":"$src-ref",
                                 "formatter":"address"
                              }
                           },
                           {
                              "input":{
                                 "field_name":"Destination",
                                 "field_label":"The address to transfer tokens to",
                                 "field_ref":"$dst-ref",
                                 "formatter":"address"
                              }
                           },
                           {
                              "input":{
                                 "field_name":"Amount",
                                 "field_label":"The amount of tokens(in wei) to send",
                                 "field_ref":"$amount-ref",
                                 "formatter":"number"
                              }
                           }
                        ]
                     }
                  ]
               }
            }
         ],
         "tx_template":{
            "method":"propose",
            "args":{
               "targets":"$target-ref",
               "values":[
                  "0"
               ],
               "signatures":[
                  ""
               ],
               "calldatas":"$calldata-ref",
               "description":"$name-ref"
            }
         }
      }
    • Governor, Parameter Change Template

      {
         "form_fields":[
            {
               "text":{
                  "field_name":"title",
                  "field_type":"h1",
                  "field_value":"New fee change proposal"
               }
            },
      			 {
               "text": {
                 "field_name": "description",
                 "field_type": "h3",
                 "field_value": "This is an example template that allows an arbitrary governance proposal to change a fee parameter in some contract"
               }
             },
            {
               "input":{
                  "field_name":"name",
                  "field_label":"Enter proposal title",
                  "field_value":"example title",
                  "field_ref":"name-ref",
                  "formatter":"string"
               }
            },
            {
               "divider":{
                  "field_name":"divider"
               }
            },
            {
               "function":{
                  "field_name":"Calldata",
                  "field_label":"The Fee transaction to send",
                  "field_ref":"calldata-ref",
                  "tx_forms":[
                     {
                        "functionABI":{
                           "name":"setFee",
                           "type":"function",
                           "inputs":[
                              {
                                 "name":"amount",
                                 "type":"uint256"
                              }
                           ]
                        },
                        "paramRefs":[
                           "$amount-ref"
                        ],
                        "form":[
                           {
                              "input":{
                                 "field_name":"Amount",
                                 "field_label":"The new fee amount to set",
                                 "field_ref":"$amount-ref",
                                 "formatter":"number"
                              }
                           }
                        ]
                     }
                  ]
               }
            }
         ],
         "tx_template":{
            "method":"propose",
            "args":{
               "targets":[
                  "0x123"
               ],
               "values":[
                  "0"
               ],
               "signatures":[
                  ""
               ],
               "calldatas":"$calldata-ref",
               "description":"$name-ref"
            }
         }
      }
    • Governor, Vote Casting Template

      {
         "form_fields":[
            {
               "input":{
                  "field_name":"Proposal number",
                  "field_label":"Proposal Number",
                  "field_value":"1234",
                  "field_ref":"prop-ref",
                  "formatter":"number"
               }
            },
            {
               "input":{
                  "field_name":"Vote (0=no, 1=yes, 2=abstain)",
                  "field_label":"Support Option",
                  "field_value":"0 or 1 or 2",
                  "field_ref":"vote-ref",
                  "formatter":"number"
               }
            }
         ],
         "tx_template":{
            "method":"castVote",
            "args":{
               "proposalId":"$prop-ref",
               "support":"$vote-ref"
            }
         }
      }

    • Aave, Treasury Spend

      {
         "form_fields":[
            {
               "text":{
                  "field_name":"title",
                  "field_type":"h1",
                  "field_value":"New AAVE Treasury Spend Proposal"
               }
            },
      			{
               "text": {
                 "field_name": "description",
                 "field_type": "h3",
                 "field_value": "This is an example template that creates a governance proposal to transfer ERC20 tokens from a DAO treasury"
               }
             },
            {
               "divider":{
                  "field_name":"divider"
               }
            },
            {
               "input":{
                  "field_name":"ERC20 Address",
                  "field_label":"Address of token to send",
                  "field_value":"['0x123...']",
                  "field_ref":"target-ref",
                  "formatter":"string"
               }
            },
            {
               "function":{
                  "field_name":"Calldata",
                  "field_label":"The transfer transactions to trigger",
                  "field_ref":"calldata-ref",
                  "tx_forms":[
                     {
                        "functionABI":{
                           "name":"transferFrom",
                           "type":"function",
                           "inputs":[
                              {
                                 "name":"src",
                                 "type":"address"
                              },
                              {
                                 "name":"dst",
                                 "type":"address"
                              },
                              {
                                 "name":"amount",
                                 "type":"uint256"
                              }
                           ]
                        },
                        "paramRefs":[
                           "$src-ref",
                           "$dst-ref",
                           "$amount-ref"
                        ],
                        "form":[
                           {
                              "input":{
                                 "field_name":"Source",
                                 "field_label":"The address to transfer tokens from",
                                 "field_ref":"$src-ref",
                                 "formatter":"address"
                              }
                           },
                           {
                              "input":{
                                 "field_name":"Destination",
                                 "field_label":"The address to transfer tokens to",
                                 "field_ref":"$dst-ref",
                                 "formatter":"address"
                              }
                           },
                           {
                              "input":{
                                 "field_name":"Amount",
                                 "field_label":"The amount of tokens(in wei) to send",
                                 "field_ref":"$amount-ref",
                                 "formatter":"number"
                              }
                           }
                        ]
                     }
                  ]
               }
            },
            {
               "input":{
                  "field_name":"IPFS Hash",
                  "field_label":"Enter IPFS Hash to Proposal",
                  "field_value":"0x1234",
                  "field_ref":"ipfs-ref",
                  "formatter":"string"
               }
            },
         ],
         "tx_template":{
            "method":"create",
            "args":{
      				 "executor": "0xEE56e2B3D491590B5b31738cC34d5232F378a8D5"
               "targets":"$target-ref",
               "values":[
                  "0"
               ],
               "signatures":[
                  ""
               ],
               "calldatas":"$calldata-ref",
               "withDelegateCall":"false",
      				 "ipfsHash":"$ipfs-ref"
            }
         }
      }

3. Connecting a CAT to your Community

  • Once a CAT is created (by you or any other Common user) it can be connected to your Common community and used to generate a Contract Action Form. This section will assume a CAT has already been created. As an example, we will use the ERC20 Send Template to walk through this process.

  • Steps to connect:

    • Adding a contract to your community

      • Identify an ERC20 contract relevant to your community. Visit the /contracts page in your community, and click “add contract”

      • Input the contract address. The ABI should auto populate- if you need to use a different ABI just paste it into the field. Click Add

  • Connecting a CAT

    • On the /contracts page, you should now see your contract

    • Assuming a CAT has been created for the ABI in question, you will see the “enable action” button appear. Click that button to open the contract action form:

    • Here, you select several fields:

      • Template (the CAT you want to connect)

      • Display Name (the name you want to appear in the link your Contract Action Form)

      • Action details

      • Slug (the url where your form will be served, i.e. mycommunity/<contract-address>/my-link-name)

      • Display Option (where the button to view the CAF will be displayed)

    • Add the connection and you should see the CAT displayed below your contract in the /contracts page.

4. Using a Contract Action Form

  • We will continue with our example of an ERC20 Transfer template from the previous section.

  • Click “New My Token Transfer” in the dropdown.

  • You will see the Contract Action Form as defined by the template! Simply add content to the fields as needed.

  • After clicking create, you will be asked to confirm the transaction. Upon doing so, the transaction will be formatted and your wallet will ask you to confirm.

  • If the transaction was successful, a confirmation toast will alert you at the bottom of the screen. You can also check your wallet for transaction confirmation, as usual.

Last updated