GnosisDAO received 15% of the total SAFE Token supply, vesting over 4 years. These tokens continuously vest to GnosisDAO, and this tool is responsible for distributing these tokens to GNO holders. The distributions for each address are calculated daily, and vested tokens are made available to claim monthly. GIP and discussion can be read here.
This repo contains the codebases for the distribution contracts, the scripts for claiming and calculating distributions, and the web app used by GNO holders to claim tokens.
Note: Token distributions will not be available until the SAFE Token is made transferable, and the Distribution contracts are deployed. However, daily harvests are live, and can already be inspected. Once SAFE is unpaused, and MerkleDistros deployed, distributions will automatically start happening monthly.
This is a React app that allows you to connect a wallet, view claimed and unclaimed tokens, and submit transactions to claim any available tokens. This app is live here -> https://safe-distro.gnosis.io
This directory contains the distribution contract code as well as the scripts that run periodically to calculate distributions and disburse tokens to the contracts.
The distribution contracts contain the tokens available for claiming, and the authorization logic (via MerkleTree/MerkleRoot) that grants whitelisted addresses a certain amount of tokens.
When a distribution runs, it funds these contracts (one on mainnet, one on gnosis), and updates their internal MerkleRoot storage variable.
Contract audit available here.
A json file containing a list of pairs (blockNumberMainnet
, blockNumberGnosis
). This file determines how allocation snapshots are taken. It has an entry for each day passed since VestingInception. The current schedule file can be found here.
For each past VestingSlice (~1 day) we select a block at random, and insert it into schedule.json. This random block becomes the upper boundary of the slice.
The task that progressively expands schedule is called schedule:expand
:
- Get all past daily intervals since VestingInception
VestingInception
is the block whereVestingId
was created
- For every interval not yet represented in the schedule:
- Generate a random mainnet blockNumber that falls within the interval
- Get the closest matching blockNumber on gnosis
- Insert pair into schedule
For each VestingSlice, two allocation files will be written one for mainnet and one for gnosis. Allocations can be viewed here.
Each allocation file contains a map of addresses to amounts, and it represents the quantities of SAFE allocated to an address in a VestingSlice.
The allocation formula inputs:
balancesGNO
: retrieved by querying subgraphs at blockHeighttotalAmountVested
: the total amount of tokens vested out of the VestingPool during a VestingSlice. Retrieved by directly calling the VestingPool contract at the respective block heights.
Formula for amount allocated to an address during a slice:
allocation[address] = balancesGNO[address] / sum(balancesGNO) * totalAmountVested
A checkpoint file is an accumulation of allocations (simple sum). It's a map of addresses to balances, and it represents the absolute amount of SAFE tokens allocated to an address, up to a certain point. We can generate checkpoints at any arbitrary block by summing all prior allocations:
For a given blockNumber (or latest):
- Filter the schedule for entries older than blockNumber:
- Load corresponding allocation file
- Sum/Accumulate allocations
- Result is a map of addresses to balances
- Transform the map into list of pairs [address, balance]
- Build a MerkleTree with the pairs as leaves
We use scheduled Github Actions to run all the above scripts. They are defined here.
The Harvest task runs everyday and it combines the tasks:
schedule:expand
allocation:all
It keeps expanding the schedule by randomly generating missing slices. Calculates allocations.
Note: The monthly task will skip execution if the distribution contracts are not deployed.
This task:
- Creates a new distribution
- Updates authorization setup
- Creates two new checkpoints (mainnet and gnosis)
- Creates MerkleTrees out of the new checkpoints
- Commits the results back to repo
Posts two transactions - mainnet and gnosis - that take care of the following steps:
- TreasurySafe Mainnet:
multisendEncode
of:- Tx that claims the tokens from the
VestingPool
- Tx that funds
MerkleDistroMainnet
with SAFE - Tx that funds
MerkleDistroGnosis
with SAFE, via OmniBridge - Tx that updates the merkleRoot in
MerkleDistroMainnet
- Tx that claims the tokens from the
- TreasurySafe Gnosis:
- Tx that updates the merkleRoot in
MerkleDistroGnosis
- Tx that updates the merkleRoot in
To deploy the MerkleDistro.sol contract:
- copy
.env.template
to.env
and fill with valid keys and a mnemonic that resolves to a funded account - run
yarn run deploy-distros yarn run verify-distros
Note: The deploy will fail if SafeToken not yet deloyed and bridged to Gnosis Chain
- run
yarn hardhat allocate --lazy false
- run
yarn hardhat allocate [blockNumber]
- run
yarn hardhat checkpoint
- run
yarn hardhat checkpoint [blockNumber]