NCIP | Title | Status | Type | Author | Created |
---|---|---|---|---|---|
17 |
Revise `ClaimStakeReward` |
Draft |
Core |
Swen Mun <swen@planetariumhq.com>, Nine Chronicles DX team <engineering@planetariumhq.com> et al. |
2023-09-05 |
This proposal addresses a fairness problem on staking system, by a new reward scheme with slightly modifications for chain states and related actions.
When staking reward policy changes from A
to B
, current calculation for staking reward is,
Total Reward
= A(Expiration Block Index of A - Staked Block Index)
+ B(Current Block Index - Expiration Block Index of A)
Although it seems quite accurate, there're two problems as below.
- Fairness
- When users stake with
A
, there is no information aboutB
for users. - This is not a problem when
B
's reward size is greater thanA
's, but it isn't otherwise.
- When users stake with
- Complexity
- To keep current scheme, we need to assign expiration block index for each reward policy.
- Also, we must perform a hard-fork for that action changes, per each revision of reward policy, regardless its complexity.
To eliminate fairness and complexity concern, this proposal suggests more simplified reward scheme that, only applied A
if users saw and staked upon A
, not B
(or any other future changes).
To simplify, ClaimStakeReward
action should know reward policy for each users. specifically, the following changes are required:
Stake
action needs to record the reward policy at the time of staking. To accomplish this, we need to keep reward policy as immutable and dedicate state address per reward policy. To keep reward policy as immutable, it stores the reward policy in a new state model called StakeStateV2
.
StakeStateV2
is a new state model to store its staking Contract in the state.
From StakeStateV2
, a Contract
is created with the information at the time of the staking contract and stored in the state. This is to be used when receiving rewards.
The state is stored in List format rather than Dictionary. The schema is as follows.
[
"stake_state", # Constant string. state type name.
2, # Constant number. state type version.
serialized(Contract), # A contract to keep the reward policy as immutable.
StaredBlockIndex, # The block index when the staking contract has been started.
ReceivedBlockIndex, # The block index when the staking rewards were claimed most recently.
]
For sake of ease, the new state model called Contract
will be needed. The Contract
model consists of for fields as below:
StakeRegularFixedRewardSheetTableName
(string
)StakeRegularRewardSheetTableName
(string
)RewardInterval
(long
)LockupInterval
(long
)
The state is stored in List format rather than Dictionary. The schema is as follows.
[
"stake_contract", # Constant string. state type name.
1, # Constant number. state type version.
StakeRegularFixedRewardSheetTableName, # The `StakeRegularFixedRewardSheet` tablesheet's name from `StakePolicySheet` when the staking contract was signed.
StakeRegularRewardSheetTableName, # The `StakeRegularRewardSheet` tablesheet's name from `StakePolicySheet` when the staking contract was signed.
RewardInterval, # The block interval to claim staking rewards of this staking contract.
LockupInterval, # The block interval to cancel this staking contract.
]
ClaimStakeReward
action needs to be processed based only recorded policy, without any other aggregation.
Staking actions before stake3
and claim_stake_reward9
, stores StakeState
to record when the staking contract has been started and when the staking rewards were claimed. The stake3
and claim_stake_reward9
actions apply NCIP-17 with rules to migrate StakeState
to StakeStateV2
, and that time the Contract
of StakeStateV2
is determined as follows.(see also GameConfigSheet)
StakeRegularFixedRewardSheetTableName
: StakeRegularFixedRewardSheet_V2StakeRegularRewardSheetTableName
: StakeRegularRewardSheet_V5RewardInterval
: 50,400(StakeState.RewardInterval)LockupInterval
: 201,600(StakeState.LockupInterval)
You'll receive the staking rewards based on those contract.
- This proposal requires the hard-forks as like below reasons:
- The proposed
Stake
action will produce different output state due to additional fields.(i.e., reward policy) - The proposed
ClaimStakeReward
action will produce different output state since it wouldn't aggregate based upon previous policies anymore.
- The proposed
- It means that, every nodes in the network must be updated to apply this changes.