@sage-protocol/cli 0.3.6 → 0.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,6 +6,7 @@ const { ethers } = require('ethers');
6
6
  const { resolveArtifact } = require('../utils/artifacts');
7
7
  const { getSignerSession } = require('../utils/cli-session');
8
8
  const { handleCLIError } = require('../utils/error-handler');
9
+ const { resolveGovContext } = require('../utils/gov-context');
9
10
 
10
11
  function hex32(id) {
11
12
  if (!id) throw new Error('Missing id');
@@ -43,22 +44,6 @@ function deriveBountyId({ idHex, slug, title }) {
43
44
  return { idHex: ethers.keccak256(ethers.toUtf8Bytes(auto)), source: 'auto', slug: auto };
44
45
  }
45
46
 
46
- async function getGovernorAddress(subdao) {
47
- if (subdao) {
48
- const rpcUrl = process.env.RPC_URL || 'https://base-sepolia.publicnode.com';
49
- try {
50
- const provider = new ethers.JsonRpcProvider(rpcUrl);
51
- const abi = ['function governor() view returns (address)'];
52
- const sub = new ethers.Contract(subdao, abi, provider);
53
- return await sub.governor();
54
- } catch (e) {
55
- console.log(`⚠️ Could not get governor from SubDAO: ${e.message}`);
56
- return process.env.GOV;
57
- }
58
- }
59
- return process.env.GOV;
60
- }
61
-
62
47
  async function maybeEnsureSxxxAllowance(governorAddr, defaultAmountEther = '100') {
63
48
  try {
64
49
  const autoYes = String(process.env.SAGE_YES || '').toLowerCase();
@@ -91,40 +76,6 @@ async function maybeEnsureSxxxAllowance(governorAddr, defaultAmountEther = '100'
91
76
  }
92
77
  }
93
78
 
94
- async function resolveGovContext(options) {
95
- let subdao = options.subdao || process.env.WORKING_SUBDAO_ADDRESS || process.env.SUBDAO;
96
- let governorAddr = process.env.GOV;
97
-
98
- if (subdao) {
99
- governorAddr = await getGovernorAddress(subdao);
100
- } else {
101
- // Fallback to wizard to select a SubDAO or main gov
102
- const inquirer = (require('inquirer').default || require('inquirer'));
103
- const { scope } = await inquirer.prompt([
104
- {
105
- type: 'list', name: 'scope', message: 'Select governance context', choices: [
106
- { name: 'A SubDAO (recommended)', value: 'subdao' },
107
- { name: 'Main protocol (use GOV from env)', value: 'main' }
108
- ]
109
- }
110
- ]);
111
-
112
- if (scope === 'subdao') {
113
- // For now, use a simple prompt for SubDAO address
114
- const { subdaoAddr } = await inquirer.prompt([
115
- { type: 'input', name: 'subdaoAddr', message: 'Enter SubDAO address:' }
116
- ]);
117
- subdao = subdaoAddr;
118
- governorAddr = await getGovernorAddress(subdao);
119
- process.env.SUBDAO = subdao;
120
- process.env.WORKING_SUBDAO_ADDRESS = subdao;
121
- cliConfig.writeAddresses({ SUBDAO: subdao, WORKING_SUBDAO_ADDRESS: subdao, GOV: governorAddr });
122
- }
123
- }
124
-
125
- return { governorAddr, subdao };
126
- }
127
-
128
79
  function register(program) {
129
80
  const bounty = new Command('bounty')
130
81
  .description('Enhanced bounty management (create, claim, complete, approve)')
@@ -142,17 +93,17 @@ function register(program) {
142
93
  .option('--salt <hex32>', 'Salt (0x... 32 bytes). Default random')
143
94
  .option('--delay <sec>', 'Override delay seconds (default Timelock value)')
144
95
  .option('--execute', 'Execute immediately if delay=0', false)
145
- .option('--subdao <address>', 'Resolve Timelock from SubDAO')
96
+ .option('--subdao <address>', 'Resolve Timelock from DAO/SubDAO')
97
+ .option('--dao <address>', 'Alias for --subdao (DAO/SubDAO)')
146
98
  .option('--gov <address>', 'Resolve Timelock via Governor')
147
99
  .action(async (opts) => {
148
100
  try {
149
101
  const { ethers } = require('ethers');
150
- const { resolveGovContext } = require('../utils/gov-context');
151
102
  const wm = new (require('@sage-protocol/wallet-manager'))();
152
103
  await wm.connect();
153
104
  const signer = wm.getSigner();
154
105
  const provider = wm.getProvider();
155
- const ctx = await resolveGovContext({ govOpt: opts.gov, subdaoOpt: opts.subdao, provider });
106
+ const ctx = await resolveGovContext({ govOpt: opts.gov, subdaoOpt: opts.subdao || opts.dao, provider });
156
107
  const tlAddr = ctx.timelock || process.env.TIMELOCK || process.env.TIMELOCK_ADDRESS;
157
108
  if (!tlAddr) throw new Error('Timelock not resolved. Pass --subdao or set TIMELOCK');
158
109
  const tlAbi = resolveArtifact('contracts/cloneable/TimelockControllerCloneable.sol/TimelockControllerCloneable.json').abi;
@@ -193,7 +144,8 @@ function register(program) {
193
144
  bounty
194
145
  .command('set-mode')
195
146
  .description('Set bounty winner mode: auto | board | token')
196
- .requiredOption('--subdao <address>', 'SubDAO to derive timelock/governor')
147
+ .requiredOption('--subdao <address>', 'DAO/SubDAO to derive timelock/governor')
148
+ .option('--dao <address>', 'Alias for --subdao')
197
149
  .requiredOption('--mode <mode>', 'auto | board | token')
198
150
  .option('--fast-track <wei>', 'Auto mode: fast-track limit in wei', (value) => BigInt(value))
199
151
  .option('--operator <address>', 'Board mode: operator Safe/EOA address')
@@ -207,7 +159,7 @@ function register(program) {
207
159
  await sdk.bounty.configureWinnerMode({
208
160
  provider,
209
161
  signer,
210
- subdao: opts.subdao,
162
+ subdao: opts.subdao || opts.dao,
211
163
  mode: String(opts.mode || '').toLowerCase(),
212
164
  fastTrackLimit: opts.fastTrack ?? defaultFastTrack,
213
165
  governanceConfig: opts.governanceConfig,
@@ -225,7 +177,8 @@ function register(program) {
225
177
  bounty
226
178
  .command('fund')
227
179
  .description('Fund a bounty via Governor proposal (approve + increaseReward)')
228
- .requiredOption('--subdao <address>', 'SubDAO (derives Governor)')
180
+ .requiredOption('--subdao <address>', 'DAO/SubDAO (derives Governor)')
181
+ .option('--dao <address>', 'Alias for --subdao')
229
182
  .requiredOption('--bounty-system <address>', 'SimpleBountySystem address')
230
183
  .requiredOption('--bounty-id <id>', 'Bounty id', (value) => BigInt(value))
231
184
  .requiredOption('--token <address>', 'ERC20 token address (e.g. SXXX)')
@@ -237,7 +190,7 @@ function register(program) {
237
190
  const { proposalCall } = await sdk.bounty.fundFromTreasury({
238
191
  provider,
239
192
  signer,
240
- subdao: opts.subdao,
193
+ subdao: opts.subdao || opts.dao,
241
194
  bountySystem: opts.bountySystem,
242
195
  bountyId: opts.bountyId,
243
196
  token: opts.token,
@@ -286,7 +239,8 @@ function register(program) {
286
239
  bounty
287
240
  .command('propose-approve')
288
241
  .description('Propose approval of bounty completion (token mode)')
289
- .requiredOption('--subdao <address>', 'SubDAO (derives Governor)')
242
+ .requiredOption('--subdao <address>', 'DAO/SubDAO (derives Governor)')
243
+ .option('--dao <address>', 'Alias for --subdao')
290
244
  .requiredOption('--bounty-system <address>', 'SimpleBountySystem address')
291
245
  .requiredOption('--bounty-id <id>', 'Bounty id', (value) => BigInt(value))
292
246
  .requiredOption('--deliverable <cid>', 'Deliverable IPFS CID')
@@ -297,7 +251,7 @@ function register(program) {
297
251
  const { proposalCall } = await sdk.bounty.proposeApproveWinner({
298
252
  provider,
299
253
  signer,
300
- subdao: opts.subdao,
254
+ subdao: opts.subdao || opts.dao,
301
255
  bountySystem: opts.bountySystem,
302
256
  bountyId: opts.bountyId,
303
257
  deliverable: opts.deliverable,
@@ -479,15 +433,21 @@ function register(program) {
479
433
  .option('--ipfs <cid>', 'IPFS CID with detailed specifications')
480
434
  .option('--deadline <days>', 'Deadline in days from now', '30')
481
435
  .option('--fast-track', 'Enable fast-track approval for small bounties', false)
482
- .option('--subdao <address>', 'SubDAO context for governance')
436
+ .option('--subdao <address>', 'DAO/SubDAO context for governance')
437
+ .option('--dao <address>', 'Alias for --subdao')
438
+ .option('--yes', 'Skip interactive confirmation prompts', false)
483
439
  .action(async (opts) => {
484
440
  try {
485
- const { governorAddr, subdao } = await resolveGovContext(opts);
441
+ const rpcUrl = process.env.RPC_URL || 'https://base-sepolia.publicnode.com';
442
+ const provider = new ethers.JsonRpcProvider(rpcUrl);
443
+ const ctx = await resolveGovContext({ govOpt: null, subdaoOpt: opts.subdao || opts.dao, provider });
444
+ const governorAddr = ctx.governor || process.env.GOV;
445
+ const subdao = ctx.subdao || opts.subdao || opts.dao || process.env.WORKING_SUBDAO_ADDRESS || process.env.SUBDAO || null;
446
+ if (!governorAddr) throw new Error('Governor address not resolved. Pass --subdao/--dao or configure GOV/SUBDAO in .sage config.');
486
447
  const bountySystem = cliConfig.resolveAddress('SIMPLE_BOUNTY_SYSTEM_ADDRESS');
487
448
  if (!bountySystem) throw new Error('SIMPLE_BOUNTY_SYSTEM_ADDRESS not set in environment');
488
449
 
489
450
  // Convert reward to wei
490
- const rpcUrl = process.env.RPC_URL || 'https://base-sepolia.publicnode.com';
491
451
  const rewardWei = ethers.parseEther(String(opts.reward)).toString();
492
452
 
493
453
  // Calculate deadline timestamp
@@ -536,8 +496,17 @@ function register(program) {
536
496
  console.log('IPFS CID:', ipfsCID);
537
497
  console.log('Fast-track eligible:', fastTrackEligible);
538
498
 
539
- const inquirer = (require('inquirer').default || require('inquirer'));
540
- const { confirm } = await inquirer.prompt([{ type: 'confirm', name: 'confirm', message: 'Create bounty proposal?', default: true }]);
499
+ let confirm = false;
500
+ const autoYes = String(process.env.SAGE_YES || '').toLowerCase();
501
+ if (opts.yes || autoYes === '1' || autoYes === 'true') {
502
+ confirm = true;
503
+ } else {
504
+ const inquirer = (require('inquirer').default || require('inquirer'));
505
+ const ans = await inquirer.prompt([
506
+ { type: 'confirm', name: 'confirm', message: 'Create bounty proposal?', default: true }
507
+ ]);
508
+ confirm = !!ans.confirm;
509
+ }
541
510
  if (!confirm) return;
542
511
 
543
512
  // Ensure SXXX allowance for bounty system (creator stake)
@@ -573,7 +542,8 @@ function register(program) {
573
542
  .command('claim')
574
543
  .description('Claim an active bounty')
575
544
  .requiredOption('--id <bountyId>', 'Bounty ID to claim')
576
- .option('--subdao <address>', 'SubDAO context')
545
+ .option('--subdao <address>', 'DAO/SubDAO context')
546
+ .option('--dao <address>', 'Alias for --subdao')
577
547
  .action(async (opts) => {
578
548
  try {
579
549
  const bountySystem = cliConfig.resolveAddress('SIMPLE_BOUNTY_SYSTEM_ADDRESS');
@@ -606,7 +576,8 @@ function register(program) {
606
576
  .description('Mark bounty as complete with deliverable')
607
577
  .requiredOption('--id <bountyId>', 'Bounty ID to complete')
608
578
  .requiredOption('--deliverable <ipfs>', 'IPFS CID of deliverable')
609
- .option('--subdao <address>', 'SubDAO context')
579
+ .option('--subdao <address>', 'DAO/SubDAO context')
580
+ .option('--dao <address>', 'Alias for --subdao')
610
581
  .action(async (opts) => {
611
582
  try {
612
583
  const bountySystem = cliConfig.resolveAddress('SIMPLE_BOUNTY_SYSTEM_ADDRESS');
@@ -636,7 +607,9 @@ function register(program) {
636
607
  .description('Manually craft a governance proposal to approve bounty completion')
637
608
  .requiredOption('--id <bountyId>', 'Bounty ID to approve')
638
609
  .option('--proposal <proposalId>', 'Reference proposal ID for context')
639
- .option('--subdao <address>', 'SubDAO context')
610
+ .option('--subdao <address>', 'DAO/SubDAO context')
611
+ .option('--dao <address>', 'Alias for --subdao')
612
+ .option('--yes', 'Skip interactive confirmation prompts', false)
640
613
  .action(async (opts) => {
641
614
  try {
642
615
  const { governorAddr } = await resolveGovContext(opts);
@@ -656,8 +629,17 @@ function register(program) {
656
629
  console.log('Bounty ID:', opts.id);
657
630
  console.log('Reference Proposal:', opts.proposal || 'None');
658
631
 
659
- const inquirer = (require('inquirer').default || require('inquirer'));
660
- const { confirm } = await inquirer.prompt([{ type: 'confirm', name: 'confirm', message: 'Create approval proposal?', default: true }]);
632
+ let confirm = false;
633
+ const autoYes = String(process.env.SAGE_YES || '').toLowerCase();
634
+ if (opts.yes || autoYes === '1' || autoYes === 'true') {
635
+ confirm = true;
636
+ } else {
637
+ const inquirer = (require('inquirer').default || require('inquirer'));
638
+ const ans = await inquirer.prompt([
639
+ { type: 'confirm', name: 'confirm', message: 'Create approval proposal?', default: true }
640
+ ]);
641
+ confirm = !!ans.confirm;
642
+ }
661
643
  if (!confirm) return;
662
644
 
663
645
  const rpcUrl = process.env.RPC_URL || 'https://base-sepolia.publicnode.com';
@@ -753,10 +735,15 @@ function register(program) {
753
735
  .option('--slug <text>', 'Bounty id slug; hashed to bytes32 if provided')
754
736
  .option('--id <bytes32>', 'Bounty id bytes32 (takes precedence)')
755
737
  .requiredOption('--cid <cid>', 'IPFS CID/URI with bounty details')
756
- .option('--subdao <address>', 'SubDAO to derive Governor from')
738
+ .option('--subdao <address>', 'DAO/SubDAO to derive Governor from')
739
+ .option('--dao <address>', 'Alias for --subdao')
740
+ .option('--yes', 'Skip interactive confirmation prompts', false)
757
741
  .action(async (opts) => {
758
742
  try {
759
- const { governorAddr } = await resolveGovContext(opts);
743
+ const rpcUrl = process.env.RPC_URL || 'https://base-sepolia.publicnode.com';
744
+ const provider = new ethers.JsonRpcProvider(rpcUrl);
745
+ const ctx = await resolveGovContext({ govOpt: null, subdaoOpt: opts.subdao || opts.dao, provider });
746
+ const governorAddr = ctx.governor || process.env.GOV;
760
747
  const announcer = opts.announcer || cliConfig.resolveAddress('BOUNTY_ANNOUNCEMENTS_ADDRESS');
761
748
  if (!announcer) throw new Error('Missing BountyAnnouncements address. Set BOUNTY_ANNOUNCEMENTS_ADDRESS or pass --announcer');
762
749
 
@@ -773,8 +760,17 @@ function register(program) {
773
760
  console.log('--- Proposal Preview (announce) ---');
774
761
  console.log({ targets, values: values.map(v => v.toString()), calldatas, description, derivedId: { idHex, source, slug } });
775
762
 
776
- const inquirer = (require('inquirer').default || require('inquirer'));
777
- const { confirm } = await inquirer.prompt([{ type: 'confirm', name: 'confirm', message: 'Submit proposal?', default: true }]);
763
+ let confirm = false;
764
+ const autoYes = String(process.env.SAGE_YES || '').toLowerCase();
765
+ if (opts.yes || autoYes === '1' || autoYes === 'true') {
766
+ confirm = true;
767
+ } else {
768
+ const inquirer = (require('inquirer').default || require('inquirer'));
769
+ const ans = await inquirer.prompt([
770
+ { type: 'confirm', name: 'confirm', message: 'Submit proposal?', default: true }
771
+ ]);
772
+ confirm = !!ans.confirm;
773
+ }
778
774
  if (!confirm) return;
779
775
 
780
776
  const WM = require('@sage-protocol/wallet-manager');
@@ -814,10 +810,15 @@ function register(program) {
814
810
  .option('--amount <amount>', 'Intended payout amount (units of asset, or wei for ETH)', '0')
815
811
  .option('--asset <address>', 'ERC20 token address (omit or 0x0 for ETH)', '0x0000000000000000000000000000000000000000')
816
812
  .option('--erc20', 'Treat payout as ERC20 (default ETH if not set)', false)
817
- .option('--subdao <address>', 'SubDAO to derive Governor from')
813
+ .option('--subdao <address>', 'DAO/SubDAO to derive Governor from')
814
+ .option('--dao <address>', 'Alias for --subdao')
815
+ .option('--yes', 'Skip interactive confirmation prompts', false)
818
816
  .action(async (opts) => {
819
817
  try {
820
- const { governorAddr } = await resolveGovContext(opts);
818
+ const rpcUrl = process.env.RPC_URL || 'https://base-sepolia.publicnode.com';
819
+ const provider = new ethers.JsonRpcProvider(rpcUrl);
820
+ const ctx = await resolveGovContext({ govOpt: null, subdaoOpt: opts.subdao || opts.dao, provider });
821
+ const governorAddr = ctx.governor || process.env.GOV;
821
822
  const announcer = opts.announcer || process.env.BOUNTY_ANNOUNCEMENTS_ADDRESS;
822
823
  if (!announcer) throw new Error('Missing BountyAnnouncements address. Set BOUNTY_ANNOUNCEMENTS_ADDRESS or pass --announcer');
823
824
  const winnerAddr = opts.winner || process.env.DEV_WALLET_ADDRESS;
@@ -839,8 +840,17 @@ function register(program) {
839
840
  console.log('--- Proposal Preview (winner) ---');
840
841
  console.log({ targets, values: values.map(v => v.toString()), calldatas, description, derivedId: { idHex, source, slug } });
841
842
 
842
- const inquirer = (require('inquirer').default || require('inquirer'));
843
- const { confirm } = await inquirer.prompt([{ type: 'confirm', name: 'confirm', message: 'Submit proposal?', default: true }]);
843
+ let confirm = false;
844
+ const autoYes = String(process.env.SAGE_YES || '').toLowerCase();
845
+ if (opts.yes || autoYes === '1' || autoYes === 'true') {
846
+ confirm = true;
847
+ } else {
848
+ const inquirer = (require('inquirer').default || require('inquirer'));
849
+ const ans = await inquirer.prompt([
850
+ { type: 'confirm', name: 'confirm', message: 'Submit proposal?', default: true }
851
+ ]);
852
+ confirm = !!ans.confirm;
853
+ }
844
854
  if (!confirm) return;
845
855
 
846
856
  const WM = require('@sage-protocol/wallet-manager');
@@ -866,11 +876,16 @@ function register(program) {
866
876
  .description('Create a proposal to pay a bounty winner in ETH from Timelock')
867
877
  .option('--to <address>', 'Winner address (defaults to DEV_WALLET_ADDRESS)')
868
878
  .requiredOption('--amount <eth>', 'Amount in ETH (decimal)')
869
- .option('--subdao <address>', 'SubDAO to derive Governor from')
879
+ .option('--subdao <address>', 'DAO/SubDAO to derive Governor from')
880
+ .option('--dao <address>', 'Alias for --subdao')
870
881
  .option('--note <text>', 'Description note')
882
+ .option('--yes', 'Skip interactive confirmation prompts', false)
871
883
  .action(async (opts) => {
872
884
  try {
873
- const { governorAddr } = await resolveGovContext(opts);
885
+ const rpcUrl = process.env.RPC_URL || 'https://base-sepolia.publicnode.com';
886
+ const provider = new ethers.JsonRpcProvider(rpcUrl);
887
+ const ctx = await resolveGovContext({ govOpt: null, subdaoOpt: opts.subdao || opts.dao, provider });
888
+ const governorAddr = ctx.governor || process.env.GOV;
874
889
  const to = opts.to || process.env.DEV_WALLET_ADDRESS;
875
890
  if (!to) throw new Error('Missing winner address. Set DEV_WALLET_ADDRESS or pass --to');
876
891
 
@@ -884,8 +899,17 @@ function register(program) {
884
899
  console.log('--- Proposal Preview (payout-eth) ---');
885
900
  console.log({ targets, values: values.map(v => v.toString()), calldatas, description });
886
901
 
887
- const inquirer = (require('inquirer').default || require('inquirer'));
888
- const { confirm } = await inquirer.prompt([{ type: 'confirm', name: 'confirm', message: 'Submit proposal?', default: true }]);
902
+ let confirm = false;
903
+ const autoYes = String(process.env.SAGE_YES || '').toLowerCase();
904
+ if (opts.yes || autoYes === '1' || autoYes === 'true') {
905
+ confirm = true;
906
+ } else {
907
+ const inquirer = (require('inquirer').default || require('inquirer'));
908
+ const ans = await inquirer.prompt([
909
+ { type: 'confirm', name: 'confirm', message: 'Submit proposal?', default: true }
910
+ ]);
911
+ confirm = !!ans.confirm;
912
+ }
889
913
  if (!confirm) return;
890
914
 
891
915
  const WM = require('@sage-protocol/wallet-manager');
@@ -920,11 +944,16 @@ function register(program) {
920
944
  .option('--token <address>', 'ERC20 token address held by Timelock (defaults to SXXX_TOKEN_ADDRESS)')
921
945
  .option('--to <address>', 'Winner address (defaults to DEV_WALLET_ADDRESS)')
922
946
  .requiredOption('--amount <units>', 'Token amount in smallest units')
923
- .option('--subdao <address>', 'SubDAO to derive Governor from')
947
+ .option('--subdao <address>', 'DAO/SubDAO to derive Governor from')
948
+ .option('--dao <address>', 'Alias for --subdao')
924
949
  .option('--note <text>', 'Description note')
950
+ .option('--yes', 'Skip interactive confirmation prompts', false)
925
951
  .action(async (opts) => {
926
952
  try {
927
- const { governorAddr } = await resolveGovContext(opts);
953
+ const rpcUrl = process.env.RPC_URL || 'https://base-sepolia.publicnode.com';
954
+ const provider = new ethers.JsonRpcProvider(rpcUrl);
955
+ const ctx = await resolveGovContext({ govOpt: null, subdaoOpt: opts.subdao || opts.dao, provider });
956
+ const governorAddr = ctx.governor || process.env.GOV;
928
957
  const token = opts.token || process.env.SXXX_TOKEN_ADDRESS;
929
958
  if (!token) throw new Error('Missing token. Set SXXX_TOKEN_ADDRESS or pass --token');
930
959
  const to = opts.to || process.env.DEV_WALLET_ADDRESS;
@@ -941,8 +970,17 @@ function register(program) {
941
970
  console.log('--- Proposal Preview (payout-erc20) ---');
942
971
  console.log({ targets, values: values.map(v => v.toString()), calldatas, description });
943
972
 
944
- const inquirer = (require('inquirer').default || require('inquirer'));
945
- const { confirm } = await inquirer.prompt([{ type: 'confirm', name: 'confirm', message: 'Submit proposal?', default: true }]);
973
+ let confirm = false;
974
+ const autoYes = String(process.env.SAGE_YES || '').toLowerCase();
975
+ if (opts.yes || autoYes === '1' || autoYes === 'true') {
976
+ confirm = true;
977
+ } else {
978
+ const inquirer = (require('inquirer').default || require('inquirer'));
979
+ const ans = await inquirer.prompt([
980
+ { type: 'confirm', name: 'confirm', message: 'Submit proposal?', default: true }
981
+ ]);
982
+ confirm = !!ans.confirm;
983
+ }
946
984
  if (!confirm) return;
947
985
 
948
986
  const WM = require('@sage-protocol/wallet-manager');
@@ -568,6 +568,8 @@ function register(program) {
568
568
  setIf('BOUNTY_ANNOUNCEMENTS_ADDRESS', data.BountyAnnouncements || data.Bounty);
569
569
  setIf('SIMPLE_BOUNTY_SYSTEM_ADDRESS', data.SimpleBountySystem || data.BountySystem);
570
570
  setIf('SIMPLE_CONTRIBUTOR_SYSTEM_ADDRESS', data.SimpleContributorSystem || data.ContributorSystem);
571
+ setIf('MOCK_USDC_ADDRESS', data.MockUSDC || data.MockUsdc || data.USDC);
572
+ setIf('BOOST_MANAGER_DIRECT_ADDRESS', data.GovernanceBoostManagerDirect || data.BoostManagerDirect);
571
573
  // Optionally include test/deployer addresses under separate keys
572
574
  // but avoid setting SUBDAO/GOV/TIMELOCK unless user decides explicitly
573
575
 
@@ -238,9 +238,14 @@ async function runDoctor(opts = {}) {
238
238
  }
239
239
  }
240
240
 
241
- // Optional feature addresses (warn-level): Bounty system, etc.
241
+ // Optional feature addresses (warn-level): Bounty system, Boost managers, Premium prompts, etc.
242
242
  const featureAddrs = [
243
- { key: 'SIMPLE_BOUNTY_SYSTEM_ADDRESS', feature: 'bounty', hint: 'Required for `sage bounty *` commands.' }
243
+ { key: 'SIMPLE_BOUNTY_SYSTEM_ADDRESS', feature: 'bounty', hint: 'Required for `sage bounty *` commands.' },
244
+ { key: 'BOOST_MANAGER_ADDRESS', feature: 'boost (merkle)', hint: 'Required for `sage boost fund/status/claim` (Merkle manager).' },
245
+ { key: 'BOOST_MANAGER_DIRECT_ADDRESS', feature: 'boost (direct)', hint: 'Required for `sage boost create/status/finalize` (direct manager).' },
246
+ { key: 'PREMIUM_PROMPTS_ADDRESS', feature: 'premium prompts', hint: 'Required for `sage premium *` commands.' },
247
+ { key: 'PREMIUM_RECEIPT_ADDRESS', feature: 'premium receipts', hint: 'Required for ERC1155 receipt checks (premium.check).' },
248
+ { key: 'SIMPLE_KEY_STORE_ADDRESS', feature: 'premium keystore', hint: 'Recommended for premium roles wiring and Lit access control.' }
244
249
  ];
245
250
  for (const entry of featureAddrs) {
246
251
  const v = process.env[entry.key];
@@ -319,7 +324,7 @@ async function runDoctor(opts = {}) {
319
324
 
320
325
  // 6) Governor diagnostics (mode, roles, quorum, cooldown, stakeRequired)
321
326
  try {
322
- const subdao = opts.subdao || process.env.WORKING_SUBDAO_ADDRESS || process.env.SUBDAO;
327
+ const subdao = opts.subdao || opts.dao || process.env.WORKING_SUBDAO_ADDRESS || process.env.SUBDAO;
323
328
  let governorAddr = process.env.GOV;
324
329
  if (subdao && provider) {
325
330
  const abi = ['function governor() view returns (address)'];
@@ -764,7 +769,8 @@ function register(program) {
764
769
  .option('--json', 'Output JSON for CI/scripts', false)
765
770
  .option('--registry', 'Also check LibraryRegistry roles (FACTORY_ROLE, LIBRARY_ADMIN_ROLE)', false)
766
771
  .option('--cid <cid>', 'Optional CID to warm-test IPFS gateways')
767
- .option('--subdao <address>', 'Target DAO for mode/roles diagnostics')
772
+ .option('--subdao <address>', 'Target DAO/SubDAO for mode/roles diagnostics')
773
+ .option('--dao <address>', 'Alias for --subdao')
768
774
  .option('--fix', 'Attempt to fix detected issues interactively', false)
769
775
  .option('--auto-fix', 'Attempt to fix non-interactively with safe defaults', false)
770
776
  .action(async (opts) => {
@@ -823,8 +829,8 @@ function register(program) {
823
829
 
824
830
  if (opts.json) {
825
831
  // Build JSON object with key facts
826
- const rpcUrl = process.env.RPC_URL || 'https://sepolia.base.org';
827
- const chainId = Number(process.env.CHAIN_ID || 0);
832
+ const rpcUrl = process.env.RPC_URL || 'https://sepolia.base.org';
833
+ const chainId = Number(process.env.CHAIN_ID || 0);
828
834
  const sageHome = _resolveSageHome();
829
835
  const out = {
830
836
  rpcUrl,
@@ -834,9 +840,14 @@ function register(program) {
834
840
  LIBRARY_REGISTRY_ADDRESS: process.env.LIBRARY_REGISTRY_ADDRESS || null,
835
841
  SUBDAO_FACTORY_ADDRESS: process.env.SUBDAO_FACTORY_ADDRESS || null,
836
842
  SIMPLE_BOUNTY_SYSTEM_ADDRESS: process.env.SIMPLE_BOUNTY_SYSTEM_ADDRESS || null,
843
+ BOOST_MANAGER_ADDRESS: process.env.BOOST_MANAGER_ADDRESS || null,
844
+ BOOST_MANAGER_DIRECT_ADDRESS: process.env.BOOST_MANAGER_DIRECT_ADDRESS || null,
845
+ PREMIUM_PROMPTS_ADDRESS: process.env.PREMIUM_PROMPTS_ADDRESS || null,
846
+ PREMIUM_RECEIPT_ADDRESS: process.env.PREMIUM_RECEIPT_ADDRESS || null,
847
+ SIMPLE_KEY_STORE_ADDRESS: process.env.SIMPLE_KEY_STORE_ADDRESS || null,
837
848
  SUBDAO: process.env.WORKING_SUBDAO_ADDRESS || process.env.SUBDAO || null,
838
849
  GOV: process.env.GOV || null,
839
- TIMELOCK: process.env.TIMELOCK || null,
850
+ TIMELOCK: process.env.TIMELOCK || process.env.TIMELOCK_ADDRESS || null,
840
851
  SAFE_TX_SERVICE_URL: process.env.SAFE_TX_SERVICE_URL || null,
841
852
  SAGE_USE_SAFE_TX_SERVICE: process.env.SAGE_USE_SAFE_TX_SERVICE || null
842
853
  },