Table of Contents
Introduction
This repository contains a collection of scripts and tools used to fetch deterministic dataset pertaining to CoW Token airdrop. According to the announcement in this Gnosis Forum Post the airdrop will be made to GNO holders on Mainnet and Gnosis Chain (formerly xDai) including consideration of the following liquidity pools:
- Balancer V2 (Mainnet)
- Uniswap V3 (Mainnet)
- HoneySwap (Gnosis Chain)
- Symmetric (Gnosis Chain)
- SushiSwap (Gnosis Chain)
- Elk (Gnosis Chain)
- Swapr (Gnosis Chain)
Note that all our holder queries use strict inequality on the block number, so the snapshot block number is the first block occurring on Jan 10. These are 13974427 on mainnet and 20024195 on gnosis chain. Relevant query here.
Note that we have excluded pools within these protocols containing a total of < 1 GNO and user accounts having a GNO balance < 0.1 at the time of the snapshot.
To, briefly summarize the approach we;
- Fetch Mainnet & Gnosis Chain GNO Token Holders at specific block
- Fetch LP holders (specifically % of the total supply they own)
- Fetch UniswapV3 Holders (specifically how much GNO)
- Fetch Gnosis Chain Depositors
- Compute GNO holdings of LP providers based on share % in correspondence with the Pool's holdings (from step 1)
- Combine all of these values together to generate combined (Mainnet and Gnosis Chain CSV) representing total GNO holdings across all eligible platforms.
- Fetch and merge trader data from both networks
- Filter the combined Trader data by the eligibility criteria outlined below
- Transform the holder and user data into allocations in their respective categories.
- Merge allocations into a combined, universal allocation list.
- The allocations are then split across both networks based loosely on the criteria that Allocations with an Airdrop claim less than 10K vCoW tokens will be eligible for claim on Gnosis Chain. Above 10K claims are on Mainnet. Note that we can only perform this splitting criteria to Externally owned accounts (since smart contracts such as Gnosis Safe are not "multi-network compatible").
- As a final step, we append Advisor, Investor and Team allocations to the bottom of the mainnet allocation file.
Resulting allocation files have the following columns:
[ Account | Airdrop | GnoOption | UserOption | Investor | Team | Advisor ]
The allocation files are committed to this repo and can be viewed here:
Allocations
An Allocation (represented in the project as a MerkleLeaf
), attributed to each
eligible account, consists of several categories briefly define here (some of which will
be elaborated upon in their own section):
- Airdrop: Consists of the combined sum of allocations for GNO holders and CoWSwap Users (outlined below)
- GNO Option: Constructed from GNO holder data. Represents amount of vCoW that can be purchased with GNO.
- User Option: Constructed from Primary Trader data (a sub-category of CoWSwap Users). Represents amount of vCoW that can be purchased with ETH/XDAI. Note that while 45M tokens we allocated for primary trader airdrop, 50M were allocated to the user option with different weight factors for each trading volume tier.
- Investor: Represents amount of vCoW that can be purchased with USDC
- Team: Represents a 4 year linearly vested entitlement of tokens for team members.
- Advisor: Allocation attributed to advisors.
Each of the last 3 categories were appended to the end of mainnet allocation file after all the holder and user data was compiled.
Allocation Splitting
Allocations were split over two networks (Mainnet and Gnosis Chain) based on the following criteria:
- Mainnet Allocation:
allocation >=
ALLOCATION_SPLIT
- Gnosis Chain:
allocation <
ALLOCATION_SPLIT
where ALLOCATION_SPLIT = 10K token units
Additionally, since externally owned accounts are cross-network compatible, but smart contracts (such as Gnosis Safe) are not, we have to investigate this for each account.
Accounts which are contracts on only one of the two networks will receive their allocation on the network they exist (regardless of allocation split).
Accounts which are contracts on both networks are treated as a "special case" we collect this list and parse through all the accounts from the data files generated throughout the program and partition them by network of eligibility.
If the account is exclusively eligible for allocation on one of the two networks the allocation is given there.
There was only one case of a Gnosis Safe contract on both networks that was eligible for allocation on both networks:
This is a Safe on both networks with an overlapping set of owner accounts (beyond the signing threshold of 2)
The total allocations to each category are
======================
Allocation Counts
Mainnet: 1465
Gnosis Chain: 53750
----------------------
Category Totals
Airdrop: 150.0M
Gno Option: 50.0M
User Option: 50.0M
Investor: 100.0M
Advisory: 106.1M
Team: 99.3M
----------------------
Grand Total: 555.3M
----------------------
Unallocated Amount
6900034565 WEI
======================
GNO Token Holders
Mainnet
Mainnet Holders
The goal of this snapshot is to deterministically and reproducibly fetch the GNO token
holders on both networks at a specific time. For this we use Dune
Analytics erc20.view_token_balances_daily
table on Mainnet.
For reference, non-deterministic (i.e. current) token balances on each network can be found on Etherscan .
Deterministically,
Mainnet Holders Query Jan 10, 2022
which returns csv with columns [ account | amount ]
.
Query here
Mainnet Liquidity Providers
- Balancer v2:
https://app.balancer.fi/#/pool/<PoolId>
The query for balancer holders is also in this repo at ./queries/generic_lp_holders.sql.
For an account of the GNO balances corresponding to each Balancer Pool at the time of the snapshot, please visit here.
- Uniswap v3:
https://info.uniswap.org/#/pools/<PoolAddress>
For current individual pool positions visit https://app.uniswap.org/#/pool/<TokenId>
Deterministically, GNO balances corresponding to holder positions were computed using their SubGraph along with code here adapted from their SDK.
- SushiSwap:
https://analytics.sushi.com/pairs/<PoolAddress>
Gnosis Chain
Gnosis Chain Holders
The balances view does not exist in Dune on gnosischain
, so we rely on event
accounting of the token contract available in the gnosis.GnosisToken
table.
The Gnosis Chain Holders can also be retrieved in JSON format via the Blockscout API Request
Deterministically, this Gnosis Chain Holders Query will become "static" as of the snapshot date. The query is also available within this repo at /queries/gchain_holders.sql
Gnosis Chain Liquidity Providers
A complete query for all eligible liquidity sources on Gnosis Chain. This is also available in Dune, but only those with GNO balance > 1 at the time of snapshot are considered eligible.
-
HoneySwap:
https://info.honeyswap.org/#/pair/<PoolAddress>
-
Symmetric:
https://xdai-pools.symmetric.exchange/#/pool/<PoolAddress>
-
SushiSwap:
https://analytics-xdai.sushi.com/pairs/<PoolAddress>
-
Elk:
https://xdai-info.elk.finance/pair/<PoolAddress>
-
Swapr:
https://dxstats.eth.link/#/pair/<PoolAddress>
Staked GNO
The set of depositors and number of deposits (up until the snapshot deadline) are fetched via Dune Analytics https://dune.xyz/queries/330002 and in this repo ./queries/staked_gno.sql. Note that the number of deposits corresponds directly to the integer number of GNO staked.
CowSwap Users
The funds allocated to users make a combined total of 50M tokens (5% of the supply). This allocation has been split up into three categories:
- Primary Trader: users (a.k.a. traders) satisfying all of the criteria outlined below
- Consolation Trader: users who were not eligible for primary allocation, but do satisfy the eligible volume criteria below
- Community: This category includes both of POAP holders and a special allocation to our alpha contract traders.
The 50M tokens has been partitioned as (44M, 3M, 3M) into each of the above three categories respectively.
We elaborate on each of the three categories in their own sections below.
Users, aka those who have traded on the platform, are entitled to an airdrop based on the following parameters:
-
Eligible Trading Volume: A weighted sum of the total USD value of all non-stable and stable trades. Everyone who has a total of at least
V
is considered. Concretely, that iseligible_volume = non_stable_volume + StableFactor * stable_volume
wherestable_volume
is the trading volume for trades between stable coins andnon_stable_volume
is the volume of all others. -
Stable Factor A factor
S
used to lower the weight of stable to stable trading volume -
Number of Trades: All users having made at least
T
trades. -
Days Between: All users having traded at least
D
apart.
Primary Trader Allocation
Users satisfying all of the above criteria have been included in this list.
The parameters used for primary allocation both networks are
V - 1000 USD
S - 0.1
T - 3 trades
D - 14 days between first and last trade
The primary trading allocation has been distributed to users with weight factors according to the following eligible volume tiers
Tier | Eligible Volume | Weight Factor |
---|---|---|
5 | >= 1M | 28 |
4 | >= 500K and < 1M | 16 |
3 | >= 100K and < 500K | 8 |
2 | >= 50K and < 100K | 4 |
1 | >= 10K and < 50K | 3 |
0 | >= 1K and < 10K | 1 |
Please refer to src/constants.py where all of these parameters are defined in the code.
Consolation Trader Allocation
Users not meeting the Primary criteria, but satisfying either
- The eligible volume condition (i.e. at least 1000 USD in eligible trading volume) OR
- Having made at least 5 trades.
There are no weight factors assigned to this category, the 3M tokens has been distributed equally to each of the eligible recipients.
Trader Data Queries
The Dune query used for both networks is available in this repo at ./queries/generic_trader_data.sql. For convenience, the permanent fixtures for each network are also available in the Dune interface at
- mainnet: https://dune.xyz/queries/344411
- gchain: https://dune.xyz/queries/344484
Community
- Retained Alpha Traders: Anyone who traded on both the alpha (unaudited) version of the contract, and the version 1 deployment. The weight factor attributed to this category (for each network) is outlined along with POAP holders in the table below Dune Query for this is here and a permanent version is available at
- mainnet: https://dune.xyz/queries/356227
- gchain: https://dune.xyz/queries/356237
- POAP Holders: The following table outlines the considered POAPs for this allocation along with their weight factors
For a full list of POAP weight factors see here.
Event | Token ID(s) | Weight Factor |
---|---|---|
Kaffeekränzchen low attendance | 4437, 4813, 5182, 6457, 6887 | 4 |
Kaffeekränzchen medium attendance | 7330, 8092 | 2 |
Kaffeekränzchen high attendance | 8824 | 1 |
Kaffeekränzchen challenge winners | 13346, 15296, 15739, 21839 | 20 |
UX Research Participants | 11743 | 40 |
Squid | 15311 | 20 |
CowStars | 6739, 9533, 12654, 18465, 22201 | 100 |
SCdeploymentMainnet | 6102 | 2 |
SCdeploymentGnoChain | 6103 | 2 |
Picasso Winners | 12420 | 100 |
AlphaUsersRetainedMainnet | N/A | 16 |
AlphaUsersRetainedGnoChain | N/A | 4 |
Unfortunately, due to an exploit in the POAP.xyz we were not able to automatically recover the correct POAP token holders, so we have fallen back on our own, internal, hard coded list of eligible token recipients.
For further details on the dates and names of specific event tokens please refer to our token data files here
Dev Guide: Installation & Usage
python3 -m venv env
source ./env/bin/activate
pip install -r requirements.txt
cp .env.sample .env
source .env
Fill out your Dune credentials in the .env
file. The Dune user and password are
straight-forward login credentials to Dune Analytics. The DUNE_QUERY_ID
is an integer
id found in the URL of a query when saved in the Dune interface. This should be created
beforehand, but the same query id can be used everywhere throughout the program (as long
as it is owned by the account corresponding to the user credentials provided).
The other necessary environment variable is NODE_URL
. This should be the entire URL
with API key. Something like
NODE_URL = https://mainnet.infura.io/v3/XXXYOURXXXXXXAPIXXXXXXKEYXXXXXXX
To generate the entire allocation from scratch run
python -m src.main
NOTE Entire allocation generation from scratch takes about 15 minutes.
DISCLAIMER Due to reliance on a non-deterministic price feed in the trader query we are experiencing infinitesimal difference from one run to the next. While the results will turn out marginally different the allocation files are not yet deterministic. We are working toward fixing this, but will rely on the committed file here at the time of deployment even if this non-determinism has not been resolved. In the meantime, we have temporarily included the trader data that these results are based on to ensure identical results.
Verifying results
If you generate the output files yourself, you can compare the output files with:
$ diff out/allocations-mainnet.csv allocations-mainnet.csv
$ diff out/allocations-gchain.csv allocations-gchain.csv
Note that every file in this repo runs as a standalone script for its intended purpose. For example, to fetch GNO token holders run
python -m src.fetch.gno_holders
or to generate sub allocations (e.g. holder
, poap
, trader
) run
python -m src.generate.{name}_allocation
This program writes files to CSV as it goes. By default, data is loaded from file when available.
This will write two files into the data/
directory. Namely
mainnet-gno-holders.csv
gchain-gno-holders.csv
To fetch LP token holder results run
python -m src.fetch_lp_holders
which will result in files ./out/{network}-lp-holders.csv
.
Gnosis Chain Transfer File
As a final step to the allocation process, we have decided to distribute 0.1 xDAI to all externally owned accounts with zero balance eligible for claim on Gnosis Chain. This is intended to provide funds for transaction cost execution to those who may not already be users on the network.
This transfer file can be generated (from the allocations-gchain.csv
) by running the
following script:
python -m scripts.gchain_transfer_file
Allocation Receipts
Requires having previously generated all the output data (by running main).
Create an accounts.csv file for the accounts you'd like receipts.
Here is a sample file:
account
0x262d23a2d916f6CF08e0235315aA51E22d142d0b
0x1e9abB8087f733ef6Ab7a0E834bf0e3164fE36BE
0xd9c2624b735c6ded3cd0bb79f04feb038cf73aa9
0xaa92521387df9cf483f0c3e0e6f5424e9998316f
python -m src.scripts.allocation_receipts --account-file <PATH_TO_ACCOUNTS_CSV>