|
| 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 | +``` |
0 commit comments