diff --git a/abis/IssuanceAllocator.json b/abis/IssuanceAllocator.json new file mode 100644 index 00000000..40b5b5fa --- /dev/null +++ b/abis/IssuanceAllocator.json @@ -0,0 +1,46 @@ +[ + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "target", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "newAllocatorMintingRate", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "newSelfMintingRate", "type": "uint256" } + ], + "name": "TargetAllocationUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "uint256", "name": "oldIssuancePerBlock", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "newIssuancePerBlock", "type": "uint256" } + ], + "name": "IssuancePerBlockUpdated", + "type": "event" + }, + { + "inputs": [], + "name": "getIssuancePerBlock", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "target", "type": "address" }], + "name": "getTargetAllocation", + "outputs": [ + { + "components": [ + { "internalType": "uint256", "name": "totalAllocationRate", "type": "uint256" }, + { "internalType": "uint256", "name": "allocatorMintingRate", "type": "uint256" }, + { "internalType": "uint256", "name": "selfMintingRate", "type": "uint256" } + ], + "internalType": "struct IIssuanceAllocationStatus.Allocation", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/abis/RewardsManagerStitched.json b/abis/RewardsManagerStitched.json index 4c58dd58..3efbe0ce 100644 --- a/abis/RewardsManagerStitched.json +++ b/abis/RewardsManagerStitched.json @@ -603,5 +603,31 @@ ], "stateMutability": "nonpayable", "type": "function" + }, + { + "inputs": [], + "name": "getAllocatedIssuancePerBlock", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getIssuanceAllocator", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" } -] +] \ No newline at end of file diff --git a/config/addresses.template.ts b/config/addresses.template.ts index 94537328..8c0ff97c 100644 --- a/config/addresses.template.ts +++ b/config/addresses.template.ts @@ -22,6 +22,7 @@ export class Addresses { l2GraphTokenGateway: string ethereumDIDRegistry: string subgraphService: string + issuanceAllocator: string graphPayments: string paymentsEscrow: string graphTallyCollector: string @@ -52,6 +53,7 @@ export let addresses: Addresses = { l2GraphTokenGateway: '{{l2GraphTokenGateway}}', ethereumDIDRegistry: '{{ethereumDIDRegistry}}', subgraphService: '{{subgraphService}}', + issuanceAllocator: '{{issuanceAllocator}}', graphPayments: '{{graphPayments}}', paymentsEscrow: '{{paymentsEscrow}}', graphTallyCollector: '{{graphTallyCollector}}', diff --git a/config/arbitrumSepoliaAddressScript.ts b/config/arbitrumSepoliaAddressScript.ts index 378cfae0..5b2913ea 100644 --- a/config/arbitrumSepoliaAddressScript.ts +++ b/config/arbitrumSepoliaAddressScript.ts @@ -5,6 +5,7 @@ import { Addresses } from './addresses.template' const horizonAddresses = require('@graphprotocol/address-book/horizon/addresses.json') const subgraphServiceAddresses = require('@graphprotocol/address-book/subgraph-service/addresses.json') +const issuanceAddresses = require('@graphprotocol/address-book/issuance/addresses.json') // mustache doesn't like numbered object keys // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -15,10 +16,15 @@ renameHorizonAddresses['arbsep'] = horizonAddresses['421614'] let renameSubgraphServiceAddresses: any = subgraphServiceAddresses renameSubgraphServiceAddresses['arbsep'] = subgraphServiceAddresses['421614'] +// eslint-disable-next-line @typescript-eslint/no-explicit-any +let renameIssuanceAddresses: any = issuanceAddresses +renameIssuanceAddresses['arbsep'] = issuanceAddresses['421614'] || {} + const combinedAddresses = { arbsep: { ...renameHorizonAddresses['arbsep'], - ...renameSubgraphServiceAddresses['arbsep'] + ...renameSubgraphServiceAddresses['arbsep'], + ...renameIssuanceAddresses['arbsep'] } } @@ -45,6 +51,7 @@ export let addresses: Addresses = { l2GraphTokenGateway: '{{arbsep.L2GraphTokenGateway.address}}', ethereumDIDRegistry: '{{arbsep.EthereumDIDRegistry.address}}', subgraphService: '{{arbsep.SubgraphService.address}}', + issuanceAllocator: '{{arbsep.IssuanceAllocator.address}}', graphPayments: '{{arbsep.GraphPayments.address}}', paymentsEscrow: '{{arbsep.PaymentsEscrow.address}}', graphTallyCollector: '{{arbsep.GraphTallyCollector.address}}', diff --git a/config/localNetworkAddressScript.ts b/config/localNetworkAddressScript.ts index 0efd624b..ee55619c 100644 --- a/config/localNetworkAddressScript.ts +++ b/config/localNetworkAddressScript.ts @@ -10,6 +10,7 @@ import { Addresses } from './addresses.template' let renameAddresses: any = { horizon: horizonAddresses['1337'], subgraphService: subgraphServiceAddresses['1337'], + issuanceAllocator: '0x0000000000000000000000000000000000000000', // IssuanceAllocator is only deployed on Arbitrum } export let addresses: Addresses = { diff --git a/config/mainnetArbitrumAddressScript.ts b/config/mainnetArbitrumAddressScript.ts index a61504bc..cc848da9 100644 --- a/config/mainnetArbitrumAddressScript.ts +++ b/config/mainnetArbitrumAddressScript.ts @@ -4,6 +4,7 @@ import { Addresses } from './addresses.template' const horizonAddresses = require('@graphprotocol/address-book/horizon/addresses.json') const subgraphServiceAddresses = require('@graphprotocol/address-book/subgraph-service/addresses.json') +const issuanceAddresses = require('@graphprotocol/address-book/issuance/addresses.json') // mustache doesn't like numbered object keys // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -14,10 +15,15 @@ renameHorizonAddresses['arbitrum'] = horizonAddresses['42161'] || {} let renameSubgraphServiceAddresses: any = subgraphServiceAddresses renameSubgraphServiceAddresses['arbitrum'] = subgraphServiceAddresses['42161'] || {} +// eslint-disable-next-line @typescript-eslint/no-explicit-any +let renameIssuanceAddresses: any = issuanceAddresses +renameIssuanceAddresses['arbitrum'] = issuanceAddresses['42161'] || {} + const combinedAddresses = { arbitrum: { ...renameHorizonAddresses['arbitrum'], - ...renameSubgraphServiceAddresses['arbitrum'] + ...renameSubgraphServiceAddresses['arbitrum'], + ...renameIssuanceAddresses['arbitrum'] } } @@ -44,6 +50,7 @@ export let addresses: Addresses = { l2GraphTokenGateway: '{{arbitrum.L2GraphTokenGateway.address}}', ethereumDIDRegistry: '{{arbitrum.IEthereumDIDRegistry.address}}', subgraphService: '{{arbitrum.SubgraphService.address}}', + issuanceAllocator: '{{arbitrum.IssuanceAllocator.address}}', graphPayments: '{{arbitrum.GraphPayments.address}}', paymentsEscrow: '{{arbitrum.PaymentsEscrow.address}}', graphTallyCollector: '{{arbitrum.GraphTallyCollector.address}}', diff --git a/config/testAddressesL1.ts b/config/testAddressesL1.ts index 8de91df5..23741222 100644 --- a/config/testAddressesL1.ts +++ b/config/testAddressesL1.ts @@ -44,6 +44,7 @@ export let addresses: Addresses = { l2GraphTokenGateway: '', ethereumDIDRegistry: '0x0000000000000000000000000000000000000000', subgraphService: '0x0000000000000000000000000000000000000000', + issuanceAllocator: '0x0000000000000000000000000000000000000000', graphPayments: '0x0000000000000000000000000000000000000000', paymentsEscrow: '0x0000000000000000000000000000000000000000', graphTallyCollector: '0x0000000000000000000000000000000000000000', diff --git a/config/testAddressesL2.ts b/config/testAddressesL2.ts index ce8d1e34..10108fbe 100644 --- a/config/testAddressesL2.ts +++ b/config/testAddressesL2.ts @@ -44,6 +44,7 @@ export let addresses: Addresses = { l2GraphTokenGateway: '0x0000000000000000000000000000000000000000', ethereumDIDRegistry: '0x0000000000000000000000000000000000000000', subgraphService: '0x0000000000000000000000000000000000000000', + issuanceAllocator: '0x0000000000000000000000000000000000000000', graphPayments: '0x0000000000000000000000000000000000000000', paymentsEscrow: '0x0000000000000000000000000000000000000000', graphTallyCollector: '0x0000000000000000000000000000000000000000', diff --git a/package.json b/package.json index dc5e698f..42d116fa 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "prepare:test-l2": "yarn && yarn prep:addresses:test:l2 && mustache ./config/generatedAddresses.json subgraph.template.yaml > subgraph.yaml && graph codegen --output-dir src/types/" }, "devDependencies": { - "@graphprotocol/address-book": "1.1.0", + "@graphprotocol/address-book": "^1.3.0", "@graphprotocol/contracts": "6.2.0", "@graphprotocol/graph-cli": "0.97.0", "@graphprotocol/graph-ts": "0.36.0", diff --git a/src/mappings/issuanceAllocator.ts b/src/mappings/issuanceAllocator.ts new file mode 100644 index 00000000..9eb48910 --- /dev/null +++ b/src/mappings/issuanceAllocator.ts @@ -0,0 +1,34 @@ +import { TargetAllocationUpdated } from '../types/IssuanceAllocator/IssuanceAllocator' +import { createOrLoadGraphNetwork } from './helpers/helpers' +import { addresses } from '../../config/addresses' +import { Address } from '@graphprotocol/graph-ts' + +/** + * @dev handleTargetAllocationUpdated + * - Keeps GraphNetwork.networkGRTIssuancePerBlock in step with the share of + * issuance the RewardsManager actually receives. + * + * Since GIP-0076/GIP-0088 the RewardsManager no longer owns the issuance rate: + * the IssuanceAllocator splits total issuance across targets and the + * RewardsManager mints only its own allocation. GIP-0089 exercised that for the + * first time on 2026-09-01, moving 24.146 GRT/block (20%) to the Foundation's + * Innovation Allocation and leaving the RewardsManager 96.584 of the unchanged + * 120.73 total. + * + * That change is invisible to the RewardsManager's own ParameterUpdated event, + * so without this handler networkGRTIssuancePerBlock stays at whatever the + * legacy `issuancePerBlock` storage slot last held — 120.73 — and every + * downstream APR calculation built on it overstates indexing rewards by 25%. + * + * The RewardsManager is an allocator target that mints its own issuance, so + * `newSelfMintingRate` is the rate it will issue at. The event carries it, so + * no contract call is needed here. + */ +export function handleTargetAllocationUpdated(event: TargetAllocationUpdated): void { + if (event.params.target != Address.fromString(addresses.rewardsManager)) { + return + } + let graphNetwork = createOrLoadGraphNetwork(event.block.number, event.address) + graphNetwork.networkGRTIssuancePerBlock = event.params.newSelfMintingRate + graphNetwork.save() +} diff --git a/src/mappings/rewardsManager.ts b/src/mappings/rewardsManager.ts index d66f33ed..52c29327 100644 --- a/src/mappings/rewardsManager.ts +++ b/src/mappings/rewardsManager.ts @@ -69,7 +69,16 @@ export function handleParameterUpdated(event: ParameterUpdated): void { if (parameter == 'issuanceRate') { graphNetwork.networkGRTIssuance = rewardsManager.issuanceRate() } else if (parameter == 'issuancePerBlock') { - graphNetwork.networkGRTIssuancePerBlock = rewardsManager.issuancePerBlock() + // Read the allocator-aware rate, not the legacy `issuancePerBlock` slot. + // Since the IssuanceAllocator was wired up (GIP-0088) the RewardsManager + // issues only its own allocation, and the legacy slot is no longer that + // number — on Arbitrum it still reads 120.73 while the allocated rate is + // 96.584. try_ so the call is safe on deployments predating the upgrade, + // where the getter does not exist. + let allocated = rewardsManager.try_getAllocatedIssuancePerBlock() + graphNetwork.networkGRTIssuancePerBlock = allocated.reverted + ? rewardsManager.issuancePerBlock() + : allocated.value } else if (parameter == 'subgraphAvailabilityOracle') { graphNetwork.subgraphAvailabilityOracle = rewardsManager.subgraphAvailabilityOracle() } diff --git a/subgraph.template.yaml b/subgraph.template.yaml index 09ffddb4..601cff61 100644 --- a/subgraph.template.yaml +++ b/subgraph.template.yaml @@ -529,6 +529,31 @@ dataSources: EpochManager.blockNum: EpochManager[{{epochManager}}].blockNum() - event: AllowedLockedVerifierSet(indexed address,bool) handler: handleAllowedLockedVerifierSet + # The IssuanceAllocator owns the split of protocol issuance across targets + # (GIP-0076, deployed by GIP-0088). The RewardsManager is one target and mints + # only its own allocation, so this is the only place a change to the indexing + # reward rate is observable — the RewardsManager itself emits nothing when the + # split moves. Arbitrum only; the address resolves empty elsewhere. + - kind: ethereum/contract + name: IssuanceAllocator + network: {{network}} + source: + address: "{{issuanceAllocator}}" + abi: IssuanceAllocator + startBlock: {{blockNumber}} + mapping: + kind: ethereum/events + apiVersion: 0.0.9 + language: wasm/assemblyscript + file: ./src/mappings/issuanceAllocator.ts + entities: + - GraphNetwork + abis: + - name: IssuanceAllocator + file: ./abis/IssuanceAllocator.json + eventHandlers: + - event: TargetAllocationUpdated(indexed address,uint256,uint256) + handler: handleTargetAllocationUpdated - kind: ethereum/contract name: SubgraphService network: {{network}} diff --git a/yarn.lock b/yarn.lock index 0f6cd4e1..034e1077 100644 --- a/yarn.lock +++ b/yarn.lock @@ -818,10 +818,10 @@ graphql-import-node "^0.0.5" js-yaml "^4.1.0" -"@graphprotocol/address-book@1.1.0": - version "1.1.0" - resolved "https://registry.npmjs.org/@graphprotocol/address-book/-/address-book-1.1.0.tgz" - integrity sha512-38NiutGOWdDIYgB/3kGp/7LxPRCNomipjo1hGlaj6RvyydySooVSmb3A+j84faUso+uhYxt8+9y1EFN7XVnT7Q== +"@graphprotocol/address-book@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@graphprotocol/address-book/-/address-book-1.3.0.tgz#234de18cd3931c309cf0385d46f88cc6d3a14355" + integrity sha512-rkrgBOOxM5WLNsoVrr6Gvg6DYvDes+P13vL5k1/QYWc5/z/G6pt4DBgzR/7wJV5xhIMevCumpg6L7ZuVQwS0ug== "@graphprotocol/common-ts@^2.0.7": version "2.0.11"