Skip to content

Commit 37faefe

Browse files
committed
docs: some more docs about ugprade
Signed-off-by: Tomás Migone <tomas@edgeandnode.com>
1 parent feea7ce commit 37faefe

2 files changed

Lines changed: 172 additions & 6 deletions

File tree

docs/IndexerRewardsUpgradeGuide.md

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
# Indexer Guide: What's Changing With the REO + Issuance Upgrade
2+
3+
## What is NOT changing
4+
5+
- The allocation lifecycle: open → present POIs → close.
6+
- How and when POIs are presented, and the POI cadence.
7+
- The staleness model (allocations still go stale if you stop presenting POIs).
8+
- Delegation, provisions, and stake mechanics.
9+
- **Rewards rate at launch** — issuance to indexing rewards is the same as before the upgrade.
10+
11+
---
12+
13+
## What IS changing
14+
15+
### 1. Issuance routing
16+
17+
```mermaid
18+
flowchart TB
19+
subgraph Before["Before — direct issuance"]
20+
direction LR
21+
P1["Protocol issuance"] --> R1["Indexing rewards pool"] --> I1["Indexers & delegators"]
22+
end
23+
subgraph After["After — routed through the allocator"]
24+
direction LR
25+
P2["Protocol issuance"] --> A{"Issuance Allocator"}
26+
A -->|100% at launch| R2["Indexing rewards pool"]
27+
A -.->|0% — dormant / future| D["DIPs (indexing-agreements path)"]
28+
R2 --> I2["Indexers & delegators"]
29+
end
30+
Before ~~~ After
31+
```
32+
33+
Issuance now flows through a central allocator that can split rewards across multiple destinations.
34+
At launch the effective flow is **identical** to today — 100% still reaches the indexing rewards pool. The allocator only adds the _ability_ to split issuance later; it doesn't divert anything on day one.
35+
36+
**What to watch:** when Indexer Agreements are activated later, expect a communicated change to the indexing rewards rate at that time.
37+
38+
### 2. Rewards Eligibility Oracle
39+
40+
The protocol can now consult an **eligibility oracle** to decide whether an indexer is eligible for rewards at claim time:
41+
42+
- The oracle will be enabled at launch with a very low threshold to qualify for eligibility.
43+
- If the oracle stops receiving updates, it treats everyone as eligible again, so a stalled oracle can't block rewards.
44+
45+
Useful links
46+
47+
- <https://hub.thegraph.foundation/reo>
48+
- <https://github.com/graphprotocol/rewards-eligibility-oracle/blob/main/ELIGIBILITY_CRITERIA.md>
49+
50+
### 3. Reward conditions and reclaiming
51+
52+
Today, when rewards can't be paid to an indexer (stale POI, denied subgraph, etc.), those tokens are **silently dropped**, they are never minted to anyone. After this upgrade, those same rewards become **reclaimed**: minted to a protocol reclaim address instead of being dropped.
53+
54+
**Important:** reclaiming does **not** take indexing rewards that were previously earned by indexers, only rewards that were already being forfeited are now captured.
55+
56+
| Condition | Trigger | Scope | Rewards outcome | Notes |
57+
| ---------------------- | ------------------------------------------------------- | ---------- | -------------------------- | --------------------------------------------------------------------- |
58+
| `NONE` | Valid POI on a non-denied subgraph, eligible allocation | Allocation | **Collected** | |
59+
| `NO_SIGNAL` | Zero total curation signal globally | Global | **Reclaimed** | |
60+
| `SUBGRAPH_DENIED` | Subgraph is on the denylist | Subgraph | **Preserved** | Claimable if the subgraph is undenied (and allocation was not closed) |
61+
| `BELOW_MINIMUM_SIGNAL` | Subgraph signal below `minimumSubgraphSignal` | Subgraph | **Reclaimed** | |
62+
| `NO_ALLOCATED_TOKENS` | Subgraph has signal but zero allocated tokens | Subgraph | **Reclaimed** | |
63+
| `STALE_POI` | POI presented after staleness deadline | Allocation | **Reclaimed** | |
64+
| `ZERO_POI` | POI is `bytes32(0)` | Allocation | **Reclaimed** | |
65+
| `ALLOCATION_TOO_YOUNG` | Allocation created in the current epoch | Allocation | **Preserved** | Claimable next epoch (assuming allocation was not closed) |
66+
| `CLOSE_ALLOCATION` | Allocation being closed with uncollected rewards | Allocation | **Reclaimed** | |
67+
| `INDEXER_INELIGIBLE` | Indexer fails eligibility oracle check at claim time | Indexer | **Preserved** — tx reverts | Claimable if you regain eligibility before staleness |
68+
69+
**Note**: Remember that by default the indexer agent combines "present POI" with "close allocation" when closing allocations.
70+
71+
### 4. POI Observability
72+
73+
Every POI presentation now records the **reward condition** that applied. If a POI didn't pay what you expected, the condition tells you exactly why instead of being invisible. This is surfaced through the network subgraph:
74+
75+
```graphql
76+
{
77+
allocations {
78+
id
79+
latestPoiCondition
80+
}
81+
}
82+
```
83+
84+
```json
85+
{
86+
"data": {
87+
"allocations": [
88+
{
89+
"id": "0x001a42df17784fe5efcd14dcb138b91df116aa62",
90+
"latestPoiCondition": "StalePoi"
91+
},
92+
{
93+
"id": "0x01d1522d4646b6119a2623d9b784cb9f1dfb924b",
94+
"latestPoiCondition": "StalePoi"
95+
},
96+
{
97+
"id": "0x0869e64ed4dbc73d961c3920e34ee7a3adf82fca",
98+
"latestPoiCondition": "StalePoi"
99+
},
100+
{
101+
"id": "0x13d53c7ddffbd00565f8ff7a0f2df0f19099075a",
102+
"latestPoiCondition": "StalePoi"
103+
},
104+
{
105+
"id": "0x144fb38b1c1418e1e0ba3d9f39e956e7e142ae37",
106+
"latestPoiCondition": "StalePoi"
107+
}
108+
]
109+
}
110+
}
111+
```
112+
113+
### 5. Eligibility enforcement revert
114+
115+
If your indexer is marked **ineligible** by the REO and you present a **normal, reward-bearing POI**, the presentation **reverts** (the transaction fails) instead of paying out.
116+
117+
Key points:
118+
119+
- **Your rewards are not burned.** They stay pending and remain claimable if you become eligible again **before the allocation goes stale**.
120+
- **The revert only affects reward-bearing POIs** (a valid, non-zero POI, on an old-enough allocation, on a non-denied subgraph). It does **not** block you from operating the allocation in other ways.
121+
- **You are never locked into an allocation.** While ineligible you can still:
122+
- **Present a zero POI** — succeeds, resets the staleness clock (keeps the allocation alive), but forfeits that period's rewards.
123+
- **Close the allocation** — succeeds; any uncollected rewards are reclaimed (forfeited), and you exit cleanly.
124+
125+
---
126+
127+
## Apendix: Reward criteria decision tree
128+
129+
The protocol evaluates these criteria in order when you present a POI, the first match wins:
130+
131+
```mermaid
132+
flowchart TD
133+
Start(["Present POI<br/>(first match wins)"])
134+
Start -->|"1 · stale"| CStale["STALE_POI<br/>Reclaimed"]
135+
Start -->|"2 · POI is 0x0"| CZero["ZERO_POI<br/>Reclaimed"]
136+
Start -->|"3 · created this epoch"| CYoung["ALLOCATION_TOO_YOUNG<br/>Preserved"]
137+
Start -->|"4 · subgraph denied"| CDenied["SUBGRAPH_DENIED<br/>Preserved"]
138+
Start -->|"5 · no rewards accrued"| CFrozen["NO_SIGNAL<br>BELOW_MINIMUM_SIGNAL<br>NO_ALLOCATED_TOKENS<br/>Preserved"]
139+
Start -->|"6 · ineligible"| CHeld["INDEXER_INELIGIBLE<br/>Preserved — tx reverts"]
140+
Start -->|"7 · otherwise"| CColl["NONE<br/>Collected"]
141+
142+
classDef collected fill:#1a7f37,stroke:#0b4a1f,color:#ffffff;
143+
classDef preserve fill:#0969da,stroke:#053a80,color:#ffffff;
144+
classDef reclaim fill:#9a6700,stroke:#5c3d00,color:#ffffff;
145+
classDef held fill:#cf222e,stroke:#82101c,color:#ffffff;
146+
classDef neutral fill:#57606a,stroke:#32383f,color:#ffffff;
147+
class CColl collected;
148+
class CYoung,CDenied preserve;
149+
class CStale,CZero reclaim;
150+
class CHeld held;
151+
class CFrozen neutral;
152+
```

docs/RewardsBehaviourChanges.md

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,16 @@ Changes fall into two categories:
88

99
- **Automatic on upgrade:** New logic that activates immediately when the upgraded contracts are deployed behind their proxies. No governance action required. These include: zero-signal detection, zero-allocated-tokens reclaim, POI presentation paths (claim/reclaim/defer), allocation resize staleness check, allocation close reclaim, and the `POIPresented` event.
1010

11-
- **Governance-gated:** Features that require explicit governance transactions after upgrade. Until configured, the system preserves legacy behaviour (rewards are dropped, not reclaimed). These include: setting the issuance allocator, configuring reclaim addresses (per-condition and default), setting the eligibility oracle, and changing the minimum subgraph signal threshold.
11+
- **Governance-gated:** Features that require explicit governance transactions after upgrade. In the abstract, until configured the system preserves legacy behaviour (rewards are dropped, not reclaimed). These include: setting the issuance allocator, configuring reclaim addresses (per-condition and default), setting the eligibility oracle, and changing the minimum subgraph signal threshold.
1212

13-
This two-phase approach allows a safe upgrade with the new infrastructure in place, while governance coordinates separate activation steps for each optional feature.
13+
**What this deployment configures.** The governance batches executed alongside this upgrade turn several of the gated features on immediately, so their legacy behaviour does _not_ persist:
14+
15+
- **Issuance allocator — set.** The RewardsManager self-mints 100% of the rate (120.73 GRT/block); the RecurringAgreementManager gets 0 issuance (DIPs dormant at launch). Effective issuance rate is unchanged from before the upgrade.
16+
- **Default reclaim address — set** (no per-condition addresses). Reclaiming is active for **every** condition via the catch-all fallback the moment the batch executes — previously-dropped rewards are now minted to the reclaim address.
17+
- **Eligibility oracle — wired, then enabled.** The oracle is set on the RewardsManager (and RecurringAgreementManager), and a subsequent governance transaction in the plan sets the oracle's validation flag to true; both switches are required before `INDEXER_INELIGIBLE` denials can occur.
18+
- **Minimum subgraph signal — unchanged.** The threshold is not modified.
19+
20+
The per-feature `Activates:` notes below reflect this deployment's configuration, not just the abstract gating.
1421

1522
## Issuance Rate
1623

@@ -44,15 +51,15 @@ A new `RewardsCondition` library defines typed `bytes32` identifiers for every s
4451

4552
**After:** Undistributable rewards are _reclaimed_ by minting them to a configurable address. Governance can set a per-condition address via `setReclaimAddress(condition, address)` and a catch-all fallback via `setDefaultReclaimAddress(address)`. If neither is configured for a given condition, rewards are still not minted (preserving the old drop behaviour). Every reclaim emits a `RewardsReclaimed` event with the condition, amount, indexer, allocation, and subgraph.
4653

47-
**Activates:** Governance-gated — requires `setReclaimAddress()` and/or `setDefaultReclaimAddress()` for each condition. Until configured, rewards are dropped (preserving legacy behaviour).
54+
**Activates:** Governance-gated — requires `setReclaimAddress()` and/or `setDefaultReclaimAddress()`. This deployment configures the **default** reclaim address (no per-condition addresses), so reclaiming is active for **every** condition via the catch-all fallback from the moment the batch executes: rewards that were previously dropped are now minted to the reclaim address.
4855

4956
## Zero Global Signal
5057

5158
**Before:** Issuance during periods with zero total curation signal was silently lost.
5259

5360
**After:** Detected in `updateAccRewardsPerSignal()` and reclaimed as `NO_SIGNAL`.
5461

55-
**Activates:** Automatic on upgrade — detection is built into the accumulator update. Reclaim requires a configured address for `NO_SIGNAL`.
62+
**Activates:** Automatic on upgrade — detection is built into the accumulator update. Reclaim requires a configured address for `NO_SIGNAL`, which this deployment supplies via the default reclaim address, so zero-signal issuance is reclaimed (minted) rather than dropped.
5663

5764
## Subgraph-Level Denial
5865

@@ -86,9 +93,16 @@ A new `RewardsCondition` library defines typed `bytes32` identifiers for every s
8693

8794
**Before:** No per-indexer eligibility checks existed.
8895

89-
**After:** An optional `rewardsEligibilityOracle` can be set by governance. When set, `takeRewards()` checks `isEligible(indexer)` at claim time. If the indexer is ineligible, rewards are denied (emitting `RewardsDeniedDueToEligibility`) and reclaimed to the `INDEXER_INELIGIBLE` address. Subgraph denial takes precedence: if a subgraph is denied, eligibility is not checked.
96+
**After:** An optional `rewardsEligibilityOracle` can be set by governance. When set, the eligibility check runs at claim time — but **only on the claim path** (`takeRewards()`, condition `NONE`): a valid, reward-bearing POI that is not stale, non-zero, old enough, and on a non-denied subgraph. Behaviour when the indexer is ineligible depends on the `revertOnIneligible` flag:
97+
98+
- **`revertOnIneligible = true` (this deployment):** the POI presentation **reverts** (`"Indexer not eligible for rewards"`). Rewards are neither minted nor reclaimed — they stay pending and become collectable if the indexer regains eligibility before the allocation goes stale.
99+
- **`revertOnIneligible = false`:** rewards are denied (emitting `RewardsDeniedDueToEligibility`) and reclaimed to the `INDEXER_INELIGIBLE` address (or the default reclaim address) — permanently forfeited.
100+
101+
Subgraph denial takes precedence: if a subgraph is denied, eligibility is not checked.
102+
103+
Because the check guards only the claim path, an ineligible indexer is **never locked out of the allocation**. A **zero POI** (`ZERO_POI`), a stale allocation (`STALE_POI`), and closing via `stopService` (`CLOSE_ALLOCATION`) all take reclaim/defer paths that skip the eligibility check and succeed — forfeiting the affected rewards to the reclaim address rather than reverting (and a zero POI still resets the staleness clock, keeping the allocation alive). Under `revertOnIneligible = true`, the only action ineligibility blocks is **collecting rewards via a valid POI**.
90104

91-
**Activates:** Governance-gated — requires `setRewardsEligibilityOracle()`. Until called, no eligibility checks are performed.
105+
**Activates:** Governance-gated by **two** switches: `setRewardsEligibilityOracle()` on the RewardsManager, and `setEligibilityValidation(true)` on the oracle itself (it ships with validation disabled, so `isEligible` returns true for everyone until enabled). This deployment does both — the oracle is wired at upgrade and validation is enabled by a subsequent governance transaction in the plan — so eligibility enforcement becomes active. With `revertOnIneligible = true` (this deployment) that enforcement is a **revert** on the claim path, not an `INDEXER_INELIGIBLE` reclaim (see above). Note the oracle also fail-opens (returns eligible) if it receives no updates within its configured timeout.
92106

93107
## POI Presentation (AllocationManager)
94108

0 commit comments

Comments
 (0)