Staking Contracts
Last updated
Last updated
Open Source Contracts: https://github.com/coinecta/staking-contracts/
Sundae Labs Audit: https://coinecta.fi/downloads/coinecta-staking-audit.pdf
The Coinecta staking contracts support the requirement of being able to allow users to lock tokens for certain time periods and get a percentage reward for it. Because these locking periods can potentially be long, we bind the time lock to a unique NFT (the stake key) minted during the locking transaction. This allows the user to move to another wallet (for example due to loss of seed phrase of the original wallet) or even sell the stake key to someone else on a secondary market such as jpg.store.
These are the main requirements the contracts need to fulfil:
Guarding the assets in the stake pool to only be used as rewards for staking
Protect the user and ensure the staking reward and time period match their expectation
Mint unique stake key to the user
Rewards are part of the lock from the beginning.
This validator ensures that the assets within the UTXO are only spent by a locking transaction or by the owner of the stake pool. Part of it's datum is a list of RewardSettings, each containing a valid locking period in days and the percentage reward that follows with it. The owner can choose to remove the assets from the stake pool or change the reward settings at any time.
Main validations to be done in a locking transaction:
A new utxo is made with the same datum and validator attached.
The reward matches the percentage in the chosen reward setting.
The assets are unchanged other than the reward that is moved to the time lock utxo
The time lock is locked according to the chosen reward setting.
This validator protects the assets that the user wants to lock. Because the stake_pool is a singleton interacting with it directly could give issues during busy periods. So the user defines their staking order in a stake_proxy utxo after which automated off chain code can ensure the order is fulfilled. If for whatever reason the order is not fulfilled the utxo can be refunded in a transaction signed by the user.
Main validations to be done in a locking transaction:
The reward is matching user expectation
The locking period is matching user expectation
The stake key is minted to the user using expected policy
This is a fairly simple time lock, making sure it can not be spend before a certain time. Once the time is reached it can be spend as long as the stake key is burned in the same transaction.
Main validations to be done in unstake transaction:
The transaction is executed after the unlock time has been reached.
The stake key is burned in the transaction.
This minting policy checks which assets are locked and when they unlock. On top of that it ensures the asset name is unique.
The expected template for the stake key asset name is:
prefix s
1
staked amount
4
Staked amount including reward, max 3 digits + letter (for example 345K)
staked asset name
6
First 6 characters of the staked asset
unlock date
6
Date on which stake is unlocked, format YYMMDD
time
4
8 most significant digits of upper validity of transaction, encoded as hex values (so 2 digits form one byte)
stake_pool_output_index
1
Output index of the stake_pool that is part of the transactions' input
stake_pool tx_hash
10
First characters of the hex representation of the tx_hash of the stake_pool input
total
32
Main validations during a mint:
Asset name follows the expected template
Only 1 NFT is minted
user wallet
stake_proxy
owner wallet
stake_pool
stake_pool
stake_pool
stake_proxy
time_lock
stake_key_mint + 1
user wallet
Off chain operator (change)
time_lock
user wallet
user wallet
stake_key_mint - 1
You can write tests in any module using the test
keyword. For example:
To run all tests, simply do:
To run only tests matching the string foo
, do:
If you're writing a library, you might want to generate an HTML documentation for it.
Use:
Find more on the Aiken's user manual.