@continuumdao/ctm-mpc-defi 0.2.37 → 0.2.38

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 +797 -46
  29. package/dist/protocols/evm/continuum-dao/index.cjs.map +1 -1
  30. package/dist/protocols/evm/continuum-dao/index.d.ts +189 -5
  31. package/dist/protocols/evm/continuum-dao/index.js +759 -48
  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,7 @@ 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 erc20Abi2 = parseAbi([
408
952
  "function allowance(address owner, address spender) view returns (uint256)",
409
953
  "function approve(address spender, uint256 amount) returns (bool)"
410
954
  ]);
@@ -420,7 +964,7 @@ var veWriteAbi = parseAbi([
420
964
  "function transferFrom(address _from, address _to, uint256 _tokenId)"
421
965
  ]);
422
966
  var attachAbi = parseAbi([
423
- `function attachVeCtm(string nodeKey, uint256 tokenId, ${VECTM_NODE_INFO_TUPLE} nodeInfo)`
967
+ `function attachVeCtm(string nodeKey, uint256 tokenId, ${VECTM_NODE_INFO_TUPLE} nodeInfo, string groupId)`
424
968
  ]);
425
969
  var detachAbi = parseAbi(["function setNodeRemovalStatus(uint256 tokenId, bool status)"]);
426
970
  var CONTINUUM_DAO_EVM_TYPES = /* @__PURE__ */ new Set([
@@ -511,7 +1055,7 @@ async function maybeApproveCtmStep(args) {
511
1055
  try {
512
1056
  allowance = await client.readContract({
513
1057
  address: args.ctm,
514
- abi: erc20Abi,
1058
+ abi: erc20Abi2,
515
1059
  functionName: "allowance",
516
1060
  args: [args.owner, args.spender]
517
1061
  });
@@ -520,7 +1064,7 @@ async function maybeApproveCtmStep(args) {
520
1064
  }
521
1065
  if (allowance >= args.amountWei) return null;
522
1066
  const data = encodeFunctionData({
523
- abi: erc20Abi,
1067
+ abi: erc20Abi2,
524
1068
  functionName: "approve",
525
1069
  args: [args.spender, args.amountWei]
526
1070
  });
@@ -540,10 +1084,17 @@ async function buildEvmMultisignBodyContinuumDaoCreateLock(args) {
540
1084
  const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? getAddress(args.votingEscrow) : requireVe(args.chainId);
541
1085
  const ctm = args.ctm && isConfiguredAddress(args.ctm) ? getAddress(args.ctm) : requireCtm(args.chainId);
542
1086
  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
1087
  const duration = BigInt(String(args.lockDurationSeconds).trim());
545
- if (duration <= 0n) throw new Error("Lock duration must be greater than zero.");
546
1088
  const owner = getAddress(args.executorAddress);
1089
+ await preflightCreateLock({
1090
+ rpcUrl: args.rpcUrl,
1091
+ chainId: args.chainId,
1092
+ votingEscrow: ve,
1093
+ ctm,
1094
+ owner,
1095
+ amountWei,
1096
+ durationSec: duration
1097
+ });
547
1098
  const approve = await maybeApproveCtmStep({
548
1099
  rpcUrl: args.rpcUrl,
549
1100
  chainId: args.chainId,
@@ -576,8 +1127,16 @@ async function buildEvmMultisignBodyContinuumDaoIncreaseAmount(args) {
576
1127
  const ctm = args.ctm && isConfiguredAddress(args.ctm) ? getAddress(args.ctm) : requireCtm(args.chainId);
577
1128
  const tokenId = parseTokenId(args.tokenId);
578
1129
  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
1130
  const owner = getAddress(args.executorAddress);
1131
+ await preflightIncreaseAmount({
1132
+ rpcUrl: args.rpcUrl,
1133
+ chainId: args.chainId,
1134
+ votingEscrow: ve,
1135
+ ctm,
1136
+ owner,
1137
+ tokenId,
1138
+ amountWei
1139
+ });
581
1140
  const approve = await maybeApproveCtmStep({
582
1141
  rpcUrl: args.rpcUrl,
583
1142
  chainId: args.chainId,
@@ -609,7 +1168,15 @@ async function buildEvmMultisignBodyContinuumDaoIncreaseUnlockTime(args) {
609
1168
  const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? getAddress(args.votingEscrow) : requireVe(args.chainId);
610
1169
  const tokenId = parseTokenId(args.tokenId);
611
1170
  const duration = BigInt(String(args.lockDurationSeconds).trim());
612
- if (duration <= 0n) throw new Error("Unlock duration must be greater than zero.");
1171
+ const owner = getAddress(args.executorAddress);
1172
+ await preflightIncreaseUnlockTime({
1173
+ rpcUrl: args.rpcUrl,
1174
+ chainId: args.chainId,
1175
+ votingEscrow: ve,
1176
+ owner,
1177
+ tokenId,
1178
+ durationSec: duration
1179
+ });
613
1180
  const data = encodeFunctionData({
614
1181
  abi: veWriteAbi,
615
1182
  functionName: "increase_unlock_time",
@@ -632,7 +1199,14 @@ async function buildEvmMultisignBodyContinuumDaoSplit(args) {
632
1199
  const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? getAddress(args.votingEscrow) : requireVe(args.chainId);
633
1200
  const tokenId = parseTokenId(args.tokenId);
634
1201
  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.");
1202
+ await preflightSplit({
1203
+ rpcUrl: args.rpcUrl,
1204
+ chainId: args.chainId,
1205
+ votingEscrow: ve,
1206
+ owner: getAddress(args.executorAddress),
1207
+ tokenId,
1208
+ extractedWei: extracted
1209
+ });
636
1210
  const data = encodeFunctionData({
637
1211
  abi: veWriteAbi,
638
1212
  functionName: "split",
@@ -655,7 +1229,14 @@ async function buildEvmMultisignBodyContinuumDaoMerge(args) {
655
1229
  const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? getAddress(args.votingEscrow) : requireVe(args.chainId);
656
1230
  const fromId = parseTokenId(args.fromTokenId);
657
1231
  const toId = parseTokenId(args.toTokenId);
658
- if (fromId === toId) throw new Error("Merge requires two different token ids.");
1232
+ await preflightMerge({
1233
+ rpcUrl: args.rpcUrl,
1234
+ chainId: args.chainId,
1235
+ votingEscrow: ve,
1236
+ owner: getAddress(args.executorAddress),
1237
+ fromTokenId: fromId,
1238
+ toTokenId: toId
1239
+ });
659
1240
  const data = encodeFunctionData({
660
1241
  abi: veWriteAbi,
661
1242
  functionName: "merge",
@@ -677,6 +1258,13 @@ async function buildEvmMultisignBodyContinuumDaoMerge(args) {
677
1258
  async function buildEvmMultisignBodyContinuumDaoWithdraw(args) {
678
1259
  const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? getAddress(args.votingEscrow) : requireVe(args.chainId);
679
1260
  const tokenId = parseTokenId(args.tokenId);
1261
+ await preflightWithdraw({
1262
+ rpcUrl: args.rpcUrl,
1263
+ chainId: args.chainId,
1264
+ votingEscrow: ve,
1265
+ owner: getAddress(args.executorAddress),
1266
+ tokenId
1267
+ });
680
1268
  const data = encodeFunctionData({ abi: veWriteAbi, functionName: "withdraw", args: [tokenId] });
681
1269
  const step = {
682
1270
  to: ve,
@@ -693,6 +1281,13 @@ async function buildEvmMultisignBodyContinuumDaoWithdraw(args) {
693
1281
  async function buildEvmMultisignBodyContinuumDaoLiquidate(args) {
694
1282
  const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? getAddress(args.votingEscrow) : requireVe(args.chainId);
695
1283
  const tokenId = parseTokenId(args.tokenId);
1284
+ await preflightLiquidate({
1285
+ rpcUrl: args.rpcUrl,
1286
+ chainId: args.chainId,
1287
+ votingEscrow: ve,
1288
+ owner: getAddress(args.executorAddress),
1289
+ tokenId
1290
+ });
696
1291
  const data = encodeFunctionData({ abi: veWriteAbi, functionName: "liquidate", args: [tokenId] });
697
1292
  const step = {
698
1293
  to: ve,
@@ -708,7 +1303,12 @@ async function buildEvmMultisignBodyContinuumDaoLiquidate(args) {
708
1303
  }
709
1304
  async function buildEvmMultisignBodyContinuumDaoDelegate(args) {
710
1305
  const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? getAddress(args.votingEscrow) : requireVe(args.chainId);
711
- const delegatee = getAddress(args.delegatee);
1306
+ const delegatee = await preflightDelegate({
1307
+ rpcUrl: args.rpcUrl,
1308
+ chainId: args.chainId,
1309
+ owner: getAddress(args.executorAddress),
1310
+ delegatee: args.delegatee
1311
+ });
712
1312
  const data = encodeFunctionData({ abi: veWriteAbi, functionName: "delegate", args: [delegatee] });
713
1313
  const step = {
714
1314
  to: ve,
@@ -725,6 +1325,12 @@ async function buildEvmMultisignBodyContinuumDaoDelegate(args) {
725
1325
  async function buildEvmMultisignBodyContinuumDaoUndelegate(args) {
726
1326
  const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? getAddress(args.votingEscrow) : requireVe(args.chainId);
727
1327
  const self = getAddress(args.executorAddress);
1328
+ await preflightUndelegate({
1329
+ rpcUrl: args.rpcUrl,
1330
+ chainId: args.chainId,
1331
+ votingEscrow: ve,
1332
+ owner: self
1333
+ });
728
1334
  const data = encodeFunctionData({ abi: veWriteAbi, functionName: "delegate", args: [self] });
729
1335
  const step = {
730
1336
  to: ve,
@@ -739,7 +1345,7 @@ async function buildEvmMultisignBodyContinuumDaoUndelegate(args) {
739
1345
  };
740
1346
  return finalize([step], args, `ContinuumDAO: undelegate voting power (delegate back to ${self}).`);
741
1347
  }
742
- var veReadAbi = parseAbi(["function delegates(address account) view returns (address)"]);
1348
+ var veReadAbi2 = parseAbi(["function delegates(address account) view returns (address)"]);
743
1349
  async function continuumDaoFetchDelegates(args) {
744
1350
  const chainId = Number(args.chainId);
745
1351
  requireLockChain(chainId);
@@ -755,15 +1361,22 @@ async function continuumDaoFetchDelegates(args) {
755
1361
  transport: http(args.rpcUrl.trim())
756
1362
  });
757
1363
  const delegatee = getAddress(
758
- await client.readContract({ address: ve, abi: veReadAbi, functionName: "delegates", args: [account] })
1364
+ await client.readContract({ address: ve, abi: veReadAbi2, functionName: "delegates", args: [account] })
759
1365
  );
760
1366
  return { account, delegatee, self: delegatee === account };
761
1367
  }
762
1368
  async function buildEvmMultisignBodyContinuumDaoTransfer(args) {
763
1369
  const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? getAddress(args.votingEscrow) : requireVe(args.chainId);
764
1370
  const tokenId = parseTokenId(args.tokenId);
765
- const to = getAddress(args.to);
766
1371
  const from = getAddress(args.executorAddress);
1372
+ const to = await preflightTransfer({
1373
+ rpcUrl: args.rpcUrl,
1374
+ chainId: args.chainId,
1375
+ votingEscrow: ve,
1376
+ owner: from,
1377
+ tokenId,
1378
+ to: args.to
1379
+ });
767
1380
  const data = encodeFunctionData({
768
1381
  abi: veWriteAbi,
769
1382
  functionName: "transferFrom",
@@ -824,11 +1437,20 @@ async function buildEvmMultisignBodyContinuumDaoAttachVeCtm(args) {
824
1437
  const tokenId = parseTokenId(args.tokenId);
825
1438
  const nodeKey = args.nodeKey.trim();
826
1439
  if (!nodeKey) throw new Error("nodeKey is required to attach veCTM.");
1440
+ const groupId = resolveAttachGroupId(args.keyGen, args.groupId);
1441
+ await preflightAttachVeCtm({
1442
+ rpcUrl: args.rpcUrl,
1443
+ chainId: args.chainId,
1444
+ mpaWallet: wallet,
1445
+ nodeKey,
1446
+ tokenId,
1447
+ owner: getAddress(args.executorAddress)
1448
+ });
827
1449
  const tuple = args.nodeInfoTuple ?? encodeContinuumDaoNodeInfo(args.nodeInfo);
828
1450
  const data = encodeFunctionData({
829
1451
  abi: attachAbi,
830
1452
  functionName: "attachVeCtm",
831
- args: [nodeKey, tokenId, tuple]
1453
+ args: [nodeKey, tokenId, tuple, groupId]
832
1454
  });
833
1455
  const step = {
834
1456
  to: wallet,
@@ -838,16 +1460,24 @@ async function buildEvmMultisignBodyContinuumDaoAttachVeCtm(args) {
838
1460
  buildBatchMeta: ({ gasLimit }) => meta("continuum_dao_attach", args.chainId, wallet, {
839
1461
  name: "MultiSignAgentWallet.attachVeCtm",
840
1462
  nodeKey,
841
- tokenId: tokenId.toString()
1463
+ tokenId: tokenId.toString(),
1464
+ groupId
842
1465
  }, gasLimit)
843
1466
  };
844
- return finalize([step], args, `ContinuumDAO: attach veCTM #${tokenId}.`);
1467
+ return finalize([step], args, `ContinuumDAO: attach veCTM #${tokenId} (group ${groupId}).`);
845
1468
  }
846
1469
  async function buildEvmMultisignBodyContinuumDaoRequestDetach(args) {
847
1470
  requireLockChain(args.chainId);
848
1471
  const np = getAddress(args.nodeProperties);
849
1472
  if (np === zeroAddress) throw new Error("NodeProperties address is required to request detach.");
850
1473
  const tokenId = parseTokenId(args.tokenId);
1474
+ await preflightRequestDetach({
1475
+ rpcUrl: args.rpcUrl,
1476
+ chainId: args.chainId,
1477
+ nodeProperties: np,
1478
+ owner: getAddress(args.executorAddress),
1479
+ tokenId
1480
+ });
851
1481
  const data = encodeFunctionData({
852
1482
  abi: detachAbi,
853
1483
  functionName: "setNodeRemovalStatus",
@@ -1447,7 +2077,9 @@ var governorReadAbi = parseAbi([
1447
2077
  "function proposalThreshold() view returns (uint256)",
1448
2078
  "function proposalCount() view returns (uint256)",
1449
2079
  "function clock() view returns (uint48)",
1450
- "function proposalVotes(uint256 proposalId) view returns (uint256 againstVotes, uint256 forVotes, uint256 abstainVotes)"
2080
+ "function proposalVotes(uint256 proposalId) view returns (uint256 againstVotes, uint256 forVotes, uint256 abstainVotes)",
2081
+ "function hasVoted(uint256 proposalId, address account) view returns (bool)",
2082
+ "function proposalSnapshot(uint256 proposalId) view returns (uint256)"
1451
2083
  ]);
1452
2084
  function splitAbiParamList(inner) {
1453
2085
  const out = [];
@@ -1699,9 +2331,32 @@ async function buildEvmMultisignBodyContinuumDaoProposeDelta(args) {
1699
2331
  );
1700
2332
  return { ...built, proposalArgs };
1701
2333
  }
2334
+ async function requireVoteEligible(args) {
2335
+ const st = await continuumDaoFetchProposalState({
2336
+ chainId: args.chainId,
2337
+ rpcUrl: args.rpcUrl,
2338
+ proposalId: args.proposalId,
2339
+ governor: args.governor
2340
+ });
2341
+ if (st.state !== 1) {
2342
+ throw new Error(`Proposal is not Active (on-chain state ${st.state}).`);
2343
+ }
2344
+ const elig = await continuumDaoFetchProposalVoteEligibility({
2345
+ chainId: args.chainId,
2346
+ rpcUrl: args.rpcUrl,
2347
+ proposalId: args.proposalId,
2348
+ account: args.executorAddress,
2349
+ governor: args.governor
2350
+ });
2351
+ if (elig.hasVoted) throw new Error("This KeyGen has already voted on this proposal.");
2352
+ if (BigInt(elig.snapshotVotes) === 0n) {
2353
+ throw new Error("No veCTM voting power at snapshot for this KeyGen.");
2354
+ }
2355
+ }
1702
2356
  async function buildEvmMultisignBodyContinuumDaoCastVoteBravo(args) {
1703
2357
  const gov = requireGovernor(args.chainId, args.governor);
1704
2358
  const proposalId = BigInt(String(args.proposalId).trim());
2359
+ await requireVoteEligible(args);
1705
2360
  const data = encodeFunctionData({
1706
2361
  abi: governorWriteAbi,
1707
2362
  functionName: "castVote",
@@ -1720,6 +2375,10 @@ async function buildEvmMultisignBodyContinuumDaoCastVoteBravo(args) {
1720
2375
  async function buildEvmMultisignBodyContinuumDaoCastVoteDelta(args) {
1721
2376
  const gov = requireGovernor(args.chainId, args.governor);
1722
2377
  const proposalId = BigInt(String(args.proposalId).trim());
2378
+ if (args.weights.length < 3) {
2379
+ throw new Error("Delta vote must include one weight per option plus a NOTA slot.");
2380
+ }
2381
+ await requireVoteEligible(args);
1723
2382
  const params = encodeDeltaVoteParams(args.weights);
1724
2383
  const data = encodeFunctionData({
1725
2384
  abi: governorWriteAbi,
@@ -1772,7 +2431,7 @@ async function buildEvmMultisignBodyContinuumDaoCancel(args) {
1772
2431
  "continuum-dao cancel"
1773
2432
  );
1774
2433
  }
1775
- function publicClient(rpcUrl, chainId) {
2434
+ function publicClient2(rpcUrl, chainId) {
1776
2435
  return createPublicClient({
1777
2436
  chain: defineChain({
1778
2437
  id: chainId,
@@ -1786,7 +2445,7 @@ function publicClient(rpcUrl, chainId) {
1786
2445
  async function continuumDaoHashProposal(args) {
1787
2446
  const chainId = Number(args.chainId);
1788
2447
  const gov = requireGovernor(chainId, args.governor);
1789
- const client = publicClient(args.rpcUrl, chainId);
2448
+ const client = publicClient2(args.rpcUrl, chainId);
1790
2449
  const id = await client.readContract({
1791
2450
  address: gov,
1792
2451
  abi: governorReadAbi,
@@ -1798,7 +2457,7 @@ async function continuumDaoHashProposal(args) {
1798
2457
  async function continuumDaoFetchVotingPower(args) {
1799
2458
  const chainId = Number(args.chainId);
1800
2459
  const gov = requireGovernor(chainId, args.governor);
1801
- const client = publicClient(args.rpcUrl, chainId);
2460
+ const client = publicClient2(args.rpcUrl, chainId);
1802
2461
  const clock = await client.readContract({ address: gov, abi: governorReadAbi, functionName: "clock" });
1803
2462
  const timepoint = clock > 0 ? clock - 1 : 0;
1804
2463
  const [votes, threshold] = await Promise.all([
@@ -1815,22 +2474,74 @@ async function continuumDaoFetchVotingPower(args) {
1815
2474
  async function continuumDaoFetchProposalState(args) {
1816
2475
  const chainId = Number(args.chainId);
1817
2476
  const gov = requireGovernor(chainId, args.governor);
1818
- const client = publicClient(args.rpcUrl, chainId);
2477
+ const client = publicClient2(args.rpcUrl, chainId);
1819
2478
  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
- ]);
2479
+ const state = await client.readContract({
2480
+ address: gov,
2481
+ abi: governorReadAbi,
2482
+ functionName: "state",
2483
+ args: [proposalId]
2484
+ });
2485
+ let againstVotes = "0";
2486
+ let forVotes = "0";
2487
+ let abstainVotes = "0";
2488
+ let votesPartial = false;
2489
+ try {
2490
+ const votes = await client.readContract({
2491
+ address: gov,
2492
+ abi: governorReadAbi,
2493
+ functionName: "proposalVotes",
2494
+ args: [proposalId]
2495
+ });
2496
+ againstVotes = votes[0].toString();
2497
+ forVotes = votes[1].toString();
2498
+ abstainVotes = votes[2].toString();
2499
+ } catch {
2500
+ votesPartial = true;
2501
+ }
1824
2502
  return {
1825
2503
  state: Number(state),
1826
- againstVotes: votes[0].toString(),
1827
- forVotes: votes[1].toString(),
1828
- abstainVotes: votes[2].toString()
2504
+ againstVotes,
2505
+ forVotes,
2506
+ abstainVotes,
2507
+ votesPartial
2508
+ };
2509
+ }
2510
+ async function continuumDaoFetchProposalVoteEligibility(args) {
2511
+ const chainId = Number(args.chainId);
2512
+ const gov = requireGovernor(chainId, args.governor);
2513
+ const client = publicClient2(args.rpcUrl, chainId);
2514
+ const proposalId = BigInt(String(args.proposalId).trim());
2515
+ const account = getAddress(args.account);
2516
+ const [hasVoted, snapshotBlock] = await Promise.all([
2517
+ client.readContract({
2518
+ address: gov,
2519
+ abi: governorReadAbi,
2520
+ functionName: "hasVoted",
2521
+ args: [proposalId, account]
2522
+ }),
2523
+ client.readContract({
2524
+ address: gov,
2525
+ abi: governorReadAbi,
2526
+ functionName: "proposalSnapshot",
2527
+ args: [proposalId]
2528
+ })
2529
+ ]);
2530
+ const snapshotVotes = await client.readContract({
2531
+ address: gov,
2532
+ abi: governorReadAbi,
2533
+ functionName: "getVotes",
2534
+ args: [account, snapshotBlock]
2535
+ });
2536
+ return {
2537
+ hasVoted,
2538
+ snapshotVotes: snapshotVotes.toString(),
2539
+ snapshotBlock: snapshotBlock.toString()
1829
2540
  };
1830
2541
  }
1831
2542
  async function continuumDaoFetchProposalCount(args) {
1832
2543
  const gov = requireGovernor(args.chainId, args.governor);
1833
- const client = publicClient(args.rpcUrl, args.chainId);
2544
+ const client = publicClient2(args.rpcUrl, args.chainId);
1834
2545
  const count = await client.readContract({ address: gov, abi: governorReadAbi, functionName: "proposalCount" });
1835
2546
  return { count: count.toString() };
1836
2547
  }
@@ -2165,7 +2876,7 @@ var continuumDaoProtocolModule = {
2165
2876
  { 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
2877
  { 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
2878
  { 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" } } },
2879
+ { 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
2880
  { 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
2881
  { 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
2882
  { 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 +2901,6 @@ var continuumDaoProtocolModule = {
2190
2901
  };
2191
2902
  registerProtocolModule(continuumDaoProtocolModule);
2192
2903
 
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 };
2904
+ 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, 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, 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
2905
  //# sourceMappingURL=index.js.map
2195
2906
  //# sourceMappingURL=index.js.map