- Clone repo
- Run
npm install
- To start a node:
npx hardhat node
- To compile:
npx hardhat compile
- To run all tests:
npx hardhat test
- To run a specific test:
npx hardhat test path/to/test
- To export abi:
npx hardhat export-abi
You need to have access to an archive node.Setup environment variables. Create .envrc file and add the following:
export ETHEREUM_NODE="http://your.rpc.url.here"
export ETHEREUM_CQT_ADDRESS="0xD417144312DbF50465b1C641d016962017Ef6240"
npx hardhat test ./test/operational-staking/unit-tests/*
npx hardhat test ./test/operational-staking/integration-tests/all
npx hardhat test ./test/proof-chain/unit-tests/*
npx hardhat coverage --testfiles "./test/operational-staking/unit-tests/*
npx hardhat coverage --testfiles "./test/proof-chain/unit-tests/*
This repo contains two contracts: ProofChain and Staking which are part of the Covalent Network. They were designed with the purpose to enable the Covalent Staking program.
Operators stake their tokens, do work by running nodes, submit a proof of work to the ProofChain and receive rewards in CQT tokens. If enough operators get the same result of work, then quorum is achieved, the rewards are emitted and pushed from the ProofChain to the Staking. Additionally, delegators (these who do not run nodes) can delegate their tokens under the operators and receive rewards. The delegators will have to pay commission fees to the operators.
For the detailed explanation of what is Covalent Network and what kind of work the operators do please refer to the white paper.
To distribute tokens to stakers.
The reward emission will be pushed from the ProofChain contract every checkpoint. A checkpoint is defined as a transaction in which the rewards are pushed and will happen every couple of blocks. The checkpoint transaction determines the rewards amount, hence it is unknown how many rewards will be pushed to a validator beforehand.
There are four types of entities: Validators (the network operators who self-delegate), Delegators (who delegate tokens to the validators), the Owner (Covalent) and the StakingManager (ProofChain contract).
Delegators pay commission fees from their earned reward based on the commission rate dictated by validators. The validators will be added to the contract by the StakingManager and will be disabled until the StakingManager activates its instance.
- If a validator misbehaves (does not run a Covalent node or cheats), the StakingManager can disable the validator instance on the contract. The StakingManager will call disabledValidator/enableValidator functions on the Staking contract.
- Maximum # of tokens that can be staked (self-delegated) by a validator.
- Validator max cap - is the maximum number of tokens the delegators can cumulatively delegate to a validator. That number is determined by the following formula:
#_of_tokens_staked_by_validator * validator_max_cap_multiplier
, wherevalidator_max_cap_multiplier
is a number that is set by Covalent.
An example of max cap:
Assuming validator_max_cap_multiplier is set to10
, then a validator comes and stakes 1 million tokens. The max cap of the validator is10 * 1 million = 10 million
. So delegatorA
comes and delegates3 million
, delegatorB
-2 million
, and delegatorC
-5 million
. In other words, the total number of tokens delegated already equals the maximum cap of10 million
. Thus, a new delegator 'D' cannot delegate tokens to the validator unless someone unstakes their tokens. - A validator cannot unstake tokens that would reduce the maximum cap below what is already delegated. If a validator is willing to do so, the validator will have to disable itself through the StakingManager. The delegators will stop earning the rewards from that validator, but they will have an option to redelegate tokens to another validator without a cool down period. The validator can enable its instance back through the StakingManager.
- It is allowed for the same validator address to have multiple ids.
- When changing its address a validator cannot transfer unstakings if there are more than 300 of them. This is to ensure the contract does not revert from too much gas used. In case if there are more than 300 unstakings, there is an option to transfer the address without unstakings.
- Due to potential conversion overflow and precision loss there is a constaint that requires the staked and rewards amounts to be greater than REWARD_REDEEM_THRESHOLD. Currently it is set to 0.0000000001 CQT. Hence, if not the full amount is unstaked or redeem there is a potential lock of the remaining 0.0000000001.
Delegators and validators can decide whether to withdraw all of their rewards or just a portion of them and may do so at any time without a cool-down period. To support certain use cases like pseudoanonymity preservation, 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 1,234,260 blocks (~180 days at the time of contract deployment, 6857 blocks per day * 180) for their unstaking to be unlocked, while delegators have to wait 191,996 blocks (~28 days at the time of contract deployment, 6857 blocks per day * 28). 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.
- Deposit tokens into the contract that will be distributed (reward pool)
- Withdraw tokens from the contract that are supposed to be distributed. The owner cannot withdraw tokens allocated for the past checkpoints that have not yet been redeemed by the delegators
- Set the validator max cap multiplier
- Set the maximum number of tokens the validator can stake
- Set the StakingManager address
- Renounce his role and disable all the following listed actions by calling renounceOwnership
- Transfer the ownership to another address by calling transferOwnership
- Set or change the stakingManager by calling setStakingManagerAddress
- Set the validator commission rate
- Add validator instances to the contract
- Reward validators
- Disable/Enable validators
- Redeem rewards (from commissions paid to them and their earned interest)
- Stake or self-delegate
- Unstake
- Transfer out unlocked unstake
- Restake back unstake
- Change validators' address
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 StakingManager adds a validator for the first time.
- Redeem rewards (their earned interest)
- Stake
- Re-delegate from disabled validator. A delegator first has to unstake then re-delegate unstaked. If a validator gets enabled back, a delegator cannot re-delegate unstake, but she or he can recover that unstake back to the current validator.
- Unstake
- Transfer out unlocked unstake
- Restake back unstake
- how rewards are calculated per validator per checkpoint
- how it's decided when validator gets disabled
Assume tokens are emitted at checkpoint = 1000
- Epoch 1 - person
A
stakes10,000
tokens and no one else has anything staked - Epoch 2 -
person
A
receives1,000
tokens reward which is100%
of tokens emitted at a particular checkpoint
                  personB
stakes20,000
tokens - Epoch 3 -
person
A
receives355
tokens since that person's ratio of tokens staked is35.5% = 11,000 / (11,000 + 20,000)
                  personB
receives645
tokens since that person's ratio of tokens staked is64.5% = 20,000 / (11,000 + 20,000)
Checkpoint # | Staked by A | New staked added by A | Ratio owned by A | Staked by B | New staked added by B | Ratio owned by B |
---|---|---|---|---|---|---|
Checkpoint 1 | 0 | 10,000 | 0 | 0 | 0 | 0 |
Checkpoint 2 | 11,000 | 0 | 100% | 0 | 20,000 | 0 |
Checkpoint 3 | 11,355 | 0 | 35.5% | 20,645 | 0 | 64.5% |
We use the concept of ratios or in other words, exchange rates. Each validator has its own exchange rate. Stakers buy validator shares. When they unstake or redeem the rewards, they sell the shares. Selling means these shares get burnt.
-
Initially the validator exchange rate is
1 share = 1 token
. -
Then staker
A
comes and stakes10,000
tokens, it receives10,000
validator shares. -
Assume at the next checkpoint the emission is
1000
tokens, and the validator commission rate is25%
. -
Then
1000
tokens will be distributed to the stakers. -
Since there is a commission rate of
25%
,250
tokens will go towards commission paid, and750
tokens are distributed among the 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 validator share = 1.075
tokens, a validator will have250
commission tokens available to redeem.
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).
- Cool down periods might be changed with the next upgrade, so these are not constant.
- Commission earned by validators is not compounded towards the shares that the validator has
- The contract is upgreadable
Allow operators to submit proofs of work.
There are five types of entities: Owner, Block Specimen Producer (BSP), Block Specimen Producer Manager, Auditor and Governor. The Block Specimen Producer is an operator who runs a covalent node and submits proofs of work to the ProofChain. The Block Specimen Producer Manager is an account the operator uses to enable/disable its operator instances. An Auditor is an operator who arbitrates sessions that did not reach quorum. A Governor is an entity who can add or remove operators and set additional storage variables.
Currently, only Operators approved by Covalent can perform the BSP role and Covalent performs the auditor and governor roles.
- A network operator gets added on the ProofChain and Staking contracts by Covalent.
- The operator stakes CQT on the Staking contract using its staking wallet.
- The operator enables its instance on the ProofChain.
- The delegators delegate their CQT tokens under the operator instance.
- The operator produces a Block Specimen and submits proofs of work in the form of Block Specimen Hashes to the ProofChain. This starts a new session for that corresponding block.
- Other operators submit the proofs for the same session as well.
- Once the session deadline has reached, a separate agent invokes a finalize transaction. Here it gets determined if the quorum was reached or not. Achieving the quorum means a majority submitted the same Block Specimen Hash.
- If quorum is achieved the participants who submitted the agreed specimen hashes get rewarded. The rewards are pushed from the ProofChain to the Staking. If quorum was not achieved nothing happens. In both cases at the end of the transaction, the session gets marked as
requires audit
. - Sometimes the nodes may end up submitting hashes for reorg blocks and Covalent wants to reward these too. The auditor will be arbitrating these submissions.
In order for a BSP Operator to be able to submit the proofs, its instance must be enabled on the ProofChain contract. Enabling/disabling an Operator on the ProofChain will enable/disable its Validator instance on the Staking contract. On the ProofChain enabling/disabling is done by the BSP Manager address which is the Validator address on the Staking contract. One BSP Manager can manage multiple BSP Operators.
A session starts when an operator submits the first proof per chain id
per block height
. A Block Specimen is an object generated by replicating block data. Block Specimens are generated by running a geth node with Covalent's Block Specimen Production agent: bsp-agent. A hash of the Block Specimen gets submitted to the ProofChain and acts as a proof of work.
- minimum stake - operators must stake a minimum number of tokens on the Staking contract to participate
- block-divisor - block specimens are produced only for every nth block.
- max specimen submissions per block height - the same operator might submit multiple block specimens per block height due to potential reorgs
- session duration - in blocks
- live sync - the contract has a mapping of the sink chain block height to the source chain block height that allows the application of a constraint that enables submissions for block specimens of the current/recently mined blocks
- reward per block hash - is distributed per block hash rather than per session
- min submissions - to achieve quorum, there should be at least a minimum number of submissions of the agreed hash
- chain id
- block height
- block hash
- specimen hash
- storage URL
The reward is allocated per block hash and distributed between the participants who submitted the agreed (when the quorum is achieved) or correct (in case of reorg) specimen hash. The reward is distributed proportionally to how much each operator has staked and how much is delegated to that operator.
The optimistic phase happens when enough participants have submitted the same block specimen hash and quorum is achieved.
In the pessimistic phase the auditor makes arbitrateBlockSpecimenSession()
transaction providing the correct block specimen hash. The Pessimistic phase may happen in the following 2 cases:
- when quorum was not achivied.
- when quorum was achivied, but the operators have submitted hashes for reorg blocks that are still valid. The auditor will be running a node and regenerate the reorgs to find valid block specimen hashes.
The pessimistic phase will be fully implemented later.
Assuming the required quorum is set to > 50%.
Block Hash A
- 5 submissions of specimen hash A'
- 2 submissions of specimen hash A''
~71% (5 out 7) participants submitted specimen hash A'. The quorum is achieved. The submitters of the specimen hash A' receive the reward. These who submitted A'' do not receive anything.
Not required since the quorum was achieved and no other block hashes submitted.
Block Hash A
- 2 submissions of specimen hash A'
- 2 submissions of specimen hash A''
50% of participants submitted specimen hash A' and A''. The quorum is not achieved. No one receives the reward in the optimistic phase.
The auditor arbitrates the session and decides that the specimen hash A' is correct. The submitters of the specimen hash A' receive the reward.
Block Hash A
- 5 submissions of specimen hash A'
- 2 submissions of specimen hash A''
Block Hash B
- 1 submissions of specimen hash B'
- 1 submissions of specimen hash B''
~55% (5 out 9) participants submitted specimen hash A'. The quorum is achieved. The submitters of the specimen hash A' receive the reward. These who submitted A'' do not receive anything.
The auditor arbitrates the session for block hash B and decides that the specimen hash B'' is correct. The submitters of the specimen hash B'' receive the reward and these who submitted B' do not receive anything.
Block Hash A
- 5 submissions of specimen hash A'
- 2 submissions of specimen hash A''
Block Hash B
- 1 submissions of specimen hash B'
- 1 submissions of specimen hash B''
- 1 submissions of specimen hash B'''
Block Hash C
- 7 submissions of specimen hash C'
Block Hash D
- 2 submissions of specimen hash D'
- 3 submissions of specimen hash D''
The quorum is not achieved since no specimen hash has >50% of submitters. No one receives the reward in the optimistic phase.
The auditor arbitrates the session for all the submitted block hashes: A, B, C and D. It decides that the specimen hashes A', B''', D' are correct. The corresponding submitters receive the reward. The submitters of the specimen hashes A'', B', B'', C' and D'' do not receive anything
- submit proofs
- enable/disable its operator instances
- arbitrate sessions
- add/remove BSP operators
- add/remove auditors
- set staking contract address
- set quorum threshold
- set block divisor (nth block)
- set reward allocated
- set session duration
- set chain sync data
- set max submissions per block height
- set min submissinos required
- set validators commission rate
- add/remove governors
- upgrade the contract