@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
@@ -1,4 +1,4 @@
1
- import { parseAbi, zeroAddress, getAddress, parseUnits, encodeFunctionData, createPublicClient, http, defineChain, keccak256, encodePacked, padHex, toHex, hashTypedData, parseGwei, serializeTransaction, stringToHex } from 'viem';
1
+ import { getAddress, parseAbi, zeroAddress, parseUnits, encodeFunctionData, createPublicClient, http, defineChain, keccak256, encodePacked, padHex, toHex, hashTypedData, parseGwei, serializeTransaction, stringToHex } from 'viem';
2
2
  import { fetchChainFeeParams, gasLimitFromEstimateAndChainConfig, gweiToDecimalString, proposalTxParamsToFeeSnapshot, alignEip1559FeesWithLatestBase, getClientIdFromKeyGenResult } from '@continuumdao/continuum-node-sdk';
3
3
 
4
4
  // src/core/registry.ts
@@ -48,6 +48,11 @@ var VECTM_TOKEN_SYMBOL = "veCTM";
48
48
  var CTM_TOKEN_DECIMALS = 18;
49
49
  var WEEK_SECONDS = 7 * 86400;
50
50
  var MAXTIME_SECONDS = 4 * 365 * 86400;
51
+ var VECTM_MAX_LOCK_WEEKS = 208;
52
+ var VECTM_DEFAULT_MINIMUM_LOCK_WEI = 10n ** 18n;
53
+ var VECTM_LIQ_MIN_LOCKED_WEI = 100n;
54
+ var VECTM_LIQ_PENALTY_NUM = 50000n;
55
+ var VECTM_LIQ_PENALTY_DEN = 100000n;
51
56
  var UNSET = {
52
57
  ctm: zeroAddress,
53
58
  votingEscrow: zeroAddress,
@@ -55,15 +60,18 @@ var UNSET = {
55
60
  rewards: zeroAddress,
56
61
  governor: zeroAddress
57
62
  };
63
+ var CTM_TOKEN_ADDRESS = getAddress("0x2026027054F5beBCEB650aBD62dC623e327658e7");
64
+ var VOTING_ESCROW_LINEA_ADDRESS = getAddress("0x221EC90B3B083A8501A37bdeb7035CeaedF3C31f");
58
65
  var CONTINUUM_DAO_DEPLOYMENTS = {
59
- [CONTINUUM_DAO_LINEA_CHAIN_ID]: { ...UNSET },
66
+ [CONTINUUM_DAO_LINEA_CHAIN_ID]: {
67
+ ...UNSET,
68
+ ctm: CTM_TOKEN_ADDRESS,
69
+ votingEscrow: VOTING_ESCROW_LINEA_ADDRESS
70
+ },
60
71
  [CONTINUUM_DAO_LINEA_SEPOLIA_CHAIN_ID]: { ...UNSET },
61
72
  [CONTINUUM_DAO_ETHEREUM_CHAIN_ID]: {
62
- ctm: zeroAddress,
63
- votingEscrow: zeroAddress,
64
- nodeProperties: zeroAddress,
65
- rewards: zeroAddress,
66
- governor: zeroAddress
73
+ ...UNSET,
74
+ ctm: CTM_TOKEN_ADDRESS
67
75
  }
68
76
  };
69
77
  var CONTINUUM_DAO_GOVERNANCE_API = "https://app-api.continuumdao.org";
@@ -154,6 +162,544 @@ function listContinuumDaoDefaultAssets() {
154
162
  }
155
163
  return out;
156
164
  }
165
+ function maxLockDurationSeconds() {
166
+ return Math.min(VECTM_MAX_LOCK_WEEKS * WEEK_SECONDS, MAXTIME_SECONDS);
167
+ }
168
+ function floorUnlockTimeSeconds(nowSec, durationSec) {
169
+ return (BigInt(nowSec) + durationSec) / BigInt(WEEK_SECONDS) * BigInt(WEEK_SECONDS);
170
+ }
171
+ function assertCtmAmountAtLeastMinimum(amountWei, minimumLockWei, label) {
172
+ const min = minimumLockWei > 0n ? minimumLockWei : VECTM_DEFAULT_MINIMUM_LOCK_WEI;
173
+ if (amountWei < min) {
174
+ throw new Error(`${label} must be at least ${formatCtmWei(min)} CTM (VotingEscrow.minimumLock).`);
175
+ }
176
+ }
177
+ function assertCreateLockAmount(amountWei, minimumLockWei, balanceWei) {
178
+ if (amountWei <= 0n) throw new Error("CTM amount must be greater than zero.");
179
+ assertCtmAmountAtLeastMinimum(amountWei, minimumLockWei, "Create lock");
180
+ if (balanceWei != null && amountWei > balanceWei) {
181
+ throw new Error(`Create lock ${formatCtmWei(amountWei)} CTM exceeds KeyGen balance ${formatCtmWei(balanceWei)} CTM.`);
182
+ }
183
+ }
184
+ function assertLockDurationSeconds(durationSec, nowSec, label) {
185
+ if (durationSec <= 0n) throw new Error(`${label} duration must be greater than zero.`);
186
+ if (durationSec < BigInt(WEEK_SECONDS)) {
187
+ throw new Error(`${label} duration must be at least 1 week (${WEEK_SECONDS} seconds).`);
188
+ }
189
+ if (durationSec > BigInt(maxLockDurationSeconds())) {
190
+ throw new Error(
191
+ `${label} duration cannot exceed ${VECTM_MAX_LOCK_WEEKS} weeks (VotingEscrow MAXTIME, week-aligned).`
192
+ );
193
+ }
194
+ const unlock = floorUnlockTimeSeconds(nowSec, durationSec);
195
+ if (unlock <= BigInt(nowSec)) {
196
+ throw new Error(`${label} unlock time must be after the current week boundary.`);
197
+ }
198
+ if (unlock > BigInt(nowSec) + BigInt(MAXTIME_SECONDS)) {
199
+ throw new Error(`${label} unlock time exceeds VotingEscrow MAXTIME (4 years).`);
200
+ }
201
+ return unlock;
202
+ }
203
+ function assertIncreaseAmount(amountWei, lock, nowSec, balanceWei) {
204
+ if (amountWei <= 0n) throw new Error("CTM amount must be greater than zero.");
205
+ if (lock.amountWei === 0n) throw new Error(`veCTM #${lock.tokenId} has no existing lock.`);
206
+ if (lock.endSec <= BigInt(nowSec)) {
207
+ throw new Error(`Cannot increase amount on expired veCTM #${lock.tokenId}. Withdraw or create a new lock.`);
208
+ }
209
+ if (balanceWei != null && amountWei > balanceWei) {
210
+ throw new Error(
211
+ `Increase amount ${formatCtmWei(amountWei)} CTM exceeds KeyGen balance ${formatCtmWei(balanceWei)} CTM.`
212
+ );
213
+ }
214
+ }
215
+ function assertIncreaseUnlockTime(durationSec, lock, nowSec) {
216
+ if (lock.amountWei === 0n) throw new Error(`veCTM #${lock.tokenId} has no existing lock.`);
217
+ if (lock.endSec <= BigInt(nowSec)) {
218
+ throw new Error(`Cannot increase unlock time on expired veCTM #${lock.tokenId}.`);
219
+ }
220
+ const unlock = assertLockDurationSeconds(durationSec, nowSec, "Increase unlock time");
221
+ if (unlock <= lock.endSec) {
222
+ throw new Error(
223
+ `New unlock time must be after the current lock end (${lock.endSec.toString()}). Pick a later week-aligned date.`
224
+ );
225
+ }
226
+ return unlock;
227
+ }
228
+ function assertSplitExtracted(extractedWei, lock, minimumLockWei, nowSec) {
229
+ if (lock.attached) {
230
+ throw new Error(`Split is blocked while veCTM #${lock.tokenId} is attached to a node. Request detach first.`);
231
+ }
232
+ if (lock.amountWei === 0n) throw new Error(`veCTM #${lock.tokenId} has no existing lock.`);
233
+ if (lock.endSec <= BigInt(nowSec)) {
234
+ throw new Error(`Cannot split expired veCTM #${lock.tokenId}.`);
235
+ }
236
+ const min = minimumLockWei > 0n ? minimumLockWei : VECTM_DEFAULT_MINIMUM_LOCK_WEI;
237
+ if (lock.amountWei < min * 2n) {
238
+ throw new Error("Split requires at least 2 CTM locked (1 CTM minimum per resulting lock).");
239
+ }
240
+ if (extractedWei < min) {
241
+ throw new Error(`Extract at least ${formatCtmWei(min)} CTM into the new lock.`);
242
+ }
243
+ if (extractedWei > lock.amountWei - min) {
244
+ throw new Error(`The original lock must keep at least ${formatCtmWei(min)} CTM after the split.`);
245
+ }
246
+ }
247
+ function assertMerge(fromLock, toLock) {
248
+ if (fromLock.tokenId === toLock.tokenId) throw new Error("Merge requires two different token ids.");
249
+ if (fromLock.attached) {
250
+ throw new Error(`Merge is blocked while veCTM #${fromLock.tokenId} is attached to a node.`);
251
+ }
252
+ if (toLock.attached) {
253
+ throw new Error(`Merge is blocked while veCTM #${toLock.tokenId} is attached to a node.`);
254
+ }
255
+ if (fromLock.owner.toLowerCase() !== toLock.owner.toLowerCase()) {
256
+ throw new Error("Merge requires both veCTM locks to have the same owner.");
257
+ }
258
+ }
259
+ function assertWithdraw(lock, nowSec) {
260
+ if (lock.attached) {
261
+ throw new Error(`Withdraw is blocked while veCTM #${lock.tokenId} is attached to a node. Request detach first.`);
262
+ }
263
+ if (lock.endSec <= 0n) throw new Error(`Unlock time unavailable for veCTM #${lock.tokenId}.`);
264
+ if (lock.endSec > BigInt(nowSec)) {
265
+ throw new Error(`Withdraw is available after the lock expires (unlock ${lock.endSec.toString()}).`);
266
+ }
267
+ }
268
+ function computeLiquidationAmounts(args) {
269
+ if (args.lockEndSec > 0n && BigInt(args.nowSec) >= args.lockEndSec) {
270
+ return {
271
+ userWei: args.lockedAmountWei,
272
+ treasuryWei: 0n,
273
+ lockExpired: true,
274
+ tooSmall: false,
275
+ zeroPenalty: false
276
+ };
277
+ }
278
+ if (args.lockedAmountWei <= VECTM_LIQ_MIN_LOCKED_WEI) {
279
+ return {
280
+ userWei: 0n,
281
+ treasuryWei: 0n,
282
+ lockExpired: false,
283
+ tooSmall: true,
284
+ zeroPenalty: false
285
+ };
286
+ }
287
+ const penalty = args.vePowerWei * VECTM_LIQ_PENALTY_NUM / VECTM_LIQ_PENALTY_DEN;
288
+ if (penalty === 0n) {
289
+ return {
290
+ userWei: 0n,
291
+ treasuryWei: 0n,
292
+ lockExpired: false,
293
+ tooSmall: false,
294
+ zeroPenalty: true
295
+ };
296
+ }
297
+ return {
298
+ userWei: args.lockedAmountWei - penalty,
299
+ treasuryWei: penalty,
300
+ lockExpired: false,
301
+ tooSmall: false,
302
+ zeroPenalty: false
303
+ };
304
+ }
305
+ function assertLiquidate(lock, liquidationsEnabled, nowSec) {
306
+ if (lock.attached) {
307
+ throw new Error(`Liquidate is blocked while veCTM #${lock.tokenId} is attached to a node. Request detach first.`);
308
+ }
309
+ if (liquidationsEnabled === false) {
310
+ throw new Error("Liquidations are currently disabled on-chain.");
311
+ }
312
+ const preview = computeLiquidationAmounts({
313
+ lockedAmountWei: lock.amountWei,
314
+ vePowerWei: lock.vePowerWei,
315
+ lockEndSec: lock.endSec,
316
+ nowSec
317
+ });
318
+ if (preview.tooSmall) throw new Error("Locked amount is too small to liquidate.");
319
+ if (preview.zeroPenalty) throw new Error("Voting power is too low to compute a liquidation penalty.");
320
+ }
321
+ function assertDelegatee(delegateeRaw, executor) {
322
+ const delegatee = getAddress(delegateeRaw);
323
+ if (delegatee === zeroAddress) {
324
+ throw new Error("Zero address is not allowed. Use undelegate to return power to this KeyGen.");
325
+ }
326
+ const self = getAddress(executor);
327
+ if (delegatee === self) {
328
+ throw new Error("This is this KeyGen\u2019s address \u2014 use undelegate instead.");
329
+ }
330
+ return delegatee;
331
+ }
332
+ function assertUndelegateNotAlreadySelf(alreadySelf, account) {
333
+ if (alreadySelf) {
334
+ throw new Error(`Voting power is already at this KeyGen (${getAddress(account)}).`);
335
+ }
336
+ }
337
+ function assertTransfer(lock, toRaw, fromRaw) {
338
+ if (lock.attached) {
339
+ throw new Error(`Transfer is blocked while veCTM #${lock.tokenId} is attached to a node.`);
340
+ }
341
+ const to = getAddress(toRaw);
342
+ const from = getAddress(fromRaw);
343
+ if (to === zeroAddress) throw new Error("Cannot transfer veCTM to the zero address.");
344
+ if (to === from) throw new Error("Transfer recipient must be different from this KeyGen.");
345
+ return to;
346
+ }
347
+ function assertExecutorOwnsLock(lock, executor) {
348
+ if (lock.owner.toLowerCase() !== getAddress(executor).toLowerCase()) {
349
+ throw new Error(`KeyGen ${getAddress(executor)} does not own veCTM #${lock.tokenId} (owner ${lock.owner}).`);
350
+ }
351
+ }
352
+ function formatCtmWei(wei) {
353
+ const whole = wei / 10n ** 18n;
354
+ const frac = wei % 10n ** 18n;
355
+ if (frac === 0n) return whole.toString();
356
+ const fracStr = frac.toString().padStart(18, "0").replace(/0+$/, "");
357
+ return `${whole.toString()}.${fracStr}`;
358
+ }
359
+ var veReadAbi = parseAbi([
360
+ "function minimumLock() view returns (uint256)",
361
+ "function locked(uint256 tokenId) view returns (int128 amount, uint256 end)",
362
+ "function ownerOf(uint256 tokenId) view returns (address)",
363
+ "function balanceOfNFT(uint256 tokenId) view returns (uint256)",
364
+ "function liquidationsEnabled() view returns (bool)",
365
+ "function nodeProperties() view returns (address)",
366
+ "function rewards() view returns (address)",
367
+ "function delegates(address account) view returns (address)",
368
+ "function token() view returns (address)"
369
+ ]);
370
+ var erc20Abi = parseAbi(["function balanceOf(address owner) view returns (uint256)"]);
371
+ var nodePropsAbi = parseAbi([
372
+ "function attachedKeyGen(uint256 tokenId) view returns (address)",
373
+ "function attachedTokenId(address keyGen) view returns (uint256)",
374
+ "function nodeRequestingDetachment(uint256 tokenId) view returns (bool)"
375
+ ]);
376
+ var rewardsAbi = parseAbi(["function unclaimedRewards(uint256 tokenId) view returns (uint256)"]);
377
+ var mpaReadAbi = parseAbi([
378
+ "function getNodeWithdrawAuthority(string nodeKey) view returns (address)",
379
+ "function ve() view returns (address)",
380
+ "function veCtmThresholdPower() view returns (uint256)",
381
+ "function nodeProperties() view returns (address)"
382
+ ]);
383
+ function publicClient(rpcUrl, chainId) {
384
+ return createPublicClient({
385
+ chain: defineChain({
386
+ id: chainId,
387
+ name: "ContinuumDAO escrow",
388
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
389
+ rpcUrls: { default: { http: [rpcUrl.trim()] } }
390
+ }),
391
+ transport: http(rpcUrl.trim())
392
+ });
393
+ }
394
+ async function readMinimumLockWei(args) {
395
+ const client = publicClient(args.rpcUrl, args.chainId);
396
+ try {
397
+ const min = await client.readContract({
398
+ address: args.votingEscrow,
399
+ abi: veReadAbi,
400
+ functionName: "minimumLock"
401
+ });
402
+ return min > 0n ? min : VECTM_DEFAULT_MINIMUM_LOCK_WEI;
403
+ } catch {
404
+ return VECTM_DEFAULT_MINIMUM_LOCK_WEI;
405
+ }
406
+ }
407
+ async function readCtmBalanceWei(args) {
408
+ const client = publicClient(args.rpcUrl, args.chainId);
409
+ try {
410
+ return await client.readContract({
411
+ address: args.ctm,
412
+ abi: erc20Abi,
413
+ functionName: "balanceOf",
414
+ args: [args.owner]
415
+ });
416
+ } catch {
417
+ return void 0;
418
+ }
419
+ }
420
+ async function readVeCtmLockState(args) {
421
+ const client = publicClient(args.rpcUrl, args.chainId);
422
+ const [lockedPair, owner, vePower, nodeProperties] = await Promise.all([
423
+ client.readContract({
424
+ address: args.votingEscrow,
425
+ abi: veReadAbi,
426
+ functionName: "locked",
427
+ args: [args.tokenId]
428
+ }),
429
+ client.readContract({
430
+ address: args.votingEscrow,
431
+ abi: veReadAbi,
432
+ functionName: "ownerOf",
433
+ args: [args.tokenId]
434
+ }),
435
+ client.readContract({
436
+ address: args.votingEscrow,
437
+ abi: veReadAbi,
438
+ functionName: "balanceOfNFT",
439
+ args: [args.tokenId]
440
+ }),
441
+ client.readContract({
442
+ address: args.votingEscrow,
443
+ abi: veReadAbi,
444
+ functionName: "nodeProperties"
445
+ })
446
+ ]);
447
+ const amountRaw = lockedPair[0];
448
+ const amountWei = amountRaw < 0n ? 0n : BigInt(amountRaw);
449
+ let attached = false;
450
+ if (isConfiguredAddress(nodeProperties)) {
451
+ try {
452
+ const attachedKey = await client.readContract({
453
+ address: getAddress(nodeProperties),
454
+ abi: nodePropsAbi,
455
+ functionName: "attachedKeyGen",
456
+ args: [args.tokenId]
457
+ });
458
+ attached = attachedKey !== zeroAddress;
459
+ } catch {
460
+ attached = false;
461
+ }
462
+ }
463
+ return {
464
+ tokenId: args.tokenId,
465
+ amountWei,
466
+ endSec: lockedPair[1],
467
+ owner: getAddress(owner),
468
+ attached,
469
+ vePowerWei: vePower
470
+ };
471
+ }
472
+ async function assertNoUnclaimedRewards(args) {
473
+ const client = publicClient(args.rpcUrl, args.chainId);
474
+ let rewards;
475
+ try {
476
+ rewards = await client.readContract({
477
+ address: args.votingEscrow,
478
+ abi: veReadAbi,
479
+ functionName: "rewards"
480
+ });
481
+ } catch {
482
+ return;
483
+ }
484
+ if (!isConfiguredAddress(rewards)) return;
485
+ try {
486
+ const unclaimed = await client.readContract({
487
+ address: rewards,
488
+ abi: rewardsAbi,
489
+ functionName: "unclaimedRewards",
490
+ args: [args.tokenId]
491
+ });
492
+ if (unclaimed !== 0n) {
493
+ throw new Error(`veCTM #${args.tokenId} has unclaimed rewards. Claim them before this escrow action.`);
494
+ }
495
+ } catch (err) {
496
+ if (err instanceof Error && err.message.includes("unclaimed rewards")) throw err;
497
+ }
498
+ }
499
+ async function preflightCreateLock(args) {
500
+ const nowSec = Math.floor(Date.now() / 1e3);
501
+ const [minimumLockWei, balanceWei] = await Promise.all([
502
+ readMinimumLockWei(args),
503
+ readCtmBalanceWei(args)
504
+ ]);
505
+ assertCreateLockAmount(args.amountWei, minimumLockWei, balanceWei);
506
+ assertLockDurationSeconds(args.durationSec, nowSec, "Create lock");
507
+ }
508
+ async function preflightIncreaseAmount(args) {
509
+ const nowSec = Math.floor(Date.now() / 1e3);
510
+ const [lock, balanceWei] = await Promise.all([
511
+ readVeCtmLockState(args),
512
+ readCtmBalanceWei(args)
513
+ ]);
514
+ assertExecutorOwnsLock(lock, args.owner);
515
+ assertIncreaseAmount(args.amountWei, lock, nowSec, balanceWei);
516
+ await assertNoUnclaimedRewards(args);
517
+ }
518
+ async function preflightIncreaseUnlockTime(args) {
519
+ const nowSec = Math.floor(Date.now() / 1e3);
520
+ const lock = await readVeCtmLockState(args);
521
+ assertExecutorOwnsLock(lock, args.owner);
522
+ assertIncreaseUnlockTime(args.durationSec, lock, nowSec);
523
+ await assertNoUnclaimedRewards(args);
524
+ }
525
+ async function preflightSplit(args) {
526
+ const nowSec = Math.floor(Date.now() / 1e3);
527
+ const [lock, minimumLockWei] = await Promise.all([
528
+ readVeCtmLockState(args),
529
+ readMinimumLockWei(args)
530
+ ]);
531
+ assertExecutorOwnsLock(lock, args.owner);
532
+ assertSplitExtracted(args.extractedWei, lock, minimumLockWei, nowSec);
533
+ await assertNoUnclaimedRewards(args);
534
+ }
535
+ async function preflightMerge(args) {
536
+ const [fromLock, toLock] = await Promise.all([
537
+ readVeCtmLockState({ ...args, tokenId: args.fromTokenId }),
538
+ readVeCtmLockState({ ...args, tokenId: args.toTokenId })
539
+ ]);
540
+ assertExecutorOwnsLock(fromLock, args.owner);
541
+ assertExecutorOwnsLock(toLock, args.owner);
542
+ assertMerge(fromLock, toLock);
543
+ await Promise.all([
544
+ assertNoUnclaimedRewards({ ...args, tokenId: args.fromTokenId }),
545
+ assertNoUnclaimedRewards({ ...args, tokenId: args.toTokenId })
546
+ ]);
547
+ }
548
+ async function preflightWithdraw(args) {
549
+ const nowSec = Math.floor(Date.now() / 1e3);
550
+ const lock = await readVeCtmLockState(args);
551
+ assertExecutorOwnsLock(lock, args.owner);
552
+ assertWithdraw(lock, nowSec);
553
+ await assertNoUnclaimedRewards(args);
554
+ }
555
+ async function preflightLiquidate(args) {
556
+ const nowSec = Math.floor(Date.now() / 1e3);
557
+ const client = publicClient(args.rpcUrl, args.chainId);
558
+ const [lock, liquidationsEnabled] = await Promise.all([
559
+ readVeCtmLockState(args),
560
+ client.readContract({
561
+ address: args.votingEscrow,
562
+ abi: veReadAbi,
563
+ functionName: "liquidationsEnabled"
564
+ }).catch(() => null)
565
+ ]);
566
+ assertExecutorOwnsLock(lock, args.owner);
567
+ assertLiquidate(lock, liquidationsEnabled, nowSec);
568
+ await assertNoUnclaimedRewards(args);
569
+ }
570
+ async function preflightDelegate(args) {
571
+ return assertDelegatee(args.delegatee, args.owner);
572
+ }
573
+ async function preflightUndelegate(args) {
574
+ const client = publicClient(args.rpcUrl, args.chainId);
575
+ const delegatee = await client.readContract({
576
+ address: args.votingEscrow,
577
+ abi: veReadAbi,
578
+ functionName: "delegates",
579
+ args: [args.owner]
580
+ });
581
+ assertUndelegateNotAlreadySelf(getAddress(delegatee) === getAddress(args.owner), args.owner);
582
+ }
583
+ async function preflightTransfer(args) {
584
+ const lock = await readVeCtmLockState(args);
585
+ assertExecutorOwnsLock(lock, args.owner);
586
+ return assertTransfer(lock, args.to, args.owner);
587
+ }
588
+ async function preflightAttachVeCtm(args) {
589
+ const client = publicClient(args.rpcUrl, args.chainId);
590
+ const [authority, veFromWallet, threshold, nodeProperties] = await Promise.all([
591
+ client.readContract({
592
+ address: args.mpaWallet,
593
+ abi: mpaReadAbi,
594
+ functionName: "getNodeWithdrawAuthority",
595
+ args: [args.nodeKey]
596
+ }),
597
+ client.readContract({ address: args.mpaWallet, abi: mpaReadAbi, functionName: "ve" }),
598
+ client.readContract({
599
+ address: args.mpaWallet,
600
+ abi: mpaReadAbi,
601
+ functionName: "veCtmThresholdPower"
602
+ }),
603
+ client.readContract({
604
+ address: args.mpaWallet,
605
+ abi: mpaReadAbi,
606
+ functionName: "nodeProperties"
607
+ })
608
+ ]);
609
+ const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? getAddress(args.votingEscrow) : getAddress(veFromWallet);
610
+ if (!isConfiguredAddress(ve)) throw new Error("veCTM is not live on the fee contract yet.");
611
+ const lock = await readVeCtmLockState({
612
+ rpcUrl: args.rpcUrl,
613
+ chainId: args.chainId,
614
+ votingEscrow: ve,
615
+ tokenId: args.tokenId
616
+ });
617
+ assertExecutorOwnsLock(lock, args.owner);
618
+ const executor = getAddress(args.owner);
619
+ if (authority === zeroAddress || getAddress(authority) !== executor) {
620
+ throw new Error(
621
+ `Compose attachVeCtm from the claimed authority KeyGen (${authority}), which must own the NFT.`
622
+ );
623
+ }
624
+ if (isConfiguredAddress(nodeProperties)) {
625
+ const np = getAddress(nodeProperties);
626
+ const [attachedKey, attachedForKey] = await Promise.all([
627
+ client.readContract({
628
+ address: np,
629
+ abi: nodePropsAbi,
630
+ functionName: "attachedKeyGen",
631
+ args: [args.tokenId]
632
+ }),
633
+ client.readContract({
634
+ address: np,
635
+ abi: nodePropsAbi,
636
+ functionName: "attachedTokenId",
637
+ args: [executor]
638
+ })
639
+ ]);
640
+ if (attachedKey !== zeroAddress) {
641
+ throw new Error(`veCTM #${args.tokenId} is already attached. You cannot replace an attached NFT.`);
642
+ }
643
+ if (attachedForKey !== 0n) {
644
+ throw new Error(
645
+ `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.`
646
+ );
647
+ }
648
+ }
649
+ if (threshold > 0n && lock.amountWei < threshold) {
650
+ throw new Error(
651
+ `Locked CTM on veCTM #${args.tokenId} is below MultiSignAgentWallet.veCtmThresholdPower (${threshold.toString()} wei).`
652
+ );
653
+ }
654
+ }
655
+ async function preflightRequestDetach(args) {
656
+ const client = publicClient(args.rpcUrl, args.chainId);
657
+ const [attachedKey, alreadyRequested] = await Promise.all([
658
+ client.readContract({
659
+ address: args.nodeProperties,
660
+ abi: nodePropsAbi,
661
+ functionName: "attachedKeyGen",
662
+ args: [args.tokenId]
663
+ }),
664
+ client.readContract({
665
+ address: args.nodeProperties,
666
+ abi: nodePropsAbi,
667
+ functionName: "nodeRequestingDetachment",
668
+ args: [args.tokenId]
669
+ })
670
+ ]);
671
+ if (attachedKey === zeroAddress) {
672
+ throw new Error(`veCTM #${args.tokenId} is not attached to a node.`);
673
+ }
674
+ if (alreadyRequested) {
675
+ throw new Error(
676
+ `Detach already requested for veCTM #${args.tokenId}. The NFT stays attached until governance executes detach.`
677
+ );
678
+ }
679
+ if (getAddress(attachedKey) !== getAddress(args.owner)) {
680
+ throw new Error(
681
+ `Request detach from the attached KeyGen (${getAddress(attachedKey)}).`
682
+ );
683
+ }
684
+ }
685
+
686
+ // src/protocols/evm/continuum-dao/attachGroup.ts
687
+ var VECTM_NODE_INFO_TUPLE = "(string,string,uint8[4],uint16[8],string,uint256,uint256,string,string,bytes)";
688
+ var ATTACH_VECTM_SIGNATURE = `attachVeCtm(string,uint256,${VECTM_NODE_INFO_TUPLE},string)`;
689
+ function resolveAttachGroupId(keyGen, explicit) {
690
+ const fromKg = (keyGen.groupId ?? keyGen.GroupId ?? "").trim();
691
+ const fromArg = (explicit ?? "").trim();
692
+ if (fromKg && fromArg && fromKg !== fromArg) {
693
+ throw new Error(
694
+ `groupId must be the attaching KeyGen's mpc-auth GroupId (${fromKg}), not a different group.`
695
+ );
696
+ }
697
+ if (fromKg) return fromKg;
698
+ if (fromArg) return fromArg;
699
+ throw new Error(
700
+ "groupId is required to attach veCTM (use the attaching KeyGen's mpc-auth GroupId)."
701
+ );
702
+ }
157
703
 
158
704
  // src/core/purpose.ts
159
705
  function mergePurposeText(purposeText, purposeSuffix) {
@@ -245,25 +791,25 @@ async function buildEvmMultisignBatch(args) {
245
791
  nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
246
792
  rpcUrls: { default: { http: [rpcUrl] } }
247
793
  });
248
- const publicClient2 = createPublicClient({ chain: ch, transport: http(rpcUrl) });
794
+ const publicClient3 = createPublicClient({ chain: ch, transport: http(rpcUrl) });
249
795
  const feeParams = await fetchChainFeeParams(rpcUrl, chainId);
250
796
  const legacy = Boolean(chainDetail?.legacy) || !feeParams.isEip1559;
251
- const latestBaseFeeWei = !legacy ? (await publicClient2.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
797
+ const latestBaseFeeWei = !legacy ? (await publicClient3.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
252
798
  const gasLimitConfig = useCustomGas && chainDetail?.gasLimit != null ? Number(chainDetail.gasLimit) : void 0;
253
799
  const chainGasLimitRouter = chainDetail?.gasLimit != null && Number.isFinite(Number(chainDetail.gasLimit)) && Number(chainDetail.gasLimit) > 0 ? Number(chainDetail.gasLimit) : void 0;
254
800
  const gasFeeMultiplier = useCustomGas && chainDetail?.gasMultiplier != null ? Number(chainDetail.gasMultiplier) : void 0;
255
801
  const executor = getAddress(executorAddress);
256
- const baseNonce = await publicClient2.getTransactionCount({ address: executor, blockTag: "pending" });
802
+ const baseNonce = await publicClient3.getTransactionCount({ address: executor, blockTag: "pending" });
257
803
  const legs = [];
258
804
  for (let i = 0; i < steps.length; i++) {
259
805
  const step = steps[i];
260
806
  const currentNonce = baseNonce + i;
261
807
  let estimatedGas;
262
808
  if (args.estimateGasForStep) {
263
- estimatedGas = await args.estimateGasForStep({ step, index: i, publicClient: publicClient2, executor });
809
+ estimatedGas = await args.estimateGasForStep({ step, index: i, publicClient: publicClient3, executor });
264
810
  } else {
265
811
  try {
266
- estimatedGas = await publicClient2.estimateGas({
812
+ estimatedGas = await publicClient3.estimateGas({
267
813
  to: step.to,
268
814
  data: step.data,
269
815
  value: step.value,
@@ -275,7 +821,7 @@ async function buildEvmMultisignBatch(args) {
275
821
  }
276
822
  let gasLimitI;
277
823
  if (args.resolveGasLimit) {
278
- gasLimitI = await args.resolveGasLimit({ step, index: i, estimatedGas, publicClient: publicClient2 });
824
+ gasLimitI = await args.resolveGasLimit({ step, index: i, estimatedGas, publicClient: publicClient3 });
279
825
  } else if (step.routerSwap) {
280
826
  gasLimitI = routerSwapGasLimitFromEstimate(estimatedGas, chainGasLimitRouter);
281
827
  } else {
@@ -285,7 +831,7 @@ async function buildEvmMultisignBatch(args) {
285
831
  let feeSnapshot;
286
832
  let serialized;
287
833
  if (legacy) {
288
- let gasPriceWei = await publicClient2.getGasPrice();
834
+ let gasPriceWei = await publicClient3.getGasPrice();
289
835
  if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
290
836
  gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
291
837
  }
@@ -402,9 +948,8 @@ var CONTINUUM_DAO_DELEGATE_FALLBACK = 160000n;
402
948
  var CONTINUUM_DAO_TRANSFER_FALLBACK = 160000n;
403
949
  var CONTINUUM_DAO_ATTACH_FALLBACK = 450000n;
404
950
  var CONTINUUM_DAO_DETACH_FALLBACK = 220000n;
405
- var VECTM_NODE_INFO_TUPLE = "(string,string,uint8[4],uint16[8],string,uint256,uint256,string,string,bytes)";
406
- var ATTACH_VECTM_SIGNATURE = `attachVeCtm(string,uint256,${VECTM_NODE_INFO_TUPLE})`;
407
- var erc20Abi = parseAbi([
951
+ var INCREASE_UNLOCK_TIME_SIGNATURE = "increase_unlock_time(uint256,uint256)";
952
+ var erc20Abi2 = parseAbi([
408
953
  "function allowance(address owner, address spender) view returns (uint256)",
409
954
  "function approve(address spender, uint256 amount) returns (bool)"
410
955
  ]);
@@ -420,7 +965,7 @@ var veWriteAbi = parseAbi([
420
965
  "function transferFrom(address _from, address _to, uint256 _tokenId)"
421
966
  ]);
422
967
  var attachAbi = parseAbi([
423
- `function attachVeCtm(string nodeKey, uint256 tokenId, ${VECTM_NODE_INFO_TUPLE} nodeInfo)`
968
+ `function attachVeCtm(string nodeKey, uint256 tokenId, ${VECTM_NODE_INFO_TUPLE} nodeInfo, string groupId)`
424
969
  ]);
425
970
  var detachAbi = parseAbi(["function setNodeRemovalStatus(uint256 tokenId, bool status)"]);
426
971
  var CONTINUUM_DAO_EVM_TYPES = /* @__PURE__ */ new Set([
@@ -459,14 +1004,17 @@ function parseTokenId(raw) {
459
1004
  if (id <= 0n) throw new Error("veCTM token id must be greater than zero.");
460
1005
  return id;
461
1006
  }
1007
+ function continuumDaoSignatureText(extra, chainId) {
1008
+ return JSON.stringify({
1009
+ kind: "ContinuumDAO",
1010
+ ...extra,
1011
+ chainId
1012
+ });
1013
+ }
462
1014
  function meta(evmType, chainId, to, extra, gasLimit) {
463
1015
  return {
464
1016
  destinationAddress: to,
465
- signatureText: JSON.stringify({
466
- kind: "ContinuumDAO",
467
- ...extra,
468
- chainId
469
- }),
1017
+ signatureText: continuumDaoSignatureText(extra, chainId),
470
1018
  evm: { type: evmType, version: 1, chainId: String(chainId), protocolId: CONTINUUM_DAO_PROTOCOL_ID },
471
1019
  continuumDao: { ...extra, gas: { baseGasUnits: gasLimit.toString() } }
472
1020
  };
@@ -511,7 +1059,7 @@ async function maybeApproveCtmStep(args) {
511
1059
  try {
512
1060
  allowance = await client.readContract({
513
1061
  address: args.ctm,
514
- abi: erc20Abi,
1062
+ abi: erc20Abi2,
515
1063
  functionName: "allowance",
516
1064
  args: [args.owner, args.spender]
517
1065
  });
@@ -520,7 +1068,7 @@ async function maybeApproveCtmStep(args) {
520
1068
  }
521
1069
  if (allowance >= args.amountWei) return null;
522
1070
  const data = encodeFunctionData({
523
- abi: erc20Abi,
1071
+ abi: erc20Abi2,
524
1072
  functionName: "approve",
525
1073
  args: [args.spender, args.amountWei]
526
1074
  });
@@ -540,10 +1088,17 @@ async function buildEvmMultisignBodyContinuumDaoCreateLock(args) {
540
1088
  const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? getAddress(args.votingEscrow) : requireVe(args.chainId);
541
1089
  const ctm = args.ctm && isConfiguredAddress(args.ctm) ? getAddress(args.ctm) : requireCtm(args.chainId);
542
1090
  const amountWei = parseUnits(String(args.amountHuman).trim().replace(/,/g, ""), CTM_TOKEN_DECIMALS);
543
- if (amountWei <= 0n) throw new Error("CTM amount must be greater than zero.");
544
1091
  const duration = BigInt(String(args.lockDurationSeconds).trim());
545
- if (duration <= 0n) throw new Error("Lock duration must be greater than zero.");
546
1092
  const owner = getAddress(args.executorAddress);
1093
+ await preflightCreateLock({
1094
+ rpcUrl: args.rpcUrl,
1095
+ chainId: args.chainId,
1096
+ votingEscrow: ve,
1097
+ ctm,
1098
+ owner,
1099
+ amountWei,
1100
+ durationSec: duration
1101
+ });
547
1102
  const approve = await maybeApproveCtmStep({
548
1103
  rpcUrl: args.rpcUrl,
549
1104
  chainId: args.chainId,
@@ -576,8 +1131,16 @@ async function buildEvmMultisignBodyContinuumDaoIncreaseAmount(args) {
576
1131
  const ctm = args.ctm && isConfiguredAddress(args.ctm) ? getAddress(args.ctm) : requireCtm(args.chainId);
577
1132
  const tokenId = parseTokenId(args.tokenId);
578
1133
  const amountWei = parseUnits(String(args.amountHuman).trim().replace(/,/g, ""), CTM_TOKEN_DECIMALS);
579
- if (amountWei <= 0n) throw new Error("CTM amount must be greater than zero.");
580
1134
  const owner = getAddress(args.executorAddress);
1135
+ await preflightIncreaseAmount({
1136
+ rpcUrl: args.rpcUrl,
1137
+ chainId: args.chainId,
1138
+ votingEscrow: ve,
1139
+ ctm,
1140
+ owner,
1141
+ tokenId,
1142
+ amountWei
1143
+ });
581
1144
  const approve = await maybeApproveCtmStep({
582
1145
  rpcUrl: args.rpcUrl,
583
1146
  chainId: args.chainId,
@@ -609,7 +1172,15 @@ async function buildEvmMultisignBodyContinuumDaoIncreaseUnlockTime(args) {
609
1172
  const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? getAddress(args.votingEscrow) : requireVe(args.chainId);
610
1173
  const tokenId = parseTokenId(args.tokenId);
611
1174
  const duration = BigInt(String(args.lockDurationSeconds).trim());
612
- if (duration <= 0n) throw new Error("Unlock duration must be greater than zero.");
1175
+ const owner = getAddress(args.executorAddress);
1176
+ await preflightIncreaseUnlockTime({
1177
+ rpcUrl: args.rpcUrl,
1178
+ chainId: args.chainId,
1179
+ votingEscrow: ve,
1180
+ owner,
1181
+ tokenId,
1182
+ durationSec: duration
1183
+ });
613
1184
  const data = encodeFunctionData({
614
1185
  abi: veWriteAbi,
615
1186
  functionName: "increase_unlock_time",
@@ -622,6 +1193,7 @@ async function buildEvmMultisignBodyContinuumDaoIncreaseUnlockTime(args) {
622
1193
  fallbackGas: CONTINUUM_DAO_INCREASE_UNLOCK_FALLBACK,
623
1194
  buildBatchMeta: ({ gasLimit }) => meta("continuum_dao_increase_unlock_time", args.chainId, ve, {
624
1195
  name: "VotingEscrow.increase_unlock_time",
1196
+ signature: INCREASE_UNLOCK_TIME_SIGNATURE,
625
1197
  tokenId: tokenId.toString(),
626
1198
  lockDurationSeconds: duration.toString()
627
1199
  }, gasLimit)
@@ -632,7 +1204,14 @@ async function buildEvmMultisignBodyContinuumDaoSplit(args) {
632
1204
  const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? getAddress(args.votingEscrow) : requireVe(args.chainId);
633
1205
  const tokenId = parseTokenId(args.tokenId);
634
1206
  const extracted = parseUnits(String(args.extractedHuman).trim().replace(/,/g, ""), CTM_TOKEN_DECIMALS);
635
- if (extracted <= 0n) throw new Error("Split amount must be greater than zero.");
1207
+ await preflightSplit({
1208
+ rpcUrl: args.rpcUrl,
1209
+ chainId: args.chainId,
1210
+ votingEscrow: ve,
1211
+ owner: getAddress(args.executorAddress),
1212
+ tokenId,
1213
+ extractedWei: extracted
1214
+ });
636
1215
  const data = encodeFunctionData({
637
1216
  abi: veWriteAbi,
638
1217
  functionName: "split",
@@ -655,7 +1234,14 @@ async function buildEvmMultisignBodyContinuumDaoMerge(args) {
655
1234
  const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? getAddress(args.votingEscrow) : requireVe(args.chainId);
656
1235
  const fromId = parseTokenId(args.fromTokenId);
657
1236
  const toId = parseTokenId(args.toTokenId);
658
- if (fromId === toId) throw new Error("Merge requires two different token ids.");
1237
+ await preflightMerge({
1238
+ rpcUrl: args.rpcUrl,
1239
+ chainId: args.chainId,
1240
+ votingEscrow: ve,
1241
+ owner: getAddress(args.executorAddress),
1242
+ fromTokenId: fromId,
1243
+ toTokenId: toId
1244
+ });
659
1245
  const data = encodeFunctionData({
660
1246
  abi: veWriteAbi,
661
1247
  functionName: "merge",
@@ -677,6 +1263,13 @@ async function buildEvmMultisignBodyContinuumDaoMerge(args) {
677
1263
  async function buildEvmMultisignBodyContinuumDaoWithdraw(args) {
678
1264
  const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? getAddress(args.votingEscrow) : requireVe(args.chainId);
679
1265
  const tokenId = parseTokenId(args.tokenId);
1266
+ await preflightWithdraw({
1267
+ rpcUrl: args.rpcUrl,
1268
+ chainId: args.chainId,
1269
+ votingEscrow: ve,
1270
+ owner: getAddress(args.executorAddress),
1271
+ tokenId
1272
+ });
680
1273
  const data = encodeFunctionData({ abi: veWriteAbi, functionName: "withdraw", args: [tokenId] });
681
1274
  const step = {
682
1275
  to: ve,
@@ -693,6 +1286,13 @@ async function buildEvmMultisignBodyContinuumDaoWithdraw(args) {
693
1286
  async function buildEvmMultisignBodyContinuumDaoLiquidate(args) {
694
1287
  const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? getAddress(args.votingEscrow) : requireVe(args.chainId);
695
1288
  const tokenId = parseTokenId(args.tokenId);
1289
+ await preflightLiquidate({
1290
+ rpcUrl: args.rpcUrl,
1291
+ chainId: args.chainId,
1292
+ votingEscrow: ve,
1293
+ owner: getAddress(args.executorAddress),
1294
+ tokenId
1295
+ });
696
1296
  const data = encodeFunctionData({ abi: veWriteAbi, functionName: "liquidate", args: [tokenId] });
697
1297
  const step = {
698
1298
  to: ve,
@@ -708,7 +1308,12 @@ async function buildEvmMultisignBodyContinuumDaoLiquidate(args) {
708
1308
  }
709
1309
  async function buildEvmMultisignBodyContinuumDaoDelegate(args) {
710
1310
  const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? getAddress(args.votingEscrow) : requireVe(args.chainId);
711
- const delegatee = getAddress(args.delegatee);
1311
+ const delegatee = await preflightDelegate({
1312
+ rpcUrl: args.rpcUrl,
1313
+ chainId: args.chainId,
1314
+ owner: getAddress(args.executorAddress),
1315
+ delegatee: args.delegatee
1316
+ });
712
1317
  const data = encodeFunctionData({ abi: veWriteAbi, functionName: "delegate", args: [delegatee] });
713
1318
  const step = {
714
1319
  to: ve,
@@ -725,6 +1330,12 @@ async function buildEvmMultisignBodyContinuumDaoDelegate(args) {
725
1330
  async function buildEvmMultisignBodyContinuumDaoUndelegate(args) {
726
1331
  const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? getAddress(args.votingEscrow) : requireVe(args.chainId);
727
1332
  const self = getAddress(args.executorAddress);
1333
+ await preflightUndelegate({
1334
+ rpcUrl: args.rpcUrl,
1335
+ chainId: args.chainId,
1336
+ votingEscrow: ve,
1337
+ owner: self
1338
+ });
728
1339
  const data = encodeFunctionData({ abi: veWriteAbi, functionName: "delegate", args: [self] });
729
1340
  const step = {
730
1341
  to: ve,
@@ -739,7 +1350,7 @@ async function buildEvmMultisignBodyContinuumDaoUndelegate(args) {
739
1350
  };
740
1351
  return finalize([step], args, `ContinuumDAO: undelegate voting power (delegate back to ${self}).`);
741
1352
  }
742
- var veReadAbi = parseAbi(["function delegates(address account) view returns (address)"]);
1353
+ var veReadAbi2 = parseAbi(["function delegates(address account) view returns (address)"]);
743
1354
  async function continuumDaoFetchDelegates(args) {
744
1355
  const chainId = Number(args.chainId);
745
1356
  requireLockChain(chainId);
@@ -755,15 +1366,22 @@ async function continuumDaoFetchDelegates(args) {
755
1366
  transport: http(args.rpcUrl.trim())
756
1367
  });
757
1368
  const delegatee = getAddress(
758
- await client.readContract({ address: ve, abi: veReadAbi, functionName: "delegates", args: [account] })
1369
+ await client.readContract({ address: ve, abi: veReadAbi2, functionName: "delegates", args: [account] })
759
1370
  );
760
1371
  return { account, delegatee, self: delegatee === account };
761
1372
  }
762
1373
  async function buildEvmMultisignBodyContinuumDaoTransfer(args) {
763
1374
  const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? getAddress(args.votingEscrow) : requireVe(args.chainId);
764
1375
  const tokenId = parseTokenId(args.tokenId);
765
- const to = getAddress(args.to);
766
1376
  const from = getAddress(args.executorAddress);
1377
+ const to = await preflightTransfer({
1378
+ rpcUrl: args.rpcUrl,
1379
+ chainId: args.chainId,
1380
+ votingEscrow: ve,
1381
+ owner: from,
1382
+ tokenId,
1383
+ to: args.to
1384
+ });
767
1385
  const data = encodeFunctionData({
768
1386
  abi: veWriteAbi,
769
1387
  functionName: "transferFrom",
@@ -824,11 +1442,20 @@ async function buildEvmMultisignBodyContinuumDaoAttachVeCtm(args) {
824
1442
  const tokenId = parseTokenId(args.tokenId);
825
1443
  const nodeKey = args.nodeKey.trim();
826
1444
  if (!nodeKey) throw new Error("nodeKey is required to attach veCTM.");
1445
+ const groupId = resolveAttachGroupId(args.keyGen, args.groupId);
1446
+ await preflightAttachVeCtm({
1447
+ rpcUrl: args.rpcUrl,
1448
+ chainId: args.chainId,
1449
+ mpaWallet: wallet,
1450
+ nodeKey,
1451
+ tokenId,
1452
+ owner: getAddress(args.executorAddress)
1453
+ });
827
1454
  const tuple = args.nodeInfoTuple ?? encodeContinuumDaoNodeInfo(args.nodeInfo);
828
1455
  const data = encodeFunctionData({
829
1456
  abi: attachAbi,
830
1457
  functionName: "attachVeCtm",
831
- args: [nodeKey, tokenId, tuple]
1458
+ args: [nodeKey, tokenId, tuple, groupId]
832
1459
  });
833
1460
  const step = {
834
1461
  to: wallet,
@@ -837,17 +1464,26 @@ async function buildEvmMultisignBodyContinuumDaoAttachVeCtm(args) {
837
1464
  fallbackGas: CONTINUUM_DAO_ATTACH_FALLBACK,
838
1465
  buildBatchMeta: ({ gasLimit }) => meta("continuum_dao_attach", args.chainId, wallet, {
839
1466
  name: "MultiSignAgentWallet.attachVeCtm",
1467
+ signature: ATTACH_VECTM_SIGNATURE,
840
1468
  nodeKey,
841
- tokenId: tokenId.toString()
1469
+ tokenId: tokenId.toString(),
1470
+ groupId
842
1471
  }, gasLimit)
843
1472
  };
844
- return finalize([step], args, `ContinuumDAO: attach veCTM #${tokenId}.`);
1473
+ return finalize([step], args, `ContinuumDAO: attach veCTM #${tokenId} (group ${groupId}).`);
845
1474
  }
846
1475
  async function buildEvmMultisignBodyContinuumDaoRequestDetach(args) {
847
1476
  requireLockChain(args.chainId);
848
1477
  const np = getAddress(args.nodeProperties);
849
1478
  if (np === zeroAddress) throw new Error("NodeProperties address is required to request detach.");
850
1479
  const tokenId = parseTokenId(args.tokenId);
1480
+ await preflightRequestDetach({
1481
+ rpcUrl: args.rpcUrl,
1482
+ chainId: args.chainId,
1483
+ nodeProperties: np,
1484
+ owner: getAddress(args.executorAddress),
1485
+ tokenId
1486
+ });
851
1487
  const data = encodeFunctionData({
852
1488
  abi: detachAbi,
853
1489
  functionName: "setNodeRemovalStatus",
@@ -1447,7 +2083,9 @@ var governorReadAbi = parseAbi([
1447
2083
  "function proposalThreshold() view returns (uint256)",
1448
2084
  "function proposalCount() view returns (uint256)",
1449
2085
  "function clock() view returns (uint48)",
1450
- "function proposalVotes(uint256 proposalId) view returns (uint256 againstVotes, uint256 forVotes, uint256 abstainVotes)"
2086
+ "function proposalVotes(uint256 proposalId) view returns (uint256 againstVotes, uint256 forVotes, uint256 abstainVotes)",
2087
+ "function hasVoted(uint256 proposalId, address account) view returns (bool)",
2088
+ "function proposalSnapshot(uint256 proposalId) view returns (uint256)"
1451
2089
  ]);
1452
2090
  function splitAbiParamList(inner) {
1453
2091
  const out = [];
@@ -1699,9 +2337,32 @@ async function buildEvmMultisignBodyContinuumDaoProposeDelta(args) {
1699
2337
  );
1700
2338
  return { ...built, proposalArgs };
1701
2339
  }
2340
+ async function requireVoteEligible(args) {
2341
+ const st = await continuumDaoFetchProposalState({
2342
+ chainId: args.chainId,
2343
+ rpcUrl: args.rpcUrl,
2344
+ proposalId: args.proposalId,
2345
+ governor: args.governor
2346
+ });
2347
+ if (st.state !== 1) {
2348
+ throw new Error(`Proposal is not Active (on-chain state ${st.state}).`);
2349
+ }
2350
+ const elig = await continuumDaoFetchProposalVoteEligibility({
2351
+ chainId: args.chainId,
2352
+ rpcUrl: args.rpcUrl,
2353
+ proposalId: args.proposalId,
2354
+ account: args.executorAddress,
2355
+ governor: args.governor
2356
+ });
2357
+ if (elig.hasVoted) throw new Error("This KeyGen has already voted on this proposal.");
2358
+ if (BigInt(elig.snapshotVotes) === 0n) {
2359
+ throw new Error("No veCTM voting power at snapshot for this KeyGen.");
2360
+ }
2361
+ }
1702
2362
  async function buildEvmMultisignBodyContinuumDaoCastVoteBravo(args) {
1703
2363
  const gov = requireGovernor(args.chainId, args.governor);
1704
2364
  const proposalId = BigInt(String(args.proposalId).trim());
2365
+ await requireVoteEligible(args);
1705
2366
  const data = encodeFunctionData({
1706
2367
  abi: governorWriteAbi,
1707
2368
  functionName: "castVote",
@@ -1720,6 +2381,10 @@ async function buildEvmMultisignBodyContinuumDaoCastVoteBravo(args) {
1720
2381
  async function buildEvmMultisignBodyContinuumDaoCastVoteDelta(args) {
1721
2382
  const gov = requireGovernor(args.chainId, args.governor);
1722
2383
  const proposalId = BigInt(String(args.proposalId).trim());
2384
+ if (args.weights.length < 3) {
2385
+ throw new Error("Delta vote must include one weight per option plus a NOTA slot.");
2386
+ }
2387
+ await requireVoteEligible(args);
1723
2388
  const params = encodeDeltaVoteParams(args.weights);
1724
2389
  const data = encodeFunctionData({
1725
2390
  abi: governorWriteAbi,
@@ -1772,7 +2437,7 @@ async function buildEvmMultisignBodyContinuumDaoCancel(args) {
1772
2437
  "continuum-dao cancel"
1773
2438
  );
1774
2439
  }
1775
- function publicClient(rpcUrl, chainId) {
2440
+ function publicClient2(rpcUrl, chainId) {
1776
2441
  return createPublicClient({
1777
2442
  chain: defineChain({
1778
2443
  id: chainId,
@@ -1786,7 +2451,7 @@ function publicClient(rpcUrl, chainId) {
1786
2451
  async function continuumDaoHashProposal(args) {
1787
2452
  const chainId = Number(args.chainId);
1788
2453
  const gov = requireGovernor(chainId, args.governor);
1789
- const client = publicClient(args.rpcUrl, chainId);
2454
+ const client = publicClient2(args.rpcUrl, chainId);
1790
2455
  const id = await client.readContract({
1791
2456
  address: gov,
1792
2457
  abi: governorReadAbi,
@@ -1798,7 +2463,7 @@ async function continuumDaoHashProposal(args) {
1798
2463
  async function continuumDaoFetchVotingPower(args) {
1799
2464
  const chainId = Number(args.chainId);
1800
2465
  const gov = requireGovernor(chainId, args.governor);
1801
- const client = publicClient(args.rpcUrl, chainId);
2466
+ const client = publicClient2(args.rpcUrl, chainId);
1802
2467
  const clock = await client.readContract({ address: gov, abi: governorReadAbi, functionName: "clock" });
1803
2468
  const timepoint = clock > 0 ? clock - 1 : 0;
1804
2469
  const [votes, threshold] = await Promise.all([
@@ -1815,22 +2480,74 @@ async function continuumDaoFetchVotingPower(args) {
1815
2480
  async function continuumDaoFetchProposalState(args) {
1816
2481
  const chainId = Number(args.chainId);
1817
2482
  const gov = requireGovernor(chainId, args.governor);
1818
- const client = publicClient(args.rpcUrl, chainId);
2483
+ const client = publicClient2(args.rpcUrl, chainId);
1819
2484
  const proposalId = BigInt(String(args.proposalId).trim());
1820
- const [state, votes] = await Promise.all([
1821
- client.readContract({ address: gov, abi: governorReadAbi, functionName: "state", args: [proposalId] }),
1822
- client.readContract({ address: gov, abi: governorReadAbi, functionName: "proposalVotes", args: [proposalId] })
1823
- ]);
2485
+ const state = await client.readContract({
2486
+ address: gov,
2487
+ abi: governorReadAbi,
2488
+ functionName: "state",
2489
+ args: [proposalId]
2490
+ });
2491
+ let againstVotes = "0";
2492
+ let forVotes = "0";
2493
+ let abstainVotes = "0";
2494
+ let votesPartial = false;
2495
+ try {
2496
+ const votes = await client.readContract({
2497
+ address: gov,
2498
+ abi: governorReadAbi,
2499
+ functionName: "proposalVotes",
2500
+ args: [proposalId]
2501
+ });
2502
+ againstVotes = votes[0].toString();
2503
+ forVotes = votes[1].toString();
2504
+ abstainVotes = votes[2].toString();
2505
+ } catch {
2506
+ votesPartial = true;
2507
+ }
1824
2508
  return {
1825
2509
  state: Number(state),
1826
- againstVotes: votes[0].toString(),
1827
- forVotes: votes[1].toString(),
1828
- abstainVotes: votes[2].toString()
2510
+ againstVotes,
2511
+ forVotes,
2512
+ abstainVotes,
2513
+ votesPartial
2514
+ };
2515
+ }
2516
+ async function continuumDaoFetchProposalVoteEligibility(args) {
2517
+ const chainId = Number(args.chainId);
2518
+ const gov = requireGovernor(chainId, args.governor);
2519
+ const client = publicClient2(args.rpcUrl, chainId);
2520
+ const proposalId = BigInt(String(args.proposalId).trim());
2521
+ const account = getAddress(args.account);
2522
+ const [hasVoted, snapshotBlock] = await Promise.all([
2523
+ client.readContract({
2524
+ address: gov,
2525
+ abi: governorReadAbi,
2526
+ functionName: "hasVoted",
2527
+ args: [proposalId, account]
2528
+ }),
2529
+ client.readContract({
2530
+ address: gov,
2531
+ abi: governorReadAbi,
2532
+ functionName: "proposalSnapshot",
2533
+ args: [proposalId]
2534
+ })
2535
+ ]);
2536
+ const snapshotVotes = await client.readContract({
2537
+ address: gov,
2538
+ abi: governorReadAbi,
2539
+ functionName: "getVotes",
2540
+ args: [account, snapshotBlock]
2541
+ });
2542
+ return {
2543
+ hasVoted,
2544
+ snapshotVotes: snapshotVotes.toString(),
2545
+ snapshotBlock: snapshotBlock.toString()
1829
2546
  };
1830
2547
  }
1831
2548
  async function continuumDaoFetchProposalCount(args) {
1832
2549
  const gov = requireGovernor(args.chainId, args.governor);
1833
- const client = publicClient(args.rpcUrl, args.chainId);
2550
+ const client = publicClient2(args.rpcUrl, args.chainId);
1834
2551
  const count = await client.readContract({ address: gov, abi: governorReadAbi, functionName: "proposalCount" });
1835
2552
  return { count: count.toString() };
1836
2553
  }
@@ -2165,7 +2882,7 @@ var continuumDaoProtocolModule = {
2165
2882
  { 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: {} },
2166
2883
  { 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" } } },
2167
2884
  { 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" } } },
2168
- { 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" } } },
2885
+ { 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" } } },
2169
2886
  { 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" } } },
2170
2887
  { 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" } } },
2171
2888
  { 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" } } },
@@ -2190,6 +2907,6 @@ var continuumDaoProtocolModule = {
2190
2907
  };
2191
2908
  registerProtocolModule(continuumDaoProtocolModule);
2192
2909
 
2193
- export { ATTACH_VECTM_SIGNATURE, CONTINUUM_DAO_APPROVE_FALLBACK, CONTINUUM_DAO_ATTACH_FALLBACK, CONTINUUM_DAO_CANCEL_FALLBACK, CONTINUUM_DAO_CREATE_LOCK_FALLBACK, CONTINUUM_DAO_DELEGATE_FALLBACK, CONTINUUM_DAO_DEPLOYMENTS, CONTINUUM_DAO_DETACH_FALLBACK, CONTINUUM_DAO_ETHEREUM_CHAIN_ID, CONTINUUM_DAO_EVM_TYPES, CONTINUUM_DAO_EXECUTE_FALLBACK, CONTINUUM_DAO_GOVERNANCE_API, CONTINUUM_DAO_GOV_EVM_TYPES, CONTINUUM_DAO_INCREASE_AMOUNT_FALLBACK, CONTINUUM_DAO_INCREASE_UNLOCK_FALLBACK, CONTINUUM_DAO_LINEA_CHAIN_ID, CONTINUUM_DAO_LINEA_SEPOLIA_CHAIN_ID, CONTINUUM_DAO_LIQUIDATE_FALLBACK, CONTINUUM_DAO_MERGE_FALLBACK, CONTINUUM_DAO_PROPOSE_FALLBACK, CONTINUUM_DAO_PROTOCOL_ID, CONTINUUM_DAO_SPLIT_FALLBACK, CONTINUUM_DAO_TRANSFER_FALLBACK, CONTINUUM_DAO_VOTE_FALLBACK, CONTINUUM_DAO_WITHDRAW_FALLBACK, CTM_TOKEN_DECIMALS, CTM_TOKEN_NAME, CTM_TOKEN_SYMBOL, FORUM_GOVERNANCE_SECTIONS, FORUM_IDEA_SECTION, FORUM_LOGIN_DELIVERY_KIND, GOVERNOR_STATE, GOVERNOR_STATE_LABELS, MAXTIME_SECONDS, PROPOSAL_TYPE_LABELS, PROPOSAL_TYPE_TO_FORUM_SECTION, VECTM_NODE_INFO_TUPLE, VECTM_TOKEN_NAME, VECTM_TOKEN_SYMBOL, WEEK_SECONDS, applyForumSectionCheck, assertContinuumDaoForumDeleteInput, assertContinuumDaoForumMarkReadInput, assertContinuumDaoForumRecentInput, assertContinuumDaoForumSearchInput, assertContinuumDaoForumTopicKey, bucketGovernorState, buildBravoProposalArgs, buildDeltaProposalArgs, buildEvmMultisignBodyContinuumDaoAttachVeCtm, buildEvmMultisignBodyContinuumDaoAttachVeCtmFromMcp, buildEvmMultisignBodyContinuumDaoCancel, buildEvmMultisignBodyContinuumDaoCastVoteBravo, buildEvmMultisignBodyContinuumDaoCastVoteBravoFromMcp, buildEvmMultisignBodyContinuumDaoCastVoteDelta, buildEvmMultisignBodyContinuumDaoCreateLock, buildEvmMultisignBodyContinuumDaoDelegate, buildEvmMultisignBodyContinuumDaoExecute, buildEvmMultisignBodyContinuumDaoForumSignIn, buildEvmMultisignBodyContinuumDaoIncreaseAmount, buildEvmMultisignBodyContinuumDaoIncreaseUnlockTime, buildEvmMultisignBodyContinuumDaoLiquidate, buildEvmMultisignBodyContinuumDaoMerge, buildEvmMultisignBodyContinuumDaoProposeBravo, buildEvmMultisignBodyContinuumDaoProposeDelta, buildEvmMultisignBodyContinuumDaoProposeDeltaFromMcp, buildEvmMultisignBodyContinuumDaoRequestDetach, buildEvmMultisignBodyContinuumDaoSplit, buildEvmMultisignBodyContinuumDaoTransfer, buildEvmMultisignBodyContinuumDaoUndelegate, buildEvmMultisignBodyContinuumDaoWithdraw, continuumDaoDeployment, continuumDaoExplainProposal, continuumDaoFetchDelegates, continuumDaoFetchLiveProposals, continuumDaoFetchProposal, continuumDaoFetchProposalCount, continuumDaoFetchProposalState, continuumDaoFetchProposals, continuumDaoFetchVotingPower, continuumDaoForumCreateIdea, continuumDaoForumCreateTopic, continuumDaoForumDelete, continuumDaoForumFetchPost, continuumDaoForumFetchThread, continuumDaoForumMarkRead, continuumDaoForumMarkUnread, continuumDaoForumMe, continuumDaoForumReact, continuumDaoForumRecent, continuumDaoForumReply, continuumDaoForumReplyCount, continuumDaoForumResolve, continuumDaoForumSearch, continuumDaoForumSections, continuumDaoForumSignInEligible, continuumDaoForumSignInNonce, continuumDaoForumSignOut, continuumDaoForumUnread, continuumDaoForumUrl, continuumDaoForumUserPostIds, continuumDaoHashProposal, continuumDaoMpaFlag, continuumDaoMpaListing, continuumDaoMpaMailboxList, continuumDaoMpaMailboxOpen, continuumDaoMpaMailboxSend, continuumDaoMpaRetract, continuumDaoProtocolModule, continuumDaoRegisterProposal, continuumDaoTechnocoreBind, ctmTokenAddressOnEvmChain, encodeContinuumDaoNodeInfo, encodeDeltaMetadata, encodeDeltaVoteParams, encodeGovActionCalldata, expectedForumSectionForProposalType, explainGovAction, explainProposalRecord, governorAddressOnEvmChain, isConfiguredAddress, isContinuumDaoCtmOnAssetsChain, isContinuumDaoLockChain, isContinuumDaoSupportedChainId, isContinuumDaoVeCtmOnAssetsChain, isCtmTokenSymbol, isForumIdeaSection, isGovNoOpAction, isVotingEscrowContinuumName, listContinuumDaoDefaultAssets, proposalDescriptionHash, summarizeLiveProposals, votingEscrowAddressOnEvmChain };
2910
+ export { ATTACH_VECTM_SIGNATURE, CONTINUUM_DAO_APPROVE_FALLBACK, CONTINUUM_DAO_ATTACH_FALLBACK, CONTINUUM_DAO_CANCEL_FALLBACK, CONTINUUM_DAO_CREATE_LOCK_FALLBACK, CONTINUUM_DAO_DELEGATE_FALLBACK, CONTINUUM_DAO_DEPLOYMENTS, CONTINUUM_DAO_DETACH_FALLBACK, CONTINUUM_DAO_ETHEREUM_CHAIN_ID, CONTINUUM_DAO_EVM_TYPES, CONTINUUM_DAO_EXECUTE_FALLBACK, CONTINUUM_DAO_GOVERNANCE_API, CONTINUUM_DAO_GOV_EVM_TYPES, CONTINUUM_DAO_INCREASE_AMOUNT_FALLBACK, CONTINUUM_DAO_INCREASE_UNLOCK_FALLBACK, CONTINUUM_DAO_LINEA_CHAIN_ID, CONTINUUM_DAO_LINEA_SEPOLIA_CHAIN_ID, CONTINUUM_DAO_LIQUIDATE_FALLBACK, CONTINUUM_DAO_MERGE_FALLBACK, CONTINUUM_DAO_PROPOSE_FALLBACK, CONTINUUM_DAO_PROTOCOL_ID, CONTINUUM_DAO_SPLIT_FALLBACK, CONTINUUM_DAO_TRANSFER_FALLBACK, CONTINUUM_DAO_VOTE_FALLBACK, CONTINUUM_DAO_WITHDRAW_FALLBACK, CTM_TOKEN_ADDRESS, CTM_TOKEN_DECIMALS, CTM_TOKEN_NAME, CTM_TOKEN_SYMBOL, FORUM_GOVERNANCE_SECTIONS, FORUM_IDEA_SECTION, FORUM_LOGIN_DELIVERY_KIND, GOVERNOR_STATE, GOVERNOR_STATE_LABELS, INCREASE_UNLOCK_TIME_SIGNATURE, MAXTIME_SECONDS, PROPOSAL_TYPE_LABELS, PROPOSAL_TYPE_TO_FORUM_SECTION, VECTM_DEFAULT_MINIMUM_LOCK_WEI, VECTM_LIQ_MIN_LOCKED_WEI, VECTM_LIQ_PENALTY_DEN, VECTM_LIQ_PENALTY_NUM, VECTM_MAX_LOCK_WEEKS, VECTM_NODE_INFO_TUPLE, VECTM_TOKEN_NAME, VECTM_TOKEN_SYMBOL, VOTING_ESCROW_LINEA_ADDRESS, WEEK_SECONDS, applyForumSectionCheck, assertContinuumDaoForumDeleteInput, assertContinuumDaoForumMarkReadInput, assertContinuumDaoForumRecentInput, assertContinuumDaoForumSearchInput, assertContinuumDaoForumTopicKey, assertCreateLockAmount, assertCtmAmountAtLeastMinimum, assertDelegatee, assertExecutorOwnsLock, assertIncreaseAmount, assertIncreaseUnlockTime, assertLiquidate, assertLockDurationSeconds, assertMerge, assertSplitExtracted, assertTransfer, assertUndelegateNotAlreadySelf, assertWithdraw, bucketGovernorState, buildBravoProposalArgs, buildDeltaProposalArgs, buildEvmMultisignBodyContinuumDaoAttachVeCtm, buildEvmMultisignBodyContinuumDaoAttachVeCtmFromMcp, buildEvmMultisignBodyContinuumDaoCancel, buildEvmMultisignBodyContinuumDaoCastVoteBravo, buildEvmMultisignBodyContinuumDaoCastVoteBravoFromMcp, buildEvmMultisignBodyContinuumDaoCastVoteDelta, buildEvmMultisignBodyContinuumDaoCreateLock, buildEvmMultisignBodyContinuumDaoDelegate, buildEvmMultisignBodyContinuumDaoExecute, buildEvmMultisignBodyContinuumDaoForumSignIn, buildEvmMultisignBodyContinuumDaoIncreaseAmount, buildEvmMultisignBodyContinuumDaoIncreaseUnlockTime, buildEvmMultisignBodyContinuumDaoLiquidate, buildEvmMultisignBodyContinuumDaoMerge, buildEvmMultisignBodyContinuumDaoProposeBravo, buildEvmMultisignBodyContinuumDaoProposeDelta, buildEvmMultisignBodyContinuumDaoProposeDeltaFromMcp, buildEvmMultisignBodyContinuumDaoRequestDetach, buildEvmMultisignBodyContinuumDaoSplit, buildEvmMultisignBodyContinuumDaoTransfer, buildEvmMultisignBodyContinuumDaoUndelegate, buildEvmMultisignBodyContinuumDaoWithdraw, computeLiquidationAmounts, continuumDaoDeployment, continuumDaoExplainProposal, continuumDaoFetchDelegates, continuumDaoFetchLiveProposals, continuumDaoFetchProposal, continuumDaoFetchProposalCount, continuumDaoFetchProposalState, continuumDaoFetchProposalVoteEligibility, continuumDaoFetchProposals, continuumDaoFetchVotingPower, continuumDaoForumCreateIdea, continuumDaoForumCreateTopic, continuumDaoForumDelete, continuumDaoForumFetchPost, continuumDaoForumFetchThread, continuumDaoForumMarkRead, continuumDaoForumMarkUnread, continuumDaoForumMe, continuumDaoForumReact, continuumDaoForumRecent, continuumDaoForumReply, continuumDaoForumReplyCount, continuumDaoForumResolve, continuumDaoForumSearch, continuumDaoForumSections, continuumDaoForumSignInEligible, continuumDaoForumSignInNonce, continuumDaoForumSignOut, continuumDaoForumUnread, continuumDaoForumUrl, continuumDaoForumUserPostIds, continuumDaoHashProposal, continuumDaoMpaFlag, continuumDaoMpaListing, continuumDaoMpaMailboxList, continuumDaoMpaMailboxOpen, continuumDaoMpaMailboxSend, continuumDaoMpaRetract, continuumDaoProtocolModule, continuumDaoRegisterProposal, continuumDaoSignatureText, continuumDaoTechnocoreBind, ctmTokenAddressOnEvmChain, encodeContinuumDaoNodeInfo, encodeDeltaMetadata, encodeDeltaVoteParams, encodeGovActionCalldata, expectedForumSectionForProposalType, explainGovAction, explainProposalRecord, floorUnlockTimeSeconds, governorAddressOnEvmChain, isConfiguredAddress, isContinuumDaoCtmOnAssetsChain, isContinuumDaoLockChain, isContinuumDaoSupportedChainId, isContinuumDaoVeCtmOnAssetsChain, isCtmTokenSymbol, isForumIdeaSection, isGovNoOpAction, isVotingEscrowContinuumName, listContinuumDaoDefaultAssets, maxLockDurationSeconds, preflightAttachVeCtm, preflightCreateLock, preflightDelegate, preflightIncreaseAmount, preflightIncreaseUnlockTime, preflightLiquidate, preflightMerge, preflightRequestDetach, preflightSplit, preflightTransfer, preflightUndelegate, preflightWithdraw, proposalDescriptionHash, readCtmBalanceWei, readMinimumLockWei, readVeCtmLockState, resolveAttachGroupId, summarizeLiveProposals, votingEscrowAddressOnEvmChain };
2194
2911
  //# sourceMappingURL=index.js.map
2195
2912
  //# sourceMappingURL=index.js.map