The MerkleMine algorithm is a distribution mechanic for allocating token at the genesis state of the network across a large number of participants.
The distribution algorithm consists of:
- A Genesis State
- A
generate()
function
A genesis state G
is consists of:
genesisRoot
- the Merkle root of the genesis statebalanceThreshold
- the minimum ETH balance for an account's address to be included in the genesis statetotalGenesisTokens
- the amount of token to be distributed for the duration of the generation periodtotalGenesisRecipients
- the # of account addresses used in the construction of the genesis stategenesisBlock
- the Ethereum block number at which a state dump of accounts is used to construct the genesis statecallerAllocationStartBlock
- the block at which a 3rd party caller can generate and split a token allocation with a recipientcallerAllocationEndBlock
- the block at which a 3rd party caller can generate the full token allocation of the original intended recipient
This is done offline and is repeatable by anyone to validate the genesisRoot
and construct their own proofs to be used for token allocation generation
- Observe the Ethereum account state database at
genesisBlock
. - For each account:
- If
getBalance(account) > balanceThreshold && len(getCode(account)) == 0
then add the account tocandidateAccounts
array.// Only user controlled acounts with balance greater than balanceThreshold.
- If
- Sort the
candidateAccounts
array in ascending order of the hexadecimal value for each account address. - Use the sorted
candidateAccounts
to create the leaf nodes of a Merkle tree. The hashing algorithm used for the Merkle tree construction is keccak256.- Each leaf node of the tree is the hash of the hexadecimal encoded account address in
candidateAccounts
. The order of the leaf nodes matches the order of the sortedcandidateAccounts
- Each intermediary parent node of the tree is a hash calculated as
keccak256(concat(sorted([A, B])))
whereA
andB
are child nodes (hashes) in the tree and they are sorted in ascending order of their hexadecimal value. When creating the parents of leaves, if there are an odd number of leaves such that a particular leafA
does not have a sibling, let the value for the parent of the leaf also beA
- Let the root of the Merkle tree be
localRoot
- Each leaf node of the tree is the hash of the hexadecimal encoded account address in
- Validate that
len(candidateAccounts) == totalGenesisRecipients
. - Validate that
localRoot == genesisRoot
. - Validate locally that the Merkle proof for the recipient is valid for the Merkle root.
- Validate that the generation period has started.
- Validate that
token.balanceOf(merkleMine) >= tokenAllocationAmount
. - Validate that the token allocation for the account has not been generated.
- Invoke
generate()
with the Merkle proof of an account's inclusion ingenesisRoot
.- The Merkle proof is an array of 32 byte hashes concatenated together
The generate()
function has the following signature:
function generate(address _recipient, bytes _merkleProof)
A user who wishes to generate token passes in the address of the recipient (it may be themselves), and the Merkle proof that verifies the recipient is in the genesis state as specified by the genesisRoot
genesis parameter.
Validations
The method should throw
and revert state if the following validations do not pass:
- Tokens were not already generated for this
_recipient
- The
_merkleProof
provided validates against thegenesisRoot
- If the txn sender (the "caller") does not equal the
_recipient
, then make sureblock.number
is greater than or equal tocallerAllocationStartBlock
Generation
Let tokensPerAllocation
= totalGenesisTokens
/ totalGenesisRecipients
. The starting point is that all recipients get an even distribution of token.
- If the caller is the
_recipient
then transfertokensPerAllocation
to the caller. - If the caller is not the
_recipient
:- Calculate the caller portion using the
callerTokenAmountAtBlock
curve. - Calculate the recipient portion by subtracting
callerTokenAmountAtBlock
fromtokensPerAllocation
. - Send the caller portion to the caller and the recipient portion to the
_recipient
.
- Calculate the caller portion using the
- Record state that this recipient's tokens have been generated, so that tokens can no longer be generated with this same
_recipient
argument and Merkle proof again.
This curve indicates how much LPT is claimable by the txn sender (caller) versus how much gets distributed to the _recipient
. This amount grows linearly with how many blocks have passed between callerAllocationStartBlock
and callerAllocationEndBlock
.
- If
block.number
>callerAllocationEndBlock
then return the fulltokensPerAllocation
. - Otherwise calculate how far proportionally you are into the period between
callerAllocationStartBlock
andcallerAllocationEndBlock
. Return thetokensPerAllocation
multiplied by this proportional percentage.
For example if tokensPerAllocation is 10, and you are 50% into the period, return 5.
The end result of all of this is that there is a wide generation of token, potentially to all accounts encoded into the genesis state. But there are incentives for others to perform the generation on behalf of many of these accounts through the growing callerTokenAmountAtBlock
value.
- Participation is open to all, whether you had enough ETH to be in the genesis set or not
- There is a period of time for recipients to learn about the process such that they can generate their full allocation
- The caller allocation period allows active participants to generate more tokens than passive recipients while still allowing for the possibility for passive participants to end up with some amount of token (depending on when active participants generate allocations on behalf of recipients) that they can use to start interacting with the protocol later