@continuumdao/ctm-mpc-defi 0.2.37 → 0.2.39
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/dist/agent/catalog.cjs +49 -38
- package/dist/agent/catalog.cjs.map +1 -1
- package/dist/agent/catalog.d.ts +722 -2
- package/dist/agent/catalog.js +49 -38
- package/dist/agent/catalog.js.map +1 -1
- package/dist/agent/skills/continuum-dao/SKILL.md +18 -2
- package/dist/{buildBatch-CngvebiD.d.ts → buildBatch-tsEUVn_a.d.ts} +3 -3
- package/dist/chains/evm/index.cjs +33 -22
- package/dist/chains/evm/index.cjs.map +1 -1
- package/dist/chains/evm/index.d.ts +10 -6
- package/dist/chains/evm/index.js +33 -22
- package/dist/chains/evm/index.js.map +1 -1
- package/dist/chains/near/index.d.ts +1 -1
- package/dist/chains/solana/index.d.ts +1 -1
- package/dist/core/index.d.ts +5 -5
- package/dist/{eip712Multisign-DCW7heqX.d.ts → eip712Multisign-6oNIlObj.d.ts} +1 -1
- package/dist/{envelope-C_bfuKab.d.ts → envelope-Cm-llShH.d.ts} +1 -1
- package/dist/index.cjs +33 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.js +33 -22
- package/dist/index.js.map +1 -1
- package/dist/protocols/evm/aave-v4/index.d.ts +1 -1
- package/dist/protocols/evm/aerodrome/index.d.ts +4 -4
- package/dist/protocols/evm/arcus/index.d.ts +2 -2
- package/dist/protocols/evm/circle-cctp/index.d.ts +2 -2
- package/dist/protocols/evm/compound-v3/index.d.ts +1 -1
- package/dist/protocols/evm/continuum-dao/index.cjs +810 -51
- package/dist/protocols/evm/continuum-dao/index.cjs.map +1 -1
- package/dist/protocols/evm/continuum-dao/index.d.ts +191 -5
- package/dist/protocols/evm/continuum-dao/index.js +770 -53
- package/dist/protocols/evm/continuum-dao/index.js.map +1 -1
- package/dist/protocols/evm/curve-dao/index.d.ts +2 -2
- package/dist/protocols/evm/ethena/index.d.ts +1 -1
- package/dist/protocols/evm/euler-v2/index.cjs +33 -22
- package/dist/protocols/evm/euler-v2/index.cjs.map +1 -1
- package/dist/protocols/evm/euler-v2/index.d.ts +1 -1
- package/dist/protocols/evm/euler-v2/index.js +33 -22
- package/dist/protocols/evm/euler-v2/index.js.map +1 -1
- package/dist/protocols/evm/gmx/index.d.ts +2 -2
- package/dist/protocols/evm/hyperliquid/index.d.ts +2 -2
- package/dist/protocols/evm/lido/index.d.ts +1 -1
- package/dist/protocols/evm/maple/index.d.ts +1 -1
- package/dist/protocols/evm/morpho/index.d.ts +1 -1
- package/dist/protocols/evm/pendle/index.d.ts +4 -4
- package/dist/protocols/evm/permit2/index.d.ts +1 -1
- package/dist/protocols/evm/sky/index.d.ts +1 -1
- package/dist/protocols/evm/uniswap-v4/index.d.ts +3 -3
- package/dist/protocols/evm/venice/index.d.ts +1 -1
- package/dist/{registry-DxDSPQ-p.d.ts → registry-BvsXh0-r.d.ts} +1 -1
- package/dist/{types-RZWOTm8c.d.ts → types-RJpoU4Pc.d.ts} +1 -1
- package/dist/{types-PZrvtjv8.d.ts → types-lIfv59N1.d.ts} +2 -0
- package/package.json +1 -1
|
@@ -50,6 +50,11 @@ var VECTM_TOKEN_SYMBOL = "veCTM";
|
|
|
50
50
|
var CTM_TOKEN_DECIMALS = 18;
|
|
51
51
|
var WEEK_SECONDS = 7 * 86400;
|
|
52
52
|
var MAXTIME_SECONDS = 4 * 365 * 86400;
|
|
53
|
+
var VECTM_MAX_LOCK_WEEKS = 208;
|
|
54
|
+
var VECTM_DEFAULT_MINIMUM_LOCK_WEI = 10n ** 18n;
|
|
55
|
+
var VECTM_LIQ_MIN_LOCKED_WEI = 100n;
|
|
56
|
+
var VECTM_LIQ_PENALTY_NUM = 50000n;
|
|
57
|
+
var VECTM_LIQ_PENALTY_DEN = 100000n;
|
|
53
58
|
var UNSET = {
|
|
54
59
|
ctm: viem.zeroAddress,
|
|
55
60
|
votingEscrow: viem.zeroAddress,
|
|
@@ -57,15 +62,18 @@ var UNSET = {
|
|
|
57
62
|
rewards: viem.zeroAddress,
|
|
58
63
|
governor: viem.zeroAddress
|
|
59
64
|
};
|
|
65
|
+
var CTM_TOKEN_ADDRESS = viem.getAddress("0x2026027054F5beBCEB650aBD62dC623e327658e7");
|
|
66
|
+
var VOTING_ESCROW_LINEA_ADDRESS = viem.getAddress("0x221EC90B3B083A8501A37bdeb7035CeaedF3C31f");
|
|
60
67
|
var CONTINUUM_DAO_DEPLOYMENTS = {
|
|
61
|
-
[CONTINUUM_DAO_LINEA_CHAIN_ID]: {
|
|
68
|
+
[CONTINUUM_DAO_LINEA_CHAIN_ID]: {
|
|
69
|
+
...UNSET,
|
|
70
|
+
ctm: CTM_TOKEN_ADDRESS,
|
|
71
|
+
votingEscrow: VOTING_ESCROW_LINEA_ADDRESS
|
|
72
|
+
},
|
|
62
73
|
[CONTINUUM_DAO_LINEA_SEPOLIA_CHAIN_ID]: { ...UNSET },
|
|
63
74
|
[CONTINUUM_DAO_ETHEREUM_CHAIN_ID]: {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
nodeProperties: viem.zeroAddress,
|
|
67
|
-
rewards: viem.zeroAddress,
|
|
68
|
-
governor: viem.zeroAddress
|
|
75
|
+
...UNSET,
|
|
76
|
+
ctm: CTM_TOKEN_ADDRESS
|
|
69
77
|
}
|
|
70
78
|
};
|
|
71
79
|
var CONTINUUM_DAO_GOVERNANCE_API = "https://app-api.continuumdao.org";
|
|
@@ -156,6 +164,544 @@ function listContinuumDaoDefaultAssets() {
|
|
|
156
164
|
}
|
|
157
165
|
return out;
|
|
158
166
|
}
|
|
167
|
+
function maxLockDurationSeconds() {
|
|
168
|
+
return Math.min(VECTM_MAX_LOCK_WEEKS * WEEK_SECONDS, MAXTIME_SECONDS);
|
|
169
|
+
}
|
|
170
|
+
function floorUnlockTimeSeconds(nowSec, durationSec) {
|
|
171
|
+
return (BigInt(nowSec) + durationSec) / BigInt(WEEK_SECONDS) * BigInt(WEEK_SECONDS);
|
|
172
|
+
}
|
|
173
|
+
function assertCtmAmountAtLeastMinimum(amountWei, minimumLockWei, label) {
|
|
174
|
+
const min = minimumLockWei > 0n ? minimumLockWei : VECTM_DEFAULT_MINIMUM_LOCK_WEI;
|
|
175
|
+
if (amountWei < min) {
|
|
176
|
+
throw new Error(`${label} must be at least ${formatCtmWei(min)} CTM (VotingEscrow.minimumLock).`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
function assertCreateLockAmount(amountWei, minimumLockWei, balanceWei) {
|
|
180
|
+
if (amountWei <= 0n) throw new Error("CTM amount must be greater than zero.");
|
|
181
|
+
assertCtmAmountAtLeastMinimum(amountWei, minimumLockWei, "Create lock");
|
|
182
|
+
if (balanceWei != null && amountWei > balanceWei) {
|
|
183
|
+
throw new Error(`Create lock ${formatCtmWei(amountWei)} CTM exceeds KeyGen balance ${formatCtmWei(balanceWei)} CTM.`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function assertLockDurationSeconds(durationSec, nowSec, label) {
|
|
187
|
+
if (durationSec <= 0n) throw new Error(`${label} duration must be greater than zero.`);
|
|
188
|
+
if (durationSec < BigInt(WEEK_SECONDS)) {
|
|
189
|
+
throw new Error(`${label} duration must be at least 1 week (${WEEK_SECONDS} seconds).`);
|
|
190
|
+
}
|
|
191
|
+
if (durationSec > BigInt(maxLockDurationSeconds())) {
|
|
192
|
+
throw new Error(
|
|
193
|
+
`${label} duration cannot exceed ${VECTM_MAX_LOCK_WEEKS} weeks (VotingEscrow MAXTIME, week-aligned).`
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
const unlock = floorUnlockTimeSeconds(nowSec, durationSec);
|
|
197
|
+
if (unlock <= BigInt(nowSec)) {
|
|
198
|
+
throw new Error(`${label} unlock time must be after the current week boundary.`);
|
|
199
|
+
}
|
|
200
|
+
if (unlock > BigInt(nowSec) + BigInt(MAXTIME_SECONDS)) {
|
|
201
|
+
throw new Error(`${label} unlock time exceeds VotingEscrow MAXTIME (4 years).`);
|
|
202
|
+
}
|
|
203
|
+
return unlock;
|
|
204
|
+
}
|
|
205
|
+
function assertIncreaseAmount(amountWei, lock, nowSec, balanceWei) {
|
|
206
|
+
if (amountWei <= 0n) throw new Error("CTM amount must be greater than zero.");
|
|
207
|
+
if (lock.amountWei === 0n) throw new Error(`veCTM #${lock.tokenId} has no existing lock.`);
|
|
208
|
+
if (lock.endSec <= BigInt(nowSec)) {
|
|
209
|
+
throw new Error(`Cannot increase amount on expired veCTM #${lock.tokenId}. Withdraw or create a new lock.`);
|
|
210
|
+
}
|
|
211
|
+
if (balanceWei != null && amountWei > balanceWei) {
|
|
212
|
+
throw new Error(
|
|
213
|
+
`Increase amount ${formatCtmWei(amountWei)} CTM exceeds KeyGen balance ${formatCtmWei(balanceWei)} CTM.`
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
function assertIncreaseUnlockTime(durationSec, lock, nowSec) {
|
|
218
|
+
if (lock.amountWei === 0n) throw new Error(`veCTM #${lock.tokenId} has no existing lock.`);
|
|
219
|
+
if (lock.endSec <= BigInt(nowSec)) {
|
|
220
|
+
throw new Error(`Cannot increase unlock time on expired veCTM #${lock.tokenId}.`);
|
|
221
|
+
}
|
|
222
|
+
const unlock = assertLockDurationSeconds(durationSec, nowSec, "Increase unlock time");
|
|
223
|
+
if (unlock <= lock.endSec) {
|
|
224
|
+
throw new Error(
|
|
225
|
+
`New unlock time must be after the current lock end (${lock.endSec.toString()}). Pick a later week-aligned date.`
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
return unlock;
|
|
229
|
+
}
|
|
230
|
+
function assertSplitExtracted(extractedWei, lock, minimumLockWei, nowSec) {
|
|
231
|
+
if (lock.attached) {
|
|
232
|
+
throw new Error(`Split is blocked while veCTM #${lock.tokenId} is attached to a node. Request detach first.`);
|
|
233
|
+
}
|
|
234
|
+
if (lock.amountWei === 0n) throw new Error(`veCTM #${lock.tokenId} has no existing lock.`);
|
|
235
|
+
if (lock.endSec <= BigInt(nowSec)) {
|
|
236
|
+
throw new Error(`Cannot split expired veCTM #${lock.tokenId}.`);
|
|
237
|
+
}
|
|
238
|
+
const min = minimumLockWei > 0n ? minimumLockWei : VECTM_DEFAULT_MINIMUM_LOCK_WEI;
|
|
239
|
+
if (lock.amountWei < min * 2n) {
|
|
240
|
+
throw new Error("Split requires at least 2 CTM locked (1 CTM minimum per resulting lock).");
|
|
241
|
+
}
|
|
242
|
+
if (extractedWei < min) {
|
|
243
|
+
throw new Error(`Extract at least ${formatCtmWei(min)} CTM into the new lock.`);
|
|
244
|
+
}
|
|
245
|
+
if (extractedWei > lock.amountWei - min) {
|
|
246
|
+
throw new Error(`The original lock must keep at least ${formatCtmWei(min)} CTM after the split.`);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
function assertMerge(fromLock, toLock) {
|
|
250
|
+
if (fromLock.tokenId === toLock.tokenId) throw new Error("Merge requires two different token ids.");
|
|
251
|
+
if (fromLock.attached) {
|
|
252
|
+
throw new Error(`Merge is blocked while veCTM #${fromLock.tokenId} is attached to a node.`);
|
|
253
|
+
}
|
|
254
|
+
if (toLock.attached) {
|
|
255
|
+
throw new Error(`Merge is blocked while veCTM #${toLock.tokenId} is attached to a node.`);
|
|
256
|
+
}
|
|
257
|
+
if (fromLock.owner.toLowerCase() !== toLock.owner.toLowerCase()) {
|
|
258
|
+
throw new Error("Merge requires both veCTM locks to have the same owner.");
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
function assertWithdraw(lock, nowSec) {
|
|
262
|
+
if (lock.attached) {
|
|
263
|
+
throw new Error(`Withdraw is blocked while veCTM #${lock.tokenId} is attached to a node. Request detach first.`);
|
|
264
|
+
}
|
|
265
|
+
if (lock.endSec <= 0n) throw new Error(`Unlock time unavailable for veCTM #${lock.tokenId}.`);
|
|
266
|
+
if (lock.endSec > BigInt(nowSec)) {
|
|
267
|
+
throw new Error(`Withdraw is available after the lock expires (unlock ${lock.endSec.toString()}).`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
function computeLiquidationAmounts(args) {
|
|
271
|
+
if (args.lockEndSec > 0n && BigInt(args.nowSec) >= args.lockEndSec) {
|
|
272
|
+
return {
|
|
273
|
+
userWei: args.lockedAmountWei,
|
|
274
|
+
treasuryWei: 0n,
|
|
275
|
+
lockExpired: true,
|
|
276
|
+
tooSmall: false,
|
|
277
|
+
zeroPenalty: false
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
if (args.lockedAmountWei <= VECTM_LIQ_MIN_LOCKED_WEI) {
|
|
281
|
+
return {
|
|
282
|
+
userWei: 0n,
|
|
283
|
+
treasuryWei: 0n,
|
|
284
|
+
lockExpired: false,
|
|
285
|
+
tooSmall: true,
|
|
286
|
+
zeroPenalty: false
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
const penalty = args.vePowerWei * VECTM_LIQ_PENALTY_NUM / VECTM_LIQ_PENALTY_DEN;
|
|
290
|
+
if (penalty === 0n) {
|
|
291
|
+
return {
|
|
292
|
+
userWei: 0n,
|
|
293
|
+
treasuryWei: 0n,
|
|
294
|
+
lockExpired: false,
|
|
295
|
+
tooSmall: false,
|
|
296
|
+
zeroPenalty: true
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
return {
|
|
300
|
+
userWei: args.lockedAmountWei - penalty,
|
|
301
|
+
treasuryWei: penalty,
|
|
302
|
+
lockExpired: false,
|
|
303
|
+
tooSmall: false,
|
|
304
|
+
zeroPenalty: false
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
function assertLiquidate(lock, liquidationsEnabled, nowSec) {
|
|
308
|
+
if (lock.attached) {
|
|
309
|
+
throw new Error(`Liquidate is blocked while veCTM #${lock.tokenId} is attached to a node. Request detach first.`);
|
|
310
|
+
}
|
|
311
|
+
if (liquidationsEnabled === false) {
|
|
312
|
+
throw new Error("Liquidations are currently disabled on-chain.");
|
|
313
|
+
}
|
|
314
|
+
const preview = computeLiquidationAmounts({
|
|
315
|
+
lockedAmountWei: lock.amountWei,
|
|
316
|
+
vePowerWei: lock.vePowerWei,
|
|
317
|
+
lockEndSec: lock.endSec,
|
|
318
|
+
nowSec
|
|
319
|
+
});
|
|
320
|
+
if (preview.tooSmall) throw new Error("Locked amount is too small to liquidate.");
|
|
321
|
+
if (preview.zeroPenalty) throw new Error("Voting power is too low to compute a liquidation penalty.");
|
|
322
|
+
}
|
|
323
|
+
function assertDelegatee(delegateeRaw, executor) {
|
|
324
|
+
const delegatee = viem.getAddress(delegateeRaw);
|
|
325
|
+
if (delegatee === viem.zeroAddress) {
|
|
326
|
+
throw new Error("Zero address is not allowed. Use undelegate to return power to this KeyGen.");
|
|
327
|
+
}
|
|
328
|
+
const self = viem.getAddress(executor);
|
|
329
|
+
if (delegatee === self) {
|
|
330
|
+
throw new Error("This is this KeyGen\u2019s address \u2014 use undelegate instead.");
|
|
331
|
+
}
|
|
332
|
+
return delegatee;
|
|
333
|
+
}
|
|
334
|
+
function assertUndelegateNotAlreadySelf(alreadySelf, account) {
|
|
335
|
+
if (alreadySelf) {
|
|
336
|
+
throw new Error(`Voting power is already at this KeyGen (${viem.getAddress(account)}).`);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
function assertTransfer(lock, toRaw, fromRaw) {
|
|
340
|
+
if (lock.attached) {
|
|
341
|
+
throw new Error(`Transfer is blocked while veCTM #${lock.tokenId} is attached to a node.`);
|
|
342
|
+
}
|
|
343
|
+
const to = viem.getAddress(toRaw);
|
|
344
|
+
const from = viem.getAddress(fromRaw);
|
|
345
|
+
if (to === viem.zeroAddress) throw new Error("Cannot transfer veCTM to the zero address.");
|
|
346
|
+
if (to === from) throw new Error("Transfer recipient must be different from this KeyGen.");
|
|
347
|
+
return to;
|
|
348
|
+
}
|
|
349
|
+
function assertExecutorOwnsLock(lock, executor) {
|
|
350
|
+
if (lock.owner.toLowerCase() !== viem.getAddress(executor).toLowerCase()) {
|
|
351
|
+
throw new Error(`KeyGen ${viem.getAddress(executor)} does not own veCTM #${lock.tokenId} (owner ${lock.owner}).`);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
function formatCtmWei(wei) {
|
|
355
|
+
const whole = wei / 10n ** 18n;
|
|
356
|
+
const frac = wei % 10n ** 18n;
|
|
357
|
+
if (frac === 0n) return whole.toString();
|
|
358
|
+
const fracStr = frac.toString().padStart(18, "0").replace(/0+$/, "");
|
|
359
|
+
return `${whole.toString()}.${fracStr}`;
|
|
360
|
+
}
|
|
361
|
+
var veReadAbi = viem.parseAbi([
|
|
362
|
+
"function minimumLock() view returns (uint256)",
|
|
363
|
+
"function locked(uint256 tokenId) view returns (int128 amount, uint256 end)",
|
|
364
|
+
"function ownerOf(uint256 tokenId) view returns (address)",
|
|
365
|
+
"function balanceOfNFT(uint256 tokenId) view returns (uint256)",
|
|
366
|
+
"function liquidationsEnabled() view returns (bool)",
|
|
367
|
+
"function nodeProperties() view returns (address)",
|
|
368
|
+
"function rewards() view returns (address)",
|
|
369
|
+
"function delegates(address account) view returns (address)",
|
|
370
|
+
"function token() view returns (address)"
|
|
371
|
+
]);
|
|
372
|
+
var erc20Abi = viem.parseAbi(["function balanceOf(address owner) view returns (uint256)"]);
|
|
373
|
+
var nodePropsAbi = viem.parseAbi([
|
|
374
|
+
"function attachedKeyGen(uint256 tokenId) view returns (address)",
|
|
375
|
+
"function attachedTokenId(address keyGen) view returns (uint256)",
|
|
376
|
+
"function nodeRequestingDetachment(uint256 tokenId) view returns (bool)"
|
|
377
|
+
]);
|
|
378
|
+
var rewardsAbi = viem.parseAbi(["function unclaimedRewards(uint256 tokenId) view returns (uint256)"]);
|
|
379
|
+
var mpaReadAbi = viem.parseAbi([
|
|
380
|
+
"function getNodeWithdrawAuthority(string nodeKey) view returns (address)",
|
|
381
|
+
"function ve() view returns (address)",
|
|
382
|
+
"function veCtmThresholdPower() view returns (uint256)",
|
|
383
|
+
"function nodeProperties() view returns (address)"
|
|
384
|
+
]);
|
|
385
|
+
function publicClient(rpcUrl, chainId) {
|
|
386
|
+
return viem.createPublicClient({
|
|
387
|
+
chain: viem.defineChain({
|
|
388
|
+
id: chainId,
|
|
389
|
+
name: "ContinuumDAO escrow",
|
|
390
|
+
nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
|
|
391
|
+
rpcUrls: { default: { http: [rpcUrl.trim()] } }
|
|
392
|
+
}),
|
|
393
|
+
transport: viem.http(rpcUrl.trim())
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
async function readMinimumLockWei(args) {
|
|
397
|
+
const client = publicClient(args.rpcUrl, args.chainId);
|
|
398
|
+
try {
|
|
399
|
+
const min = await client.readContract({
|
|
400
|
+
address: args.votingEscrow,
|
|
401
|
+
abi: veReadAbi,
|
|
402
|
+
functionName: "minimumLock"
|
|
403
|
+
});
|
|
404
|
+
return min > 0n ? min : VECTM_DEFAULT_MINIMUM_LOCK_WEI;
|
|
405
|
+
} catch {
|
|
406
|
+
return VECTM_DEFAULT_MINIMUM_LOCK_WEI;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
async function readCtmBalanceWei(args) {
|
|
410
|
+
const client = publicClient(args.rpcUrl, args.chainId);
|
|
411
|
+
try {
|
|
412
|
+
return await client.readContract({
|
|
413
|
+
address: args.ctm,
|
|
414
|
+
abi: erc20Abi,
|
|
415
|
+
functionName: "balanceOf",
|
|
416
|
+
args: [args.owner]
|
|
417
|
+
});
|
|
418
|
+
} catch {
|
|
419
|
+
return void 0;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
async function readVeCtmLockState(args) {
|
|
423
|
+
const client = publicClient(args.rpcUrl, args.chainId);
|
|
424
|
+
const [lockedPair, owner, vePower, nodeProperties] = await Promise.all([
|
|
425
|
+
client.readContract({
|
|
426
|
+
address: args.votingEscrow,
|
|
427
|
+
abi: veReadAbi,
|
|
428
|
+
functionName: "locked",
|
|
429
|
+
args: [args.tokenId]
|
|
430
|
+
}),
|
|
431
|
+
client.readContract({
|
|
432
|
+
address: args.votingEscrow,
|
|
433
|
+
abi: veReadAbi,
|
|
434
|
+
functionName: "ownerOf",
|
|
435
|
+
args: [args.tokenId]
|
|
436
|
+
}),
|
|
437
|
+
client.readContract({
|
|
438
|
+
address: args.votingEscrow,
|
|
439
|
+
abi: veReadAbi,
|
|
440
|
+
functionName: "balanceOfNFT",
|
|
441
|
+
args: [args.tokenId]
|
|
442
|
+
}),
|
|
443
|
+
client.readContract({
|
|
444
|
+
address: args.votingEscrow,
|
|
445
|
+
abi: veReadAbi,
|
|
446
|
+
functionName: "nodeProperties"
|
|
447
|
+
})
|
|
448
|
+
]);
|
|
449
|
+
const amountRaw = lockedPair[0];
|
|
450
|
+
const amountWei = amountRaw < 0n ? 0n : BigInt(amountRaw);
|
|
451
|
+
let attached = false;
|
|
452
|
+
if (isConfiguredAddress(nodeProperties)) {
|
|
453
|
+
try {
|
|
454
|
+
const attachedKey = await client.readContract({
|
|
455
|
+
address: viem.getAddress(nodeProperties),
|
|
456
|
+
abi: nodePropsAbi,
|
|
457
|
+
functionName: "attachedKeyGen",
|
|
458
|
+
args: [args.tokenId]
|
|
459
|
+
});
|
|
460
|
+
attached = attachedKey !== viem.zeroAddress;
|
|
461
|
+
} catch {
|
|
462
|
+
attached = false;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
return {
|
|
466
|
+
tokenId: args.tokenId,
|
|
467
|
+
amountWei,
|
|
468
|
+
endSec: lockedPair[1],
|
|
469
|
+
owner: viem.getAddress(owner),
|
|
470
|
+
attached,
|
|
471
|
+
vePowerWei: vePower
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
async function assertNoUnclaimedRewards(args) {
|
|
475
|
+
const client = publicClient(args.rpcUrl, args.chainId);
|
|
476
|
+
let rewards;
|
|
477
|
+
try {
|
|
478
|
+
rewards = await client.readContract({
|
|
479
|
+
address: args.votingEscrow,
|
|
480
|
+
abi: veReadAbi,
|
|
481
|
+
functionName: "rewards"
|
|
482
|
+
});
|
|
483
|
+
} catch {
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
if (!isConfiguredAddress(rewards)) return;
|
|
487
|
+
try {
|
|
488
|
+
const unclaimed = await client.readContract({
|
|
489
|
+
address: rewards,
|
|
490
|
+
abi: rewardsAbi,
|
|
491
|
+
functionName: "unclaimedRewards",
|
|
492
|
+
args: [args.tokenId]
|
|
493
|
+
});
|
|
494
|
+
if (unclaimed !== 0n) {
|
|
495
|
+
throw new Error(`veCTM #${args.tokenId} has unclaimed rewards. Claim them before this escrow action.`);
|
|
496
|
+
}
|
|
497
|
+
} catch (err) {
|
|
498
|
+
if (err instanceof Error && err.message.includes("unclaimed rewards")) throw err;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
async function preflightCreateLock(args) {
|
|
502
|
+
const nowSec = Math.floor(Date.now() / 1e3);
|
|
503
|
+
const [minimumLockWei, balanceWei] = await Promise.all([
|
|
504
|
+
readMinimumLockWei(args),
|
|
505
|
+
readCtmBalanceWei(args)
|
|
506
|
+
]);
|
|
507
|
+
assertCreateLockAmount(args.amountWei, minimumLockWei, balanceWei);
|
|
508
|
+
assertLockDurationSeconds(args.durationSec, nowSec, "Create lock");
|
|
509
|
+
}
|
|
510
|
+
async function preflightIncreaseAmount(args) {
|
|
511
|
+
const nowSec = Math.floor(Date.now() / 1e3);
|
|
512
|
+
const [lock, balanceWei] = await Promise.all([
|
|
513
|
+
readVeCtmLockState(args),
|
|
514
|
+
readCtmBalanceWei(args)
|
|
515
|
+
]);
|
|
516
|
+
assertExecutorOwnsLock(lock, args.owner);
|
|
517
|
+
assertIncreaseAmount(args.amountWei, lock, nowSec, balanceWei);
|
|
518
|
+
await assertNoUnclaimedRewards(args);
|
|
519
|
+
}
|
|
520
|
+
async function preflightIncreaseUnlockTime(args) {
|
|
521
|
+
const nowSec = Math.floor(Date.now() / 1e3);
|
|
522
|
+
const lock = await readVeCtmLockState(args);
|
|
523
|
+
assertExecutorOwnsLock(lock, args.owner);
|
|
524
|
+
assertIncreaseUnlockTime(args.durationSec, lock, nowSec);
|
|
525
|
+
await assertNoUnclaimedRewards(args);
|
|
526
|
+
}
|
|
527
|
+
async function preflightSplit(args) {
|
|
528
|
+
const nowSec = Math.floor(Date.now() / 1e3);
|
|
529
|
+
const [lock, minimumLockWei] = await Promise.all([
|
|
530
|
+
readVeCtmLockState(args),
|
|
531
|
+
readMinimumLockWei(args)
|
|
532
|
+
]);
|
|
533
|
+
assertExecutorOwnsLock(lock, args.owner);
|
|
534
|
+
assertSplitExtracted(args.extractedWei, lock, minimumLockWei, nowSec);
|
|
535
|
+
await assertNoUnclaimedRewards(args);
|
|
536
|
+
}
|
|
537
|
+
async function preflightMerge(args) {
|
|
538
|
+
const [fromLock, toLock] = await Promise.all([
|
|
539
|
+
readVeCtmLockState({ ...args, tokenId: args.fromTokenId }),
|
|
540
|
+
readVeCtmLockState({ ...args, tokenId: args.toTokenId })
|
|
541
|
+
]);
|
|
542
|
+
assertExecutorOwnsLock(fromLock, args.owner);
|
|
543
|
+
assertExecutorOwnsLock(toLock, args.owner);
|
|
544
|
+
assertMerge(fromLock, toLock);
|
|
545
|
+
await Promise.all([
|
|
546
|
+
assertNoUnclaimedRewards({ ...args, tokenId: args.fromTokenId }),
|
|
547
|
+
assertNoUnclaimedRewards({ ...args, tokenId: args.toTokenId })
|
|
548
|
+
]);
|
|
549
|
+
}
|
|
550
|
+
async function preflightWithdraw(args) {
|
|
551
|
+
const nowSec = Math.floor(Date.now() / 1e3);
|
|
552
|
+
const lock = await readVeCtmLockState(args);
|
|
553
|
+
assertExecutorOwnsLock(lock, args.owner);
|
|
554
|
+
assertWithdraw(lock, nowSec);
|
|
555
|
+
await assertNoUnclaimedRewards(args);
|
|
556
|
+
}
|
|
557
|
+
async function preflightLiquidate(args) {
|
|
558
|
+
const nowSec = Math.floor(Date.now() / 1e3);
|
|
559
|
+
const client = publicClient(args.rpcUrl, args.chainId);
|
|
560
|
+
const [lock, liquidationsEnabled] = await Promise.all([
|
|
561
|
+
readVeCtmLockState(args),
|
|
562
|
+
client.readContract({
|
|
563
|
+
address: args.votingEscrow,
|
|
564
|
+
abi: veReadAbi,
|
|
565
|
+
functionName: "liquidationsEnabled"
|
|
566
|
+
}).catch(() => null)
|
|
567
|
+
]);
|
|
568
|
+
assertExecutorOwnsLock(lock, args.owner);
|
|
569
|
+
assertLiquidate(lock, liquidationsEnabled, nowSec);
|
|
570
|
+
await assertNoUnclaimedRewards(args);
|
|
571
|
+
}
|
|
572
|
+
async function preflightDelegate(args) {
|
|
573
|
+
return assertDelegatee(args.delegatee, args.owner);
|
|
574
|
+
}
|
|
575
|
+
async function preflightUndelegate(args) {
|
|
576
|
+
const client = publicClient(args.rpcUrl, args.chainId);
|
|
577
|
+
const delegatee = await client.readContract({
|
|
578
|
+
address: args.votingEscrow,
|
|
579
|
+
abi: veReadAbi,
|
|
580
|
+
functionName: "delegates",
|
|
581
|
+
args: [args.owner]
|
|
582
|
+
});
|
|
583
|
+
assertUndelegateNotAlreadySelf(viem.getAddress(delegatee) === viem.getAddress(args.owner), args.owner);
|
|
584
|
+
}
|
|
585
|
+
async function preflightTransfer(args) {
|
|
586
|
+
const lock = await readVeCtmLockState(args);
|
|
587
|
+
assertExecutorOwnsLock(lock, args.owner);
|
|
588
|
+
return assertTransfer(lock, args.to, args.owner);
|
|
589
|
+
}
|
|
590
|
+
async function preflightAttachVeCtm(args) {
|
|
591
|
+
const client = publicClient(args.rpcUrl, args.chainId);
|
|
592
|
+
const [authority, veFromWallet, threshold, nodeProperties] = await Promise.all([
|
|
593
|
+
client.readContract({
|
|
594
|
+
address: args.mpaWallet,
|
|
595
|
+
abi: mpaReadAbi,
|
|
596
|
+
functionName: "getNodeWithdrawAuthority",
|
|
597
|
+
args: [args.nodeKey]
|
|
598
|
+
}),
|
|
599
|
+
client.readContract({ address: args.mpaWallet, abi: mpaReadAbi, functionName: "ve" }),
|
|
600
|
+
client.readContract({
|
|
601
|
+
address: args.mpaWallet,
|
|
602
|
+
abi: mpaReadAbi,
|
|
603
|
+
functionName: "veCtmThresholdPower"
|
|
604
|
+
}),
|
|
605
|
+
client.readContract({
|
|
606
|
+
address: args.mpaWallet,
|
|
607
|
+
abi: mpaReadAbi,
|
|
608
|
+
functionName: "nodeProperties"
|
|
609
|
+
})
|
|
610
|
+
]);
|
|
611
|
+
const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? viem.getAddress(args.votingEscrow) : viem.getAddress(veFromWallet);
|
|
612
|
+
if (!isConfiguredAddress(ve)) throw new Error("veCTM is not live on the fee contract yet.");
|
|
613
|
+
const lock = await readVeCtmLockState({
|
|
614
|
+
rpcUrl: args.rpcUrl,
|
|
615
|
+
chainId: args.chainId,
|
|
616
|
+
votingEscrow: ve,
|
|
617
|
+
tokenId: args.tokenId
|
|
618
|
+
});
|
|
619
|
+
assertExecutorOwnsLock(lock, args.owner);
|
|
620
|
+
const executor = viem.getAddress(args.owner);
|
|
621
|
+
if (authority === viem.zeroAddress || viem.getAddress(authority) !== executor) {
|
|
622
|
+
throw new Error(
|
|
623
|
+
`Compose attachVeCtm from the claimed authority KeyGen (${authority}), which must own the NFT.`
|
|
624
|
+
);
|
|
625
|
+
}
|
|
626
|
+
if (isConfiguredAddress(nodeProperties)) {
|
|
627
|
+
const np = viem.getAddress(nodeProperties);
|
|
628
|
+
const [attachedKey, attachedForKey] = await Promise.all([
|
|
629
|
+
client.readContract({
|
|
630
|
+
address: np,
|
|
631
|
+
abi: nodePropsAbi,
|
|
632
|
+
functionName: "attachedKeyGen",
|
|
633
|
+
args: [args.tokenId]
|
|
634
|
+
}),
|
|
635
|
+
client.readContract({
|
|
636
|
+
address: np,
|
|
637
|
+
abi: nodePropsAbi,
|
|
638
|
+
functionName: "attachedTokenId",
|
|
639
|
+
args: [executor]
|
|
640
|
+
})
|
|
641
|
+
]);
|
|
642
|
+
if (attachedKey !== viem.zeroAddress) {
|
|
643
|
+
throw new Error(`veCTM #${args.tokenId} is already attached. You cannot replace an attached NFT.`);
|
|
644
|
+
}
|
|
645
|
+
if (attachedForKey !== 0n) {
|
|
646
|
+
throw new Error(
|
|
647
|
+
`This KeyGen already has veCTM #${attachedForKey} attached. Request detach and wait for governance. A second group on this node attaches after rotating nodeWithdrawAuthority to that group's secp256k1 KeyGen.`
|
|
648
|
+
);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
if (threshold > 0n && lock.amountWei < threshold) {
|
|
652
|
+
throw new Error(
|
|
653
|
+
`Locked CTM on veCTM #${args.tokenId} is below MultiSignAgentWallet.veCtmThresholdPower (${threshold.toString()} wei).`
|
|
654
|
+
);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
async function preflightRequestDetach(args) {
|
|
658
|
+
const client = publicClient(args.rpcUrl, args.chainId);
|
|
659
|
+
const [attachedKey, alreadyRequested] = await Promise.all([
|
|
660
|
+
client.readContract({
|
|
661
|
+
address: args.nodeProperties,
|
|
662
|
+
abi: nodePropsAbi,
|
|
663
|
+
functionName: "attachedKeyGen",
|
|
664
|
+
args: [args.tokenId]
|
|
665
|
+
}),
|
|
666
|
+
client.readContract({
|
|
667
|
+
address: args.nodeProperties,
|
|
668
|
+
abi: nodePropsAbi,
|
|
669
|
+
functionName: "nodeRequestingDetachment",
|
|
670
|
+
args: [args.tokenId]
|
|
671
|
+
})
|
|
672
|
+
]);
|
|
673
|
+
if (attachedKey === viem.zeroAddress) {
|
|
674
|
+
throw new Error(`veCTM #${args.tokenId} is not attached to a node.`);
|
|
675
|
+
}
|
|
676
|
+
if (alreadyRequested) {
|
|
677
|
+
throw new Error(
|
|
678
|
+
`Detach already requested for veCTM #${args.tokenId}. The NFT stays attached until governance executes detach.`
|
|
679
|
+
);
|
|
680
|
+
}
|
|
681
|
+
if (viem.getAddress(attachedKey) !== viem.getAddress(args.owner)) {
|
|
682
|
+
throw new Error(
|
|
683
|
+
`Request detach from the attached KeyGen (${viem.getAddress(attachedKey)}).`
|
|
684
|
+
);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
// src/protocols/evm/continuum-dao/attachGroup.ts
|
|
689
|
+
var VECTM_NODE_INFO_TUPLE = "(string,string,uint8[4],uint16[8],string,uint256,uint256,string,string,bytes)";
|
|
690
|
+
var ATTACH_VECTM_SIGNATURE = `attachVeCtm(string,uint256,${VECTM_NODE_INFO_TUPLE},string)`;
|
|
691
|
+
function resolveAttachGroupId(keyGen, explicit) {
|
|
692
|
+
const fromKg = (keyGen.groupId ?? keyGen.GroupId ?? "").trim();
|
|
693
|
+
const fromArg = (explicit ?? "").trim();
|
|
694
|
+
if (fromKg && fromArg && fromKg !== fromArg) {
|
|
695
|
+
throw new Error(
|
|
696
|
+
`groupId must be the attaching KeyGen's mpc-auth GroupId (${fromKg}), not a different group.`
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
if (fromKg) return fromKg;
|
|
700
|
+
if (fromArg) return fromArg;
|
|
701
|
+
throw new Error(
|
|
702
|
+
"groupId is required to attach veCTM (use the attaching KeyGen's mpc-auth GroupId)."
|
|
703
|
+
);
|
|
704
|
+
}
|
|
159
705
|
|
|
160
706
|
// src/core/purpose.ts
|
|
161
707
|
function mergePurposeText(purposeText, purposeSuffix) {
|
|
@@ -247,25 +793,25 @@ async function buildEvmMultisignBatch(args) {
|
|
|
247
793
|
nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
|
|
248
794
|
rpcUrls: { default: { http: [rpcUrl] } }
|
|
249
795
|
});
|
|
250
|
-
const
|
|
796
|
+
const publicClient3 = viem.createPublicClient({ chain: ch, transport: viem.http(rpcUrl) });
|
|
251
797
|
const feeParams = await continuumNodeSdk.fetchChainFeeParams(rpcUrl, chainId);
|
|
252
798
|
const legacy = Boolean(chainDetail?.legacy) || !feeParams.isEip1559;
|
|
253
|
-
const latestBaseFeeWei = !legacy ? (await
|
|
799
|
+
const latestBaseFeeWei = !legacy ? (await publicClient3.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
|
|
254
800
|
const gasLimitConfig = useCustomGas && chainDetail?.gasLimit != null ? Number(chainDetail.gasLimit) : void 0;
|
|
255
801
|
const chainGasLimitRouter = chainDetail?.gasLimit != null && Number.isFinite(Number(chainDetail.gasLimit)) && Number(chainDetail.gasLimit) > 0 ? Number(chainDetail.gasLimit) : void 0;
|
|
256
802
|
const gasFeeMultiplier = useCustomGas && chainDetail?.gasMultiplier != null ? Number(chainDetail.gasMultiplier) : void 0;
|
|
257
803
|
const executor = viem.getAddress(executorAddress);
|
|
258
|
-
const baseNonce = await
|
|
804
|
+
const baseNonce = await publicClient3.getTransactionCount({ address: executor, blockTag: "pending" });
|
|
259
805
|
const legs = [];
|
|
260
806
|
for (let i = 0; i < steps.length; i++) {
|
|
261
807
|
const step = steps[i];
|
|
262
808
|
const currentNonce = baseNonce + i;
|
|
263
809
|
let estimatedGas;
|
|
264
810
|
if (args.estimateGasForStep) {
|
|
265
|
-
estimatedGas = await args.estimateGasForStep({ step, index: i, publicClient:
|
|
811
|
+
estimatedGas = await args.estimateGasForStep({ step, index: i, publicClient: publicClient3, executor });
|
|
266
812
|
} else {
|
|
267
813
|
try {
|
|
268
|
-
estimatedGas = await
|
|
814
|
+
estimatedGas = await publicClient3.estimateGas({
|
|
269
815
|
to: step.to,
|
|
270
816
|
data: step.data,
|
|
271
817
|
value: step.value,
|
|
@@ -277,7 +823,7 @@ async function buildEvmMultisignBatch(args) {
|
|
|
277
823
|
}
|
|
278
824
|
let gasLimitI;
|
|
279
825
|
if (args.resolveGasLimit) {
|
|
280
|
-
gasLimitI = await args.resolveGasLimit({ step, index: i, estimatedGas, publicClient:
|
|
826
|
+
gasLimitI = await args.resolveGasLimit({ step, index: i, estimatedGas, publicClient: publicClient3 });
|
|
281
827
|
} else if (step.routerSwap) {
|
|
282
828
|
gasLimitI = routerSwapGasLimitFromEstimate(estimatedGas, chainGasLimitRouter);
|
|
283
829
|
} else {
|
|
@@ -287,7 +833,7 @@ async function buildEvmMultisignBatch(args) {
|
|
|
287
833
|
let feeSnapshot;
|
|
288
834
|
let serialized;
|
|
289
835
|
if (legacy) {
|
|
290
|
-
let gasPriceWei = await
|
|
836
|
+
let gasPriceWei = await publicClient3.getGasPrice();
|
|
291
837
|
if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
|
|
292
838
|
gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
293
839
|
}
|
|
@@ -404,9 +950,8 @@ var CONTINUUM_DAO_DELEGATE_FALLBACK = 160000n;
|
|
|
404
950
|
var CONTINUUM_DAO_TRANSFER_FALLBACK = 160000n;
|
|
405
951
|
var CONTINUUM_DAO_ATTACH_FALLBACK = 450000n;
|
|
406
952
|
var CONTINUUM_DAO_DETACH_FALLBACK = 220000n;
|
|
407
|
-
var
|
|
408
|
-
var
|
|
409
|
-
var erc20Abi = viem.parseAbi([
|
|
953
|
+
var INCREASE_UNLOCK_TIME_SIGNATURE = "increase_unlock_time(uint256,uint256)";
|
|
954
|
+
var erc20Abi2 = viem.parseAbi([
|
|
410
955
|
"function allowance(address owner, address spender) view returns (uint256)",
|
|
411
956
|
"function approve(address spender, uint256 amount) returns (bool)"
|
|
412
957
|
]);
|
|
@@ -422,7 +967,7 @@ var veWriteAbi = viem.parseAbi([
|
|
|
422
967
|
"function transferFrom(address _from, address _to, uint256 _tokenId)"
|
|
423
968
|
]);
|
|
424
969
|
var attachAbi = viem.parseAbi([
|
|
425
|
-
`function attachVeCtm(string nodeKey, uint256 tokenId, ${VECTM_NODE_INFO_TUPLE} nodeInfo)`
|
|
970
|
+
`function attachVeCtm(string nodeKey, uint256 tokenId, ${VECTM_NODE_INFO_TUPLE} nodeInfo, string groupId)`
|
|
426
971
|
]);
|
|
427
972
|
var detachAbi = viem.parseAbi(["function setNodeRemovalStatus(uint256 tokenId, bool status)"]);
|
|
428
973
|
var CONTINUUM_DAO_EVM_TYPES = /* @__PURE__ */ new Set([
|
|
@@ -461,14 +1006,17 @@ function parseTokenId(raw) {
|
|
|
461
1006
|
if (id <= 0n) throw new Error("veCTM token id must be greater than zero.");
|
|
462
1007
|
return id;
|
|
463
1008
|
}
|
|
1009
|
+
function continuumDaoSignatureText(extra, chainId) {
|
|
1010
|
+
return JSON.stringify({
|
|
1011
|
+
kind: "ContinuumDAO",
|
|
1012
|
+
...extra,
|
|
1013
|
+
chainId
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
464
1016
|
function meta(evmType, chainId, to, extra, gasLimit) {
|
|
465
1017
|
return {
|
|
466
1018
|
destinationAddress: to,
|
|
467
|
-
signatureText:
|
|
468
|
-
kind: "ContinuumDAO",
|
|
469
|
-
...extra,
|
|
470
|
-
chainId
|
|
471
|
-
}),
|
|
1019
|
+
signatureText: continuumDaoSignatureText(extra, chainId),
|
|
472
1020
|
evm: { type: evmType, version: 1, chainId: String(chainId), protocolId: CONTINUUM_DAO_PROTOCOL_ID },
|
|
473
1021
|
continuumDao: { ...extra, gas: { baseGasUnits: gasLimit.toString() } }
|
|
474
1022
|
};
|
|
@@ -513,7 +1061,7 @@ async function maybeApproveCtmStep(args) {
|
|
|
513
1061
|
try {
|
|
514
1062
|
allowance = await client.readContract({
|
|
515
1063
|
address: args.ctm,
|
|
516
|
-
abi:
|
|
1064
|
+
abi: erc20Abi2,
|
|
517
1065
|
functionName: "allowance",
|
|
518
1066
|
args: [args.owner, args.spender]
|
|
519
1067
|
});
|
|
@@ -522,7 +1070,7 @@ async function maybeApproveCtmStep(args) {
|
|
|
522
1070
|
}
|
|
523
1071
|
if (allowance >= args.amountWei) return null;
|
|
524
1072
|
const data = viem.encodeFunctionData({
|
|
525
|
-
abi:
|
|
1073
|
+
abi: erc20Abi2,
|
|
526
1074
|
functionName: "approve",
|
|
527
1075
|
args: [args.spender, args.amountWei]
|
|
528
1076
|
});
|
|
@@ -542,10 +1090,17 @@ async function buildEvmMultisignBodyContinuumDaoCreateLock(args) {
|
|
|
542
1090
|
const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? viem.getAddress(args.votingEscrow) : requireVe(args.chainId);
|
|
543
1091
|
const ctm = args.ctm && isConfiguredAddress(args.ctm) ? viem.getAddress(args.ctm) : requireCtm(args.chainId);
|
|
544
1092
|
const amountWei = viem.parseUnits(String(args.amountHuman).trim().replace(/,/g, ""), CTM_TOKEN_DECIMALS);
|
|
545
|
-
if (amountWei <= 0n) throw new Error("CTM amount must be greater than zero.");
|
|
546
1093
|
const duration = BigInt(String(args.lockDurationSeconds).trim());
|
|
547
|
-
if (duration <= 0n) throw new Error("Lock duration must be greater than zero.");
|
|
548
1094
|
const owner = viem.getAddress(args.executorAddress);
|
|
1095
|
+
await preflightCreateLock({
|
|
1096
|
+
rpcUrl: args.rpcUrl,
|
|
1097
|
+
chainId: args.chainId,
|
|
1098
|
+
votingEscrow: ve,
|
|
1099
|
+
ctm,
|
|
1100
|
+
owner,
|
|
1101
|
+
amountWei,
|
|
1102
|
+
durationSec: duration
|
|
1103
|
+
});
|
|
549
1104
|
const approve = await maybeApproveCtmStep({
|
|
550
1105
|
rpcUrl: args.rpcUrl,
|
|
551
1106
|
chainId: args.chainId,
|
|
@@ -578,8 +1133,16 @@ async function buildEvmMultisignBodyContinuumDaoIncreaseAmount(args) {
|
|
|
578
1133
|
const ctm = args.ctm && isConfiguredAddress(args.ctm) ? viem.getAddress(args.ctm) : requireCtm(args.chainId);
|
|
579
1134
|
const tokenId = parseTokenId(args.tokenId);
|
|
580
1135
|
const amountWei = viem.parseUnits(String(args.amountHuman).trim().replace(/,/g, ""), CTM_TOKEN_DECIMALS);
|
|
581
|
-
if (amountWei <= 0n) throw new Error("CTM amount must be greater than zero.");
|
|
582
1136
|
const owner = viem.getAddress(args.executorAddress);
|
|
1137
|
+
await preflightIncreaseAmount({
|
|
1138
|
+
rpcUrl: args.rpcUrl,
|
|
1139
|
+
chainId: args.chainId,
|
|
1140
|
+
votingEscrow: ve,
|
|
1141
|
+
ctm,
|
|
1142
|
+
owner,
|
|
1143
|
+
tokenId,
|
|
1144
|
+
amountWei
|
|
1145
|
+
});
|
|
583
1146
|
const approve = await maybeApproveCtmStep({
|
|
584
1147
|
rpcUrl: args.rpcUrl,
|
|
585
1148
|
chainId: args.chainId,
|
|
@@ -611,7 +1174,15 @@ async function buildEvmMultisignBodyContinuumDaoIncreaseUnlockTime(args) {
|
|
|
611
1174
|
const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? viem.getAddress(args.votingEscrow) : requireVe(args.chainId);
|
|
612
1175
|
const tokenId = parseTokenId(args.tokenId);
|
|
613
1176
|
const duration = BigInt(String(args.lockDurationSeconds).trim());
|
|
614
|
-
|
|
1177
|
+
const owner = viem.getAddress(args.executorAddress);
|
|
1178
|
+
await preflightIncreaseUnlockTime({
|
|
1179
|
+
rpcUrl: args.rpcUrl,
|
|
1180
|
+
chainId: args.chainId,
|
|
1181
|
+
votingEscrow: ve,
|
|
1182
|
+
owner,
|
|
1183
|
+
tokenId,
|
|
1184
|
+
durationSec: duration
|
|
1185
|
+
});
|
|
615
1186
|
const data = viem.encodeFunctionData({
|
|
616
1187
|
abi: veWriteAbi,
|
|
617
1188
|
functionName: "increase_unlock_time",
|
|
@@ -624,6 +1195,7 @@ async function buildEvmMultisignBodyContinuumDaoIncreaseUnlockTime(args) {
|
|
|
624
1195
|
fallbackGas: CONTINUUM_DAO_INCREASE_UNLOCK_FALLBACK,
|
|
625
1196
|
buildBatchMeta: ({ gasLimit }) => meta("continuum_dao_increase_unlock_time", args.chainId, ve, {
|
|
626
1197
|
name: "VotingEscrow.increase_unlock_time",
|
|
1198
|
+
signature: INCREASE_UNLOCK_TIME_SIGNATURE,
|
|
627
1199
|
tokenId: tokenId.toString(),
|
|
628
1200
|
lockDurationSeconds: duration.toString()
|
|
629
1201
|
}, gasLimit)
|
|
@@ -634,7 +1206,14 @@ async function buildEvmMultisignBodyContinuumDaoSplit(args) {
|
|
|
634
1206
|
const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? viem.getAddress(args.votingEscrow) : requireVe(args.chainId);
|
|
635
1207
|
const tokenId = parseTokenId(args.tokenId);
|
|
636
1208
|
const extracted = viem.parseUnits(String(args.extractedHuman).trim().replace(/,/g, ""), CTM_TOKEN_DECIMALS);
|
|
637
|
-
|
|
1209
|
+
await preflightSplit({
|
|
1210
|
+
rpcUrl: args.rpcUrl,
|
|
1211
|
+
chainId: args.chainId,
|
|
1212
|
+
votingEscrow: ve,
|
|
1213
|
+
owner: viem.getAddress(args.executorAddress),
|
|
1214
|
+
tokenId,
|
|
1215
|
+
extractedWei: extracted
|
|
1216
|
+
});
|
|
638
1217
|
const data = viem.encodeFunctionData({
|
|
639
1218
|
abi: veWriteAbi,
|
|
640
1219
|
functionName: "split",
|
|
@@ -657,7 +1236,14 @@ async function buildEvmMultisignBodyContinuumDaoMerge(args) {
|
|
|
657
1236
|
const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? viem.getAddress(args.votingEscrow) : requireVe(args.chainId);
|
|
658
1237
|
const fromId = parseTokenId(args.fromTokenId);
|
|
659
1238
|
const toId = parseTokenId(args.toTokenId);
|
|
660
|
-
|
|
1239
|
+
await preflightMerge({
|
|
1240
|
+
rpcUrl: args.rpcUrl,
|
|
1241
|
+
chainId: args.chainId,
|
|
1242
|
+
votingEscrow: ve,
|
|
1243
|
+
owner: viem.getAddress(args.executorAddress),
|
|
1244
|
+
fromTokenId: fromId,
|
|
1245
|
+
toTokenId: toId
|
|
1246
|
+
});
|
|
661
1247
|
const data = viem.encodeFunctionData({
|
|
662
1248
|
abi: veWriteAbi,
|
|
663
1249
|
functionName: "merge",
|
|
@@ -679,6 +1265,13 @@ async function buildEvmMultisignBodyContinuumDaoMerge(args) {
|
|
|
679
1265
|
async function buildEvmMultisignBodyContinuumDaoWithdraw(args) {
|
|
680
1266
|
const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? viem.getAddress(args.votingEscrow) : requireVe(args.chainId);
|
|
681
1267
|
const tokenId = parseTokenId(args.tokenId);
|
|
1268
|
+
await preflightWithdraw({
|
|
1269
|
+
rpcUrl: args.rpcUrl,
|
|
1270
|
+
chainId: args.chainId,
|
|
1271
|
+
votingEscrow: ve,
|
|
1272
|
+
owner: viem.getAddress(args.executorAddress),
|
|
1273
|
+
tokenId
|
|
1274
|
+
});
|
|
682
1275
|
const data = viem.encodeFunctionData({ abi: veWriteAbi, functionName: "withdraw", args: [tokenId] });
|
|
683
1276
|
const step = {
|
|
684
1277
|
to: ve,
|
|
@@ -695,6 +1288,13 @@ async function buildEvmMultisignBodyContinuumDaoWithdraw(args) {
|
|
|
695
1288
|
async function buildEvmMultisignBodyContinuumDaoLiquidate(args) {
|
|
696
1289
|
const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? viem.getAddress(args.votingEscrow) : requireVe(args.chainId);
|
|
697
1290
|
const tokenId = parseTokenId(args.tokenId);
|
|
1291
|
+
await preflightLiquidate({
|
|
1292
|
+
rpcUrl: args.rpcUrl,
|
|
1293
|
+
chainId: args.chainId,
|
|
1294
|
+
votingEscrow: ve,
|
|
1295
|
+
owner: viem.getAddress(args.executorAddress),
|
|
1296
|
+
tokenId
|
|
1297
|
+
});
|
|
698
1298
|
const data = viem.encodeFunctionData({ abi: veWriteAbi, functionName: "liquidate", args: [tokenId] });
|
|
699
1299
|
const step = {
|
|
700
1300
|
to: ve,
|
|
@@ -710,7 +1310,12 @@ async function buildEvmMultisignBodyContinuumDaoLiquidate(args) {
|
|
|
710
1310
|
}
|
|
711
1311
|
async function buildEvmMultisignBodyContinuumDaoDelegate(args) {
|
|
712
1312
|
const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? viem.getAddress(args.votingEscrow) : requireVe(args.chainId);
|
|
713
|
-
const delegatee =
|
|
1313
|
+
const delegatee = await preflightDelegate({
|
|
1314
|
+
rpcUrl: args.rpcUrl,
|
|
1315
|
+
chainId: args.chainId,
|
|
1316
|
+
owner: viem.getAddress(args.executorAddress),
|
|
1317
|
+
delegatee: args.delegatee
|
|
1318
|
+
});
|
|
714
1319
|
const data = viem.encodeFunctionData({ abi: veWriteAbi, functionName: "delegate", args: [delegatee] });
|
|
715
1320
|
const step = {
|
|
716
1321
|
to: ve,
|
|
@@ -727,6 +1332,12 @@ async function buildEvmMultisignBodyContinuumDaoDelegate(args) {
|
|
|
727
1332
|
async function buildEvmMultisignBodyContinuumDaoUndelegate(args) {
|
|
728
1333
|
const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? viem.getAddress(args.votingEscrow) : requireVe(args.chainId);
|
|
729
1334
|
const self = viem.getAddress(args.executorAddress);
|
|
1335
|
+
await preflightUndelegate({
|
|
1336
|
+
rpcUrl: args.rpcUrl,
|
|
1337
|
+
chainId: args.chainId,
|
|
1338
|
+
votingEscrow: ve,
|
|
1339
|
+
owner: self
|
|
1340
|
+
});
|
|
730
1341
|
const data = viem.encodeFunctionData({ abi: veWriteAbi, functionName: "delegate", args: [self] });
|
|
731
1342
|
const step = {
|
|
732
1343
|
to: ve,
|
|
@@ -741,7 +1352,7 @@ async function buildEvmMultisignBodyContinuumDaoUndelegate(args) {
|
|
|
741
1352
|
};
|
|
742
1353
|
return finalize([step], args, `ContinuumDAO: undelegate voting power (delegate back to ${self}).`);
|
|
743
1354
|
}
|
|
744
|
-
var
|
|
1355
|
+
var veReadAbi2 = viem.parseAbi(["function delegates(address account) view returns (address)"]);
|
|
745
1356
|
async function continuumDaoFetchDelegates(args) {
|
|
746
1357
|
const chainId = Number(args.chainId);
|
|
747
1358
|
requireLockChain(chainId);
|
|
@@ -757,15 +1368,22 @@ async function continuumDaoFetchDelegates(args) {
|
|
|
757
1368
|
transport: viem.http(args.rpcUrl.trim())
|
|
758
1369
|
});
|
|
759
1370
|
const delegatee = viem.getAddress(
|
|
760
|
-
await client.readContract({ address: ve, abi:
|
|
1371
|
+
await client.readContract({ address: ve, abi: veReadAbi2, functionName: "delegates", args: [account] })
|
|
761
1372
|
);
|
|
762
1373
|
return { account, delegatee, self: delegatee === account };
|
|
763
1374
|
}
|
|
764
1375
|
async function buildEvmMultisignBodyContinuumDaoTransfer(args) {
|
|
765
1376
|
const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? viem.getAddress(args.votingEscrow) : requireVe(args.chainId);
|
|
766
1377
|
const tokenId = parseTokenId(args.tokenId);
|
|
767
|
-
const to = viem.getAddress(args.to);
|
|
768
1378
|
const from = viem.getAddress(args.executorAddress);
|
|
1379
|
+
const to = await preflightTransfer({
|
|
1380
|
+
rpcUrl: args.rpcUrl,
|
|
1381
|
+
chainId: args.chainId,
|
|
1382
|
+
votingEscrow: ve,
|
|
1383
|
+
owner: from,
|
|
1384
|
+
tokenId,
|
|
1385
|
+
to: args.to
|
|
1386
|
+
});
|
|
769
1387
|
const data = viem.encodeFunctionData({
|
|
770
1388
|
abi: veWriteAbi,
|
|
771
1389
|
functionName: "transferFrom",
|
|
@@ -826,11 +1444,20 @@ async function buildEvmMultisignBodyContinuumDaoAttachVeCtm(args) {
|
|
|
826
1444
|
const tokenId = parseTokenId(args.tokenId);
|
|
827
1445
|
const nodeKey = args.nodeKey.trim();
|
|
828
1446
|
if (!nodeKey) throw new Error("nodeKey is required to attach veCTM.");
|
|
1447
|
+
const groupId = resolveAttachGroupId(args.keyGen, args.groupId);
|
|
1448
|
+
await preflightAttachVeCtm({
|
|
1449
|
+
rpcUrl: args.rpcUrl,
|
|
1450
|
+
chainId: args.chainId,
|
|
1451
|
+
mpaWallet: wallet,
|
|
1452
|
+
nodeKey,
|
|
1453
|
+
tokenId,
|
|
1454
|
+
owner: viem.getAddress(args.executorAddress)
|
|
1455
|
+
});
|
|
829
1456
|
const tuple = args.nodeInfoTuple ?? encodeContinuumDaoNodeInfo(args.nodeInfo);
|
|
830
1457
|
const data = viem.encodeFunctionData({
|
|
831
1458
|
abi: attachAbi,
|
|
832
1459
|
functionName: "attachVeCtm",
|
|
833
|
-
args: [nodeKey, tokenId, tuple]
|
|
1460
|
+
args: [nodeKey, tokenId, tuple, groupId]
|
|
834
1461
|
});
|
|
835
1462
|
const step = {
|
|
836
1463
|
to: wallet,
|
|
@@ -839,17 +1466,26 @@ async function buildEvmMultisignBodyContinuumDaoAttachVeCtm(args) {
|
|
|
839
1466
|
fallbackGas: CONTINUUM_DAO_ATTACH_FALLBACK,
|
|
840
1467
|
buildBatchMeta: ({ gasLimit }) => meta("continuum_dao_attach", args.chainId, wallet, {
|
|
841
1468
|
name: "MultiSignAgentWallet.attachVeCtm",
|
|
1469
|
+
signature: ATTACH_VECTM_SIGNATURE,
|
|
842
1470
|
nodeKey,
|
|
843
|
-
tokenId: tokenId.toString()
|
|
1471
|
+
tokenId: tokenId.toString(),
|
|
1472
|
+
groupId
|
|
844
1473
|
}, gasLimit)
|
|
845
1474
|
};
|
|
846
|
-
return finalize([step], args, `ContinuumDAO: attach veCTM #${tokenId}.`);
|
|
1475
|
+
return finalize([step], args, `ContinuumDAO: attach veCTM #${tokenId} (group ${groupId}).`);
|
|
847
1476
|
}
|
|
848
1477
|
async function buildEvmMultisignBodyContinuumDaoRequestDetach(args) {
|
|
849
1478
|
requireLockChain(args.chainId);
|
|
850
1479
|
const np = viem.getAddress(args.nodeProperties);
|
|
851
1480
|
if (np === viem.zeroAddress) throw new Error("NodeProperties address is required to request detach.");
|
|
852
1481
|
const tokenId = parseTokenId(args.tokenId);
|
|
1482
|
+
await preflightRequestDetach({
|
|
1483
|
+
rpcUrl: args.rpcUrl,
|
|
1484
|
+
chainId: args.chainId,
|
|
1485
|
+
nodeProperties: np,
|
|
1486
|
+
owner: viem.getAddress(args.executorAddress),
|
|
1487
|
+
tokenId
|
|
1488
|
+
});
|
|
853
1489
|
const data = viem.encodeFunctionData({
|
|
854
1490
|
abi: detachAbi,
|
|
855
1491
|
functionName: "setNodeRemovalStatus",
|
|
@@ -1449,7 +2085,9 @@ var governorReadAbi = viem.parseAbi([
|
|
|
1449
2085
|
"function proposalThreshold() view returns (uint256)",
|
|
1450
2086
|
"function proposalCount() view returns (uint256)",
|
|
1451
2087
|
"function clock() view returns (uint48)",
|
|
1452
|
-
"function proposalVotes(uint256 proposalId) view returns (uint256 againstVotes, uint256 forVotes, uint256 abstainVotes)"
|
|
2088
|
+
"function proposalVotes(uint256 proposalId) view returns (uint256 againstVotes, uint256 forVotes, uint256 abstainVotes)",
|
|
2089
|
+
"function hasVoted(uint256 proposalId, address account) view returns (bool)",
|
|
2090
|
+
"function proposalSnapshot(uint256 proposalId) view returns (uint256)"
|
|
1453
2091
|
]);
|
|
1454
2092
|
function splitAbiParamList(inner) {
|
|
1455
2093
|
const out = [];
|
|
@@ -1701,9 +2339,32 @@ async function buildEvmMultisignBodyContinuumDaoProposeDelta(args) {
|
|
|
1701
2339
|
);
|
|
1702
2340
|
return { ...built, proposalArgs };
|
|
1703
2341
|
}
|
|
2342
|
+
async function requireVoteEligible(args) {
|
|
2343
|
+
const st = await continuumDaoFetchProposalState({
|
|
2344
|
+
chainId: args.chainId,
|
|
2345
|
+
rpcUrl: args.rpcUrl,
|
|
2346
|
+
proposalId: args.proposalId,
|
|
2347
|
+
governor: args.governor
|
|
2348
|
+
});
|
|
2349
|
+
if (st.state !== 1) {
|
|
2350
|
+
throw new Error(`Proposal is not Active (on-chain state ${st.state}).`);
|
|
2351
|
+
}
|
|
2352
|
+
const elig = await continuumDaoFetchProposalVoteEligibility({
|
|
2353
|
+
chainId: args.chainId,
|
|
2354
|
+
rpcUrl: args.rpcUrl,
|
|
2355
|
+
proposalId: args.proposalId,
|
|
2356
|
+
account: args.executorAddress,
|
|
2357
|
+
governor: args.governor
|
|
2358
|
+
});
|
|
2359
|
+
if (elig.hasVoted) throw new Error("This KeyGen has already voted on this proposal.");
|
|
2360
|
+
if (BigInt(elig.snapshotVotes) === 0n) {
|
|
2361
|
+
throw new Error("No veCTM voting power at snapshot for this KeyGen.");
|
|
2362
|
+
}
|
|
2363
|
+
}
|
|
1704
2364
|
async function buildEvmMultisignBodyContinuumDaoCastVoteBravo(args) {
|
|
1705
2365
|
const gov = requireGovernor(args.chainId, args.governor);
|
|
1706
2366
|
const proposalId = BigInt(String(args.proposalId).trim());
|
|
2367
|
+
await requireVoteEligible(args);
|
|
1707
2368
|
const data = viem.encodeFunctionData({
|
|
1708
2369
|
abi: governorWriteAbi,
|
|
1709
2370
|
functionName: "castVote",
|
|
@@ -1722,6 +2383,10 @@ async function buildEvmMultisignBodyContinuumDaoCastVoteBravo(args) {
|
|
|
1722
2383
|
async function buildEvmMultisignBodyContinuumDaoCastVoteDelta(args) {
|
|
1723
2384
|
const gov = requireGovernor(args.chainId, args.governor);
|
|
1724
2385
|
const proposalId = BigInt(String(args.proposalId).trim());
|
|
2386
|
+
if (args.weights.length < 3) {
|
|
2387
|
+
throw new Error("Delta vote must include one weight per option plus a NOTA slot.");
|
|
2388
|
+
}
|
|
2389
|
+
await requireVoteEligible(args);
|
|
1725
2390
|
const params = encodeDeltaVoteParams(args.weights);
|
|
1726
2391
|
const data = viem.encodeFunctionData({
|
|
1727
2392
|
abi: governorWriteAbi,
|
|
@@ -1774,7 +2439,7 @@ async function buildEvmMultisignBodyContinuumDaoCancel(args) {
|
|
|
1774
2439
|
"continuum-dao cancel"
|
|
1775
2440
|
);
|
|
1776
2441
|
}
|
|
1777
|
-
function
|
|
2442
|
+
function publicClient2(rpcUrl, chainId) {
|
|
1778
2443
|
return viem.createPublicClient({
|
|
1779
2444
|
chain: viem.defineChain({
|
|
1780
2445
|
id: chainId,
|
|
@@ -1788,7 +2453,7 @@ function publicClient(rpcUrl, chainId) {
|
|
|
1788
2453
|
async function continuumDaoHashProposal(args) {
|
|
1789
2454
|
const chainId = Number(args.chainId);
|
|
1790
2455
|
const gov = requireGovernor(chainId, args.governor);
|
|
1791
|
-
const client =
|
|
2456
|
+
const client = publicClient2(args.rpcUrl, chainId);
|
|
1792
2457
|
const id = await client.readContract({
|
|
1793
2458
|
address: gov,
|
|
1794
2459
|
abi: governorReadAbi,
|
|
@@ -1800,7 +2465,7 @@ async function continuumDaoHashProposal(args) {
|
|
|
1800
2465
|
async function continuumDaoFetchVotingPower(args) {
|
|
1801
2466
|
const chainId = Number(args.chainId);
|
|
1802
2467
|
const gov = requireGovernor(chainId, args.governor);
|
|
1803
|
-
const client =
|
|
2468
|
+
const client = publicClient2(args.rpcUrl, chainId);
|
|
1804
2469
|
const clock = await client.readContract({ address: gov, abi: governorReadAbi, functionName: "clock" });
|
|
1805
2470
|
const timepoint = clock > 0 ? clock - 1 : 0;
|
|
1806
2471
|
const [votes, threshold] = await Promise.all([
|
|
@@ -1817,22 +2482,74 @@ async function continuumDaoFetchVotingPower(args) {
|
|
|
1817
2482
|
async function continuumDaoFetchProposalState(args) {
|
|
1818
2483
|
const chainId = Number(args.chainId);
|
|
1819
2484
|
const gov = requireGovernor(chainId, args.governor);
|
|
1820
|
-
const client =
|
|
2485
|
+
const client = publicClient2(args.rpcUrl, chainId);
|
|
1821
2486
|
const proposalId = BigInt(String(args.proposalId).trim());
|
|
1822
|
-
const
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
2487
|
+
const state = await client.readContract({
|
|
2488
|
+
address: gov,
|
|
2489
|
+
abi: governorReadAbi,
|
|
2490
|
+
functionName: "state",
|
|
2491
|
+
args: [proposalId]
|
|
2492
|
+
});
|
|
2493
|
+
let againstVotes = "0";
|
|
2494
|
+
let forVotes = "0";
|
|
2495
|
+
let abstainVotes = "0";
|
|
2496
|
+
let votesPartial = false;
|
|
2497
|
+
try {
|
|
2498
|
+
const votes = await client.readContract({
|
|
2499
|
+
address: gov,
|
|
2500
|
+
abi: governorReadAbi,
|
|
2501
|
+
functionName: "proposalVotes",
|
|
2502
|
+
args: [proposalId]
|
|
2503
|
+
});
|
|
2504
|
+
againstVotes = votes[0].toString();
|
|
2505
|
+
forVotes = votes[1].toString();
|
|
2506
|
+
abstainVotes = votes[2].toString();
|
|
2507
|
+
} catch {
|
|
2508
|
+
votesPartial = true;
|
|
2509
|
+
}
|
|
1826
2510
|
return {
|
|
1827
2511
|
state: Number(state),
|
|
1828
|
-
againstVotes
|
|
1829
|
-
forVotes
|
|
1830
|
-
abstainVotes
|
|
2512
|
+
againstVotes,
|
|
2513
|
+
forVotes,
|
|
2514
|
+
abstainVotes,
|
|
2515
|
+
votesPartial
|
|
2516
|
+
};
|
|
2517
|
+
}
|
|
2518
|
+
async function continuumDaoFetchProposalVoteEligibility(args) {
|
|
2519
|
+
const chainId = Number(args.chainId);
|
|
2520
|
+
const gov = requireGovernor(chainId, args.governor);
|
|
2521
|
+
const client = publicClient2(args.rpcUrl, chainId);
|
|
2522
|
+
const proposalId = BigInt(String(args.proposalId).trim());
|
|
2523
|
+
const account = viem.getAddress(args.account);
|
|
2524
|
+
const [hasVoted, snapshotBlock] = await Promise.all([
|
|
2525
|
+
client.readContract({
|
|
2526
|
+
address: gov,
|
|
2527
|
+
abi: governorReadAbi,
|
|
2528
|
+
functionName: "hasVoted",
|
|
2529
|
+
args: [proposalId, account]
|
|
2530
|
+
}),
|
|
2531
|
+
client.readContract({
|
|
2532
|
+
address: gov,
|
|
2533
|
+
abi: governorReadAbi,
|
|
2534
|
+
functionName: "proposalSnapshot",
|
|
2535
|
+
args: [proposalId]
|
|
2536
|
+
})
|
|
2537
|
+
]);
|
|
2538
|
+
const snapshotVotes = await client.readContract({
|
|
2539
|
+
address: gov,
|
|
2540
|
+
abi: governorReadAbi,
|
|
2541
|
+
functionName: "getVotes",
|
|
2542
|
+
args: [account, snapshotBlock]
|
|
2543
|
+
});
|
|
2544
|
+
return {
|
|
2545
|
+
hasVoted,
|
|
2546
|
+
snapshotVotes: snapshotVotes.toString(),
|
|
2547
|
+
snapshotBlock: snapshotBlock.toString()
|
|
1831
2548
|
};
|
|
1832
2549
|
}
|
|
1833
2550
|
async function continuumDaoFetchProposalCount(args) {
|
|
1834
2551
|
const gov = requireGovernor(args.chainId, args.governor);
|
|
1835
|
-
const client =
|
|
2552
|
+
const client = publicClient2(args.rpcUrl, args.chainId);
|
|
1836
2553
|
const count = await client.readContract({ address: gov, abi: governorReadAbi, functionName: "proposalCount" });
|
|
1837
2554
|
return { count: count.toString() };
|
|
1838
2555
|
}
|
|
@@ -2167,7 +2884,7 @@ var continuumDaoProtocolModule = {
|
|
|
2167
2884
|
{ id: "continuum-dao.undelegate", protocolId: CONTINUUM_DAO_PROTOCOL_ID, chainCategory: "evm", description: "Return voting power to the KeyGen (delegate to self)", commonParams: ["keyGen", "purposeText", "useCustomGas"], params: {} },
|
|
2168
2885
|
{ id: "continuum-dao.fetch-delegates", protocolId: CONTINUUM_DAO_PROTOCOL_ID, chainCategory: "evm", description: "Read VotingEscrow.delegates(account)", commonParams: [], params: { account: { type: "string", required: true, description: "KeyGen or wallet address" } } },
|
|
2169
2886
|
{ id: "continuum-dao.transfer", protocolId: CONTINUUM_DAO_PROTOCOL_ID, chainCategory: "evm", description: "Transfer a veCTM NFT", commonParams: ["keyGen", "purposeText", "useCustomGas"], params: { tokenId: { type: "string", required: true, description: "veCTM token id" }, to: { type: "string", required: true, description: "Recipient" } } },
|
|
2170
|
-
{ id: "continuum-dao.attach", protocolId: CONTINUUM_DAO_PROTOCOL_ID, chainCategory: "evm", description: "Attach veCTM to the node (authority KeyGen)", commonParams: ["keyGen", "purposeText", "useCustomGas"], params: { nodeKey: { type: "string", required: true, description: "Node key" }, tokenId: { type: "string", required: true, description: "veCTM token id" }, mpaWallet: { type: "string", required: true, description: "MultiSignAgentWallet address" } } },
|
|
2887
|
+
{ id: "continuum-dao.attach", protocolId: CONTINUUM_DAO_PROTOCOL_ID, chainCategory: "evm", description: "Attach veCTM to the node (authority KeyGen). groupId is implicit from that KeyGen and binds the fee waiver. A second group attaches after rotating authority.", commonParams: ["keyGen", "purposeText", "useCustomGas"], params: { nodeKey: { type: "string", required: true, description: "Node key" }, tokenId: { type: "string", required: true, description: "veCTM token id" }, mpaWallet: { type: "string", required: true, description: "MultiSignAgentWallet address" } } },
|
|
2171
2888
|
{ id: "continuum-dao.request-detach", protocolId: CONTINUUM_DAO_PROTOCOL_ID, chainCategory: "evm", description: "Request governance detach", commonParams: ["keyGen", "purposeText", "useCustomGas"], params: { tokenId: { type: "string", required: true, description: "Attached token id" }, nodeProperties: { type: "string", required: true, description: "NodeProperties address" } } },
|
|
2172
2889
|
{ id: "continuum-dao.propose-bravo", protocolId: CONTINUUM_DAO_PROTOCOL_ID, chainCategory: "evm", description: "Propose a Bravo (For/Against/Abstain) proposal", commonParams: ["keyGen", "purposeText", "useCustomGas"], params: { title: { type: "string", required: true, description: "On-chain description (title)" }, forumKey: { type: "string", required: true, description: "Created forum thread URL from forum_create_topic" } } },
|
|
2173
2890
|
{ id: "continuum-dao.propose-delta", protocolId: CONTINUUM_DAO_PROTOCOL_ID, chainCategory: "evm", description: "Propose a Delta multi-option proposal", commonParams: ["keyGen", "purposeText", "useCustomGas"], params: { title: { type: "string", required: true, description: "On-chain description (title)" }, nWinners: { type: "number", required: true, description: "Winning options to execute" }, forumKey: { type: "string", required: true, description: "Created forum thread URL from forum_create_topic" } } },
|
|
@@ -2217,6 +2934,7 @@ exports.CONTINUUM_DAO_SPLIT_FALLBACK = CONTINUUM_DAO_SPLIT_FALLBACK;
|
|
|
2217
2934
|
exports.CONTINUUM_DAO_TRANSFER_FALLBACK = CONTINUUM_DAO_TRANSFER_FALLBACK;
|
|
2218
2935
|
exports.CONTINUUM_DAO_VOTE_FALLBACK = CONTINUUM_DAO_VOTE_FALLBACK;
|
|
2219
2936
|
exports.CONTINUUM_DAO_WITHDRAW_FALLBACK = CONTINUUM_DAO_WITHDRAW_FALLBACK;
|
|
2937
|
+
exports.CTM_TOKEN_ADDRESS = CTM_TOKEN_ADDRESS;
|
|
2220
2938
|
exports.CTM_TOKEN_DECIMALS = CTM_TOKEN_DECIMALS;
|
|
2221
2939
|
exports.CTM_TOKEN_NAME = CTM_TOKEN_NAME;
|
|
2222
2940
|
exports.CTM_TOKEN_SYMBOL = CTM_TOKEN_SYMBOL;
|
|
@@ -2225,12 +2943,19 @@ exports.FORUM_IDEA_SECTION = FORUM_IDEA_SECTION;
|
|
|
2225
2943
|
exports.FORUM_LOGIN_DELIVERY_KIND = FORUM_LOGIN_DELIVERY_KIND;
|
|
2226
2944
|
exports.GOVERNOR_STATE = GOVERNOR_STATE;
|
|
2227
2945
|
exports.GOVERNOR_STATE_LABELS = GOVERNOR_STATE_LABELS;
|
|
2946
|
+
exports.INCREASE_UNLOCK_TIME_SIGNATURE = INCREASE_UNLOCK_TIME_SIGNATURE;
|
|
2228
2947
|
exports.MAXTIME_SECONDS = MAXTIME_SECONDS;
|
|
2229
2948
|
exports.PROPOSAL_TYPE_LABELS = PROPOSAL_TYPE_LABELS;
|
|
2230
2949
|
exports.PROPOSAL_TYPE_TO_FORUM_SECTION = PROPOSAL_TYPE_TO_FORUM_SECTION;
|
|
2950
|
+
exports.VECTM_DEFAULT_MINIMUM_LOCK_WEI = VECTM_DEFAULT_MINIMUM_LOCK_WEI;
|
|
2951
|
+
exports.VECTM_LIQ_MIN_LOCKED_WEI = VECTM_LIQ_MIN_LOCKED_WEI;
|
|
2952
|
+
exports.VECTM_LIQ_PENALTY_DEN = VECTM_LIQ_PENALTY_DEN;
|
|
2953
|
+
exports.VECTM_LIQ_PENALTY_NUM = VECTM_LIQ_PENALTY_NUM;
|
|
2954
|
+
exports.VECTM_MAX_LOCK_WEEKS = VECTM_MAX_LOCK_WEEKS;
|
|
2231
2955
|
exports.VECTM_NODE_INFO_TUPLE = VECTM_NODE_INFO_TUPLE;
|
|
2232
2956
|
exports.VECTM_TOKEN_NAME = VECTM_TOKEN_NAME;
|
|
2233
2957
|
exports.VECTM_TOKEN_SYMBOL = VECTM_TOKEN_SYMBOL;
|
|
2958
|
+
exports.VOTING_ESCROW_LINEA_ADDRESS = VOTING_ESCROW_LINEA_ADDRESS;
|
|
2234
2959
|
exports.WEEK_SECONDS = WEEK_SECONDS;
|
|
2235
2960
|
exports.applyForumSectionCheck = applyForumSectionCheck;
|
|
2236
2961
|
exports.assertContinuumDaoForumDeleteInput = assertContinuumDaoForumDeleteInput;
|
|
@@ -2238,6 +2963,19 @@ exports.assertContinuumDaoForumMarkReadInput = assertContinuumDaoForumMarkReadIn
|
|
|
2238
2963
|
exports.assertContinuumDaoForumRecentInput = assertContinuumDaoForumRecentInput;
|
|
2239
2964
|
exports.assertContinuumDaoForumSearchInput = assertContinuumDaoForumSearchInput;
|
|
2240
2965
|
exports.assertContinuumDaoForumTopicKey = assertContinuumDaoForumTopicKey;
|
|
2966
|
+
exports.assertCreateLockAmount = assertCreateLockAmount;
|
|
2967
|
+
exports.assertCtmAmountAtLeastMinimum = assertCtmAmountAtLeastMinimum;
|
|
2968
|
+
exports.assertDelegatee = assertDelegatee;
|
|
2969
|
+
exports.assertExecutorOwnsLock = assertExecutorOwnsLock;
|
|
2970
|
+
exports.assertIncreaseAmount = assertIncreaseAmount;
|
|
2971
|
+
exports.assertIncreaseUnlockTime = assertIncreaseUnlockTime;
|
|
2972
|
+
exports.assertLiquidate = assertLiquidate;
|
|
2973
|
+
exports.assertLockDurationSeconds = assertLockDurationSeconds;
|
|
2974
|
+
exports.assertMerge = assertMerge;
|
|
2975
|
+
exports.assertSplitExtracted = assertSplitExtracted;
|
|
2976
|
+
exports.assertTransfer = assertTransfer;
|
|
2977
|
+
exports.assertUndelegateNotAlreadySelf = assertUndelegateNotAlreadySelf;
|
|
2978
|
+
exports.assertWithdraw = assertWithdraw;
|
|
2241
2979
|
exports.bucketGovernorState = bucketGovernorState;
|
|
2242
2980
|
exports.buildBravoProposalArgs = buildBravoProposalArgs;
|
|
2243
2981
|
exports.buildDeltaProposalArgs = buildDeltaProposalArgs;
|
|
@@ -2263,6 +3001,7 @@ exports.buildEvmMultisignBodyContinuumDaoSplit = buildEvmMultisignBodyContinuumD
|
|
|
2263
3001
|
exports.buildEvmMultisignBodyContinuumDaoTransfer = buildEvmMultisignBodyContinuumDaoTransfer;
|
|
2264
3002
|
exports.buildEvmMultisignBodyContinuumDaoUndelegate = buildEvmMultisignBodyContinuumDaoUndelegate;
|
|
2265
3003
|
exports.buildEvmMultisignBodyContinuumDaoWithdraw = buildEvmMultisignBodyContinuumDaoWithdraw;
|
|
3004
|
+
exports.computeLiquidationAmounts = computeLiquidationAmounts;
|
|
2266
3005
|
exports.continuumDaoDeployment = continuumDaoDeployment;
|
|
2267
3006
|
exports.continuumDaoExplainProposal = continuumDaoExplainProposal;
|
|
2268
3007
|
exports.continuumDaoFetchDelegates = continuumDaoFetchDelegates;
|
|
@@ -2270,6 +3009,7 @@ exports.continuumDaoFetchLiveProposals = continuumDaoFetchLiveProposals;
|
|
|
2270
3009
|
exports.continuumDaoFetchProposal = continuumDaoFetchProposal;
|
|
2271
3010
|
exports.continuumDaoFetchProposalCount = continuumDaoFetchProposalCount;
|
|
2272
3011
|
exports.continuumDaoFetchProposalState = continuumDaoFetchProposalState;
|
|
3012
|
+
exports.continuumDaoFetchProposalVoteEligibility = continuumDaoFetchProposalVoteEligibility;
|
|
2273
3013
|
exports.continuumDaoFetchProposals = continuumDaoFetchProposals;
|
|
2274
3014
|
exports.continuumDaoFetchVotingPower = continuumDaoFetchVotingPower;
|
|
2275
3015
|
exports.continuumDaoForumCreateIdea = continuumDaoForumCreateIdea;
|
|
@@ -2302,6 +3042,7 @@ exports.continuumDaoMpaMailboxSend = continuumDaoMpaMailboxSend;
|
|
|
2302
3042
|
exports.continuumDaoMpaRetract = continuumDaoMpaRetract;
|
|
2303
3043
|
exports.continuumDaoProtocolModule = continuumDaoProtocolModule;
|
|
2304
3044
|
exports.continuumDaoRegisterProposal = continuumDaoRegisterProposal;
|
|
3045
|
+
exports.continuumDaoSignatureText = continuumDaoSignatureText;
|
|
2305
3046
|
exports.continuumDaoTechnocoreBind = continuumDaoTechnocoreBind;
|
|
2306
3047
|
exports.ctmTokenAddressOnEvmChain = ctmTokenAddressOnEvmChain;
|
|
2307
3048
|
exports.encodeContinuumDaoNodeInfo = encodeContinuumDaoNodeInfo;
|
|
@@ -2311,6 +3052,7 @@ exports.encodeGovActionCalldata = encodeGovActionCalldata;
|
|
|
2311
3052
|
exports.expectedForumSectionForProposalType = expectedForumSectionForProposalType;
|
|
2312
3053
|
exports.explainGovAction = explainGovAction;
|
|
2313
3054
|
exports.explainProposalRecord = explainProposalRecord;
|
|
3055
|
+
exports.floorUnlockTimeSeconds = floorUnlockTimeSeconds;
|
|
2314
3056
|
exports.governorAddressOnEvmChain = governorAddressOnEvmChain;
|
|
2315
3057
|
exports.isConfiguredAddress = isConfiguredAddress;
|
|
2316
3058
|
exports.isContinuumDaoCtmOnAssetsChain = isContinuumDaoCtmOnAssetsChain;
|
|
@@ -2322,7 +3064,24 @@ exports.isForumIdeaSection = isForumIdeaSection;
|
|
|
2322
3064
|
exports.isGovNoOpAction = isGovNoOpAction;
|
|
2323
3065
|
exports.isVotingEscrowContinuumName = isVotingEscrowContinuumName;
|
|
2324
3066
|
exports.listContinuumDaoDefaultAssets = listContinuumDaoDefaultAssets;
|
|
3067
|
+
exports.maxLockDurationSeconds = maxLockDurationSeconds;
|
|
3068
|
+
exports.preflightAttachVeCtm = preflightAttachVeCtm;
|
|
3069
|
+
exports.preflightCreateLock = preflightCreateLock;
|
|
3070
|
+
exports.preflightDelegate = preflightDelegate;
|
|
3071
|
+
exports.preflightIncreaseAmount = preflightIncreaseAmount;
|
|
3072
|
+
exports.preflightIncreaseUnlockTime = preflightIncreaseUnlockTime;
|
|
3073
|
+
exports.preflightLiquidate = preflightLiquidate;
|
|
3074
|
+
exports.preflightMerge = preflightMerge;
|
|
3075
|
+
exports.preflightRequestDetach = preflightRequestDetach;
|
|
3076
|
+
exports.preflightSplit = preflightSplit;
|
|
3077
|
+
exports.preflightTransfer = preflightTransfer;
|
|
3078
|
+
exports.preflightUndelegate = preflightUndelegate;
|
|
3079
|
+
exports.preflightWithdraw = preflightWithdraw;
|
|
2325
3080
|
exports.proposalDescriptionHash = proposalDescriptionHash;
|
|
3081
|
+
exports.readCtmBalanceWei = readCtmBalanceWei;
|
|
3082
|
+
exports.readMinimumLockWei = readMinimumLockWei;
|
|
3083
|
+
exports.readVeCtmLockState = readVeCtmLockState;
|
|
3084
|
+
exports.resolveAttachGroupId = resolveAttachGroupId;
|
|
2326
3085
|
exports.summarizeLiveProposals = summarizeLiveProposals;
|
|
2327
3086
|
exports.votingEscrowAddressOnEvmChain = votingEscrowAddressOnEvmChain;
|
|
2328
3087
|
//# sourceMappingURL=index.cjs.map
|