diff --git a/KIPs/kip-163.md b/KIPs/kip-163.md index 4954d033..9631253a 100644 --- a/KIPs/kip-163.md +++ b/KIPs/kip-163.md @@ -15,12 +15,14 @@ Introducing a new CnStakingV3 with public delegation. ## Abstract -The new CnStakingV3 will be compatible with public delegation. The public delegation will support `delegation` and `redelegation` for general users. +The new CnStakingV3 will be compatible with public delegation. The public delegation will support `delegation` and `redelegation` for general users. Also, the staking information interval will be changed to 1 block, which is currently 86,400 blocks. ## Motivation In KIP-81, Klaytn introduced CnStakingV2. However, it’s difficult for general users to delegate their KLAY if a validator doesn’t provide public delegation services. For example, users can delegate to `SwapScanner` and `KommuneDAO` since they provide their own public delegation services. This narrowed the delegation options to a small number of GC members. In addition, Klaytn expects various GC members to join in the merged chain; there is a strong need to introduce a new CnStakingV3 with public delegation. +The `redelegation` can make massive staking changes in a short period, which can lead to non-negligible errors in the validator set and reward distribution with current 86,400 blocks interval. To prevent this, the staking information intervl will be changed to 1 block. + ## Specification The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119. @@ -29,23 +31,25 @@ The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL CnStakingV3 is basically an upgraded version of CnStakingV2, so it must be compatible with the existing core contracts like `AddressBook`, `StakingTracker` and so on. The notifying changes include the followings: -- CnStakingV3 supports public delegation natively, allowing it to receive delegations and re-delegation from general users. +- CnStakingV3 supports public delegation natively, allowing it to receive delegations and re-delegation from general users. -- Re-delegation is supported between CnStakingV3 enabling public delegation and re-delegation. +- Re-delegation is supported between CnStakingV3 enabling public delegation and re-delegation. A GC member can enable public delegation from CnStakingV3. If it is enabled, all staking functionalities must be proxied via a public delegation contract defined in this proposal, namely `PublicDelegation`. When users want to delegate their KLAY, they stake KLAY to `PublicDelegation` and receive `shares` in return, which represent users' assets. `PublicDelegation` shall be implemented based on an interest-bearing token model, especially ERC-4626. However, `shares` must not be transferable. +Staking information for block `N` will come from block `N-1` instead of `CalcStakingBlockNumber(N)` from `FORK_BLOCK`. + ### Smart Contracts Overview This KIP contains upgraded and newly implemented smart contracts. The overall structure is shown in below diagram. -- CnStakingV3: an upgraded version of CnStakingV2. It can enable public delegation and use re-delegation between CNs enable public delegation. +- CnStakingV3: an upgraded version of CnStakingV2. It can enable public delegation and use re-delegation between CNs enable public delegation. -- IKIP163: an interface that a public delegation must implement to be compatible with CnStakingV3. +- IKIP163: an interface that a public delegation must implement to be compatible with CnStakingV3. -- PublicDelegation: a public delegation contract provided by Klaytn. Note that it will be only compatible with CnStakingV3. +- PublicDelegation: a public delegation contract provided by Klaytn. Note that it will be only compatible with CnStakingV3. Henceforth, CnStakingV3 shall be denoted as CNv3, and Public Delegation as PD. @@ -114,10 +118,10 @@ Deploys and sets a PD to CnV3. The `PublicDelegation` will be deployed via `_pdF The function validates the following requirement(s): -- The function MUST revert if PD is not enabled. -- The function MUST revert if CnV3 already initialized. -- The function MUST revert if `_pdArgs` isn’t encoded correctly. -- The function MUST revert if PD deployment fails. +- The function MUST revert if PD is not enabled. +- The function MUST revert if CnV3 already initialized. +- The function MUST revert if `_pdArgs` isn’t encoded correctly. +- The function MUST revert if PD deployment fails. The function emits `SetPublicDelegation` event. @@ -129,12 +133,12 @@ Requests CNv3 to redelegate `_user`’s `_value` of KLAY to `_targetCnV3`. CNv3 The function validates the following requirement(s): -- The function MUST revert if CNv3 does not allow PD, or if the caller is not PD. -- The function MUST revert if CNv3 does not allow redelegation. -- The function MUST revert if `_targetCnV3` is itself. -- The function MUST revert if `_targetCnV3` isn’t a valid CNv3 staking contract registered in AddressBook. -- The function MUST revert if `_value` is larger than total staking amount. -- The function MUST revert if `lastRedelegation[_user] != 0 && lastRedelegation[_user] + lockup <= block.timestamp`. +- The function MUST revert if CNv3 does not allow PD, or if the caller is not PD. +- The function MUST revert if CNv3 does not allow redelegation. +- The function MUST revert if `_targetCnV3` is itself. +- The function MUST revert if `_targetCnV3` isn’t a valid CNv3 staking contract registered in AddressBook. +- The function MUST revert if `_value` is larger than total staking amount. +- The function MUST revert if `lastRedelegation[_user] != 0 && lastRedelegation[_user] + lockup <= block.timestamp`. The function emits `Redelegation` event. @@ -146,11 +150,11 @@ Handles redelegation request from departure CNv3. This CNv3 will stake msg.value The function validates the following requirement(s): -- The function MUST revert if CNv3 does not allow PD. -- The function MUST revert if CNv3 does not allow redelegation. -- The function MUST revert if departure CNv3 isn’t a valid CNv3 staking contract registered in AddressBook. -- The function MUST revert if balance after calling `stakeFor` is lower than expected. - - In `stakeFor`, PD will stake `msg.value` back to CNv3. It means CNv3 can compute deterministic balance changes. +- The function MUST revert if CNv3 does not allow PD. +- The function MUST revert if CNv3 does not allow redelegation. +- The function MUST revert if departure CNv3 isn’t a valid CNv3 staking contract registered in AddressBook. +- The function MUST revert if balance after calling `stakeFor` is lower than expected. + - In `stakeFor`, PD will stake `msg.value` back to CNv3. It means CNv3 can compute deterministic balance changes. The function emits `HandleRedelegation` event. @@ -205,8 +209,8 @@ When block reward is auto-compounding, the commission is calculated and paid aut Since PD is a type of token contract, its name and symbol will be determined as follows: -- Name: `{gcName} Public Delegated KLAY` (ex. `KF Public Delegated KLAY`) -- Symbol: `{gcName}-pdKLAY` (ex. `KF-pdKLAY`) +- Name: `{gcName} Public Delegated KLAY` (ex. `KF Public Delegated KLAY`) +- Symbol: `{gcName}-pdKLAY` (ex. `KF-pdKLAY`) ##### Interface @@ -350,20 +354,20 @@ interface IPublicDelegation is IKIP163 { The PublicDelegation has constants related to commission. -- `MAX_COMMISSION_RATE`: Max commission rate. It will be 3,000. (= 30%) -- `COMMISSION_DENOMINATOR`: Commission denominator. It will be 10,000. +- `MAX_COMMISSION_RATE`: Max commission rate. It will be 3,000. (= 30%) +- `COMMISSION_DENOMINATOR`: Commission denominator. It will be 10,000. ##### Methods All math for managing a user's staking follows ERC4626 standard. Note that PD isn’t based on ERC20 assets, but native KLAY. -- `totalAssets()`: Total assets managed by PD. It contains reward. -- `maxRedeem(address owner)`: Max reedeemable shares of owner. -- `maxWithdraw(address owner)`: Max withdrawable KLAY of owner. -- `convertToAssets(uint256 shares)`: Expected KLAY when convert shares. -- `previewDeposit(uint256 assets)`: Expected shares when deposit assets of KLAY. -- `previewWithdraw(uint256 assets)`: Expected shares to withdraw assets of KLAY. -- `previewRedeem(uint256 shares)`: Expected KLAY when redeem shares. +- `totalAssets()`: Total assets managed by PD. It contains reward. +- `maxRedeem(address owner)`: Max reedeemable shares of owner. +- `maxWithdraw(address owner)`: Max withdrawable KLAY of owner. +- `convertToAssets(uint256 shares)`: Expected KLAY when convert shares. +- `previewDeposit(uint256 assets)`: Expected shares when deposit assets of KLAY. +- `previewWithdraw(uint256 assets)`: Expected shares to withdraw assets of KLAY. +- `previewRedeem(uint256 shares)`: Expected KLAY when redeem shares. To enable auto-compounding, PD stakes cumulative KLAY rewards to CNv3 automatically when the below functions are called or call `sweep` function explicitly. @@ -375,7 +379,7 @@ It updates commission receiver address of PD. Previously accumulated commission The function validates the following requirement(s): -- The function MUST revert if caller is not an owner. +- The function MUST revert if caller is not an owner. The function emits `UpdateCommissionTo` event. @@ -387,8 +391,8 @@ It updates commission rate of PD. Previously accumulated rewards must follow pre The function validates the following requirement(s): -- The function MUST revert if caller is not an owner. -- The function MUST revert if `_commissionRate` is higher than `MAX_COMMISSION_RATE`. +- The function MUST revert if caller is not an owner. +- The function MUST revert if `_commissionRate` is higher than `MAX_COMMISSION_RATE`. The function emits `UpdateCommissionRate` event. @@ -405,8 +409,8 @@ Call `delegate` with `msg.value` to CNv3 internally. It must mint corresponding The function validates the following requirement(s): -- The function MUST revert if user would receive 0 shares. -- The function MUST revert if delegate call reverts. +- The function MUST revert if user would receive 0 shares. +- The function MUST revert if delegate call reverts. The function emits `Staked` event. @@ -424,9 +428,9 @@ Call `approveStakingWithdrawal` to CNv3 internally. It must burn corresponding s The function validates the following requirement(s): -- The function MUST revert if user requests to withdraw 0 KLAY. -- The function MUST revert if user withdraws more than staked. -- The function MUST revert if `approveStakingWithdrawal` call reverts. +- The function MUST revert if user requests to withdraw 0 KLAY. +- The function MUST revert if user withdraws more than staked. +- The function MUST revert if `approveStakingWithdrawal` call reverts. The functions emit `RequestWithdrawal` event. @@ -442,8 +446,8 @@ Call `cancelApprovedStakingWithdrawal` to CNv3 internally. User’s shares must The function validates the following requirement(s): -- The function MUST revert if a user attempts to withdraw another user’s request. -- The function MUST revert if `cancelApprovedStakingWithdrawal` call reverts. +- The function MUST revert if a user attempts to withdraw another user’s request. +- The function MUST revert if `cancelApprovedStakingWithdrawal` call reverts. The function emits `RequestCancelWithdrawal` event. @@ -460,8 +464,8 @@ Calls `withdrawApprovedStaking` to CNv3 internally. If the withdrawal was cancel The function validates the following requirement(s): -- The function MUST revert if a user attempts to claim another user’s request. -- The function MUST revert if `withdrawApprovedStaking` call reverts. +- The function MUST revert if a user attempts to claim another user’s request. +- The function MUST revert if `withdrawApprovedStaking` call reverts. The function emits `Claimed` if withdrew successfully. @@ -479,14 +483,50 @@ Call `redelegate` to CNv3 internally. It must burn corresponding shares of users The function validates the following requirement(s): -- The function MUST revert if `_targetCnV3` isn’t a valid CNv3 staking contract. -- The function MUST revert if current CNv3 doesn’t allow redelegation. -- The function MUST revert if a user redelegates more KLAY than staked. -- The function MUST revert is a user redelegates 0 KLAY. -- The function MUST revert if `redelegate` call reverts. +- The function MUST revert if `_targetCnV3` isn’t a valid CNv3 staking contract. +- The function MUST revert if current CNv3 doesn’t allow redelegation. +- The function MUST revert if a user redelegates more KLAY than staked. +- The function MUST revert is a user redelegates 0 KLAY. +- The function MUST revert if `redelegate` call reverts. The functions emit `Redelegated` event. +### Core Logic + +From `FORK_BLOCK`, the staking information for block `N` will be recorded in block `N-1` instead of `CalcStakingBlockNumber(N)`. + +#### Parameters + +| Constant | Value | +| ------------ | ----- | +| `FORK_BLOCK` | TBD | + +```go +// In staking_manager.go +func GetStakingInfo(blockNum uint64) *StakingInfo { + stakingBlockNumber := blockNum + var stakingInfo *StakingInfo + if isForkBlockEnabled(blockNum) { + // After `FORK_BLOCK`, staking information for block `N` will be recorded in block `N-1`. + if blockNum > 0 { + stakingBlockNumber-- + } + stakingInfo = GetStakingInfoForForkBlock(stakingBlockNumber) + } else { + // Before `FORK_BLOCK`, staking information for block `N` will be recorded in `CalcStakingBlockNumber(N)`. + stakingBlockNumber = params.CalcStakingBlockNumber(blockNum) + stakingInfo = GetStakingInfoOnStakingBlock(stakingBlockNumber) + } + return stakingInfo +} +``` + +| Function | Description | +| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | +| `GetStakingInfo(blockNum)` | Returns staking information needed to handle block `blockNum`. | +| `GetStakingInfoForForkBlock(blockNum)` | Returns staking information at block `blockNum`. If `blockNum` is before `FORK_BLOCK - 1`, return nil. | +| `GetStakingInfoOnStakingBlock(blockNum)` | Returns staking information at staking block `blockNum`. If `blockNum` is not a staking interval block nor after `FORK_BLOCK`, return nil. | + ## Rationale ### Non-transferable shares in PublicDelegation @@ -522,9 +562,9 @@ TBA ## References -- [ERC4626](https://eips.ethereum.org/EIPS/eip-4626) -- [Inflation Attack](https://blog.openzeppelin.com/a-novel-defense-against-erc4626-inflation-attacks) -- [Cosmos Staking Module](https://docs.cosmos.network/v0.46/modules/staking/) +- [ERC4626](https://eips.ethereum.org/EIPS/eip-4626) +- [Inflation Attack](https://blog.openzeppelin.com/a-novel-defense-against-erc4626-inflation-attacks) +- [Cosmos Staking Module](https://docs.cosmos.network/v0.46/modules/staking/) ## Copyright