IIP: 13
Title: Represent Staking Buckets As Non-fungible Tokens
Author: Seedlet (zhi@iotex.io)
Status: WIP
Type: Standards Track
Category: Core
Created: 2023-02-22
This IIP proposes to natively support the representation of staking buckets as Non-fungible Tokens (NFT) on the IoTeX blockchain, which opens opportunities for applications such as Liquid Staking Derivatives (LSD) and potentially increases the staking ratio and therefore enhances the degrees of security and decentralization of the IoTeX blockchain.
The Proof of Stake (PoS) consensus mechanism secures the vast majority of modern blockchains, IoTeX L1 included, as it disincentivizes an attack on the network by requiring control of the majority of coins to control the blockchain. In contrast to the traditional Proof of Work (PoW) consensus mechanism, PoS consensus dramatically reduces the energy required to run a secure and decentralized blockchain. Simply put, the more coins that are staked to govern the protocol, the more secure the blockchain is. Therefore, increasing the total amount of $IOTX staked (i.e., the "staking ratio") has always been the goal for IoTeX.
An interesting primitive called "Liquid Staking" has been introduced by Lido in 2020 which means a liquid staking provider takes coin deposits, stakes those coins, and gives the depositor a receipt that is redeemable for the staked coins. The receipt is a representation of those staked coins that can be traded or used as collateral elsewhere, such as DeFi protocols to be lent, borrowed, or traded. For this reason, liquid staking coins are sometimes referred to as liquid staking derivatives (LSD). Major L1s such as Ethereum, Polygon, Solana, Polkadot, and so on all adopted liquid staking and achieved tremendous success in improving the number of staked coins (and thus the underlying security of the blockchain). However, staking on IoTeX L1 is implemented in a native way, which does not support interaction from smart contracts. Hence, popular liquid staking solutions can not be applied directly.
To better support liquid staking and other innovations at the smart contracts or dApp level, this IIP suggests adding support natively to represent staking buckets as Non-fungible Tokens on the IoTeX blockchain. Therefore, liquid staking solutions, which rely on smart contracts, could manage their stakes in system smart contracts. With this feature implemented and deployed on testnet and mainnet, builders can easily launch their respective liquid staking dApps such as Lido and RocketPool on IoTeX L1.
To support the native representation of staking buckets as NFTs, the high-level intuition is simple: we let users stake via newly-added system contracts to get NFTs, while the updated staking subprotocol on-chain combines the number of staked IOTX from both the existent staking subprotocol and the system contracts introduced.
There are three major components in this proposal: the system contract, the native modification of the staking subprotocol, and a pre-compiled contract to expose delegate and staking info to dApps. The overall flow is listed below where the orange boxes are the new components introduced in this proposal.
Modification of the staking subprotocol involves a few native changes such as
Since the main flow will not be changed, limited modifications are needed in staking subprotocol.
System Contract, deployed and managed by gnosis safe, manages a new set of buckets that have the following attributes:
Three major functions of the system contract are:
function stake(uint256 duration, bytes12 delegate) external payable returns (uint256);
function stake(uint256 amount, uint256 duration, bytes12[] calldata delegates) external payable returns (uint256[] memory);
function unstake(uint256 tokenId) external onlyStakedToken(tokenId) onlyTokenOwner(tokenId);
function unlock(uint256 tokenId) external returns (uint256);
function lock(uint256 tokenId, uint256 duration) external);
function withdraw(uint256 tokenId, address payable recipient) external onlyTokenOwner(tokenId);
function extendDuration(uint256 tokenId, uint256 duration) external onlyLockedToken(tokenId) onlyTokenOwner(tokendId);
function increaseAmount(uint256 tokenId, uint256 amount) external onlyLockedToken(tokenId) onlyTokenOwner(tokendId);
function changeDelegate(uint256 tokenId, bytes12 delegate) external onlyStakedToken(tokenId) onlyTokenOwner(tokenId);
function changeDelegates(uint256[] calldata tokenIds, bytes12 delegate) external;
function emergencyWithdraw(uint256 tokenId, address payable recipient) external;
function emergencyWithdrawPenaltyRate() external view returns (uint256);
function isActiveBucketType(uint256 _amount, uint256 _duration) external view returns (bool);
function numOfBucketTypes() public view returns (uint256);
function bucketTypes(uint256 _offset, uint256 _size) external view returns (BucketType[] memory types_);
function blocksToUnstake(uint256 _tokenId) public view onlyStakedToken(_tokenId) returns (uint256);
function blocksToWithdraw(uint256 _tokenId) public view onlyValidToken(_tokenId) returns (uint256);
function bucketOf(uint256 _tokenId) external view onlyValidToken(_tokenId) returns (uint256, uint256, uint256, uint256, bytes12);
function lockedVotesTo(bytes12[] calldata _delegates) external view returns (uint256[][] memory counts_);
function unlockedVotesTo(bytes12[] calldata _delegates) external view returns (uint256[][] memory counts_);
This pre-compiled contract will be responsible to expose staking information such as the number of delegates and who are they, to dApp contracts deployed on the IoTeX blockchain to improve dApps' using experience. This pre-compiled contract will use the address 0x10002.
function delegates(uint16 _offset, uint16 _size) external view returns (Delegate[]); (function hash 48b4b63d)
function numOfDelegates() external view returns (uint16); (function hash 13c8852e)
function lookup(bytes12 _delegate) external view returns (Delegate); (function hash 77b0a032)
We provide an example of liquid staking dApp here to demonstrate how it can interact with the system contract. This example dApp can do the following:
The proposed approach maximizes upgradability, security, and flexibility.
Users who stake via the original staking subprotocol are not affected. The native buckets could be moved to system contract at any time by sending a migration action. A native bucket with amount a and duration d could be migrated as n system contract buckets with amount a' and d', if
d' is not smaller than d.a' * n = a + diff, where diff is the value ($IOTX) of the migration transaction.
The new bucket in system contract will be an auto-staking bucket.Copyright and related rights waived via CC0.