Integration & Direct Contract Interactions

This section describes how best to integrate with the contracts directly, for dApp & smart contract direct interactions.

There are three typical ways you may want to integrate or interact with the smart contracts;

  1. dApp integrations using Web3.js | Ethers.js | Viem (Wagmi) or other SDK & Library

  2. Governance Contract interactions

  3. Smart Contract interactions & Modules

Creating Lockup Plans

For creating vesting plans, there are two ways to interact with the smart contract directly - either using the createPlan function itself, or by using the BatchPlanner contract, which will mint many plans in a single transaction.

dApp Integrations

Governance Contract (Governor Bravo) Interaction

We assume that you will be leveraging a Governor Bravo style contract, with the following function:

function propose(
  address[] memory targets, 
  uint[] memory values, 
  string[] memory signatures, 
  bytes[] memory calldatas, 
  string memory description) returns (uint)
  • targets: The ordered list of target addresses for calls to be made during proposal execution. This array must be the same length as all other array parameters in this function.

  • calldatas: The ordered list of data to be passed to each individual function call during proposal execution. This array must be the same length as all other array parameters in this function.

You can find more information here about the Governor Bravo Smart Contract. There is no need to send a value in the propose function, but we will address the targets and the calldatas elements.

target will be the BatchPlanner address: 0x3466EB008EDD8d5052446293D1a7D212cb65C646

calldatas To generate this, you can use web3 library (ethersjs or web3js), and create the array of plans and parameters, and ABI encode the plans, converting to bytes. Here's an example script you can run to convert multiple plans into encoded data.

const { ethers,  BigNumber } = require("ethers")

let planA = {
    recipient: '0x0CCFAb8d9DBE788808EeCCC3bf0295278E307777',
    amount: BigNumber.from(10).pow(18), // 10 tokens
    start: '1696161600', // Oct 1st
    cliff: '1698840000', // Nov 1st
    rate: '1000',

let planB = {
    recipient: '0xDD06fEee44433A0B9eEEEE0Ed777721F88885555',
    amount: BigNumber.from(10).pow(18).mul(2), // 10 tokens
    start: '1696161600', // Oct 1st
    cliff: '1698840000', // Nov 1st
    rate: '2000',

let locker = '0x73cD8626b3cD47B009E68380720CFE6679A3Ec3D' // voting vesting address
let token = '0xE13FB676E9bdd7AFda91495eC4e027FC63212FC3' //input token address here
let period = '1' // use 1 for streaming version

async function encodeData(vestingAddress, tokenAddress, plans, period, daoAddress) {
    let totalAmount = BigNumber.from(0);
    for (let i = 0; i < plans.length; i++) {
    const abiCoder = new ethers.utils.AbiCoder();
    const encodedData = abiCoder.encode(
        ['address', 'address', 'uint256', "tuple(address recipient, uint256 amount, uint256 start, uint256 cliff, uint256 rate)[]", 'uint256', 'uint8'],
        [vestingAddress, tokenAddress, totalAmount, plans, period, '5']
    const hexString = encodedData.slice(2);
    const bytes = Buffer.from(hexString, 'hex')
    return bytes;

encodeData(locker, token, [planA, planB], period, dao);

With this information, you can now create a proposal and input the other information required by the DAO in its governance system into the propose function.

Smart Contract Interaction via Contract Interface

pragma solidity 0.8.19;

interface IBatchPlanner {
  struct Plan {
    address recipient;
    uint256 amount;
    uint256 start;
    uint256 cliff;
    uint256 rate;

  function batchLockingPlans(
    address locker,
    address token,
    uint256 totalAmount,
    Plan[] calldata plans,
    uint256 period,
    uint8 mintType
  ) external;

Last updated