How to Multi-Distribute ERC-20 tokens via Gnosis SDK

As part of the Atem.green project I’m helping to build, we want to incentivize early contributors by handing out (pre-launch) ERC-20 tokens. We use Coordinape to determine the impact every contributor has in a given month. From that we can calculate how many (pre-launch) token each contributor earned. Now the question is:

How do we distribute these tokens?

As we don’t want a single person to have control over the token supply we spun up a Gnosis safe as owner of these token*. The safe is controlled by trusted project members where every transaction needs to be approved by multiple members.

However, distributing token using the Gnosis UI is somewhat limited:

  • Sending tokens requires individual transactions each costing gas and requiring separate approval
  • The Multisend transaction builder allows for a batch transactions but needs to be manually composed whenever we want to distribute tokens

On the positive side, the Gnosis UI offers human-readable multisend transactions. That’s important in order to make sure that all multisig owners can verify the transaction data is correct before approving it.

Automating the multisend transaction creation

With that I knew it would be easier to do this from the command line and write my own script that creates the gnosis transaction. This would allow for further automation by automatically reading the coordinape results and calculating the distribution amounts.

Luckily the gnosis team offers a Javascript Safe-Apps-SDK that can be used to create transactions from the command line. With additional help through their Discord I was able to write a script that fully automates the multisend transaction creation.

Let my guide you through the different steps necessary. I’m using Hardhat and Ethers for this script.

Create list of receivers

The first step is to create an array of receivers. We need their addresses and the amount of tokens they should receive. I’ll not share here how I calculate this as it’s probably specific for every project. What I’m basically doing in calculateTokenShare is to parse the CSV from coordinape and map the GIVE distribution to our monthly token allocation (find their docs here).

interface Receiver {
  address: string
  amount: BigNumber
}

const receivers: Receiver[] = calculateTokenShare(epoch, sharesPerMonth)

Initializing the Gnosis SDKs

Next we need to connect to our already existing Gnosis safe using their SDK. In order for the transaction to show up in the Gnosis UI we need the safe-service-client. And for creating the multisend transaction we have to use the safe-core-sdk.

const safeAddress = "0x..." // the address of your gnosis safe

const [owner] = await ethers.getSigners()
const ethAdapter = new EthersAdapter({
  ethers,
  signer: owner
})

const safeService = new SafeServiceClient("https://safe-transaction.rinkeby.gnosis.io/")
const safeSdk = await Safe.create({ ethAdapter, safeAddress })

For this example I connect to the rinkeby testnet, that’s why I pass https://safe-transaction.rinkeby.gnosis.io/ as service URL. This needs to be adjusted to the correct URL, depending on the network you are using. You can use the above URL and replace rinkeby with your target network name (e.g. xdai). Use https://safe-transaction.gnosis.io for Ethereum mainnet.

Creating the transaction

When we distribute our token, we call the transfer function on our ERC-20 token contract. As we want to batch multiple of these transfers together we need to generate unsigned transactions for each of them. With Ethers we can use populateTransaction for this. To do so we first have to connect Ethers to our already deployed smart contract. Replace MyERC20Token with the name of your token contract and tokenAddress with your contracts deployment address.

const TokenFactory = await ethers.getContractFactory("MyERC20Token")
const token = TokenFactory.attach(tokenAddress)

Now we can iterate over our receivers array and create a list of transaction data (MetaTransactionData) that is then used to create a single transaction for the gnosis multisend contract.

const transactions: MetaTransactionData[] = await Promise.all(
  receivers.map(async receiver => {
    const unsignedTransaction = await token.populateTransaction.transfer(receiver.address, receiver.token)
    return {
      to: tokenAddress,
      value: "0",
      data: unsignedTransaction.data!
    }
  })
)

const safeTransaction = await safeSdk.createTransaction(transactions)

Please note that we set a value of 0 as we don’t want to send any ETH on top of our ERC-20 token transfer.

Submitting transaction to Gnosis safe

Next we want our transaction to be proposed and visible in the Gnosis UI so other multisig owners can verify and approve it. In order to submit (propose) our transaction we need a safeTxHash which we can get by calling getTransactionHash on the safeSdk.

const safeTxHash = await safeSdk.getTransactionHash(safeTransaction)

await safeService.proposeTransaction({
  safeAddress: safeAddress,
  senderAddress: owner.address,
  safeTransaction: safeTransaction,
  safeTxHash: safeTxHash
})

Approving the transaction (optional)

If the account that is proposing the transaction is also one of the multisig owners they can directly confirm the transaction so this does not have to be done through the UI, either. Please note that the other owners need to use the UI unless we have their private keys or they have a similar script they can execute themselves.

const signature = await safeSdk.signTransactionHash(safeTxHash)
await safeService.confirmTransaction(safeTxHash, signature.data)

Summary

With the gnosis SDK we are able to automate the distribution of our ERC-20 token to the extend that we don’t have to manually create transactions to multiple recipients and approve them one-by-one. With this basic setup you can automate the whole process from calculating to distributing tokens.

The nice thing about this solution is that less technical people can verify and approve the transaction through the beautiful Gnosis UI. All relevant data is readable: token contract address, receiver address and the amount of tokens.

Gnosis Transaction History
Gnosis Transaction History

You can find the whole script in this public gist. Feel free to comment there as well.


*) Please make sure to transfer the total supply of your token to the safe first

Subscribe to cmdzro
Receive the latest updates directly to your inbox.
Verification
This entry has been permanently stored onchain and signed by its creator.