How Routines Contracts and Operations Work

The Routines protocol is built using the Covenants AMM Aggregator, so that it supports multiple AMMs and Uniswap v3.

This protocol is Factory based, which means that anyone can deploy its own customized Inflation contract interacting with the Covenants Inflation Factory.

Learn what a factory is here.

The Inflation contract can be hosted by any Organization, DAO wallet and smart contract using Extension. The Extension is a contract, linked to the main contract, that allows interacting with the Inflation contract itself. In this way, all the specific integration code can be written in the Extension and not in the general purpose contract.

How Routines contract works

Each Covenants Routines contract is composed of one or more executable operations. Each operation can only circulate one token; to manage the inflation of two different tokens, two different operations are required.

An operation can involve any combination of the following:

  • The minting of the input token.

  • The transferral of the input token from one address to one or more others.

  • The swapping of the input token for one or more other tokens in one AMM.

  • The burning of the input token or the burning of the output token after a swap.

If an operation involves a swap, the input token is the token being swapped, and the output token is the token the input token is being swapped for. So, for example, if the operation will swap USDC for OS, USDC is the input token and OS is the output token. If the operation involves a transfer, we have only one token, so the input-output dichotomy does not apply.

Each contract, which can involve multiple and different operations, has one Entry.

The Entry is represented by the following struct in the contract:

struct FixedInflationEntry {
    string name;
    uint256 blockInterval;
    uint256 lastBlock;
    uint256 callerRewardPercentage;
  • name -> represents the name of the contract.

  • blockInterval -> the interval of time (expressed in blocks) that must pass between each execution of an operation.

  • lastBlock -> It can be populated to program a delayed operation start. In this way the first Operation cannot be executed until the lastBlock+blockInterval is reached. If it is set as 0 the first Operation can be performed at any time. In both cases, all subsequent operations can be executed every blockInterval.

  • callerRewardPercentage -> the reward the executor will receive. Allows the creation of an incentive for executing the operation with a public call, not only the call by the internal hosting of the contract. The executor can freely choose to be rewarded with either input or output tokens; for example, if the operation swaps OS (input token) for ETH (output token), he can choose to be rewarded in either.


Every Routines Operation has the following parameters:

struct FixedInflationOperation {

    address inputTokenAddress;
    uint256 inputTokenAmount;
    bool inputTokenAmountIsPercentage;
    bool inputTokenAmountIsByMint;

    address ammPlugin;
    address[] liquidityPoolAddresses;
    address[] swapPath;
    bool enterInETH;
    bool exitInETH;

    address[] receivers;
    uint256[] receiversPercentages;
  • inputTokenAddress -> address of the operations' input token.

  • inputTokenAmount -> amount of the input token.

  • inputTokenAmountIsPercentage -> boolean value that expresses whether the amount of the input token (previous field) is expressed as a percentage value (true) or not (false). If the InputTokenAmountIsPercentage value is true, the percentage is expressed in reference to the total supply of the input token. If the value is false , only the amount is expressed.

  • ammPlugin -> address of the AMM chosen to be used for possible token swap operations. If the ammPlugin address is passed as the Uniswap v3 swap router address, Uniswap v3 is used to perform the Operation. This field is equal to address(0) if the operation is a transfer.

  • liquidityPoolAddresses -> array containing the liquidity pool token addresses of a swap operation (so this field is only populated if a swap operation is involved). The first element of the array must necessarily contain the input token.

  • swapPath -> This array contains the path that the swap operation must follow (so this field is only populated if a swap operation is involved). So for example, if you have an operation that swaps OS to ETH and then to USDC, you'll have the liquidityPoolAddresses containing OS/ETH LP address in the fist position and ETH/USDC LP address in the second place. The swapPath array will contain ETH in the first position and USDC in the second one.

  • enterInEth -> expresses whether the input token is ETH (true) or not (false).

  • exitInEth -> expresses whether the output token is ETH (true) or not (false).

  • receivers -> array that contains the addresses of the various receivers in case of a transfer operation. If it is passed as address(0), a burn operation is performed.

  • receiversPercentages -> array that contains the percentages of the various receivers in case of a transfer operation. The length of this array must be equal to receivers array length -1, the last percentage is in fact calculated automatically.

The logic to mint the tokens to be used in a swap or transfer must be written in the Extension logic. Look at here for an example.

Last updated