@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.
Files changed (53) hide show
  1. package/dist/agent/catalog.cjs +49 -38
  2. package/dist/agent/catalog.cjs.map +1 -1
  3. package/dist/agent/catalog.d.ts +722 -2
  4. package/dist/agent/catalog.js +49 -38
  5. package/dist/agent/catalog.js.map +1 -1
  6. package/dist/agent/skills/continuum-dao/SKILL.md +18 -2
  7. package/dist/{buildBatch-CngvebiD.d.ts → buildBatch-tsEUVn_a.d.ts} +3 -3
  8. package/dist/chains/evm/index.cjs +33 -22
  9. package/dist/chains/evm/index.cjs.map +1 -1
  10. package/dist/chains/evm/index.d.ts +10 -6
  11. package/dist/chains/evm/index.js +33 -22
  12. package/dist/chains/evm/index.js.map +1 -1
  13. package/dist/chains/near/index.d.ts +1 -1
  14. package/dist/chains/solana/index.d.ts +1 -1
  15. package/dist/core/index.d.ts +5 -5
  16. package/dist/{eip712Multisign-DCW7heqX.d.ts → eip712Multisign-6oNIlObj.d.ts} +1 -1
  17. package/dist/{envelope-C_bfuKab.d.ts → envelope-Cm-llShH.d.ts} +1 -1
  18. package/dist/index.cjs +33 -22
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.ts +6 -6
  21. package/dist/index.js +33 -22
  22. package/dist/index.js.map +1 -1
  23. package/dist/protocols/evm/aave-v4/index.d.ts +1 -1
  24. package/dist/protocols/evm/aerodrome/index.d.ts +4 -4
  25. package/dist/protocols/evm/arcus/index.d.ts +2 -2
  26. package/dist/protocols/evm/circle-cctp/index.d.ts +2 -2
  27. package/dist/protocols/evm/compound-v3/index.d.ts +1 -1
  28. package/dist/protocols/evm/continuum-dao/index.cjs +810 -51
  29. package/dist/protocols/evm/continuum-dao/index.cjs.map +1 -1
  30. package/dist/protocols/evm/continuum-dao/index.d.ts +191 -5
  31. package/dist/protocols/evm/continuum-dao/index.js +770 -53
  32. package/dist/protocols/evm/continuum-dao/index.js.map +1 -1
  33. package/dist/protocols/evm/curve-dao/index.d.ts +2 -2
  34. package/dist/protocols/evm/ethena/index.d.ts +1 -1
  35. package/dist/protocols/evm/euler-v2/index.cjs +33 -22
  36. package/dist/protocols/evm/euler-v2/index.cjs.map +1 -1
  37. package/dist/protocols/evm/euler-v2/index.d.ts +1 -1
  38. package/dist/protocols/evm/euler-v2/index.js +33 -22
  39. package/dist/protocols/evm/euler-v2/index.js.map +1 -1
  40. package/dist/protocols/evm/gmx/index.d.ts +2 -2
  41. package/dist/protocols/evm/hyperliquid/index.d.ts +2 -2
  42. package/dist/protocols/evm/lido/index.d.ts +1 -1
  43. package/dist/protocols/evm/maple/index.d.ts +1 -1
  44. package/dist/protocols/evm/morpho/index.d.ts +1 -1
  45. package/dist/protocols/evm/pendle/index.d.ts +4 -4
  46. package/dist/protocols/evm/permit2/index.d.ts +1 -1
  47. package/dist/protocols/evm/sky/index.d.ts +1 -1
  48. package/dist/protocols/evm/uniswap-v4/index.d.ts +3 -3
  49. package/dist/protocols/evm/venice/index.d.ts +1 -1
  50. package/dist/{registry-DxDSPQ-p.d.ts → registry-BvsXh0-r.d.ts} +1 -1
  51. package/dist/{types-RZWOTm8c.d.ts → types-RJpoU4Pc.d.ts} +1 -1
  52. package/dist/{types-PZrvtjv8.d.ts → types-lIfv59N1.d.ts} +2 -0
  53. 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]: { ...UNSET },
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
- ctm: viem.zeroAddress,
65
- votingEscrow: viem.zeroAddress,
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 publicClient2 = viem.createPublicClient({ chain: ch, transport: viem.http(rpcUrl) });
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 publicClient2.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
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 publicClient2.getTransactionCount({ address: executor, blockTag: "pending" });
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: publicClient2, executor });
811
+ estimatedGas = await args.estimateGasForStep({ step, index: i, publicClient: publicClient3, executor });
266
812
  } else {
267
813
  try {
268
- estimatedGas = await publicClient2.estimateGas({
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: publicClient2 });
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 publicClient2.getGasPrice();
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 VECTM_NODE_INFO_TUPLE = "(string,string,uint8[4],uint16[8],string,uint256,uint256,string,string,bytes)";
408
- var ATTACH_VECTM_SIGNATURE = `attachVeCtm(string,uint256,${VECTM_NODE_INFO_TUPLE})`;
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: JSON.stringify({
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: erc20Abi,
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: erc20Abi,
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
- if (duration <= 0n) throw new Error("Unlock duration must be greater than zero.");
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
- if (extracted <= 0n) throw new Error("Split amount must be greater than zero.");
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
- if (fromId === toId) throw new Error("Merge requires two different token ids.");
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 = viem.getAddress(args.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 veReadAbi = viem.parseAbi(["function delegates(address account) view returns (address)"]);
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: veReadAbi, functionName: "delegates", args: [account] })
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 publicClient(rpcUrl, chainId) {
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 = publicClient(args.rpcUrl, chainId);
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 = publicClient(args.rpcUrl, chainId);
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 = publicClient(args.rpcUrl, chainId);
2485
+ const client = publicClient2(args.rpcUrl, chainId);
1821
2486
  const proposalId = BigInt(String(args.proposalId).trim());
1822
- const [state, votes] = await Promise.all([
1823
- client.readContract({ address: gov, abi: governorReadAbi, functionName: "state", args: [proposalId] }),
1824
- client.readContract({ address: gov, abi: governorReadAbi, functionName: "proposalVotes", args: [proposalId] })
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: votes[0].toString(),
1829
- forVotes: votes[1].toString(),
1830
- abstainVotes: votes[2].toString()
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 = publicClient(args.rpcUrl, args.chainId);
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