@sage-protocol/cli 0.8.0 → 0.8.3
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.
- package/README.md +12 -11
- package/dist/cli/commands/boost.js +339 -62
- package/dist/cli/commands/bounty.js +28 -4
- package/dist/cli/commands/config.js +10 -1
- package/dist/cli/commands/contributor.js +16 -6
- package/dist/cli/commands/dao.js +1 -1
- package/dist/cli/commands/discover.js +3 -3
- package/dist/cli/commands/governance.js +141 -58
- package/dist/cli/commands/install.js +178 -36
- package/dist/cli/commands/ipfs.js +12 -2
- package/dist/cli/commands/library.js +277 -268
- package/dist/cli/commands/members.js +132 -18
- package/dist/cli/commands/multiplier.js +101 -13
- package/dist/cli/commands/nft.js +16 -3
- package/dist/cli/commands/personal.js +69 -2
- package/dist/cli/commands/prompt.js +1 -1
- package/dist/cli/commands/proposals.js +153 -3
- package/dist/cli/commands/stake-status.js +130 -56
- package/dist/cli/commands/sxxx.js +37 -4
- package/dist/cli/commands/wallet.js +5 -10
- package/dist/cli/contracts/index.js +2 -1
- package/dist/cli/index.js +5 -0
- package/dist/cli/privy-auth-wallet-manager.js +3 -2
- package/dist/cli/services/config/chain-defaults.js +1 -1
- package/dist/cli/services/config/manager.js +3 -0
- package/dist/cli/services/config/schema.js +1 -0
- package/dist/cli/services/ipfs/onboarding.js +11 -0
- package/dist/cli/utils/aliases.js +62 -3
- package/dist/cli/utils/cli-ui.js +1 -1
- package/dist/cli/utils/provider.js +7 -3
- package/dist/cli/wallet-manager.js +7 -12
- package/dist/prompts/e2e-test-prompt.md +22 -0
- package/dist/prompts/skills/build-web3/plugin.json +11 -0
- package/package.json +1 -1
|
@@ -16,10 +16,19 @@ function register(program) {
|
|
|
16
16
|
try {
|
|
17
17
|
const chalk = { green: (s)=>s, yellow: (s)=>s, red: (s)=>s, cyan: (s)=>s, gray: (s)=>s };
|
|
18
18
|
const { ethers } = require('ethers');
|
|
19
|
-
const sxxxTokenAddress = cliConfig.resolveAddress('SXXX_TOKEN_ADDRESS');
|
|
20
19
|
const system = cliConfig.resolveAddress('SIMPLE_CONTRIBUTOR_SYSTEM_ADDRESS');
|
|
21
20
|
if (!system) throw new Error('SimpleContributorSystem address not configured');
|
|
21
|
+
const rpcUrl = cliConfig.resolveRpcUrl({ allowEnv: true });
|
|
22
|
+
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
|
23
|
+
const sysAbi = resolveArtifact('contracts/SimpleContributorSystem.sol/SimpleContributorSystem.json').abi;
|
|
24
|
+
const sysRead = new ethers.Contract(system, sysAbi, provider);
|
|
25
|
+
const systemToken = await sysRead.sxxxToken().catch(() => null);
|
|
26
|
+
const cfgToken = cliConfig.resolveAddress('SXXX_TOKEN_ADDRESS');
|
|
27
|
+
const sxxxTokenAddress = systemToken || cfgToken;
|
|
22
28
|
if (!sxxxTokenAddress) throw new Error('SXXX token address not configured');
|
|
29
|
+
if (systemToken && cfgToken && systemToken.toLowerCase() !== cfgToken.toLowerCase()) {
|
|
30
|
+
ui.warn(`SXXX token mismatch: contributor system uses ${systemToken}, config has ${cfgToken}. Using system token.`);
|
|
31
|
+
}
|
|
23
32
|
const WM = require('../wallet-manager');
|
|
24
33
|
const wm = new WM();
|
|
25
34
|
await wm.connect();
|
|
@@ -36,13 +45,12 @@ function register(program) {
|
|
|
36
45
|
const ERC20 = [ 'function approve(address,uint256) returns (bool)' ];
|
|
37
46
|
const tok = new ethers.Contract(sxxxTokenAddress, ERC20, signer);
|
|
38
47
|
const txa = await tok.approve(system, amountWei);
|
|
39
|
-
{ const { waitForReceipt } = require('../utils/tx-wait');
|
|
48
|
+
{ const { waitForReceipt } = require('../utils/tx-wait'); await waitForReceipt(provider, txa, Number(process.env.SAGE_TX_WAIT_MS || 60000)); }
|
|
40
49
|
ui.success('SXXX approval confirmed');
|
|
41
50
|
ui.info('Staking tokens...');
|
|
42
|
-
const sysAbi = resolveArtifact('contracts/SimpleContributorSystem.sol/SimpleContributorSystem.json').abi;
|
|
43
51
|
const c = new ethers.Contract(system, sysAbi, signer);
|
|
44
52
|
const txs = await c.stake(amountWei);
|
|
45
|
-
{ const { waitForReceipt } = require('../utils/tx-wait');
|
|
53
|
+
{ const { waitForReceipt } = require('../utils/tx-wait'); await waitForReceipt(provider, txs, Number(process.env.SAGE_TX_WAIT_MS || 60000)); }
|
|
46
54
|
if (options.json) ui.json({ success: true, amount: amountWei.toString() });
|
|
47
55
|
else ui.success(`Successfully staked ${ethers.formatEther(amountWei)} SXXX`);
|
|
48
56
|
} catch (error) {
|
|
@@ -61,6 +69,8 @@ function register(program) {
|
|
|
61
69
|
const { ethers } = require('ethers');
|
|
62
70
|
const system = cliConfig.resolveAddress('SIMPLE_CONTRIBUTOR_SYSTEM_ADDRESS');
|
|
63
71
|
if (!system) throw new Error('SimpleContributorSystem address not configured');
|
|
72
|
+
const rpcUrl = cliConfig.resolveRpcUrl({ allowEnv: true });
|
|
73
|
+
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
|
64
74
|
const WM = require('../wallet-manager');
|
|
65
75
|
const wm = new WM();
|
|
66
76
|
await wm.connect();
|
|
@@ -72,7 +82,7 @@ function register(program) {
|
|
|
72
82
|
const sysAbi = resolveArtifact('contracts/SimpleContributorSystem.sol/SimpleContributorSystem.json').abi;
|
|
73
83
|
const c = new ethers.Contract(system, sysAbi, signer);
|
|
74
84
|
const tx = await c.withdrawStake(amountWei);
|
|
75
|
-
{ const { waitForReceipt } = require('../utils/tx-wait');
|
|
85
|
+
{ const { waitForReceipt } = require('../utils/tx-wait'); await waitForReceipt(provider, tx, Number(process.env.SAGE_TX_WAIT_MS || 60000)); }
|
|
76
86
|
if (options.json) ui.json({ success: true, amount: amountWei.toString() });
|
|
77
87
|
else ui.success(`Successfully unstaked ${ethers.formatEther(amountWei)} SXXX`);
|
|
78
88
|
} catch (error) {
|
|
@@ -91,7 +101,7 @@ function register(program) {
|
|
|
91
101
|
const { ethers } = require('ethers');
|
|
92
102
|
const system = cliConfig.resolveAddress('SIMPLE_CONTRIBUTOR_SYSTEM_ADDRESS');
|
|
93
103
|
if (!system) throw new Error('SimpleContributorSystem address not configured');
|
|
94
|
-
const rpcUrl =
|
|
104
|
+
const rpcUrl = cliConfig.resolveRpcUrl({ allowEnv: true });
|
|
95
105
|
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
|
96
106
|
const sysAbi = resolveArtifact('contracts/SimpleContributorSystem.sol/SimpleContributorSystem.json').abi;
|
|
97
107
|
const c = new ethers.Contract(system, sysAbi, provider);
|
package/dist/cli/commands/dao.js
CHANGED
|
@@ -847,7 +847,7 @@ function register(program) {
|
|
|
847
847
|
const favorites = subdaoStore.listFavorites();
|
|
848
848
|
const recent = subdaoStore.listRecent();
|
|
849
849
|
if (opts.json) {
|
|
850
|
-
ui.json({ favorites, recent },
|
|
850
|
+
ui.json({ favorites, recent }, { pretty: false });
|
|
851
851
|
return;
|
|
852
852
|
}
|
|
853
853
|
if (!favorites.length && !recent.length) return ui.info('No saved DAOs. Use `sage dao save <address> --alias <name>`.');
|
|
@@ -41,7 +41,7 @@ function register(program) {
|
|
|
41
41
|
if (options.tags) params.set('tags', options.tags);
|
|
42
42
|
if (options.vector === false) params.set('useVector', 'false');
|
|
43
43
|
|
|
44
|
-
const { body } = await client._fetchJson(`/
|
|
44
|
+
const { body } = await client._fetchJson(`/prompts/search?${params}`);
|
|
45
45
|
|
|
46
46
|
if (options.json) {
|
|
47
47
|
ui.json(body);
|
|
@@ -167,7 +167,7 @@ function register(program) {
|
|
|
167
167
|
method: options.method,
|
|
168
168
|
});
|
|
169
169
|
|
|
170
|
-
const { body } = await client._fetchJson(`/
|
|
170
|
+
const { body } = await client._fetchJson(`/prompts/similar/${cid}?${params}`);
|
|
171
171
|
|
|
172
172
|
if (options.json) {
|
|
173
173
|
ui.json(body);
|
|
@@ -246,7 +246,7 @@ function register(program) {
|
|
|
246
246
|
payload.diversify = true;
|
|
247
247
|
}
|
|
248
248
|
|
|
249
|
-
const { body } = await client._fetchJson('/
|
|
249
|
+
const { body } = await client._fetchJson('/prompts/match', {
|
|
250
250
|
method: 'POST',
|
|
251
251
|
headers: client.headers(),
|
|
252
252
|
body: JSON.stringify(payload),
|
|
@@ -19,6 +19,56 @@ const logger = require('../utils/logger');
|
|
|
19
19
|
const hooks = require('../utils/hooks');
|
|
20
20
|
const contracts = require('../contracts');
|
|
21
21
|
|
|
22
|
+
async function getLogsChunked(provider, filter, maxRange = 50000) {
|
|
23
|
+
// Default to 50k blocks to stay under most RPC limits (Base Sepolia limits to 100k)
|
|
24
|
+
const latest = filter.toBlock === 'latest' || typeof filter.toBlock === 'undefined'
|
|
25
|
+
? await provider.getBlockNumber()
|
|
26
|
+
: Number(filter.toBlock);
|
|
27
|
+
const from = Number(filter.fromBlock || 0);
|
|
28
|
+
const results = [];
|
|
29
|
+
for (let start = from; start <= latest; start += maxRange) {
|
|
30
|
+
const end = Math.min(latest, start + maxRange - 1);
|
|
31
|
+
try {
|
|
32
|
+
const batch = await provider.getLogs({ ...filter, fromBlock: start, toBlock: end });
|
|
33
|
+
if (batch && batch.length) results.push(...batch);
|
|
34
|
+
} catch (e) {
|
|
35
|
+
if (process.env.SAGE_VERBOSE === '1') {
|
|
36
|
+
console.warn(`Log query failed for blocks ${start}-${end}: ${e.message}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return results;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function findProposalCreatedLog(provider, govAddr, gov, pid, lookbacks) {
|
|
44
|
+
const eventSig = gov.interface.getEvent('ProposalCreated').topicHash;
|
|
45
|
+
const currentBlock = await provider.getBlockNumber();
|
|
46
|
+
const pidHex = ethers.hexlify(ethers.toBeHex(pid));
|
|
47
|
+
for (const span of lookbacks) {
|
|
48
|
+
const from = currentBlock > span ? currentBlock - span : 0;
|
|
49
|
+
let logs = await getLogsChunked(provider, {
|
|
50
|
+
topics: [eventSig, ethers.zeroPadValue(pidHex, 32)],
|
|
51
|
+
address: govAddr,
|
|
52
|
+
fromBlock: from,
|
|
53
|
+
toBlock: 'latest'
|
|
54
|
+
});
|
|
55
|
+
if (!logs.length) {
|
|
56
|
+
const all = await getLogsChunked(provider, { topics: [eventSig], address: govAddr, fromBlock: from, toBlock: 'latest' });
|
|
57
|
+
for (const l of all) {
|
|
58
|
+
try {
|
|
59
|
+
const p = gov.interface.parseLog(l);
|
|
60
|
+
if (p && p.name === 'ProposalCreated' && BigInt(p.args.proposalId.toString()) === pid) {
|
|
61
|
+
logs = [l];
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
} catch { }
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (logs.length) return logs[0];
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
22
72
|
function register(program) {
|
|
23
73
|
const governanceCommand = new Command('governance')
|
|
24
74
|
.description('Governance management commands')
|
|
@@ -69,7 +119,7 @@ function register(program) {
|
|
|
69
119
|
try {
|
|
70
120
|
const { resolveGovContext } = require('../utils/gov-context');
|
|
71
121
|
const { ethers } = require('ethers');
|
|
72
|
-
const provider = new ethers.JsonRpcProvider(
|
|
122
|
+
const provider = new ethers.JsonRpcProvider(cliConfig.resolveRpcUrl({ allowEnv: true }));
|
|
73
123
|
const ctx = await resolveGovContext({ govOpt: opts.gov, subdaoOpt: opts.subdao, provider, useGovernorOnly: process.env.SAGE_USE_GOVERNOR === '1' });
|
|
74
124
|
const govAddr = ctx.governor || process.env.GOV;
|
|
75
125
|
if (!govAddr) throw new Error('Governor address not resolved. Pass --gov or --subdao.');
|
|
@@ -79,22 +129,10 @@ function register(program) {
|
|
|
79
129
|
const pid = (typeof id === 'string' && id.startsWith('0x')) ? BigInt(id) : BigInt(String(id));
|
|
80
130
|
|
|
81
131
|
// Locate ProposalCreated event
|
|
82
|
-
const eventSig = gov.interface.getEvent('ProposalCreated').topicHash;
|
|
83
|
-
const currentBlock = await provider.getBlockNumber();
|
|
84
132
|
let parsed = null; let logTxHash = null;
|
|
85
133
|
const lookbacks = [20000, 100000, 500000];
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
const from = currentBlock > span ? currentBlock - span : 0;
|
|
89
|
-
let logs = await provider.getLogs({ topics: [eventSig, ethers.zeroPadValue(pidHex, 32)], address: govAddr, fromBlock: from, toBlock: 'latest' });
|
|
90
|
-
if (!logs.length) {
|
|
91
|
-
const all = await provider.getLogs({ topics: [eventSig], address: govAddr, fromBlock: from, toBlock: 'latest' });
|
|
92
|
-
for (const l of all) {
|
|
93
|
-
try { const p = gov.interface.parseLog(l); if (p && BigInt(p.args.proposalId.toString()) === pid) { logs = [l]; break; } } catch {}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
if (logs.length) { parsed = gov.interface.parseLog(logs[0]); logTxHash = logs[0].transactionHash; break; }
|
|
97
|
-
}
|
|
134
|
+
const hit = await findProposalCreatedLog(provider, govAddr, gov, pid, lookbacks);
|
|
135
|
+
if (hit) { parsed = gov.interface.parseLog(hit); logTxHash = hit.transactionHash; }
|
|
98
136
|
if (!parsed) throw new Error('ProposalCreated event not found; verify Governor/ID');
|
|
99
137
|
|
|
100
138
|
const targets = Array.from(parsed.args.targets, (a)=>String(a));
|
|
@@ -218,7 +256,7 @@ function register(program) {
|
|
|
218
256
|
try {
|
|
219
257
|
const { resolveGovContext } = require('../utils/gov-context');
|
|
220
258
|
const { ethers } = require('ethers');
|
|
221
|
-
const provider = new ethers.JsonRpcProvider(
|
|
259
|
+
const provider = new ethers.JsonRpcProvider(cliConfig.resolveRpcUrl({ allowEnv: true }));
|
|
222
260
|
const ctx = await resolveGovContext({ govOpt: opts.gov, subdaoOpt: opts.subdao, provider, useGovernorOnly: process.env.SAGE_USE_GOVERNOR === '1' });
|
|
223
261
|
const govAddr = ctx.governor || process.env.GOV;
|
|
224
262
|
if (!govAddr) throw new Error('Governor address not resolved. Pass --gov or --subdao.');
|
|
@@ -241,14 +279,10 @@ function register(program) {
|
|
|
241
279
|
} catch(_) {}
|
|
242
280
|
|
|
243
281
|
// --- Decode effects (library/prompt) with previous ⇒ new
|
|
244
|
-
const ProposalCreatedABI = ['event ProposalCreated(uint256 id,address proposer,address[] targets,uint256[] values,string[] signatures,bytes[] calldatas,uint256 startBlock,uint256 endBlock,string description)'];
|
|
245
|
-
const evIface = new ethers.Interface(ProposalCreatedABI);
|
|
246
|
-
const topic = evIface.getEvent('ProposalCreated').topicHash;
|
|
247
282
|
let parsed = null; let logTxHash = null;
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
}
|
|
283
|
+
const lookbacks = [20000, 100000, 500000];
|
|
284
|
+
const hit = await findProposalCreatedLog(provider, govAddr, gov, pid, lookbacks);
|
|
285
|
+
if (hit) { parsed = gov.interface.parseLog(hit); logTxHash = hit.transactionHash; }
|
|
252
286
|
if (!parsed) throw new Error('ProposalCreated event not found');
|
|
253
287
|
const targets = parsed.args.targets.map(String);
|
|
254
288
|
const calldatas = parsed.args.calldatas.map((b)=> ethers.hexlify(b));
|
|
@@ -498,7 +532,7 @@ function register(program) {
|
|
|
498
532
|
.option('--json', 'Output JSON', false)
|
|
499
533
|
.action(async (opts) => {
|
|
500
534
|
try {
|
|
501
|
-
const provider = new ethers.JsonRpcProvider(
|
|
535
|
+
const provider = new ethers.JsonRpcProvider(cliConfig.resolveRpcUrl({ allowEnv: true }));
|
|
502
536
|
const { resolveGovContext } = require('../utils/gov-context');
|
|
503
537
|
const ctx = await resolveGovContext({ govOpt: opts.gov, subdaoOpt: opts.subdao, provider });
|
|
504
538
|
if (opts.subdao) {
|
|
@@ -563,7 +597,7 @@ function register(program) {
|
|
|
563
597
|
try {
|
|
564
598
|
const { resolveGovContext } = require('../utils/gov-context');
|
|
565
599
|
const { ethers } = require('ethers');
|
|
566
|
-
const provider = new ethers.JsonRpcProvider(
|
|
600
|
+
const provider = new ethers.JsonRpcProvider(cliConfig.resolveRpcUrl({ allowEnv: true }));
|
|
567
601
|
const ctx = await resolveGovContext({ govOpt: options.gov, subdaoOpt: options.subdao, provider, useGovernorOnly: process.env.SAGE_USE_GOVERNOR === '1' });
|
|
568
602
|
const govAddr = ctx.governor || process.env.GOV;
|
|
569
603
|
if (!govAddr) throw new Error('Governor address not resolved. Pass --gov or --subdao.');
|
|
@@ -572,43 +606,91 @@ function register(program) {
|
|
|
572
606
|
const gov = new ethers.Contract(govAddr, GovernorABI, provider);
|
|
573
607
|
const pid = (typeof id === 'bigint') ? id : (typeof id === 'string' && id.startsWith('0x') ? BigInt(id) : BigInt(String(id)));
|
|
574
608
|
|
|
575
|
-
// --- Load
|
|
576
|
-
const
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
let
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
609
|
+
// --- Load proposal data: prefer subgraph (no block range limits), fallback to RPC ---
|
|
610
|
+
const subgraphClient = require('../utils/subgraph-client');
|
|
611
|
+
let targets = [];
|
|
612
|
+
let values = [];
|
|
613
|
+
let calldatas = [];
|
|
614
|
+
let description = '';
|
|
615
|
+
let logTxHash = null;
|
|
616
|
+
let usedSubgraph = false;
|
|
617
|
+
|
|
618
|
+
// Try subgraph first (preferred - no block range limits)
|
|
619
|
+
try {
|
|
620
|
+
const status = await subgraphClient.checkSubgraphStatus(provider);
|
|
621
|
+
if (status.available) {
|
|
622
|
+
const proposals = await subgraphClient.getProposals(govAddr, { first: 200 });
|
|
623
|
+
const pidDec = pid.toString();
|
|
624
|
+
const match = proposals.find((p) => {
|
|
625
|
+
const parts = String(p.id || '').split('-');
|
|
626
|
+
const last = parts.length > 1 ? parts[parts.length - 1] : String(p.id || '');
|
|
627
|
+
if (!last) return false;
|
|
628
|
+
if (last === pidDec) return true;
|
|
629
|
+
try { if (String(last).startsWith('0x') && BigInt(String(last)) === pid) return true; } catch (_) { }
|
|
630
|
+
return false;
|
|
631
|
+
});
|
|
632
|
+
if (match && match.targets?.length) {
|
|
633
|
+
targets = Array.from(match.targets || [], (a) => String(a));
|
|
634
|
+
values = Array.from(match.values || [], (v) => BigInt(String(v || 0)));
|
|
635
|
+
calldatas = Array.from(match.calldatas || [], (b) => String(b));
|
|
636
|
+
description = String(match.description || '');
|
|
637
|
+
usedSubgraph = true;
|
|
638
|
+
if (process.env.SAGE_VERBOSE === '1') {
|
|
639
|
+
ui.info(`Using subgraph (indexed to block ${status.indexedBlock})`);
|
|
640
|
+
}
|
|
592
641
|
}
|
|
593
642
|
}
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
break;
|
|
643
|
+
} catch (sgErr) {
|
|
644
|
+
if (process.env.SAGE_VERBOSE === '1') {
|
|
645
|
+
ui.warn(`Subgraph unavailable: ${sgErr.message}. Falling back to RPC.`);
|
|
598
646
|
}
|
|
599
647
|
}
|
|
600
|
-
if (!parsed) throw new Error('ProposalCreated event not found; try increasing lookback or verify Governor/ID');
|
|
601
648
|
|
|
602
|
-
//
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
649
|
+
// Fallback to RPC log scan (chunked to stay under block range limits)
|
|
650
|
+
if (!usedSubgraph) {
|
|
651
|
+
const eventSig = gov.interface.getEvent('ProposalCreated').topicHash;
|
|
652
|
+
const currentBlock = await provider.getBlockNumber();
|
|
653
|
+
const MAX_CHUNK = 50000;
|
|
654
|
+
const lookbacks = [20000, 100000, 500000];
|
|
655
|
+
let parsed = null;
|
|
656
|
+
const pidHex = (typeof id === 'string' && id.startsWith('0x')) ? id : ethers.hexlify(ethers.toBeHex(pid));
|
|
657
|
+
|
|
658
|
+
for (const span of lookbacks) {
|
|
659
|
+
if (parsed) break;
|
|
660
|
+
const from = currentBlock > span ? currentBlock - span : 0;
|
|
661
|
+
for (let start = from; start <= currentBlock && !parsed; start += MAX_CHUNK) {
|
|
662
|
+
const chunkEnd = Math.min(start + MAX_CHUNK - 1, currentBlock);
|
|
663
|
+
try {
|
|
664
|
+
let logs = await provider.getLogs({ topics: [eventSig, ethers.zeroPadValue(pidHex, 32)], address: govAddr, fromBlock: start, toBlock: chunkEnd });
|
|
665
|
+
if (!logs.length) {
|
|
666
|
+
const all = await provider.getLogs({ topics: [eventSig], address: govAddr, fromBlock: start, toBlock: chunkEnd });
|
|
667
|
+
for (const l of all) {
|
|
668
|
+
try {
|
|
669
|
+
const p = gov.interface.parseLog(l);
|
|
670
|
+
if (p && p.name === 'ProposalCreated' && BigInt(p.args.proposalId.toString()) === pid) { logs = [l]; break; }
|
|
671
|
+
} catch { }
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
if (logs.length) {
|
|
675
|
+
parsed = gov.interface.parseLog(logs[0]);
|
|
676
|
+
logTxHash = logs[0].transactionHash;
|
|
677
|
+
}
|
|
678
|
+
} catch (e) {
|
|
679
|
+
if (process.env.SAGE_VERBOSE === '1') {
|
|
680
|
+
console.warn(`Log query failed for blocks ${start}-${chunkEnd}: ${e.message}`);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
if (!parsed) throw new Error('ProposalCreated event not found; configure SUBGRAPH_URL or verify Governor/ID');
|
|
686
|
+
targets = Array.from(parsed.args.targets, (a) => String(a));
|
|
687
|
+
values = Array.from(parsed.args.values, (v) => BigInt(v.toString()));
|
|
688
|
+
calldatas = Array.from(parsed.args.calldatas, (b) => ethers.hexlify(b));
|
|
689
|
+
description = String(parsed.args.description ?? '');
|
|
690
|
+
}
|
|
608
691
|
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
: ethers.keccak256(ethers.toUtf8Bytes(description));
|
|
692
|
+
if (values.length === 0 && targets.length > 0) values = targets.map(() => 0n);
|
|
693
|
+
const descriptionHash = ethers.keccak256(ethers.toUtf8Bytes(description));
|
|
612
694
|
|
|
613
695
|
// --- State, ETA, Timelock ---
|
|
614
696
|
const stateNum = Number(await gov.state(pid).catch(() => 255));
|
|
@@ -1935,11 +2017,12 @@ function register(program) {
|
|
|
1935
2017
|
.action(async (options) => {
|
|
1936
2018
|
try {
|
|
1937
2019
|
if (options.json) process.env.SAGE_QUIET_JSON = '1';
|
|
1938
|
-
if (options.rpc) process.env.RPC_URL = options.rpc;
|
|
1939
2020
|
// Unified context resolution
|
|
1940
2021
|
const { resolveGovContext } = require('../utils/gov-context');
|
|
1941
2022
|
const { ethers } = require('ethers');
|
|
1942
|
-
const
|
|
2023
|
+
const rpcUrl = options.rpc || cliConfig.resolveRpcUrl({ allowEnv: true });
|
|
2024
|
+
process.env.SAGE_RPC_URL = rpcUrl;
|
|
2025
|
+
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
|
1943
2026
|
const ctx = await resolveGovContext({ govOpt: options.gov, subdaoOpt: options.subdao, provider });
|
|
1944
2027
|
if (ctx.governor) process.env.GOV = ctx.governor;
|
|
1945
2028
|
if (ctx.subdao) process.env.WORKING_SUBDAO_ADDRESS = ctx.subdao, process.env.SUBDAO = ctx.subdao;
|