Platform: Code4rena
Start Date: 19/10/2021
End Date: 21/10/2021
Period: 3 days
Status: Completed
Reporters: CloudEllie, moneylegobatman
Pot Size: $30,000 ETH
Participants: 13
Reporters: CloudEllie, moneylegobatman
Judge: GalloDaSballo
Id: 43
League: ETH
Auditor per page
npm install
You need to have access to an archive node. Place the node url into hardhat.config.js:
... networks: { hardhat: { chainId: 1, forking: { url: "http://you.rpc.url.here", blockNumber: 13182263 } ...
npx hardhat test ./test/unit-tests/*
You can watch a walkthrough video here.
To distribute tokens to the stakers. For the first iteration, no external agent or entity will be updating the reward emission rate. Therefore, the emission rate will be fixed per epoch and distributed to stakeholders based on how many tokens they have staked plus their accumulated interest. One epoch is one block.
There are three types of users: Validators (who self-delegate), Delegators (who delegate tokens to the validators), and the Owner (Covalent). Delegators pay commission fees from their earned reward based on the commission rate dictated by validators. A limited number of validators will run Covalent nodes. They will set up and run the nodes, then we will add them to the contract.
#_of_tokens_staked_by_validator * validator_max_cap_multiplier
, where validator_max_cap_multiplier
is a number that is set by Covalent. </br>
An example of max cap: </br>
Assuming validator_max_cap_multiplier is set to 10
, then a validator comes and stakes 1 million tokens. The max cap of the validator is 10 * 1 million = 10 million
. So delegator A
comes and delegates 3 million
, delegator B
- 2 million
, and delegator C
- 5 million
. In other words, the total number of tokens delegated already equals the maximum cap of 10 million
. Thus, a new delegator 'D' cannot delegate tokens to the validator unless someone unstakes their tokens.Delegators and validators can decide whether to withdraw all their rewards or just a portion of them and may do so at any time without a cool-down period. For taxation purposes, the users can set a different address from their wallet address to which rewards will be transferred.
Validators who self-delegate will have to wait 180 days for their unstaking to be unlocked, while delegators have to wait 28 days. Once unstaking is unlocked, tokens can be transferred back into the delegator's or validator's wallet. An unstaked amount can always be recovered: The unstaked amount (partially or in full) can be delegated back to the same validator.
Note: Validators cannot set their commission rate, and if they wish to change it, they must contact Covalent. Commission rates are initially set when the owner adds a validator for the first time.
You can watch the UI walkthrough video here.
Assuming allocated tokens per epoch = 1000
A
stakes 10,000
tokens and no one else has anything stakedA
receives 1,000
tokens reward which is 100%
of tokens allocated per epoch <br />
person B
stakes 20,000
tokensA
receives 355
tokens since that person's ratio of tokens staked is 35.5% = 11,000 / (11,000 + 20,000)
<br />
person B
receives 645
tokens since that person's ratio of tokens staked is 64.5% = 20,000 / (11,000 + 20,000)
Epoch # | Staked by A | New staked added by A | Ratio owned by A | Staked by B | New staked added by B | Ratio owned by B |
---|---|---|---|---|---|---|
Epoch 1 | 0 | 10,000 | 0 | 0 | 0 | 0 |
Epoch 2 | 11,000 | 0 | 100% | 0 | 20,000 | 0 |
Epoch 3 | 11,355 | 0 | 35.5% | 20,645 | 0 | 64.5% |
View the spreadsheet for a better understanding here.
We use the concept of ratios or in other words, exchange rates. There is a global exchange rate and an exchange rate per validator. Stakers buy both global and validator shares. When they unstake or redeem the rewards, they sell global and per validator shares.
Initially, the global exchange rate and validator exchange rate is 1 share = 1 token
.
Then staker A
comes and stakes 10,000
tokens, so receives 10,000
global shares and 10,000
per validator shares.
Assume the emission rate is 1000
tokens per epoch, and the validator commission rate is 25%
.
In the next epoch, 1000
tokens will be distributed between 10,000
global shares and 10,000
validator shares.
The new global exchange rate will become:
old_global_exchange_rate + 1000 tokens / 10,000 global shares = 1 + 0.1 = 1.1
As per the validator exchange rate, since there is a commission rate of 25%
, 250
tokens will go towards commission paid, and 750
tokens are distributed among validator shares.
Then the validator exchange rate would be:
old_validator_exchange_rate + 750 tokens / 10,000 validator shares = 1 + 0.075 = 1.075
So 1 global share = 1.1
tokens and 1 validator share = 1.075
tokens, a validator will have 250
commission tokens available to redeem.
If a validator decides to redeem the commission, 250
tokens convert to global shares: 250 / 1.1 = ~ 227.2727 shares
.
The new global number of shares would be 10,000 - 227.27 = 9772.73
. We do not need to subtract any number from per validator share since these already exclude the commission rate.
There is a slight precision loss in rewards calculation. It is acceptable as long as it is small enough (less than ~ 0.01 a day).