-
Notifications
You must be signed in to change notification settings - Fork 47
Description
Description
In Horizon mode, the Indexer.delegatedTokens field in the subgraph does not match the actual delegation.tokens value from the HorizonStaking contract's delegation pool. This causes tokenCapacity to be underestimated.
Observed Behavior
For indexer 0xf92f430dd8567b0d466358c79594ab58d919a6d4:
| Source | Value (GRT) |
|---|---|
Subgraph Indexer.delegatedTokens |
116,157,557.38 |
Contract getDelegationPool().tokens |
117,126,781.76 |
| Difference | 969,224.39 (~0.83%) |
This propagates to tokenCapacity:
| Source | Value (GRT) |
|---|---|
Subgraph tokenCapacity |
122,223,783.22 |
Contract getTokensAvailable() |
123,193,007.61 |
| Difference | 969,224.39 |
Root Cause Analysis
The subgraph calculates delegationExchangeRate = delegatedTokens / delegatorShares.
Comparing exchange rates:
- Subgraph:
1.772679126585520324 - Contract (actual):
1.787470448629024056
The subgraph's exchange rate is stale. With 65.5M shares, this rate difference of ~0.0148 produces the ~969k GRT discrepancy.
Hypothesis
In Horizon mode, rewards (indexing rewards + query fees) accumulate in the delegation pool and increase delegation.tokens in the contract. The subgraph updates delegatedTokens via TokensToDelegationPoolAdded events in handleTokensToDelegationPoolAdded, but it appears that:
- Some reward accumulation events may not be triggering the update, OR
- The
Indexer.delegatedTokensis not being updated whenProvision.delegatedTokenschanges, OR - There's a timing/synchronization issue between when rewards are added to the contract pool and when corresponding events are emitted
Steps to Reproduce
- Query the subgraph for an active indexer with delegations:
{
indexer(id: "0xf92f430dd8567b0d466358c79594ab58d919a6d4") {
delegatedTokens
delegatorShares
delegationExchangeRate
tokenCapacity
}
}- Query the contract directly:
# Get delegation pool
cast call 0x00669A4CF01450B64E8A2A20E9b1FCB71E61eF03 \
"getDelegationPool(address,address)((uint256,uint256,uint256,uint256,uint256))" \
0xf92f430dd8567b0d466358c79594ab58d919a6d4 \
0xb2Bb92d0DE618878E438b55D5846cfecD9301105 \
--rpc-url https://arb1.arbitrum.io/rpc
# Get tokens available
cast call 0x00669A4CF01450B64E8A2A20E9b1FCB71E61eF03 \
"getTokensAvailable(address,address,uint32)(uint256)" \
0xf92f430dd8567b0d466358c79594ab58d919a6d4 \
0xb2Bb92d0DE618878E438b55D5846cfecD9301105 \
16 \
--rpc-url https://arb1.arbitrum.io/rpc- Compare
delegatedTokensfrom subgraph withdelegation.tokensfrom contract
Expected Behavior
Indexer.delegatedTokens should match the contract's getDelegationPool().tokens value (minus any thawing tokens that are excluded).
Impact
tokenCapacityis underestimated in the subgraphavailableStakecalculations are incorrect- Any tooling relying on subgraph data for capacity planning will have inaccurate data
Environment
- Network: Arbitrum One
- Subgraph: Graph Network Subgraph
- Contracts: HorizonStaking at
0x00669A4CF01450B64E8A2A20E9b1FCB71E61eF03 - SubgraphService at
0xb2Bb92d0DE618878E438b55D5846cfecD9301105