-
Notifications
You must be signed in to change notification settings - Fork 30
Description
Problem
figuring out exactly what state a stake account is in is complex and error-prone:
- determining if a stake account is active, activating, deactivating, or inactive is typically done via a combination of matching on
StakeStateV2enum variants and manually comparingDelegation.activation_epochandDelegation.deactivation_epochtoClock.epochandu64::MAX - a common mistake (that i have made myself) is doing the steps in 1 and looking at
Delegation.stake. this is not correct: it fails to consider partial activation or deactivation in the case of warmup or cooldown, and the "delegated" amount is left intact for an inactive account that was previously active - the true way to determine stake account state is to call
stake_activating_and_deactivating()onDelegationwithClockandStakeHistory. this returnsStakeActivationStatus, which is just a type alias forStakeHistoryEntry, which is a triple ofeffective,activating, anddeactivating. this can be used to determine the true state of a stake account, but:- it requires end-users to compare values by hand
- you still need to match on
StakeStateV2for uninitialized and initialized accounts - distinguishing between deactivating and partially deactivated also requires looking at
Delegation.stake
Solution
add a new enum StakeAccountStatus or similar, with variants: uninitialized, activating, partially active, fully active, deactivating, partially deactivated, fully inactive. abstract over the distinction between an initialized account and a fully deactivated account. carry StakeActivationStatus as a value on variants where it makes sense. have whatever boolean-return helpers are convenient. possibly include helpers for determining things about minimum delegation and excess undelegated lamports
most of the required logic is actually already implemented: its just part of MergeKind, an internal stake program helper for Merge (also now used by MoveStake and MoveLamports because it is so convenient, but not Split, Withdraw, or anything else). we should abstract this out, move it into sdk, and use it across all relevant stake program process functions and the stake pools to replace the ad hoc way we do all this now
until very recently the hard requirement on StakeHistory made this kind of accounting a huge burden for stake operations that didnt truly "need" it. but last week (note: this issue was originally opened in february) we activated get_sysvar on mainnet, which enables StakeHistory access without:
- needing to include the account in the accounts list
- needing to deserialize it into an
AccountInfo - needing to store a 16kb vec on the heap
with the ability to get individual stake history entries via syscall (and only one is needed if there is no warmup/cooldown), we can implement a one true way of doing this now
(this is the new home of anza-xyz/solana-sdk#61)