feat(horizon): add delegation withdrawable bucket (tokensWithdrawable)#1340
Open
cargopete wants to merge 1 commit into
Open
feat(horizon): add delegation withdrawable bucket (tokensWithdrawable)#1340cargopete wants to merge 1 commit into
cargopete wants to merge 1 commit into
Conversation
…hdrawable tokens Introduces pool-level and per-delegator to track delegation that has completed thawing but not yet been withdrawn. Adds a permissionless so bots and dashboards can maintain accurate pool accounting without requiring each delegator to call first. Key changes: - DelegationPool / DelegationPoolInternal: add tokensWithdrawable field - DelegationInternal: add tokensReleasedPendingWithdrawal field - _getDelegatedTokensAvailable: tokens - tokensThawing - tokensWithdrawable - _undelegate: use updated active-base formula when converting shares - _withdrawDelegated: call _releaseThawedDelegation, drain per-delegator tally - New _releaseThawedDelegation (private) + releaseThawedDelegation (external) - New DelegationThawReleased event - New getDelegatedTokensWithdrawable view getter
feat(horizon): add delegation withdrawable bucket (tokensWithdrawable)
🚨 Report Summary
For more details view the full report in OpenZeppelin Code Inspector |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements the "withdrawable bucket" design proposed by PaulieB in the Graph Forum (Accurately Representing Delegated GRT in APR/APY Calculations), which addresses a distortion in delegation APR/APY metrics caused by thawed-but-not-yet-withdrawn tokens remaining in the delegation pool denominator.
The Problem
When a delegator calls
undelegate, their tokens enter a 28-day thaw period. After the thaw completes, the tokens still sit inpool.tokensuntilwithdrawDelegatedis explicitly called. This means:pool.tokensincludes tokens that no longer earn rewardspool.tokensare systematically depressedThe Fix
This PR introduces a pool-level
tokensWithdrawablebucket — a running total of delegation that has completed thawing but hasn't been withdrawn yet. A new permissionless functionreleaseThawedDelegationallows anyone (bots, dashboards, delegators) to scan expired thaw requests and move them into this bucket without transferring tokens.The key formula change:
This correctly reflects only the actively-earning delegation base for APR/APY and capacity calculations.
Changes
IHorizonStakingTypes.solDelegationPool(public struct): addtokensWithdrawableDelegationPoolInternal: addtokensWithdrawableDelegationInternal: addtokensReleasedPendingWithdrawal(per-delegator tally)IHorizonStakingMain.solDelegationThawReleasedeventreleaseThawedDelegation(serviceProvider, verifier, delegator, nThawRequests)external functionIHorizonStakingBase.solgetDelegatedTokensAvailableNatSpec to document new formulagetDelegatedTokensWithdrawableview getterHorizonStakingBase.solgetDelegationPool: exposetokensWithdrawablein public return valuegetDelegatedTokensWithdrawable: implemented_getDelegatedTokensAvailable: updated formulaHorizonStaking.sol_undelegate: active-base fix — excludetokensWithdrawablewhen computing share → token conversion_withdrawDelegated: calls_releaseThawedDelegationfirst (handles the common case where delegators skip the explicit release step), then drainsdelegation.tokensReleasedPendingWithdrawalreleaseThawedDelegationexternal function (permissionless wrapper)_releaseThawedDelegationprivate implementationBackward Compatibility
withdrawDelegatedcalls_releaseThawedDelegationinternally as a lazy first stepreleaseThawedDelegationis a pure value-add: bots or dashboards can call it to keeptokensWithdrawableaccurate before APR/APY snapshotsTesting Checklist
_releaseThawedDelegationprocesses expired requests and skips unexpired onespool.tokensWithdrawableis correctly incremented/decremented across release → withdraw lifecyclepool.tokensThawing/sharesThawingare correctly decremented on releasedelegation.tokensReleasedPendingWithdrawalis zeroed afterwithdrawDelegatedgetDelegatedTokensAvailablereturnstokens - tokensThawing - tokensWithdrawable_undelegateshare-value calculation uses corrected active basereleaseThawedDelegationis callable by any address (permissionless)withdrawDelegatedwith no priorreleaseThawedDelegationcall still works end-to-endRef: https://forum.thegraph.com/t/accurately-representing-delegated-grt-in-apr-apy-calculations/6924