From 9dc67cd40d1a77464ab7b4ca3cd252b793913ab0 Mon Sep 17 00:00:00 2001 From: MoonBoi9001 Date: Wed, 11 Mar 2026 15:46:09 -0500 Subject: [PATCH 1/6] fix: add missing Ignition dependency for HorizonStaking deployment HorizonStaking's constructor extends GraphDirectory, which queries the Controller for GraphToken, EpochManager, RewardsManager, etc. These are registered by GraphPeripheryModule. Without an explicit `after` dependency, Ignition may schedule HorizonStaking before the periphery registrations, causing the constructor to read address(0) and revert with GraphDirectoryInvalidZeroAddress. Every other core module (GraphPayments, PaymentsEscrow, GraphTallyCollector, RecurringCollector) declares this dependency. HorizonStaking was the only outlier. Co-Authored-By: Claude Opus 4.6 --- packages/horizon/ignition/modules/core/HorizonStaking.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/horizon/ignition/modules/core/HorizonStaking.ts b/packages/horizon/ignition/modules/core/HorizonStaking.ts index a7bec9076..0a13e9123 100644 --- a/packages/horizon/ignition/modules/core/HorizonStaking.ts +++ b/packages/horizon/ignition/modules/core/HorizonStaking.ts @@ -15,12 +15,12 @@ export default buildModule('HorizonStaking', (m) => { const subgraphServiceAddress = m.getParameter('subgraphServiceAddress') const maxThawingPeriod = m.getParameter('maxThawingPeriod') - // Deploy HorizonStaking implementation + // Deploy HorizonStaking implementation - requires periphery and proxies to be registered in the controller const HorizonStakingImplementation = deployImplementation(m, { name: 'HorizonStaking', artifact: HorizonStakingArtifact, constructorArgs: [Controller, subgraphServiceAddress], - }) + }, { after: [GraphPeripheryModule, HorizonProxiesModule] }) // Upgrade proxy to implementation contract const HorizonStaking = upgradeGraphProxy(m, GraphProxyAdmin, HorizonStakingProxy, HorizonStakingImplementation, { From 7e6e471d838e41e4bbf0dce73832f90255f44d5b Mon Sep 17 00:00:00 2001 From: Miguel de Elias Date: Wed, 25 Feb 2026 11:07:38 -0300 Subject: [PATCH 2/6] fix: add RecurringCollector to horizon contracts in toolshed --- packages/toolshed/src/deployments/horizon/contracts.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/toolshed/src/deployments/horizon/contracts.ts b/packages/toolshed/src/deployments/horizon/contracts.ts index bd852d5f0..8b6dea184 100644 --- a/packages/toolshed/src/deployments/horizon/contracts.ts +++ b/packages/toolshed/src/deployments/horizon/contracts.ts @@ -36,6 +36,7 @@ export const GraphHorizonContractNameList = [ 'GraphPayments', 'PaymentsEscrow', 'GraphTallyCollector', + 'RecurringCollector', ] as const export interface GraphHorizonContracts extends ContractList { From 25c0b88252dba9d3b6264e2325e80ea6ece80622 Mon Sep 17 00:00:00 2001 From: Miguel de Elias Date: Wed, 25 Feb 2026 16:34:02 -0300 Subject: [PATCH 3/6] feat: add RecurringCollector type and DIPs helpers to toolshed - Add IRecurringCollector to GraphHorizonContracts interface - Re-export IRecurringCollector from interfaces main entrypoint - Add encodeCollectIndexingFeesData() helper for indexing fee collection - Add decoders: decodeSignedRCA, decodeAcceptIndexingAgreementMetadata, decodeIndexingAgreementTermsV1 - Add round-trip tests for all decoders and encoder --- packages/interfaces/src/types/horizon.ts | 2 + packages/toolshed/src/core/index.ts | 1 + .../toolshed/src/core/recurring-collector.ts | 83 +++++++++ .../toolshed/src/core/subgraph-service.ts | 15 ++ .../src/deployments/horizon/contracts.ts | 2 + .../toolshed/test/recurring-collector.test.ts | 176 ++++++++++++++++++ packages/toolshed/tsconfig.json | 3 +- 7 files changed, 281 insertions(+), 1 deletion(-) create mode 100644 packages/toolshed/src/core/recurring-collector.ts create mode 100644 packages/toolshed/test/recurring-collector.test.ts diff --git a/packages/interfaces/src/types/horizon.ts b/packages/interfaces/src/types/horizon.ts index c2a09abb6..1208dd11d 100644 --- a/packages/interfaces/src/types/horizon.ts +++ b/packages/interfaces/src/types/horizon.ts @@ -10,6 +10,7 @@ import type { IL2GNSToolshed, ILegacyRewardsManager, IPaymentsEscrowToolshed, + IRecurringCollector, IRewardsManagerToolshed, IStaking, ISubgraphNFT, @@ -29,5 +30,6 @@ export { IStaking as LegacyStaking, IPaymentsEscrowToolshed as PaymentsEscrow, IRewardsManagerToolshed as RewardsManager, + IRecurringCollector, ISubgraphNFT as SubgraphNFT, } diff --git a/packages/toolshed/src/core/index.ts b/packages/toolshed/src/core/index.ts index 3934ed378..7dbbb79ba 100644 --- a/packages/toolshed/src/core/index.ts +++ b/packages/toolshed/src/core/index.ts @@ -6,5 +6,6 @@ export * from './custom-errors' export * from './disputes' export * from './graph-tally' export * from './poi' +export * from './recurring-collector' export * from './subgraph-service' export * from './types' diff --git a/packages/toolshed/src/core/recurring-collector.ts b/packages/toolshed/src/core/recurring-collector.ts new file mode 100644 index 000000000..9ddf5af98 --- /dev/null +++ b/packages/toolshed/src/core/recurring-collector.ts @@ -0,0 +1,83 @@ +import { BytesLike, ethers } from 'ethers' + +// -- ABI tuple types for decoding -- + +const RCA_TUPLE = + 'tuple(uint64 deadline, uint64 endsAt, address payer, address dataService, address serviceProvider, uint256 maxInitialTokens, uint256 maxOngoingTokensPerSecond, uint32 minSecondsPerCollection, uint32 maxSecondsPerCollection, uint256 nonce, bytes metadata)' + +const SIGNED_RCA_TUPLE = `tuple(${RCA_TUPLE} rca, bytes signature)` + +const ACCEPT_METADATA_TUPLE = 'tuple(bytes32 subgraphDeploymentId, uint8 version, bytes terms)' + +const TERMS_V1_TUPLE = 'tuple(uint256 tokensPerSecond, uint256 tokensPerEntityPerSecond)' + +// -- Return types -- + +export interface RecurringCollectionAgreement { + deadline: bigint + endsAt: bigint + payer: string + dataService: string + serviceProvider: string + maxInitialTokens: bigint + maxOngoingTokensPerSecond: bigint + minSecondsPerCollection: bigint + maxSecondsPerCollection: bigint + nonce: bigint + metadata: string +} + +export interface SignedRCA { + rca: RecurringCollectionAgreement + signature: string +} + +export interface AcceptIndexingAgreementMetadata { + subgraphDeploymentId: string + version: bigint + terms: string +} + +export interface IndexingAgreementTermsV1 { + tokensPerSecond: bigint + tokensPerEntityPerSecond: bigint +} + +// -- Decoders -- + +export function decodeSignedRCA(data: BytesLike): SignedRCA { + const [decoded] = ethers.AbiCoder.defaultAbiCoder().decode([SIGNED_RCA_TUPLE], data) + return { + rca: { + deadline: decoded.rca.deadline, + endsAt: decoded.rca.endsAt, + payer: decoded.rca.payer, + dataService: decoded.rca.dataService, + serviceProvider: decoded.rca.serviceProvider, + maxInitialTokens: decoded.rca.maxInitialTokens, + maxOngoingTokensPerSecond: decoded.rca.maxOngoingTokensPerSecond, + minSecondsPerCollection: decoded.rca.minSecondsPerCollection, + maxSecondsPerCollection: decoded.rca.maxSecondsPerCollection, + nonce: decoded.rca.nonce, + metadata: decoded.rca.metadata, + }, + signature: decoded.signature, + } +} + +export function decodeAcceptIndexingAgreementMetadata(data: BytesLike): AcceptIndexingAgreementMetadata { + const [decoded] = ethers.AbiCoder.defaultAbiCoder().decode([ACCEPT_METADATA_TUPLE], data) + return { + subgraphDeploymentId: decoded.subgraphDeploymentId, + version: decoded.version, + terms: decoded.terms, + } +} + +export function decodeIndexingAgreementTermsV1(data: BytesLike): IndexingAgreementTermsV1 { + const [decoded] = ethers.AbiCoder.defaultAbiCoder().decode([TERMS_V1_TUPLE], data) + return { + tokensPerSecond: decoded.tokensPerSecond, + tokensPerEntityPerSecond: decoded.tokensPerEntityPerSecond, + } +} diff --git a/packages/toolshed/src/core/subgraph-service.ts b/packages/toolshed/src/core/subgraph-service.ts index b4301900f..03a7840d0 100644 --- a/packages/toolshed/src/core/subgraph-service.ts +++ b/packages/toolshed/src/core/subgraph-service.ts @@ -32,6 +32,21 @@ export function encodeCollectQueryFeesData(rav: RAV, signature: string, tokensTo ) } +export function encodeCollectIndexingFeesData( + agreementId: string, + entities: bigint, + poi: BytesLike, + poiBlockNumber: bigint, + metadata: BytesLike, + maxSlippage: bigint, +) { + const innerData = ethers.AbiCoder.defaultAbiCoder().encode( + ['uint256', 'bytes32', 'uint256', 'bytes', 'uint256'], + [entities, poi, poiBlockNumber, metadata, maxSlippage], + ) + return ethers.AbiCoder.defaultAbiCoder().encode(['bytes16', 'bytes'], [agreementId, innerData]) +} + export function encodeStopServiceData(allocationId: string) { return ethers.AbiCoder.defaultAbiCoder().encode(['address'], [allocationId]) } diff --git a/packages/toolshed/src/deployments/horizon/contracts.ts b/packages/toolshed/src/deployments/horizon/contracts.ts index 8b6dea184..4221d9694 100644 --- a/packages/toolshed/src/deployments/horizon/contracts.ts +++ b/packages/toolshed/src/deployments/horizon/contracts.ts @@ -5,6 +5,7 @@ import type { GraphProxyAdmin, GraphTallyCollector, HorizonStaking, + IRecurringCollector, L2Curation, L2GNS, L2GraphToken, @@ -57,6 +58,7 @@ export interface GraphHorizonContracts extends ContractList Date: Wed, 15 Apr 2026 23:29:34 +0100 Subject: [PATCH 4/6] fix: link subgraph-service libraries in Ignition module The audit-branch SubgraphService.sol now depends on four external libraries (StakeClaims, AllocationHandler, IndexingAgreement, IndexingAgreementDecoder) and a fifth constructor argument for the RecurringCollector, but the Ignition deploy module was not updated to match. Deploys fail at validation time with IGN716 (missing libraries) and IGN703 (4 args expected 5), blocking any fresh hardhat deployment. Deploy the four libraries up-front via m.library() and pass them through deployImplementation's libraries option so the bytecode placeholders resolve. Thread the RecurringCollector address from the horizon deployment into the $global patch in deploy.ts, and add a recurringCollectorAddress placeholder to both the default and localNetwork protocol configs so Ignition can getParameter() it. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../ignition/configs/protocol.default.json5 | 3 +- .../configs/protocol.localNetwork.json5 | 3 +- .../ignition/modules/SubgraphService.ts | 36 ++++++++++++++++--- packages/subgraph-service/tasks/deploy.ts | 1 + 4 files changed, 37 insertions(+), 6 deletions(-) diff --git a/packages/subgraph-service/ignition/configs/protocol.default.json5 b/packages/subgraph-service/ignition/configs/protocol.default.json5 index aedf53531..b48120ee7 100644 --- a/packages/subgraph-service/ignition/configs/protocol.default.json5 +++ b/packages/subgraph-service/ignition/configs/protocol.default.json5 @@ -23,7 +23,8 @@ "disputeManagerProxyAdminAddress": "", "subgraphServiceProxyAddress": "", "subgraphServiceProxyAdminAddress": "", - "graphTallyCollectorAddress": "" + "graphTallyCollectorAddress": "", + "recurringCollectorAddress": "" }, "DisputeManager": { "disputePeriod": 2419200, diff --git a/packages/subgraph-service/ignition/configs/protocol.localNetwork.json5 b/packages/subgraph-service/ignition/configs/protocol.localNetwork.json5 index 1b35b18c1..978e56a61 100644 --- a/packages/subgraph-service/ignition/configs/protocol.localNetwork.json5 +++ b/packages/subgraph-service/ignition/configs/protocol.localNetwork.json5 @@ -23,7 +23,8 @@ "disputeManagerProxyAdminAddress": "", "subgraphServiceProxyAddress": "", "subgraphServiceProxyAdminAddress": "", - "graphTallyCollectorAddress": "" + "graphTallyCollectorAddress": "", + "recurringCollectorAddress": "" }, "DisputeManager": { "disputePeriod": 7200, // 2 hours = 7200 seconds diff --git a/packages/subgraph-service/ignition/modules/SubgraphService.ts b/packages/subgraph-service/ignition/modules/SubgraphService.ts index 8efb6800b..f1ae0382e 100644 --- a/packages/subgraph-service/ignition/modules/SubgraphService.ts +++ b/packages/subgraph-service/ignition/modules/SubgraphService.ts @@ -14,6 +14,7 @@ export default buildModule('SubgraphService', (m) => { const subgraphServiceProxyAdminAddress = m.getParameter('subgraphServiceProxyAdminAddress') const disputeManagerProxyAddress = m.getParameter('disputeManagerProxyAddress') const graphTallyCollectorAddress = m.getParameter('graphTallyCollectorAddress') + const recurringCollectorAddress = m.getParameter('recurringCollectorAddress') const curationProxyAddress = m.getParameter('curationProxyAddress') const minimumProvisionTokens = m.getParameter('minimumProvisionTokens') const maximumDelegationRatio = m.getParameter('maximumDelegationRatio') @@ -28,11 +29,38 @@ export default buildModule('SubgraphService', (m) => { subgraphServiceProxyAddress, ) + // Deploy libraries required by SubgraphService's audit-branch constructor. + // The contract has been refactored to pull allocation, stake-claim, and + // indexing-agreement logic into stand-alone libraries; these must be + // deployed and link-passed to the implementation so the bytecode's + // placeholder slots resolve at deploy time. + const StakeClaims = m.library('StakeClaims') + const AllocationHandler = m.library('AllocationHandler') + const IndexingAgreement = m.library('IndexingAgreement') + const IndexingAgreementDecoder = m.library('IndexingAgreementDecoder') + // Deploy implementation - const SubgraphServiceImplementation = deployImplementation(m, { - name: 'SubgraphService', - constructorArgs: [controllerAddress, disputeManagerProxyAddress, graphTallyCollectorAddress, curationProxyAddress], - }) + const SubgraphServiceImplementation = deployImplementation( + m, + { + name: 'SubgraphService', + constructorArgs: [ + controllerAddress, + disputeManagerProxyAddress, + graphTallyCollectorAddress, + curationProxyAddress, + recurringCollectorAddress, + ], + }, + { + libraries: { + StakeClaims, + AllocationHandler, + IndexingAgreement, + IndexingAgreementDecoder, + }, + }, + ) // Upgrade implementation const SubgraphService = upgradeTransparentUpgradeableProxy( diff --git a/packages/subgraph-service/tasks/deploy.ts b/packages/subgraph-service/tasks/deploy.ts index 581138439..860e8c67b 100644 --- a/packages/subgraph-service/tasks/deploy.ts +++ b/packages/subgraph-service/tasks/deploy.ts @@ -91,6 +91,7 @@ task('deploy:protocol', 'Deploy a new version of the Graph Protocol Horizon cont subgraphServiceProxyAddress: proxiesDeployment.Transparent_Proxy_SubgraphService.target as string, subgraphServiceProxyAdminAddress: proxiesDeployment.Transparent_ProxyAdmin_SubgraphService.target as string, graphTallyCollectorAddress: horizonDeployment.GraphTallyCollector.target as string, + recurringCollectorAddress: horizonDeployment.Transparent_Proxy_RecurringCollector.target as string, gnsProxyAddress: horizonDeployment.Graph_Proxy_L2GNS.target as string, gnsImplementationAddress: horizonDeployment.Implementation_L2GNS.target as string, subgraphNFTAddress: horizonDeployment.SubgraphNFT.target as string, From e7fd28173b1f750db98135edea8385454c6e0759 Mon Sep 17 00:00:00 2001 From: MoonBoi9001 Date: Thu, 16 Apr 2026 00:07:42 +0100 Subject: [PATCH 5/6] fix: link transitive library deps for IndexingAgreement stack Ignition's validator walks each deployed library's bytecode, so it is not enough to link the four libraries into SubgraphService and stop there. IndexingAgreement calls into IndexingAgreementDecoder, which calls into IndexingAgreementDecoderRaw, so both transitive links have to be declared up-front or the deploy aborts on IGN716 while validating the library futures themselves. Deploy IndexingAgreementDecoderRaw first, link it into the Decoder, and link the Decoder into IndexingAgreement. SubgraphService keeps its existing four links unchanged. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../ignition/modules/SubgraphService.ts | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/subgraph-service/ignition/modules/SubgraphService.ts b/packages/subgraph-service/ignition/modules/SubgraphService.ts index f1ae0382e..5b52706ba 100644 --- a/packages/subgraph-service/ignition/modules/SubgraphService.ts +++ b/packages/subgraph-service/ignition/modules/SubgraphService.ts @@ -34,10 +34,23 @@ export default buildModule('SubgraphService', (m) => { // indexing-agreement logic into stand-alone libraries; these must be // deployed and link-passed to the implementation so the bytecode's // placeholder slots resolve at deploy time. + // + // Ordering matters: IndexingAgreement calls into IndexingAgreementDecoder, + // which in turn calls into IndexingAgreementDecoderRaw, so the deeper + // libraries have to be deployed and linked first. const StakeClaims = m.library('StakeClaims') const AllocationHandler = m.library('AllocationHandler') - const IndexingAgreement = m.library('IndexingAgreement') - const IndexingAgreementDecoder = m.library('IndexingAgreementDecoder') + const IndexingAgreementDecoderRaw = m.library('IndexingAgreementDecoderRaw') + const IndexingAgreementDecoder = m.library('IndexingAgreementDecoder', { + libraries: { + IndexingAgreementDecoderRaw, + }, + }) + const IndexingAgreement = m.library('IndexingAgreement', { + libraries: { + IndexingAgreementDecoder, + }, + }) // Deploy implementation const SubgraphServiceImplementation = deployImplementation( From 3e739a38834c00816d2cae4cc87a8527e225f6b5 Mon Sep 17 00:00:00 2001 From: MoonBoi9001 Date: Thu, 16 Apr 2026 18:13:38 +0100 Subject: [PATCH 6/6] fix(toolshed): add conditions field to RCA decoder tuple The audit-branch RecurringCollectionAgreement struct added a uint16 conditions field at position 9 (between maxSecondsPerCollection and nonce). The toolshed decoder tuple was missing it, so decodeSignedRCA read 10 fields against 11 ABI-encoded fields: nonce read what was actually conditions, metadata read the wrong offset, decode threw. The indexer-agent relies on this decoder to read pending RCA proposals the indexer-service persists. Without the fix, every proposal logs "Failed to decode pending RCA proposal" and is skipped, so the agent never calls acceptIndexingAgreement on-chain and DIPs agreements expire in dipper's DB. Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/toolshed/src/core/recurring-collector.ts | 4 +++- packages/toolshed/test/recurring-collector.test.ts | 7 +++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/toolshed/src/core/recurring-collector.ts b/packages/toolshed/src/core/recurring-collector.ts index 9ddf5af98..9be27f44b 100644 --- a/packages/toolshed/src/core/recurring-collector.ts +++ b/packages/toolshed/src/core/recurring-collector.ts @@ -3,7 +3,7 @@ import { BytesLike, ethers } from 'ethers' // -- ABI tuple types for decoding -- const RCA_TUPLE = - 'tuple(uint64 deadline, uint64 endsAt, address payer, address dataService, address serviceProvider, uint256 maxInitialTokens, uint256 maxOngoingTokensPerSecond, uint32 minSecondsPerCollection, uint32 maxSecondsPerCollection, uint256 nonce, bytes metadata)' + 'tuple(uint64 deadline, uint64 endsAt, address payer, address dataService, address serviceProvider, uint256 maxInitialTokens, uint256 maxOngoingTokensPerSecond, uint32 minSecondsPerCollection, uint32 maxSecondsPerCollection, uint16 conditions, uint256 nonce, bytes metadata)' const SIGNED_RCA_TUPLE = `tuple(${RCA_TUPLE} rca, bytes signature)` @@ -23,6 +23,7 @@ export interface RecurringCollectionAgreement { maxOngoingTokensPerSecond: bigint minSecondsPerCollection: bigint maxSecondsPerCollection: bigint + conditions: bigint nonce: bigint metadata: string } @@ -58,6 +59,7 @@ export function decodeSignedRCA(data: BytesLike): SignedRCA { maxOngoingTokensPerSecond: decoded.rca.maxOngoingTokensPerSecond, minSecondsPerCollection: decoded.rca.minSecondsPerCollection, maxSecondsPerCollection: decoded.rca.maxSecondsPerCollection, + conditions: decoded.rca.conditions, nonce: decoded.rca.nonce, metadata: decoded.rca.metadata, }, diff --git a/packages/toolshed/test/recurring-collector.test.ts b/packages/toolshed/test/recurring-collector.test.ts index 51c28b509..1bbc9ceb5 100644 --- a/packages/toolshed/test/recurring-collector.test.ts +++ b/packages/toolshed/test/recurring-collector.test.ts @@ -23,6 +23,7 @@ const coder = ethers.AbiCoder.defaultAbiCoder() maxOngoingTokensPerSecond: 1n * 10n ** 15n, minSecondsPerCollection: 3600n, maxSecondsPerCollection: 86400n, + conditions: 0n, nonce: 42n, metadata: '0xdeadbeef', } @@ -30,7 +31,7 @@ const coder = ethers.AbiCoder.defaultAbiCoder() const encoded = coder.encode( [ - 'tuple(tuple(uint64 deadline, uint64 endsAt, address payer, address dataService, address serviceProvider, uint256 maxInitialTokens, uint256 maxOngoingTokensPerSecond, uint32 minSecondsPerCollection, uint32 maxSecondsPerCollection, uint256 nonce, bytes metadata) rca, bytes signature)', + 'tuple(tuple(uint64 deadline, uint64 endsAt, address payer, address dataService, address serviceProvider, uint256 maxInitialTokens, uint256 maxOngoingTokensPerSecond, uint32 minSecondsPerCollection, uint32 maxSecondsPerCollection, uint16 conditions, uint256 nonce, bytes metadata) rca, bytes signature)', ], [{ rca, signature }], ) @@ -46,6 +47,7 @@ const coder = ethers.AbiCoder.defaultAbiCoder() assert.equal(decoded.rca.maxOngoingTokensPerSecond, rca.maxOngoingTokensPerSecond) assert.equal(decoded.rca.minSecondsPerCollection, rca.minSecondsPerCollection) assert.equal(decoded.rca.maxSecondsPerCollection, rca.maxSecondsPerCollection) + assert.equal(decoded.rca.conditions, rca.conditions) assert.equal(decoded.rca.nonce, rca.nonce) assert.equal(decoded.rca.metadata, rca.metadata) assert.equal(decoded.signature, signature) @@ -65,6 +67,7 @@ const coder = ethers.AbiCoder.defaultAbiCoder() maxOngoingTokensPerSecond: 0n, minSecondsPerCollection: 0n, maxSecondsPerCollection: 0n, + conditions: 0n, nonce: 0n, metadata: '0x', } @@ -72,7 +75,7 @@ const coder = ethers.AbiCoder.defaultAbiCoder() const encoded = coder.encode( [ - 'tuple(tuple(uint64 deadline, uint64 endsAt, address payer, address dataService, address serviceProvider, uint256 maxInitialTokens, uint256 maxOngoingTokensPerSecond, uint32 minSecondsPerCollection, uint32 maxSecondsPerCollection, uint256 nonce, bytes metadata) rca, bytes signature)', + 'tuple(tuple(uint64 deadline, uint64 endsAt, address payer, address dataService, address serviceProvider, uint256 maxInitialTokens, uint256 maxOngoingTokensPerSecond, uint32 minSecondsPerCollection, uint32 maxSecondsPerCollection, uint16 conditions, uint256 nonce, bytes metadata) rca, bytes signature)', ], [{ rca, signature }], )