@@ -10,9 +10,15 @@ import {
1010import { canSignAsGovernor } from '@graphprotocol/deployment/lib/controller-utils.js'
1111import { getResolvedSettingsForEnv } from '@graphprotocol/deployment/lib/deployment-config.js'
1212import { assumeUpgraded , ComponentTags , GoalTags } from '@graphprotocol/deployment/lib/deployment-tags.js'
13+ import {
14+ createGovernanceTxBuilder ,
15+ executeTxBatchDirect ,
16+ saveGovernanceTx ,
17+ } from '@graphprotocol/deployment/lib/execute-governance.js'
1318import { requireContracts } from '@graphprotocol/deployment/lib/issuance-deploy-utils.js'
1419import { createActionModule } from '@graphprotocol/deployment/lib/script-factories.js'
1520import { syncComponentsFromRegistry } from '@graphprotocol/deployment/lib/sync-utils.js'
21+ import type { TxBuilder } from '@graphprotocol/deployment/lib/tx-builder.js'
1622import { graph } from '@graphprotocol/deployment/rocketh/deploy.js'
1723import type { Environment } from '@rocketh/core/types'
1824import type { PublicClient } from 'viem'
@@ -30,34 +36,41 @@ import type { PublicClient } from 'viem'
3036async function integrateOracle (
3137 env : Environment ,
3238 client : PublicClient ,
39+ builder : TxBuilder ,
40+ governor : string ,
41+ canSign : boolean ,
3342 targetLabel : string ,
3443 targetEntry : RegistryEntry ,
3544 oracleName : EligibilityOracleContractName | undefined ,
36- ) : Promise < void > {
45+ ) : Promise < boolean > {
3746 if ( ! oracleName ) {
3847 env . showMessage ( `\n ○ ${ targetLabel } : no eligibility oracle configured — skipping\n` )
39- return
48+ return false
4049 }
4150
4251 const reoEntry = eligibilityOracleContract ( oracleName )
4352 await syncComponentsFromRegistry ( env , [ reoEntry , targetEntry ] )
4453 const [ reo , target ] = requireContracts ( env , [ reoEntry , targetEntry ] )
4554
46- const { governor, canSign } = await canSignAsGovernor ( env )
55+ const applyOpts = {
56+ contractName : `${ targetEntry . name } -REO` ,
57+ contractAddress : target . address ,
58+ canExecuteDirectly : canSign ,
59+ executor : governor ,
60+ // Append to the shared batch; the caller executes/saves once for all targets.
61+ builder,
62+ }
4763
4864 // Sequenced-bundle generation: the target proxy isn't upgraded yet, so the
4965 // oracle getter would revert. Skip the probe/idempotency read and emit the
5066 // set-oracle TX unconditionally. The resulting bundle is sequenced-only —
5167 // execute it after the upgrade bundle (nonce order enforces this).
5268 if ( assumeUpgraded ( ) ) {
53- await applyConfiguration ( env , client , [ createRMIntegrationCondition ( reo . address ) ] , {
54- contractName : `${ target . name } -REO` ,
55- contractAddress : target . address ,
56- canExecuteDirectly : canSign ,
57- executor : governor ,
69+ const result = await applyConfiguration ( env , client , [ createRMIntegrationCondition ( reo . address ) ] , {
70+ ...applyOpts ,
5871 assumeUndone : true ,
5972 } )
60- return
73+ return result . changesNeeded
6174 }
6275
6376 // Skip only if the target isn't upgraded yet (no oracle getter). Once it
@@ -73,15 +86,11 @@ async function integrateOracle(
7386 } catch {
7487 // Function not available — target not upgraded, skip
7588 env . showMessage ( `\n ○ ${ targetLabel } does not support getProviderEligibilityOracle — skipping\n` )
76- return
89+ return false
7790 }
7891
79- await applyConfiguration ( env , client , [ createRMIntegrationCondition ( reo . address ) ] , {
80- contractName : `${ target . name } -REO` ,
81- contractAddress : target . address ,
82- canExecuteDirectly : canSign ,
83- executor : governor ,
84- } )
92+ const result = await applyConfiguration ( env , client , [ createRMIntegrationCondition ( reo . address ) ] , applyOpts )
93+ return result . changesNeeded
8594}
8695
8796/**
@@ -103,21 +112,48 @@ export default createActionModule(
103112 async ( env ) => {
104113 const settings = await getResolvedSettingsForEnv ( env )
105114 const client = graph . getPublicClient ( env ) as PublicClient
115+ const { governor, canSign } = await canSignAsGovernor ( env )
116+
117+ // One shared batch for every configured target (RM and/or RAM), so both
118+ // setProviderEligibilityOracle TXs land in a single governance bundle.
119+ const builder = await createGovernanceTxBuilder ( env , 'gip-0088-eligibility-integrate' , {
120+ name : 'GIP-0088 Eligibility Integration' ,
121+ description : 'Set the provider eligibility oracle on RewardsManager and RecurringAgreementManager' ,
122+ } )
106123
107- await integrateOracle (
124+ const rmChanged = await integrateOracle (
108125 env ,
109126 client ,
127+ builder ,
128+ governor ,
129+ canSign ,
110130 'RM' ,
111131 Contracts . horizon . RewardsManager ,
112132 settings . rewardsManager . eligibilityOracle ,
113133 )
114- await integrateOracle (
134+ const ramChanged = await integrateOracle (
115135 env ,
116136 client ,
137+ builder ,
138+ governor ,
139+ canSign ,
117140 'RAM' ,
118141 Contracts . issuance . RecurringAgreementManager ,
119142 settings . recurringAgreementManager . eligibilityOracle ,
120143 )
144+
145+ if ( ! rmChanged && ! ramChanged ) {
146+ env . showMessage ( '\n✅ Eligibility oracles already match config — nothing to do\n' )
147+ return
148+ }
149+
150+ if ( canSign ) {
151+ env . showMessage ( '\n🔨 Executing eligibility integration batch...\n' )
152+ await executeTxBatchDirect ( env , builder , governor )
153+ env . showMessage ( '\n✅ Eligibility integration complete\n' )
154+ } else {
155+ saveGovernanceTx ( env , builder , 'GIP-0088 Eligibility Integration' )
156+ }
121157 } ,
122158 {
123159 // Ordering anchor for a combined `--tags GIP-0088` run: REO-A always deploys
0 commit comments