@sage-protocol/cli 0.3.6 → 0.3.7
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.
|
@@ -41,7 +41,7 @@ function register(program) {
|
|
|
41
41
|
// ===== Manager-type preflight (DIRECT vs MERKLE) =====
|
|
42
42
|
const detectManagerType = async (address) => {
|
|
43
43
|
const directIface = new ethers.Interface([
|
|
44
|
-
'function getBoost(uint256) view returns (address,address,uint256,uint256,uint256,uint256,uint96,uint8,address,bool,bool)'
|
|
44
|
+
'function getBoost(uint256) view returns (tuple(address creator, address governor, uint256 proposalId, uint256 snapshot, uint256 perVoter, uint256 maxVoters, uint256 votersPaid, uint96 minVotes, uint8 payoutMode, uint8 support, uint256 startAt, uint256 expiresAt, uint256 totalPool, uint256 totalPaid, uint8 kind, address policy, bool active, bool paused) boost)'
|
|
45
45
|
]);
|
|
46
46
|
const merkleIface = new ethers.Interface([
|
|
47
47
|
'function getBoost(uint256) view returns (uint256,uint256,bytes32,bool,bool,address)'
|
|
@@ -78,20 +78,20 @@ function register(program) {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
const mapKind = (k) => {
|
|
81
|
-
const v = String(k||'').toLowerCase();
|
|
81
|
+
const v = String(k || '').toLowerCase();
|
|
82
82
|
if (v === 'direct') return 0; // EligibilityKind.DIRECT
|
|
83
83
|
if (v === 'merkle') return 1; // EligibilityKind.MERKLE
|
|
84
84
|
if (v === 'custom') return 2; // EligibilityKind.CUSTOM
|
|
85
85
|
throw new Error(`Unknown kind: ${k}`);
|
|
86
86
|
};
|
|
87
87
|
const mapPayout = (m) => {
|
|
88
|
-
const v = String(m||'').toLowerCase();
|
|
88
|
+
const v = String(m || '').toLowerCase();
|
|
89
89
|
if (v === 'fixed') return 0; // PayoutMode.FIXED
|
|
90
90
|
if (v === 'variable') return 1;// PayoutMode.VARIABLE
|
|
91
91
|
throw new Error(`Unknown payout-mode: ${m}`);
|
|
92
92
|
};
|
|
93
93
|
const mapSupport = (s) => {
|
|
94
|
-
const v = String(s||'').toLowerCase();
|
|
94
|
+
const v = String(s || '').toLowerCase();
|
|
95
95
|
if (v === 'any') return 0; // EligibilitySupport.Any
|
|
96
96
|
if (v === 'for') return 1; // EligibilitySupport.ForOnly
|
|
97
97
|
throw new Error(`Unknown support: ${s}`);
|
|
@@ -116,7 +116,7 @@ function register(program) {
|
|
|
116
116
|
|
|
117
117
|
// Preflight checks
|
|
118
118
|
const owner = await signer.getAddress();
|
|
119
|
-
const nowTs = BigInt(Math.floor(Date.now()/1000));
|
|
119
|
+
const nowTs = BigInt(Math.floor(Date.now() / 1000));
|
|
120
120
|
if (startAt && expiresAt && expiresAt <= startAt) {
|
|
121
121
|
console.log('⚠️ expires-at <= start-at; window may be invalid');
|
|
122
122
|
}
|
|
@@ -149,54 +149,56 @@ function register(program) {
|
|
|
149
149
|
'function nonces(address) view returns (uint256)',
|
|
150
150
|
'function permit(address,address,uint256,uint256,uint8,bytes32,bytes32)'
|
|
151
151
|
], signer);
|
|
152
|
-
const balance = await usdc.balanceOf(owner).catch(()=>0n);
|
|
152
|
+
const balance = await usdc.balanceOf(owner).catch(() => 0n);
|
|
153
153
|
if (balance < totalPool) console.log(`⚠️ USDC balance ${balance.toString()} < required ${totalPool.toString()}`);
|
|
154
|
-
let allowance = await usdc.allowance(owner, mgrAddr).catch(()=>0n);
|
|
154
|
+
let allowance = await usdc.allowance(owner, mgrAddr).catch(() => 0n);
|
|
155
155
|
if (allowance < totalPool) {
|
|
156
156
|
const wantPermit = !!opts.permit || process.env.SAGE_USE_PERMIT === '1' || process.env.SAGE_USE_PERMIT === 'true';
|
|
157
157
|
if (wantPermit) {
|
|
158
158
|
try {
|
|
159
159
|
const chain = await provider.getNetwork();
|
|
160
|
-
const name = await usdc.name().catch(()=> 'Token');
|
|
160
|
+
const name = await usdc.name().catch(() => 'Token');
|
|
161
161
|
const nonce = await usdc.nonces(owner);
|
|
162
|
-
const deadline = BigInt(Math.floor(Date.now()/1000) + 3600);
|
|
162
|
+
const deadline = BigInt(Math.floor(Date.now() / 1000) + 3600);
|
|
163
163
|
const domain = { name, version: '1', chainId: Number(chain.chainId), verifyingContract: usdcAddr };
|
|
164
|
-
const types = {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
164
|
+
const types = {
|
|
165
|
+
Permit: [
|
|
166
|
+
{ name: 'owner', type: 'address' },
|
|
167
|
+
{ name: 'spender', type: 'address' },
|
|
168
|
+
{ name: 'value', type: 'uint256' },
|
|
169
|
+
{ name: 'nonce', type: 'uint256' },
|
|
170
|
+
{ name: 'deadline', type: 'uint256' }
|
|
171
|
+
]
|
|
172
|
+
};
|
|
171
173
|
const value = { owner, spender: mgrAddr, value: totalPool, nonce, deadline };
|
|
172
174
|
const sig = await signer.signTypedData(domain, types, value);
|
|
173
175
|
const { v, r, s } = ethers.Signature.from(sig);
|
|
174
176
|
const txp = await usdc.permit(owner, mgrAddr, totalPool, deadline, v, r, s);
|
|
175
177
|
{ const { waitForReceipt } = require('../utils/tx-wait'); const { ethers } = require('ethers'); const pr = new ethers.JsonRpcProvider(process.env.RPC_URL || process.env.BASE_SEPOLIA_RPC || process.env.BASE_RPC_URL || 'https://base-sepolia.publicnode.com'); await waitForReceipt(pr, txp, Number(process.env.SAGE_TX_WAIT_MS || 60000)); }
|
|
176
178
|
console.log('✅ Permit submitted');
|
|
177
|
-
allowance = await usdc.allowance(owner, mgrAddr).catch(()=>0n);
|
|
179
|
+
allowance = await usdc.allowance(owner, mgrAddr).catch(() => 0n);
|
|
178
180
|
} catch (e) {
|
|
179
181
|
console.log('⚠️ Permit failed or unsupported, falling back to approve:', e.message);
|
|
180
182
|
}
|
|
181
183
|
}
|
|
182
184
|
if (allowance < totalPool) {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
185
|
+
console.log('💳 Approving USDC to DIRECT manager');
|
|
186
|
+
console.log(` token: ${usdcAddr}`);
|
|
187
|
+
console.log(` owner: ${owner}`);
|
|
188
|
+
console.log(` spender: ${mgrAddr}`);
|
|
189
|
+
console.log(` need: ${totalPool.toString()} current: ${allowance.toString()}`);
|
|
190
|
+
const txa = await usdc.approve(mgrAddr, totalPool); { const { waitForReceipt } = require('../utils/tx-wait'); const { ethers } = require('ethers'); const pr = new ethers.JsonRpcProvider(process.env.RPC_URL || process.env.BASE_SEPOLIA_RPC || process.env.BASE_RPC_URL || 'https://base-sepolia.publicnode.com'); await waitForReceipt(pr, txa, Number(process.env.SAGE_TX_WAIT_MS || 60000)); }
|
|
191
|
+
console.log('✅ Approve confirmed');
|
|
190
192
|
}
|
|
191
193
|
} else {
|
|
192
194
|
console.log(`✅ USDC allowance sufficient: ${allowance.toString()} (need ${totalPool.toString()})`);
|
|
193
195
|
}
|
|
194
196
|
|
|
195
197
|
const iface = new ethers.Interface([
|
|
196
|
-
'function createBoost(address,uint256,uint256,uint256,uint8,address,uint96,uint8,uint8,uint256,uint256)'
|
|
198
|
+
'function createBoost(tuple(address governor, uint256 proposalId, uint256 perVoter, uint256 maxVoters, uint8 kind, address policy, uint96 minVotes, uint8 payoutMode, uint8 support, uint256 startAt, uint256 expiresAt) params)'
|
|
197
199
|
]);
|
|
198
200
|
|
|
199
|
-
const
|
|
201
|
+
const params = [
|
|
200
202
|
opts.governor,
|
|
201
203
|
BigInt(opts.proposalId),
|
|
202
204
|
perVoter,
|
|
@@ -208,10 +210,11 @@ function register(program) {
|
|
|
208
210
|
support,
|
|
209
211
|
startAt,
|
|
210
212
|
expiresAt,
|
|
211
|
-
]
|
|
213
|
+
];
|
|
214
|
+
const data = iface.encodeFunctionData('createBoost', [params]);
|
|
212
215
|
const tx = await signer.sendTransaction({ to: mgrAddr, data });
|
|
213
216
|
console.log('tx:', tx.hash);
|
|
214
|
-
} catch (e) { console.error('❌ create failed:', e.message); process.exit(1);}
|
|
217
|
+
} catch (e) { console.error('❌ create failed:', e.message); process.exit(1); }
|
|
215
218
|
});
|
|
216
219
|
|
|
217
220
|
// New helper: set root on MerkleEligibilityPolicy adapter
|
|
@@ -231,7 +234,7 @@ function register(program) {
|
|
|
231
234
|
|
|
232
235
|
if (opts.policy && opts.policy !== ethers.ZeroAddress) {
|
|
233
236
|
const iface = new ethers.Interface(['function setRoot(uint256,bytes32)']);
|
|
234
|
-
const tx = await signer.sendTransaction({ to: opts.policy, data: iface.encodeFunctionData('setRoot',[id, opts.root]) });
|
|
237
|
+
const tx = await signer.sendTransaction({ to: opts.policy, data: iface.encodeFunctionData('setRoot', [id, opts.root]) });
|
|
235
238
|
console.log('tx:', tx.hash);
|
|
236
239
|
return;
|
|
237
240
|
}
|
|
@@ -242,9 +245,9 @@ function register(program) {
|
|
|
242
245
|
}
|
|
243
246
|
// Call manager.setMerkleRoot
|
|
244
247
|
const ifMgr = new ethers.Interface(['function setMerkleRoot(uint256,bytes32)']);
|
|
245
|
-
const tx = await signer.sendTransaction({ to: mgrAddr, data: ifMgr.encodeFunctionData('setMerkleRoot',[id, opts.root]) });
|
|
248
|
+
const tx = await signer.sendTransaction({ to: mgrAddr, data: ifMgr.encodeFunctionData('setMerkleRoot', [id, opts.root]) });
|
|
246
249
|
console.log('tx:', tx.hash);
|
|
247
|
-
} catch (e) { console.error('❌ set-root failed:', e.message); process.exit(1);}
|
|
250
|
+
} catch (e) { console.error('❌ set-root failed:', e.message); process.exit(1); }
|
|
248
251
|
});
|
|
249
252
|
|
|
250
253
|
// Deprecated: legacy setter on old manager; keep for guidance
|
|
@@ -263,7 +266,7 @@ function register(program) {
|
|
|
263
266
|
const id = BigInt(opts.proposalId);
|
|
264
267
|
if (opts.policy && opts.policy !== ethers.ZeroAddress) {
|
|
265
268
|
const iface = new ethers.Interface(['function setRoot(uint256,bytes32)']);
|
|
266
|
-
const tx = await signer.sendTransaction({ to: opts.policy, data: iface.encodeFunctionData('setRoot',[id, opts.root]) });
|
|
269
|
+
const tx = await signer.sendTransaction({ to: opts.policy, data: iface.encodeFunctionData('setRoot', [id, opts.root]) });
|
|
267
270
|
console.log('tx:', tx.hash);
|
|
268
271
|
console.log('ℹ️ Note: set-merkle-root is deprecated; use boost set-root going forward.');
|
|
269
272
|
return;
|
|
@@ -271,14 +274,14 @@ function register(program) {
|
|
|
271
274
|
const mgr = opts.manager || process.env.BOOST_MANAGER_ADDRESS;
|
|
272
275
|
if (mgr && mgr !== ethers.ZeroAddress) {
|
|
273
276
|
const ifMgr = new ethers.Interface(['function setMerkleRoot(uint256,bytes32)']);
|
|
274
|
-
const tx = await signer.sendTransaction({ to: mgr, data: ifMgr.encodeFunctionData('setMerkleRoot',[id, opts.root]) });
|
|
277
|
+
const tx = await signer.sendTransaction({ to: mgr, data: ifMgr.encodeFunctionData('setMerkleRoot', [id, opts.root]) });
|
|
275
278
|
console.log('tx:', tx.hash);
|
|
276
279
|
console.log('ℹ️ Note: set-merkle-root is deprecated; use boost set-root going forward.');
|
|
277
280
|
return;
|
|
278
281
|
}
|
|
279
282
|
console.error('❌ set-merkle-root is deprecated. Use: boost set-root --policy <address> --proposal-id <id> --root <hex32>');
|
|
280
283
|
process.exit(1);
|
|
281
|
-
} catch (e) { console.error('❌ set-merkle-root failed:', e.message); process.exit(1);}
|
|
284
|
+
} catch (e) { console.error('❌ set-merkle-root failed:', e.message); process.exit(1); }
|
|
282
285
|
});
|
|
283
286
|
|
|
284
287
|
boost
|
|
@@ -294,27 +297,30 @@ function register(program) {
|
|
|
294
297
|
const mgrAddr = opts.manager || process.env.BOOST_MANAGER_ADDRESS;
|
|
295
298
|
const id = BigInt(opts.proposalId);
|
|
296
299
|
const ifMerkle = new ethers.Interface(['function getBoost(uint256) view returns (uint256,uint256,bytes32,bool,bool,address)']);
|
|
297
|
-
const ifDirect = new ethers.Interface(['function getBoost(uint256) view returns (address,address,uint256,uint256,uint256,uint256,uint96,uint8,address,bool,bool)']);
|
|
300
|
+
const ifDirect = new ethers.Interface(['function getBoost(uint256) view returns (tuple(address creator, address governor, uint256 proposalId, uint256 snapshot, uint256 perVoter, uint256 maxVoters, uint256 votersPaid, uint96 minVotes, uint8 payoutMode, uint8 support, uint256 startAt, uint256 expiresAt, uint256 totalPool, uint256 totalPaid, uint8 kind, address policy, bool active, bool paused) boost)']);
|
|
298
301
|
// Try DIRECT first
|
|
299
302
|
try {
|
|
300
|
-
const retD = await provider.call({ to: mgrAddr, data: ifDirect.encodeFunctionData('getBoost',[id]) });
|
|
301
|
-
const [
|
|
303
|
+
const retD = await provider.call({ to: mgrAddr, data: ifDirect.encodeFunctionData('getBoost', [id]) });
|
|
304
|
+
const [b] = ifDirect.decodeFunctionResult('getBoost', retD);
|
|
302
305
|
console.log(JSON.stringify({
|
|
303
306
|
managerType: 'direct', proposalId: opts.proposalId,
|
|
304
|
-
creator, governor, snapshot: snapshot.toString(), perVoter: perVoter.toString(),
|
|
305
|
-
maxVoters: maxVoters.toString(), votersPaid: votersPaid.toString(), minVotes: Number(minVotes),
|
|
306
|
-
kind: Number(kind), policy, active, paused
|
|
307
|
+
creator: b.creator, governor: b.governor, snapshot: b.snapshot.toString(), perVoter: b.perVoter.toString(),
|
|
308
|
+
maxVoters: b.maxVoters.toString(), votersPaid: b.votersPaid.toString(), minVotes: Number(b.minVotes),
|
|
309
|
+
kind: Number(b.kind), policy: b.policy, active: b.active, paused: b.paused,
|
|
310
|
+
payoutMode: Number(b.payoutMode), support: Number(b.support),
|
|
311
|
+
startAt: b.startAt.toString(), expiresAt: b.expiresAt.toString(),
|
|
312
|
+
totalPool: b.totalPool.toString(), totalPaid: b.totalPaid.toString()
|
|
307
313
|
}, null, 2));
|
|
308
314
|
} catch (_) {
|
|
309
315
|
// Fallback to MERKLE
|
|
310
|
-
const retM = await provider.call({ to: mgrAddr, data: ifMerkle.encodeFunctionData('getBoost',[id]) });
|
|
311
|
-
const [totalPool,totalClaimed,merkleRoot,active,finalized,creator] = ifMerkle.decodeFunctionResult('getBoost', retM);
|
|
316
|
+
const retM = await provider.call({ to: mgrAddr, data: ifMerkle.encodeFunctionData('getBoost', [id]) });
|
|
317
|
+
const [totalPool, totalClaimed, merkleRoot, active, finalized, creator] = ifMerkle.decodeFunctionResult('getBoost', retM);
|
|
312
318
|
console.log(JSON.stringify({
|
|
313
319
|
managerType: 'merkle', proposalId: opts.proposalId,
|
|
314
320
|
totalPool: totalPool.toString(), totalClaimed: totalClaimed.toString(), merkleRoot, active, finalized, creator
|
|
315
321
|
}, null, 2));
|
|
316
322
|
}
|
|
317
|
-
} catch (e) { console.error('❌ status failed:', e.message); process.exit(1);}
|
|
323
|
+
} catch (e) { console.error('❌ status failed:', e.message); process.exit(1); }
|
|
318
324
|
});
|
|
319
325
|
|
|
320
326
|
boost
|
|
@@ -333,18 +339,18 @@ function register(program) {
|
|
|
333
339
|
// Manager-type preflight + helpful hint if mismatched
|
|
334
340
|
const detectManagerType = async (address) => {
|
|
335
341
|
const directIface = new ethers.Interface([
|
|
336
|
-
'function getBoost(uint256) view returns (address,address,uint256,uint256,uint256,uint256,uint96,uint8,address,bool,bool)'
|
|
342
|
+
'function getBoost(uint256) view returns (tuple(address creator, address governor, uint256 proposalId, uint256 snapshot, uint256 perVoter, uint256 maxVoters, uint256 votersPaid, uint96 minVotes, uint8 payoutMode, uint8 support, uint256 startAt, uint256 expiresAt, uint256 totalPool, uint256 totalPaid, uint8 kind, address policy, bool active, bool paused) boost)'
|
|
337
343
|
]);
|
|
338
344
|
const merkleIface = new ethers.Interface([
|
|
339
345
|
'function getBoost(uint256) view returns (uint256,uint256,bytes32,bool,bool,address)'
|
|
340
346
|
]);
|
|
341
347
|
try {
|
|
342
|
-
const retD = await provider.call({ to: address, data: directIface.encodeFunctionData('getBoost',[0n]) });
|
|
348
|
+
const retD = await provider.call({ to: address, data: directIface.encodeFunctionData('getBoost', [0n]) });
|
|
343
349
|
directIface.decodeFunctionResult('getBoost', retD);
|
|
344
350
|
return 'direct';
|
|
345
351
|
} catch (_) {
|
|
346
352
|
try {
|
|
347
|
-
const retM = await provider.call({ to: address, data: merkleIface.encodeFunctionData('getBoost',[0n]) });
|
|
353
|
+
const retM = await provider.call({ to: address, data: merkleIface.encodeFunctionData('getBoost', [0n]) });
|
|
348
354
|
merkleIface.decodeFunctionResult('getBoost', retM);
|
|
349
355
|
return 'merkle';
|
|
350
356
|
} catch (_) { return 'unknown'; }
|
|
@@ -356,16 +362,16 @@ function register(program) {
|
|
|
356
362
|
let looksEmpty = false;
|
|
357
363
|
if (mgrType === 'direct') {
|
|
358
364
|
try {
|
|
359
|
-
const ifD = new ethers.Interface(['function getBoost(uint256) view returns (address,address,uint256,uint256,uint256,uint256,uint96,uint8,address,bool,bool)']);
|
|
360
|
-
const ret = await provider.call({ to: mgrAddr, data: ifD.encodeFunctionData('getBoost',[id]) });
|
|
361
|
-
const [
|
|
362
|
-
looksEmpty = (creator === ethers.ZeroAddress) || !active;
|
|
365
|
+
const ifD = new ethers.Interface(['function getBoost(uint256) view returns (tuple(address creator, address governor, uint256 proposalId, uint256 snapshot, uint256 perVoter, uint256 maxVoters, uint256 votersPaid, uint96 minVotes, uint8 payoutMode, uint8 support, uint256 startAt, uint256 expiresAt, uint256 totalPool, uint256 totalPaid, uint8 kind, address policy, bool active, bool paused) boost)']);
|
|
366
|
+
const ret = await provider.call({ to: mgrAddr, data: ifD.encodeFunctionData('getBoost', [id]) });
|
|
367
|
+
const [b] = ifD.decodeFunctionResult('getBoost', ret);
|
|
368
|
+
looksEmpty = (b.creator === ethers.ZeroAddress) || !b.active;
|
|
363
369
|
} catch (_) { /* ignore */ }
|
|
364
370
|
} else if (mgrType === 'merkle') {
|
|
365
371
|
try {
|
|
366
372
|
const ifM = new ethers.Interface(['function getBoost(uint256) view returns (uint256,uint256,bytes32,bool,bool,address)']);
|
|
367
|
-
const ret = await provider.call({ to: mgrAddr, data: ifM.encodeFunctionData('getBoost',[id]) });
|
|
368
|
-
const [totalPool
|
|
373
|
+
const ret = await provider.call({ to: mgrAddr, data: ifM.encodeFunctionData('getBoost', [id]) });
|
|
374
|
+
const [totalPool, , , active] = ifM.decodeFunctionResult('getBoost', ret);
|
|
369
375
|
looksEmpty = (totalPool === 0n) || !active;
|
|
370
376
|
} catch (_) { /* ignore */ }
|
|
371
377
|
}
|
|
@@ -377,9 +383,9 @@ function register(program) {
|
|
|
377
383
|
}
|
|
378
384
|
|
|
379
385
|
const iface = new ethers.Interface(['function finalize(uint256)']);
|
|
380
|
-
const tx = await signer.sendTransaction({ to: mgrAddr, data: iface.encodeFunctionData('finalize',[id]) });
|
|
386
|
+
const tx = await signer.sendTransaction({ to: mgrAddr, data: iface.encodeFunctionData('finalize', [id]) });
|
|
381
387
|
console.log('tx:', tx.hash);
|
|
382
|
-
} catch (e) { console.error('❌ finalize failed:', e.message); process.exit(1);}
|
|
388
|
+
} catch (e) { console.error('❌ finalize failed:', e.message); process.exit(1); }
|
|
383
389
|
});
|
|
384
390
|
|
|
385
391
|
boost
|
|
@@ -402,9 +408,9 @@ function register(program) {
|
|
|
402
408
|
}
|
|
403
409
|
const proof = JSON.parse(proofStr);
|
|
404
410
|
const iface = new ethers.Interface(['function claim(uint256,address,uint256,bytes32[])']);
|
|
405
|
-
const tx = await signer.sendTransaction({ to: mgrAddr, data: iface.encodeFunctionData('claim',[BigInt(opts.proposalId), await signer.getAddress(), BigInt(opts.amount), proof]) });
|
|
411
|
+
const tx = await signer.sendTransaction({ to: mgrAddr, data: iface.encodeFunctionData('claim', [BigInt(opts.proposalId), await signer.getAddress(), BigInt(opts.amount), proof]) });
|
|
406
412
|
console.log('tx:', tx.hash);
|
|
407
|
-
} catch (e) { console.error('❌ claim failed:', e.message); process.exit(1);}
|
|
413
|
+
} catch (e) { console.error('❌ claim failed:', e.message); process.exit(1); }
|
|
408
414
|
});
|
|
409
415
|
|
|
410
416
|
// New: fund a Merkle boost pool and create boost entry
|
|
@@ -429,7 +435,7 @@ function register(program) {
|
|
|
429
435
|
// Sanity: detect manager is Merkle flavor
|
|
430
436
|
try {
|
|
431
437
|
const ifMerkle = new ethers.Interface(['function getBoost(uint256) view returns (uint256,uint256,bytes32,bool,bool,address)']);
|
|
432
|
-
await provider.call({ to: mgrAddr, data: ifMerkle.encodeFunctionData('getBoost',[0n]) });
|
|
438
|
+
await provider.call({ to: mgrAddr, data: ifMerkle.encodeFunctionData('getBoost', [0n]) });
|
|
433
439
|
} catch (_) {
|
|
434
440
|
console.warn('⚠️ Manager at', mgrAddr, 'does not look like Merkle manager; proceeding anyway');
|
|
435
441
|
}
|
|
@@ -451,17 +457,19 @@ function register(program) {
|
|
|
451
457
|
if (wantPermit) {
|
|
452
458
|
try {
|
|
453
459
|
const chain = await provider.getNetwork();
|
|
454
|
-
const name = await usdc.name().catch(()=> 'Token');
|
|
460
|
+
const name = await usdc.name().catch(() => 'Token');
|
|
455
461
|
const nonce = await usdc.nonces(signerAddr);
|
|
456
|
-
const deadline = BigInt(Math.floor(Date.now()/1000) + 3600);
|
|
462
|
+
const deadline = BigInt(Math.floor(Date.now() / 1000) + 3600);
|
|
457
463
|
const domain = { name, version: '1', chainId: Number(chain.chainId), verifyingContract: usdcAddr };
|
|
458
|
-
const types = {
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
464
|
+
const types = {
|
|
465
|
+
Permit: [
|
|
466
|
+
{ name: 'owner', type: 'address' },
|
|
467
|
+
{ name: 'spender', type: 'address' },
|
|
468
|
+
{ name: 'value', type: 'uint256' },
|
|
469
|
+
{ name: 'nonce', type: 'uint256' },
|
|
470
|
+
{ name: 'deadline', type: 'uint256' }
|
|
471
|
+
]
|
|
472
|
+
};
|
|
465
473
|
const value = { owner: signerAddr, spender: mgrAddr, value: amount, nonce, deadline };
|
|
466
474
|
const sig = await signer.signTypedData(domain, types, value);
|
|
467
475
|
const { v, r, s } = ethers.Signature.from(sig);
|
|
@@ -480,9 +488,9 @@ function register(program) {
|
|
|
480
488
|
}
|
|
481
489
|
}
|
|
482
490
|
const ifMgr = new ethers.Interface(['function createBoost(uint256,uint256)']);
|
|
483
|
-
const tx = await signer.sendTransaction({ to: mgrAddr, data: ifMgr.encodeFunctionData('createBoost',[BigInt(opts.proposalId), amount]) });
|
|
491
|
+
const tx = await signer.sendTransaction({ to: mgrAddr, data: ifMgr.encodeFunctionData('createBoost', [BigInt(opts.proposalId), amount]) });
|
|
484
492
|
console.log('tx:', tx.hash);
|
|
485
|
-
} catch (e) { console.error('❌ fund failed:', e.message); process.exit(1);}
|
|
493
|
+
} catch (e) { console.error('❌ fund failed:', e.message); process.exit(1); }
|
|
486
494
|
});
|
|
487
495
|
|
|
488
496
|
program.addCommand(boost);
|
|
@@ -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
|
|
|
@@ -552,6 +552,8 @@ function register(program) {
|
|
|
552
552
|
.option('--title <t>', 'Proposal title', 'Batch Prompt Update')
|
|
553
553
|
.option('--desc <d>', 'Proposal description', '')
|
|
554
554
|
.option('--library-id <id>', 'Logical library id', 'main')
|
|
555
|
+
.option('--dao <address>', 'Target DAO address')
|
|
556
|
+
.option('--subdao <address>', '[deprecated] Alias for --dao')
|
|
555
557
|
.option('--strict', 'Strict validation (recompute CIDs, require remote availability)', false)
|
|
556
558
|
.option('--preview', 'Show payload and pointer (previous→new) before submit', true)
|
|
557
559
|
.option('--pin', 'Pin manifest CID before proposing (Pinata)', false)
|
|
@@ -637,7 +639,10 @@ function register(program) {
|
|
|
637
639
|
prompts.length,
|
|
638
640
|
opts.title,
|
|
639
641
|
opts.desc || `Manifest CID: ${manifestCID}`,
|
|
640
|
-
{
|
|
642
|
+
{
|
|
643
|
+
libraryId: opts.libraryId || 'main',
|
|
644
|
+
ctx: { subdaoOpt: opts.dao || opts.subdao }
|
|
645
|
+
}
|
|
641
646
|
);
|
|
642
647
|
console.log(`🆔 Proposal ID: ${pid}`);
|
|
643
648
|
// Update snapshot now that the proposal has been created
|
|
@@ -508,9 +508,10 @@ function register(program) {
|
|
|
508
508
|
console.log('Tx:', gtx.hash); await gtx.wait();
|
|
509
509
|
} else {
|
|
510
510
|
console.log(colors.red('❌ Missing PROPOSER_ROLE on Timelock for current signer.'));
|
|
511
|
-
console.log(' Hint:
|
|
512
|
-
console.log(
|
|
513
|
-
console.log('
|
|
511
|
+
console.log(' Hint: use one of:');
|
|
512
|
+
console.log(' - sage timelock fix-roles --subdao <subdao-address> (admin only, interactive)');
|
|
513
|
+
console.log(' - sage roles plan --subdao <subdao-address> && sage roles apply --subdao <subdao-address>');
|
|
514
|
+
console.log(' Or rerun with --auto if your signer is an admin (schedule will grant proposer automatically).');
|
|
514
515
|
return process.exit(1);
|
|
515
516
|
}
|
|
516
517
|
}
|
|
@@ -525,13 +526,11 @@ function register(program) {
|
|
|
525
526
|
const msg = String(e?.message || e);
|
|
526
527
|
// Common revert: AccessControlUnauthorizedAccount on schedule
|
|
527
528
|
if (msg.includes('AccessControl') || msg.includes('0xe2517d3f')) {
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
console.log(' Or rerun with --auto if your signer is an admin (schedule will grant proposer automatically).');
|
|
534
|
-
} catch (_) {}
|
|
529
|
+
console.log(colors.red('❌ Schedule failed: missing PROPOSER_ROLE on Timelock for current signer.'));
|
|
530
|
+
console.log(' Grant role via governance or admin helpers, for example:');
|
|
531
|
+
console.log(' - sage timelock fix-roles --subdao <subdao-address>');
|
|
532
|
+
console.log(' - sage roles plan --subdao <subdao-address> && sage roles apply --subdao <subdao-address>');
|
|
533
|
+
console.log(' Or rerun with --auto if your signer is an admin (schedule will grant proposer automatically).');
|
|
535
534
|
}
|
|
536
535
|
console.error(colors.red('❌ Schedule failed:'), msg);
|
|
537
536
|
process.exit(1);
|
|
@@ -116,12 +116,6 @@ const ERROR_GUIDANCE = {
|
|
|
116
116
|
(ctx) => (ctx?.subdao ? `sage dao doctor --subdao ${ctx.subdao}` : 'sage dao doctor --subdao <subdao-address>'),
|
|
117
117
|
() => 'sage roles matrix --subdao <subdao-address>'
|
|
118
118
|
],
|
|
119
|
-
'timelock:grant-role': [
|
|
120
|
-
(ctx) =>
|
|
121
|
-
ctx?.timelock
|
|
122
|
-
? `sage timelock doctor --timelock ${ctx.timelock}`
|
|
123
|
-
: 'sage timelock doctor --subdao <subdao-address>'
|
|
124
|
-
],
|
|
125
119
|
'timelock:execute': [
|
|
126
120
|
(ctx) =>
|
|
127
121
|
ctx?.timelock
|
|
@@ -69,10 +69,10 @@ echo "GRAPH_STUDIO_PROJECT_ID: ${GRAPH_STUDIO_PROJECT_ID:-unset}"
|
|
|
69
69
|
|
|
70
70
|
export BOOST_MANAGER_ADDRESS=0xCaF1439bAbAb97C081Eac8aAb7749fE25B0515e7
|
|
71
71
|
|
|
72
|
-
export BOOST_MANAGER_DIRECT_ADDRESS=
|
|
72
|
+
export BOOST_MANAGER_DIRECT_ADDRESS=0x7C9Ba615322FB9FdBC383040A77E6d0F80c7e349
|
|
73
73
|
|
|
74
|
-
export BOOST_VIEW_GLUE_ADDRESS=
|
|
74
|
+
export BOOST_VIEW_GLUE_ADDRESS=0x56747BfD90F3017Ef69A35DEe3e9560aF9778dD0
|
|
75
75
|
|
|
76
|
-
export BOOST_LOGGER_GLUE_ADDRESS=
|
|
76
|
+
export BOOST_LOGGER_GLUE_ADDRESS=0x2371093e9fA09c5e2Da4556b3487Db3f7863D438
|
|
77
77
|
|
|
78
|
-
export BOOST_CREATION_GLUE_ADDRESS=
|
|
78
|
+
export BOOST_CREATION_GLUE_ADDRESS=0x401e8BFbe2eEfC4b178Cc2Ff5FeeA84FaD7f7A96
|