feat(horizon): expose Provision.delegatedTokensActive as the APR/APY denominator#331
Conversation
Context: off-chain fix for the delegation APR/APY distortionThis PR is the off-chain half of the fix discussed in forum: Separate withdrawable bucket for fully-thawed delegation tokens and Accurately Representing Delegated GRT in APR/APY Calculations. The key point: thawed-but-unwithdrawn delegation stays counted in For completeness, a hardened protocol-level variant (exposing the same split on-chain, with the storage/slashing issues from the original attempt fixed) is available at graphprotocol/contracts#1340 — but the recommendation is to fix this off-chain via this subgraph change. |
feat(horizon): expose
Provision.delegatedTokensActiveas the APR/APY denominatorSummary
Adds an explicit, maintained
delegatedTokensActivefield to theProvisionentity:This is the correct denominator for delegation APR/APY. It already exists implicitly
(the subgraph computes the same subtraction for
delegationExchangeRateand indexercapacity), but consumers computing returns tend to divide by
delegatedTokens, whichincludes both in-period thawing and completed-but-not-yet-withdrawn delegation —
systematically depressing reported APR/APY.
Why this is the right layer
Thawed-but-unwithdrawn delegation remains counted in
delegatedThawingTokens(mirroringthe on-chain
tokensThawing, which is only decremented whenwithdrawDelegatedfulfils arequest — not when the thaw clock merely expires). So subtracting
delegatedThawingTokensalready excludes it. No protocol change is required; this is purely a reporting fix, which
is the conclusion reached in the forum discussion
(thread).
For consumers who additionally want the in-period vs completed split of thawing
delegation, the existing
ThawRequestentity (thawingUntil,fulfilled,type) isalready sufficient to compute it client-side.
Changes
Provision.delegatedTokensActive: BigInt!with documentation.createOrLoadProvision: initialise the field.updateAdvancedProvisionMetrics: recompute it (covers delegate, add-to-pool,undelegate and slash paths, which already call this helper).
handleDelegatedTokensWithdrawn: set the field directly (thishandler doesn't go through
updateAdvancedProvisionMetrics).Validation
yarn prepare:arbitrum(codegen) ✅ — schema parses, types regenerate.yarn build(AssemblyScript compile) ✅.Note:
yarn testcurrently fails to compile onmasterdue to a pre-existing,unrelated issue in
tests/helpers.test.ts(callscreateOrLoadIndexerwith 2 args; ittakes 3). Not touched by this PR.
Notes / scope
Provision(the Horizon delegation unit). The legacyIndexer-levelequivalent is derivable the same way (
Indexer.delegatedTokens - Indexer.delegatedThawingTokens) and is already used internally for capacity; happy toadd an
Indexer.delegatedTokensActivemirror if wanted.all provisions via
createOrLoadProvision; no migration concern.