Architecture
The K9 Finance DAO Liquid Staking architecture has 6 main parts:
KnBONE contract
UnstBONE NFT contract
NodeOperatorRegistry contract
InstantPool contract
BridgeETH contract
BridgeSHIB contract
KnBONE contract
The KnBONE contract is an ERC20 token contract which performs the following functions:
User interaction
Delegate to validators
Reward Distribution
Manage withdrawals
Manage reward fees
Mint and burn NFTs
Sensitive contract interaction and management is executed by the following roles:
PAUSE_ROLE - pauses the KnBONE contract
UNPAUSE_ROLE - unpauses the KnBONE contract
DAO_ROLE - responsible for the main contract settings
BRIDGE_ROLE - manages the token transfer between Ethereum and Shibarium when user deposits/withdraws.
User Interaction
A user can interact only with the KnBONE staking contract to:
Submit ERC20 BONE
Request Withdrawal
Claim withdraw
Call ERC20 functions
Users can submit BONE and get knBONE automatically by calling the submit
function inside the KnBONE contract and passing the delegated amount.
Depositing BONE and minting knBONE
The total amount of knBONE that a user will get when delegate his BONE tokens is calculated as follows:
sharePerUser = submittedBONE * totalShares / totalPooledBONE
The totalPooledBONE is the total amount of the buffered tokens (submitted by the user but not yet delegated) plus the total delegated.
totalPooledBONE = totalStaked + totalBuffered + calculatePendingBufferedTokens() - reservedFunds
Example:
Case 1
Initial state
User A submits:
submit ==> 1000 BONE
receive ==> 1000 knBONE
Updated state:
Users Shares:
Case 2
User B submits:
submit ==> 500 BONE
receive ==> 500 * 1000 / 1000 = 500 knBONE
Update state:
Users Shares:
Case 3
The system was slashed => -100 BONE
Updated state:
Users Shares:
Case 4
User C submits:
submit ==> 500 BONE
receive ==> 500 * 1500 / 1400 = 535.71 knBONE
Updated state:
Users Shares:
Case 5
The system accumulates reward => +200 BONE
Updated state:
Users Shares:
When the system gets slashed, the total pooled BONE decreases, and it increases when a user submits BONE again or the system gets rewarded.
Delegate to Validators
The knBONE contract is used to delegate tokens to validators.
The delegation flow is based on the actual totalBuffered
BONE contained inside the KnBONE contract, when we reach the delegationLowerBound
, we start to delegate to all the Staked operators.
Delegation is performed as a scheduled job by a Delegator/Distributor bot.
Manage Withdrawals
The withdrawal uses the ValidatorShare API - an interface of ValidatorShare contract to interact with the validator. This allows us to have a nonce that we can use to map each user request with this nonce. The knBONE contract tracks each validatorShare nonce which will increment each time a new withdrawal request happens.
Request withdrawal: When a user requests the system to withdraw his BONE tokens by withdrawal request, a new ERC721 token is minted and mapped to this request. The owner can trade this token, sell it or use it to claim his BONE tokens.
The user requests withdrawal
Withdrawal request NFT is minted and mapped with the request by its id
The request nonce of the validatorShare and validatorShare address are stored
The sellVoucher_new function is called
Instant Withdraw: When a user requests the instant withdrawal, BONE is withdrawn from the instant reward pool and the instant reward fee is applied.
The user requests instant withdrawal
BONE is withdrawn from instant reward pool, no interaction with the ValidatorShare API
Claim tokens:
The user calls the claim token function and passes the tokenId
Check if the msg.sender is the owner of this NFT
Call the claim unstake tokens function on the validatorShare contract
Transfer tokens to the user
Burn the NFT
Distribute Rewards
BONE tokens are accumulated and stored inside the KnBONE contract upon 2 events:
Each time a user requests to withdraw, the validatorShare contract transfers the rewards
Scheduled job by the Delegator/Distributor bot.
The protocol regularly check if the amount is greater than rewardDistributionLowerBound
(a variable that can be set). If that requirement is fulfilled, knBONE calculates the amount of Protocol Fee, transfers it to the receivers. Finally, the remaining BONE tokens are added to the BONE buffer and re-delegated to validators, which increases totalPooledBONE value.
The Protocol Fee is distributed between:
DAO treasury - used to cover the DAO costs and other expenses;
Operator reward - split evenly between all validators;
Instant Withdraw pool - the amount that is accumulated for instant rewards payment (when claimed by a user);
Real Yield Staking - the share of tokens that is used to refill the rewards.
Withdraw Total Delegated
When an operator gets unstaked, the nodeOperator contract calls withdrawTotalDelegated
function which claims all the delegated BONE from the unstaked validator. An NFT token is minted and mapped with this request, later a claimTokensFromValidatorToContract
function is called to withdraw BONE from the validatorShare.
ValidatorShare contract interaction
When a user requests withdrawal, the validatorShare contract automatically transfers the accumulated rewards. The same thing happens when BONE tokens are delegated.
When this transfer happens, we consider the received BONE tokens, same as any BONE that was not submitted and buffered, as a reward. And later these tokens are distributed and re-delegated.
ValidatorShare API
The KnBONE implements the validatorShare contract API:
BuyVoucher: buy shares from a validator link
SellVoucher_new: sell an amount of shares
unstakeClaimTokens_new: claim the token by a nonce
getTotalStake: get the total staked amount
getLiquidRewards: get the accumulated rewards
and other functions to interact with the ValidatorShare contract of a node operator.
UnstBONE NFT contract
The UnstBONE contract is an ERC721 contract used by the KnBONE contract to manage withdrawal requests.
Each time a user calls the requestWithdrawSplit
function inside the KnBONE contract and creates a withdrawal request, a new NFT is minted and mapped with the request.
When a user owns an NFT he can:
Claim BONE from the withdraw request
Trade NFT to someone else, who will then be able to claim
Approve it to someone else who will then also be able to claim
This ERC721 is slightly modified so it returns a list of owned tokens of an address by using the public mapping owner2Tokens
. Same goes for retrieving the list of approved tokens by using the mapping address2Approved
.
NodeOperatorRegistry contract
This contract is responsible for the Node operators management and their parameters. All decisions are made by DAO and and then executed by the following roles:
PAUSE_ROLE - pauses the NodeOperatorRegistry contract
UNPAUSE_ROLE - unpauses the NodeOperatorRegistry contract
DAO_ROLE - responsible for the main contract settings. Some functions are available only to the Node operator owner.
ADD_NODE_OPERATOR_ROLE - adds new node operator to the protocol
REMOVE_NODE_OPERATOR_ROLE - removes the node operator from protocol
Manage Operators
Add an operator
A new Operator expresses their interest to join the K9 Finance DAO protocol.
The DAO votes to include the new operator
The validator is added to the Node Operators List by
ADD_NODE_OPERATOR_ROLE
usingaddNodeOperator
Exit Node operator registry
Node Operator owner decides to leave the K9 Finance DAO protocol
This owner calls
exitNodeOperatorRegistry
(only the owner can call this function)Delegated BONE is withdrawn from validator and claimed later by a cron job
Node operator is removed from the operators list
Remove Node operator
DAO made a decision about removing an operator
Node operator is removed by
REMOVE_NODE_OPERATOR_ROLE
usingremoveNodeOperator
Delegated BONE is withdrawn from validator and claimed later by a cron job
Node operator is removed from the operators list
Remove invalid Node operator
An invalid operator was detected by DAO (UNSTAKED or EJECTED status)
This invalid operator is removed using
removeInvalidNodeOperator
Delegated BONE is withdrawn from validator and claimed later by a cron job
Node operator is removed from the operators list
Multiple 'view' functions are implemented to get the updated info about validators and the system state.
InstantPool contract
Instant pool is used for storing and paying out BONE tokens when user requests instant rewards. Only the following roles can interact with the contract:
PAUSE_ROLE - pauses the InstantPool contract
UNPAUSE_ROLE - unpauses the InstantPool contract
WITHDRAWER_ROLE - transfers BONE from contract to user.
So, when user requests instant rewards, the withdraw
function is called by KnBONE contract. By calling this function the BONE transfer is performed to the user's wallet. Only the KnBONE contract can call this function since it is the only instance that manages the rewards.
BridgeETH and BridgeSHIB
These two contracts are used for:
when user submits - transfer the knBONE tokens from Ethereum to Shibarium
when user withdraws - transfer the knBONE tokens from Shibarium to Ethereum.
Last updated