@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.
- package/dist/agent/catalog.cjs +49 -38
- package/dist/agent/catalog.cjs.map +1 -1
- package/dist/agent/catalog.d.ts +722 -2
- package/dist/agent/catalog.js +49 -38
- package/dist/agent/catalog.js.map +1 -1
- package/dist/agent/skills/continuum-dao/SKILL.md +18 -2
- package/dist/{buildBatch-CngvebiD.d.ts → buildBatch-tsEUVn_a.d.ts} +3 -3
- package/dist/chains/evm/index.cjs +33 -22
- package/dist/chains/evm/index.cjs.map +1 -1
- package/dist/chains/evm/index.d.ts +10 -6
- package/dist/chains/evm/index.js +33 -22
- package/dist/chains/evm/index.js.map +1 -1
- package/dist/chains/near/index.d.ts +1 -1
- package/dist/chains/solana/index.d.ts +1 -1
- package/dist/core/index.d.ts +5 -5
- package/dist/{eip712Multisign-DCW7heqX.d.ts → eip712Multisign-6oNIlObj.d.ts} +1 -1
- package/dist/{envelope-C_bfuKab.d.ts → envelope-Cm-llShH.d.ts} +1 -1
- package/dist/index.cjs +33 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.js +33 -22
- package/dist/index.js.map +1 -1
- package/dist/protocols/evm/aave-v4/index.d.ts +1 -1
- package/dist/protocols/evm/aerodrome/index.d.ts +4 -4
- package/dist/protocols/evm/arcus/index.d.ts +2 -2
- package/dist/protocols/evm/circle-cctp/index.d.ts +2 -2
- package/dist/protocols/evm/compound-v3/index.d.ts +1 -1
- package/dist/protocols/evm/continuum-dao/index.cjs +797 -46
- package/dist/protocols/evm/continuum-dao/index.cjs.map +1 -1
- package/dist/protocols/evm/continuum-dao/index.d.ts +189 -5
- package/dist/protocols/evm/continuum-dao/index.js +759 -48
- package/dist/protocols/evm/continuum-dao/index.js.map +1 -1
- package/dist/protocols/evm/curve-dao/index.d.ts +2 -2
- package/dist/protocols/evm/ethena/index.d.ts +1 -1
- package/dist/protocols/evm/euler-v2/index.cjs +33 -22
- package/dist/protocols/evm/euler-v2/index.cjs.map +1 -1
- package/dist/protocols/evm/euler-v2/index.d.ts +1 -1
- package/dist/protocols/evm/euler-v2/index.js +33 -22
- package/dist/protocols/evm/euler-v2/index.js.map +1 -1
- package/dist/protocols/evm/gmx/index.d.ts +2 -2
- package/dist/protocols/evm/hyperliquid/index.d.ts +2 -2
- package/dist/protocols/evm/lido/index.d.ts +1 -1
- package/dist/protocols/evm/maple/index.d.ts +1 -1
- package/dist/protocols/evm/morpho/index.d.ts +1 -1
- package/dist/protocols/evm/pendle/index.d.ts +4 -4
- package/dist/protocols/evm/permit2/index.d.ts +1 -1
- package/dist/protocols/evm/sky/index.d.ts +1 -1
- package/dist/protocols/evm/uniswap-v4/index.d.ts +3 -3
- package/dist/protocols/evm/venice/index.d.ts +1 -1
- package/dist/{registry-DxDSPQ-p.d.ts → registry-BvsXh0-r.d.ts} +1 -1
- package/dist/{types-RZWOTm8c.d.ts → types-RJpoU4Pc.d.ts} +1 -1
- package/dist/{types-PZrvtjv8.d.ts → types-lIfv59N1.d.ts} +2 -0
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { parseAbi, zeroAddress,
|
|
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]: {
|
|
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
|
-
|
|
63
|
-
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
809
|
+
estimatedGas = await args.estimateGasForStep({ step, index: i, publicClient: publicClient3, executor });
|
|
264
810
|
} else {
|
|
265
811
|
try {
|
|
266
|
-
estimatedGas = await
|
|
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:
|
|
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
|
|
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
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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:
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
2477
|
+
const client = publicClient2(args.rpcUrl, chainId);
|
|
1819
2478
|
const proposalId = BigInt(String(args.proposalId).trim());
|
|
1820
|
-
const
|
|
1821
|
-
|
|
1822
|
-
|
|
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
|
|
1827
|
-
forVotes
|
|
1828
|
-
abstainVotes
|
|
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 =
|
|
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
|