diff --git a/docs/networks/arbitrum-sepolia.md b/docs/networks/arbitrum-sepolia.md index 18483d1ad..85113a2af 100644 --- a/docs/networks/arbitrum-sepolia.md +++ b/docs/networks/arbitrum-sepolia.md @@ -7,8 +7,8 @@ The Graph Network's testnet is on Arbitrum Sepolia (eip155:421614). Sepolia netw | Component | Release | | ------------------ | ------------------------------------------------------------------------------------ | | contracts | [5.3.3](https://github.com/graphprotocol/contracts/releases/tag/v5.3.3) | -| indexer-agent | [0.25.0](https://github.com/graphprotocol/indexer/releases/tag/v0.25.0) | -| indexer-cli | [0.25.0](https://github.com/graphprotocol/indexer/releases/tag/v0.25.0) | +| indexer-agent | [0.25.4](https://github.com/graphprotocol/indexer/releases/tag/v0.25.4) | +| indexer-cli | [0.25.4](https://github.com/graphprotocol/indexer/releases/tag/v0.25.4) | | indexer-service-rs | [1.0.0](https://github.com/graphprotocol/indexer-rs/releases/tag/v1.0.0) | | tap-agent | [1.0.0](https://github.com/graphprotocol/indexer-rs/releases/tag/v1.0.0) | | graph-node | [0.35.1](https://github.com/graphprotocol/graph-node/releases/tag/v0.35.1) | diff --git a/lerna.json b/lerna.json index f36969874..6fabcdf11 100644 --- a/lerna.json +++ b/lerna.json @@ -4,5 +4,5 @@ ], "npmClient": "yarn", "useWorkspaces": true, - "version": "0.25.2" + "version": "0.25.4" } diff --git a/packages/indexer-agent/package.json b/packages/indexer-agent/package.json index 45d16a046..5dcf6a95b 100644 --- a/packages/indexer-agent/package.json +++ b/packages/indexer-agent/package.json @@ -1,6 +1,6 @@ { "name": "@graphprotocol/indexer-agent", - "version": "0.25.3", + "version": "0.25.4", "description": "Indexer agent", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -30,7 +30,7 @@ }, "dependencies": { "@graphprotocol/common-ts": "3.0.1", - "@graphprotocol/indexer-common": "0.25.3", + "@graphprotocol/indexer-common": "0.25.4", "@thi.ng/heaps": "^1.3.1", "axios": "0.26.1", "bs58": "5.0.0", diff --git a/packages/indexer-cli/package.json b/packages/indexer-cli/package.json index fd883a4f1..77dc28927 100644 --- a/packages/indexer-cli/package.json +++ b/packages/indexer-cli/package.json @@ -1,6 +1,6 @@ { "name": "@graphprotocol/indexer-cli", - "version": "0.25.3", + "version": "0.25.4", "description": "Indexer CLI for The Graph Network", "main": "./dist/cli.js", "files": [ @@ -27,7 +27,7 @@ }, "dependencies": { "@graphprotocol/common-ts": "3.0.1", - "@graphprotocol/indexer-common": "0.25.3", + "@graphprotocol/indexer-common": "0.25.4", "@iarna/toml": "2.2.5", "@thi.ng/iterators": "5.1.74", "@urql/core": "3.1.0", diff --git a/packages/indexer-common/package.json b/packages/indexer-common/package.json index 52186a332..5bfa7b0b3 100644 --- a/packages/indexer-common/package.json +++ b/packages/indexer-common/package.json @@ -1,6 +1,6 @@ { "name": "@graphprotocol/indexer-common", - "version": "0.25.3", + "version": "0.25.4", "description": "Common library for Graph Protocol indexer components", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/packages/indexer-common/src/indexer-management/allocations.ts b/packages/indexer-common/src/indexer-management/allocations.ts index 8170a2855..153fee73c 100644 --- a/packages/indexer-common/src/indexer-management/allocations.ts +++ b/packages/indexer-common/src/indexer-management/allocations.ts @@ -1123,7 +1123,16 @@ export class AllocationManager { ...tx, } } else { - // Horizon: Need to multicall collect and stopService + // Horizon: Need to collect indexing rewards and stop service + // Check if indexer is over-allocated - if so, collect() will auto-close the allocation + // and we should NOT call stopService to avoid "AllocationClosed" revert + const isOverAllocated = + await this.network.contracts.SubgraphService.isOverAllocated(params.indexer) + + logger.debug('Checking over-allocation status for unallocate', { + allocationID: params.allocationID, + isOverAllocated, + }) // collect const collectIndexingRewardsData = encodeCollectIndexingRewardsData( @@ -1137,29 +1146,48 @@ export class AllocationManager { 0, ), ) - const collectCallData = - this.network.contracts.SubgraphService.interface.encodeFunctionData('collect', [ - params.indexer, - PaymentTypes.IndexingRewards, - collectIndexingRewardsData, - ]) - // stopService - const stopServiceCallData = - this.network.contracts.SubgraphService.interface.encodeFunctionData( - 'stopService', - [params.indexer, encodeStopServiceData(params.allocationID)], + if (isOverAllocated) { + logger.info( + 'Indexer is over-allocated, using collect-only transaction (allocation will auto-close)', + { allocationID: params.allocationID }, ) + const tx = + await this.network.contracts.SubgraphService.collect.populateTransaction( + params.indexer, + PaymentTypes.IndexingRewards, + collectIndexingRewardsData, + ) + return { + protocolNetwork: params.protocolNetwork, + actionID: params.actionID, + ...tx, + } + } else { + // Normal path: multicall collect + stopService + const collectCallData = + this.network.contracts.SubgraphService.interface.encodeFunctionData('collect', [ + params.indexer, + PaymentTypes.IndexingRewards, + collectIndexingRewardsData, + ]) + + const stopServiceCallData = + this.network.contracts.SubgraphService.interface.encodeFunctionData( + 'stopService', + [params.indexer, encodeStopServiceData(params.allocationID)], + ) - const tx = - await this.network.contracts.SubgraphService.multicall.populateTransaction([ - collectCallData, - stopServiceCallData, - ]) - return { - protocolNetwork: params.protocolNetwork, - actionID: params.actionID, - ...tx, + const tx = + await this.network.contracts.SubgraphService.multicall.populateTransaction([ + collectCallData, + stopServiceCallData, + ]) + return { + protocolNetwork: params.protocolNetwork, + actionID: params.actionID, + ...tx, + } } } } diff --git a/packages/indexer-common/src/indexer-management/resolvers/allocations.ts b/packages/indexer-common/src/indexer-management/resolvers/allocations.ts index 2f7803c62..3f0eef1e9 100644 --- a/packages/indexer-common/src/indexer-management/resolvers/allocations.ts +++ b/packages/indexer-common/src/indexer-management/resolvers/allocations.ts @@ -3,7 +3,7 @@ import pMap from 'p-map' import gql from 'graphql-tag' -import { ethers, hexlify, ZeroAddress } from 'ethers' +import { ethers, hexlify, TransactionReceipt, ZeroAddress } from 'ethers' import { Address, @@ -735,6 +735,15 @@ async function closeHorizonAllocation( throw indexerError(IndexerErrorCode.IE065, 'Allocation has already been closed') } + // Check if indexer is over-allocated - if so, collect() will auto-close the allocation + // and we should NOT call stopService to avoid "AllocationClosed" revert + const isOverAllocated = await contracts.SubgraphService.isOverAllocated(address) + + logger.debug('Checking over-allocation status for close allocation', { + allocationId: allocation.id, + isOverAllocated, + }) + const encodedPOIMetadata = encodePOIMetadata( poiData.blockNumber, poiData.publicPOI, @@ -748,28 +757,54 @@ async function closeHorizonAllocation( encodedPOIMetadata, ) - const collectCallData = contracts.SubgraphService.interface.encodeFunctionData( - 'collect', - [address, PaymentTypes.IndexingRewards, collectIndexingRewardsData], - ) - const closeAllocationData = encodeStopServiceData(allocation.id) - const stopServiceCallData = contracts.SubgraphService.interface.encodeFunctionData( - 'stopService', - [address, closeAllocationData], - ) + let receipt: TransactionReceipt | 'paused' | 'unauthorized' - const receipt = await transactionManager.executeTransaction( - async () => - contracts.SubgraphService.multicall.estimateGas([ - collectCallData, - stopServiceCallData, - ]), - async (gasLimit) => - contracts.SubgraphService.multicall([collectCallData, stopServiceCallData], { - gasLimit, - }), - logger, - ) + if (isOverAllocated) { + logger.info( + 'Indexer is over-allocated, using collect-only transaction (allocation will auto-close)', + { allocationId: allocation.id }, + ) + receipt = await transactionManager.executeTransaction( + async () => + contracts.SubgraphService.collect.estimateGas( + address, + PaymentTypes.IndexingRewards, + collectIndexingRewardsData, + ), + async (gasLimit) => + contracts.SubgraphService.collect( + address, + PaymentTypes.IndexingRewards, + collectIndexingRewardsData, + { gasLimit }, + ), + logger, + ) + } else { + // Normal path: multicall collect + stopService + const collectCallData = contracts.SubgraphService.interface.encodeFunctionData( + 'collect', + [address, PaymentTypes.IndexingRewards, collectIndexingRewardsData], + ) + const closeAllocationData = encodeStopServiceData(allocation.id) + const stopServiceCallData = contracts.SubgraphService.interface.encodeFunctionData( + 'stopService', + [address, closeAllocationData], + ) + + receipt = await transactionManager.executeTransaction( + async () => + contracts.SubgraphService.multicall.estimateGas([ + collectCallData, + stopServiceCallData, + ]), + async (gasLimit) => + contracts.SubgraphService.multicall([collectCallData, stopServiceCallData], { + gasLimit, + }), + logger, + ) + } if (receipt === 'paused' || receipt === 'unauthorized') { throw indexerError( @@ -802,10 +837,10 @@ async function closeHorizonAllocation( } const closeAllocationEventLogs = transactionManager.findEvent( - 'ServiceStopped', + 'AllocationClosed', contracts.SubgraphService.interface, - 'serviceProvider', - address, + 'allocationId', + allocation.id, receipt, logger, )