@continuumdao/ctm-mpc-defi 0.2.37 → 0.2.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/catalog.cjs +49 -38
- package/dist/agent/catalog.cjs.map +1 -1
- package/dist/agent/catalog.d.ts +722 -2
- package/dist/agent/catalog.js +49 -38
- package/dist/agent/catalog.js.map +1 -1
- package/dist/agent/skills/continuum-dao/SKILL.md +18 -2
- package/dist/{buildBatch-CngvebiD.d.ts → buildBatch-tsEUVn_a.d.ts} +3 -3
- package/dist/chains/evm/index.cjs +33 -22
- package/dist/chains/evm/index.cjs.map +1 -1
- package/dist/chains/evm/index.d.ts +10 -6
- package/dist/chains/evm/index.js +33 -22
- package/dist/chains/evm/index.js.map +1 -1
- package/dist/chains/near/index.d.ts +1 -1
- package/dist/chains/solana/index.d.ts +1 -1
- package/dist/core/index.d.ts +5 -5
- package/dist/{eip712Multisign-DCW7heqX.d.ts → eip712Multisign-6oNIlObj.d.ts} +1 -1
- package/dist/{envelope-C_bfuKab.d.ts → envelope-Cm-llShH.d.ts} +1 -1
- package/dist/index.cjs +33 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.js +33 -22
- package/dist/index.js.map +1 -1
- package/dist/protocols/evm/aave-v4/index.d.ts +1 -1
- package/dist/protocols/evm/aerodrome/index.d.ts +4 -4
- package/dist/protocols/evm/arcus/index.d.ts +2 -2
- package/dist/protocols/evm/circle-cctp/index.d.ts +2 -2
- package/dist/protocols/evm/compound-v3/index.d.ts +1 -1
- package/dist/protocols/evm/continuum-dao/index.cjs +810 -51
- package/dist/protocols/evm/continuum-dao/index.cjs.map +1 -1
- package/dist/protocols/evm/continuum-dao/index.d.ts +191 -5
- package/dist/protocols/evm/continuum-dao/index.js +770 -53
- package/dist/protocols/evm/continuum-dao/index.js.map +1 -1
- package/dist/protocols/evm/curve-dao/index.d.ts +2 -2
- package/dist/protocols/evm/ethena/index.d.ts +1 -1
- package/dist/protocols/evm/euler-v2/index.cjs +33 -22
- package/dist/protocols/evm/euler-v2/index.cjs.map +1 -1
- package/dist/protocols/evm/euler-v2/index.d.ts +1 -1
- package/dist/protocols/evm/euler-v2/index.js +33 -22
- package/dist/protocols/evm/euler-v2/index.js.map +1 -1
- package/dist/protocols/evm/gmx/index.d.ts +2 -2
- package/dist/protocols/evm/hyperliquid/index.d.ts +2 -2
- package/dist/protocols/evm/lido/index.d.ts +1 -1
- package/dist/protocols/evm/maple/index.d.ts +1 -1
- package/dist/protocols/evm/morpho/index.d.ts +1 -1
- package/dist/protocols/evm/pendle/index.d.ts +4 -4
- package/dist/protocols/evm/permit2/index.d.ts +1 -1
- package/dist/protocols/evm/sky/index.d.ts +1 -1
- package/dist/protocols/evm/uniswap-v4/index.d.ts +3 -3
- package/dist/protocols/evm/venice/index.d.ts +1 -1
- package/dist/{registry-DxDSPQ-p.d.ts → registry-BvsXh0-r.d.ts} +1 -1
- package/dist/{types-RZWOTm8c.d.ts → types-RJpoU4Pc.d.ts} +1 -1
- package/dist/{types-PZrvtjv8.d.ts → types-lIfv59N1.d.ts} +2 -0
- package/package.json +1 -1
|
@@ -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,8 @@ var CONTINUUM_DAO_DELEGATE_FALLBACK = 160000n;
|
|
|
402
948
|
var CONTINUUM_DAO_TRANSFER_FALLBACK = 160000n;
|
|
403
949
|
var CONTINUUM_DAO_ATTACH_FALLBACK = 450000n;
|
|
404
950
|
var CONTINUUM_DAO_DETACH_FALLBACK = 220000n;
|
|
405
|
-
var
|
|
406
|
-
var
|
|
407
|
-
var erc20Abi = parseAbi([
|
|
951
|
+
var INCREASE_UNLOCK_TIME_SIGNATURE = "increase_unlock_time(uint256,uint256)";
|
|
952
|
+
var erc20Abi2 = parseAbi([
|
|
408
953
|
"function allowance(address owner, address spender) view returns (uint256)",
|
|
409
954
|
"function approve(address spender, uint256 amount) returns (bool)"
|
|
410
955
|
]);
|
|
@@ -420,7 +965,7 @@ var veWriteAbi = parseAbi([
|
|
|
420
965
|
"function transferFrom(address _from, address _to, uint256 _tokenId)"
|
|
421
966
|
]);
|
|
422
967
|
var attachAbi = parseAbi([
|
|
423
|
-
`function attachVeCtm(string nodeKey, uint256 tokenId, ${VECTM_NODE_INFO_TUPLE} nodeInfo)`
|
|
968
|
+
`function attachVeCtm(string nodeKey, uint256 tokenId, ${VECTM_NODE_INFO_TUPLE} nodeInfo, string groupId)`
|
|
424
969
|
]);
|
|
425
970
|
var detachAbi = parseAbi(["function setNodeRemovalStatus(uint256 tokenId, bool status)"]);
|
|
426
971
|
var CONTINUUM_DAO_EVM_TYPES = /* @__PURE__ */ new Set([
|
|
@@ -459,14 +1004,17 @@ function parseTokenId(raw) {
|
|
|
459
1004
|
if (id <= 0n) throw new Error("veCTM token id must be greater than zero.");
|
|
460
1005
|
return id;
|
|
461
1006
|
}
|
|
1007
|
+
function continuumDaoSignatureText(extra, chainId) {
|
|
1008
|
+
return JSON.stringify({
|
|
1009
|
+
kind: "ContinuumDAO",
|
|
1010
|
+
...extra,
|
|
1011
|
+
chainId
|
|
1012
|
+
});
|
|
1013
|
+
}
|
|
462
1014
|
function meta(evmType, chainId, to, extra, gasLimit) {
|
|
463
1015
|
return {
|
|
464
1016
|
destinationAddress: to,
|
|
465
|
-
signatureText:
|
|
466
|
-
kind: "ContinuumDAO",
|
|
467
|
-
...extra,
|
|
468
|
-
chainId
|
|
469
|
-
}),
|
|
1017
|
+
signatureText: continuumDaoSignatureText(extra, chainId),
|
|
470
1018
|
evm: { type: evmType, version: 1, chainId: String(chainId), protocolId: CONTINUUM_DAO_PROTOCOL_ID },
|
|
471
1019
|
continuumDao: { ...extra, gas: { baseGasUnits: gasLimit.toString() } }
|
|
472
1020
|
};
|
|
@@ -511,7 +1059,7 @@ async function maybeApproveCtmStep(args) {
|
|
|
511
1059
|
try {
|
|
512
1060
|
allowance = await client.readContract({
|
|
513
1061
|
address: args.ctm,
|
|
514
|
-
abi:
|
|
1062
|
+
abi: erc20Abi2,
|
|
515
1063
|
functionName: "allowance",
|
|
516
1064
|
args: [args.owner, args.spender]
|
|
517
1065
|
});
|
|
@@ -520,7 +1068,7 @@ async function maybeApproveCtmStep(args) {
|
|
|
520
1068
|
}
|
|
521
1069
|
if (allowance >= args.amountWei) return null;
|
|
522
1070
|
const data = encodeFunctionData({
|
|
523
|
-
abi:
|
|
1071
|
+
abi: erc20Abi2,
|
|
524
1072
|
functionName: "approve",
|
|
525
1073
|
args: [args.spender, args.amountWei]
|
|
526
1074
|
});
|
|
@@ -540,10 +1088,17 @@ async function buildEvmMultisignBodyContinuumDaoCreateLock(args) {
|
|
|
540
1088
|
const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? getAddress(args.votingEscrow) : requireVe(args.chainId);
|
|
541
1089
|
const ctm = args.ctm && isConfiguredAddress(args.ctm) ? getAddress(args.ctm) : requireCtm(args.chainId);
|
|
542
1090
|
const amountWei = parseUnits(String(args.amountHuman).trim().replace(/,/g, ""), CTM_TOKEN_DECIMALS);
|
|
543
|
-
if (amountWei <= 0n) throw new Error("CTM amount must be greater than zero.");
|
|
544
1091
|
const duration = BigInt(String(args.lockDurationSeconds).trim());
|
|
545
|
-
if (duration <= 0n) throw new Error("Lock duration must be greater than zero.");
|
|
546
1092
|
const owner = getAddress(args.executorAddress);
|
|
1093
|
+
await preflightCreateLock({
|
|
1094
|
+
rpcUrl: args.rpcUrl,
|
|
1095
|
+
chainId: args.chainId,
|
|
1096
|
+
votingEscrow: ve,
|
|
1097
|
+
ctm,
|
|
1098
|
+
owner,
|
|
1099
|
+
amountWei,
|
|
1100
|
+
durationSec: duration
|
|
1101
|
+
});
|
|
547
1102
|
const approve = await maybeApproveCtmStep({
|
|
548
1103
|
rpcUrl: args.rpcUrl,
|
|
549
1104
|
chainId: args.chainId,
|
|
@@ -576,8 +1131,16 @@ async function buildEvmMultisignBodyContinuumDaoIncreaseAmount(args) {
|
|
|
576
1131
|
const ctm = args.ctm && isConfiguredAddress(args.ctm) ? getAddress(args.ctm) : requireCtm(args.chainId);
|
|
577
1132
|
const tokenId = parseTokenId(args.tokenId);
|
|
578
1133
|
const amountWei = parseUnits(String(args.amountHuman).trim().replace(/,/g, ""), CTM_TOKEN_DECIMALS);
|
|
579
|
-
if (amountWei <= 0n) throw new Error("CTM amount must be greater than zero.");
|
|
580
1134
|
const owner = getAddress(args.executorAddress);
|
|
1135
|
+
await preflightIncreaseAmount({
|
|
1136
|
+
rpcUrl: args.rpcUrl,
|
|
1137
|
+
chainId: args.chainId,
|
|
1138
|
+
votingEscrow: ve,
|
|
1139
|
+
ctm,
|
|
1140
|
+
owner,
|
|
1141
|
+
tokenId,
|
|
1142
|
+
amountWei
|
|
1143
|
+
});
|
|
581
1144
|
const approve = await maybeApproveCtmStep({
|
|
582
1145
|
rpcUrl: args.rpcUrl,
|
|
583
1146
|
chainId: args.chainId,
|
|
@@ -609,7 +1172,15 @@ async function buildEvmMultisignBodyContinuumDaoIncreaseUnlockTime(args) {
|
|
|
609
1172
|
const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? getAddress(args.votingEscrow) : requireVe(args.chainId);
|
|
610
1173
|
const tokenId = parseTokenId(args.tokenId);
|
|
611
1174
|
const duration = BigInt(String(args.lockDurationSeconds).trim());
|
|
612
|
-
|
|
1175
|
+
const owner = getAddress(args.executorAddress);
|
|
1176
|
+
await preflightIncreaseUnlockTime({
|
|
1177
|
+
rpcUrl: args.rpcUrl,
|
|
1178
|
+
chainId: args.chainId,
|
|
1179
|
+
votingEscrow: ve,
|
|
1180
|
+
owner,
|
|
1181
|
+
tokenId,
|
|
1182
|
+
durationSec: duration
|
|
1183
|
+
});
|
|
613
1184
|
const data = encodeFunctionData({
|
|
614
1185
|
abi: veWriteAbi,
|
|
615
1186
|
functionName: "increase_unlock_time",
|
|
@@ -622,6 +1193,7 @@ async function buildEvmMultisignBodyContinuumDaoIncreaseUnlockTime(args) {
|
|
|
622
1193
|
fallbackGas: CONTINUUM_DAO_INCREASE_UNLOCK_FALLBACK,
|
|
623
1194
|
buildBatchMeta: ({ gasLimit }) => meta("continuum_dao_increase_unlock_time", args.chainId, ve, {
|
|
624
1195
|
name: "VotingEscrow.increase_unlock_time",
|
|
1196
|
+
signature: INCREASE_UNLOCK_TIME_SIGNATURE,
|
|
625
1197
|
tokenId: tokenId.toString(),
|
|
626
1198
|
lockDurationSeconds: duration.toString()
|
|
627
1199
|
}, gasLimit)
|
|
@@ -632,7 +1204,14 @@ async function buildEvmMultisignBodyContinuumDaoSplit(args) {
|
|
|
632
1204
|
const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? getAddress(args.votingEscrow) : requireVe(args.chainId);
|
|
633
1205
|
const tokenId = parseTokenId(args.tokenId);
|
|
634
1206
|
const extracted = parseUnits(String(args.extractedHuman).trim().replace(/,/g, ""), CTM_TOKEN_DECIMALS);
|
|
635
|
-
|
|
1207
|
+
await preflightSplit({
|
|
1208
|
+
rpcUrl: args.rpcUrl,
|
|
1209
|
+
chainId: args.chainId,
|
|
1210
|
+
votingEscrow: ve,
|
|
1211
|
+
owner: getAddress(args.executorAddress),
|
|
1212
|
+
tokenId,
|
|
1213
|
+
extractedWei: extracted
|
|
1214
|
+
});
|
|
636
1215
|
const data = encodeFunctionData({
|
|
637
1216
|
abi: veWriteAbi,
|
|
638
1217
|
functionName: "split",
|
|
@@ -655,7 +1234,14 @@ async function buildEvmMultisignBodyContinuumDaoMerge(args) {
|
|
|
655
1234
|
const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? getAddress(args.votingEscrow) : requireVe(args.chainId);
|
|
656
1235
|
const fromId = parseTokenId(args.fromTokenId);
|
|
657
1236
|
const toId = parseTokenId(args.toTokenId);
|
|
658
|
-
|
|
1237
|
+
await preflightMerge({
|
|
1238
|
+
rpcUrl: args.rpcUrl,
|
|
1239
|
+
chainId: args.chainId,
|
|
1240
|
+
votingEscrow: ve,
|
|
1241
|
+
owner: getAddress(args.executorAddress),
|
|
1242
|
+
fromTokenId: fromId,
|
|
1243
|
+
toTokenId: toId
|
|
1244
|
+
});
|
|
659
1245
|
const data = encodeFunctionData({
|
|
660
1246
|
abi: veWriteAbi,
|
|
661
1247
|
functionName: "merge",
|
|
@@ -677,6 +1263,13 @@ async function buildEvmMultisignBodyContinuumDaoMerge(args) {
|
|
|
677
1263
|
async function buildEvmMultisignBodyContinuumDaoWithdraw(args) {
|
|
678
1264
|
const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? getAddress(args.votingEscrow) : requireVe(args.chainId);
|
|
679
1265
|
const tokenId = parseTokenId(args.tokenId);
|
|
1266
|
+
await preflightWithdraw({
|
|
1267
|
+
rpcUrl: args.rpcUrl,
|
|
1268
|
+
chainId: args.chainId,
|
|
1269
|
+
votingEscrow: ve,
|
|
1270
|
+
owner: getAddress(args.executorAddress),
|
|
1271
|
+
tokenId
|
|
1272
|
+
});
|
|
680
1273
|
const data = encodeFunctionData({ abi: veWriteAbi, functionName: "withdraw", args: [tokenId] });
|
|
681
1274
|
const step = {
|
|
682
1275
|
to: ve,
|
|
@@ -693,6 +1286,13 @@ async function buildEvmMultisignBodyContinuumDaoWithdraw(args) {
|
|
|
693
1286
|
async function buildEvmMultisignBodyContinuumDaoLiquidate(args) {
|
|
694
1287
|
const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? getAddress(args.votingEscrow) : requireVe(args.chainId);
|
|
695
1288
|
const tokenId = parseTokenId(args.tokenId);
|
|
1289
|
+
await preflightLiquidate({
|
|
1290
|
+
rpcUrl: args.rpcUrl,
|
|
1291
|
+
chainId: args.chainId,
|
|
1292
|
+
votingEscrow: ve,
|
|
1293
|
+
owner: getAddress(args.executorAddress),
|
|
1294
|
+
tokenId
|
|
1295
|
+
});
|
|
696
1296
|
const data = encodeFunctionData({ abi: veWriteAbi, functionName: "liquidate", args: [tokenId] });
|
|
697
1297
|
const step = {
|
|
698
1298
|
to: ve,
|
|
@@ -708,7 +1308,12 @@ async function buildEvmMultisignBodyContinuumDaoLiquidate(args) {
|
|
|
708
1308
|
}
|
|
709
1309
|
async function buildEvmMultisignBodyContinuumDaoDelegate(args) {
|
|
710
1310
|
const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? getAddress(args.votingEscrow) : requireVe(args.chainId);
|
|
711
|
-
const delegatee =
|
|
1311
|
+
const delegatee = await preflightDelegate({
|
|
1312
|
+
rpcUrl: args.rpcUrl,
|
|
1313
|
+
chainId: args.chainId,
|
|
1314
|
+
owner: getAddress(args.executorAddress),
|
|
1315
|
+
delegatee: args.delegatee
|
|
1316
|
+
});
|
|
712
1317
|
const data = encodeFunctionData({ abi: veWriteAbi, functionName: "delegate", args: [delegatee] });
|
|
713
1318
|
const step = {
|
|
714
1319
|
to: ve,
|
|
@@ -725,6 +1330,12 @@ async function buildEvmMultisignBodyContinuumDaoDelegate(args) {
|
|
|
725
1330
|
async function buildEvmMultisignBodyContinuumDaoUndelegate(args) {
|
|
726
1331
|
const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? getAddress(args.votingEscrow) : requireVe(args.chainId);
|
|
727
1332
|
const self = getAddress(args.executorAddress);
|
|
1333
|
+
await preflightUndelegate({
|
|
1334
|
+
rpcUrl: args.rpcUrl,
|
|
1335
|
+
chainId: args.chainId,
|
|
1336
|
+
votingEscrow: ve,
|
|
1337
|
+
owner: self
|
|
1338
|
+
});
|
|
728
1339
|
const data = encodeFunctionData({ abi: veWriteAbi, functionName: "delegate", args: [self] });
|
|
729
1340
|
const step = {
|
|
730
1341
|
to: ve,
|
|
@@ -739,7 +1350,7 @@ async function buildEvmMultisignBodyContinuumDaoUndelegate(args) {
|
|
|
739
1350
|
};
|
|
740
1351
|
return finalize([step], args, `ContinuumDAO: undelegate voting power (delegate back to ${self}).`);
|
|
741
1352
|
}
|
|
742
|
-
var
|
|
1353
|
+
var veReadAbi2 = parseAbi(["function delegates(address account) view returns (address)"]);
|
|
743
1354
|
async function continuumDaoFetchDelegates(args) {
|
|
744
1355
|
const chainId = Number(args.chainId);
|
|
745
1356
|
requireLockChain(chainId);
|
|
@@ -755,15 +1366,22 @@ async function continuumDaoFetchDelegates(args) {
|
|
|
755
1366
|
transport: http(args.rpcUrl.trim())
|
|
756
1367
|
});
|
|
757
1368
|
const delegatee = getAddress(
|
|
758
|
-
await client.readContract({ address: ve, abi:
|
|
1369
|
+
await client.readContract({ address: ve, abi: veReadAbi2, functionName: "delegates", args: [account] })
|
|
759
1370
|
);
|
|
760
1371
|
return { account, delegatee, self: delegatee === account };
|
|
761
1372
|
}
|
|
762
1373
|
async function buildEvmMultisignBodyContinuumDaoTransfer(args) {
|
|
763
1374
|
const ve = args.votingEscrow && isConfiguredAddress(args.votingEscrow) ? getAddress(args.votingEscrow) : requireVe(args.chainId);
|
|
764
1375
|
const tokenId = parseTokenId(args.tokenId);
|
|
765
|
-
const to = getAddress(args.to);
|
|
766
1376
|
const from = getAddress(args.executorAddress);
|
|
1377
|
+
const to = await preflightTransfer({
|
|
1378
|
+
rpcUrl: args.rpcUrl,
|
|
1379
|
+
chainId: args.chainId,
|
|
1380
|
+
votingEscrow: ve,
|
|
1381
|
+
owner: from,
|
|
1382
|
+
tokenId,
|
|
1383
|
+
to: args.to
|
|
1384
|
+
});
|
|
767
1385
|
const data = encodeFunctionData({
|
|
768
1386
|
abi: veWriteAbi,
|
|
769
1387
|
functionName: "transferFrom",
|
|
@@ -824,11 +1442,20 @@ async function buildEvmMultisignBodyContinuumDaoAttachVeCtm(args) {
|
|
|
824
1442
|
const tokenId = parseTokenId(args.tokenId);
|
|
825
1443
|
const nodeKey = args.nodeKey.trim();
|
|
826
1444
|
if (!nodeKey) throw new Error("nodeKey is required to attach veCTM.");
|
|
1445
|
+
const groupId = resolveAttachGroupId(args.keyGen, args.groupId);
|
|
1446
|
+
await preflightAttachVeCtm({
|
|
1447
|
+
rpcUrl: args.rpcUrl,
|
|
1448
|
+
chainId: args.chainId,
|
|
1449
|
+
mpaWallet: wallet,
|
|
1450
|
+
nodeKey,
|
|
1451
|
+
tokenId,
|
|
1452
|
+
owner: getAddress(args.executorAddress)
|
|
1453
|
+
});
|
|
827
1454
|
const tuple = args.nodeInfoTuple ?? encodeContinuumDaoNodeInfo(args.nodeInfo);
|
|
828
1455
|
const data = encodeFunctionData({
|
|
829
1456
|
abi: attachAbi,
|
|
830
1457
|
functionName: "attachVeCtm",
|
|
831
|
-
args: [nodeKey, tokenId, tuple]
|
|
1458
|
+
args: [nodeKey, tokenId, tuple, groupId]
|
|
832
1459
|
});
|
|
833
1460
|
const step = {
|
|
834
1461
|
to: wallet,
|
|
@@ -837,17 +1464,26 @@ async function buildEvmMultisignBodyContinuumDaoAttachVeCtm(args) {
|
|
|
837
1464
|
fallbackGas: CONTINUUM_DAO_ATTACH_FALLBACK,
|
|
838
1465
|
buildBatchMeta: ({ gasLimit }) => meta("continuum_dao_attach", args.chainId, wallet, {
|
|
839
1466
|
name: "MultiSignAgentWallet.attachVeCtm",
|
|
1467
|
+
signature: ATTACH_VECTM_SIGNATURE,
|
|
840
1468
|
nodeKey,
|
|
841
|
-
tokenId: tokenId.toString()
|
|
1469
|
+
tokenId: tokenId.toString(),
|
|
1470
|
+
groupId
|
|
842
1471
|
}, gasLimit)
|
|
843
1472
|
};
|
|
844
|
-
return finalize([step], args, `ContinuumDAO: attach veCTM #${tokenId}.`);
|
|
1473
|
+
return finalize([step], args, `ContinuumDAO: attach veCTM #${tokenId} (group ${groupId}).`);
|
|
845
1474
|
}
|
|
846
1475
|
async function buildEvmMultisignBodyContinuumDaoRequestDetach(args) {
|
|
847
1476
|
requireLockChain(args.chainId);
|
|
848
1477
|
const np = getAddress(args.nodeProperties);
|
|
849
1478
|
if (np === zeroAddress) throw new Error("NodeProperties address is required to request detach.");
|
|
850
1479
|
const tokenId = parseTokenId(args.tokenId);
|
|
1480
|
+
await preflightRequestDetach({
|
|
1481
|
+
rpcUrl: args.rpcUrl,
|
|
1482
|
+
chainId: args.chainId,
|
|
1483
|
+
nodeProperties: np,
|
|
1484
|
+
owner: getAddress(args.executorAddress),
|
|
1485
|
+
tokenId
|
|
1486
|
+
});
|
|
851
1487
|
const data = encodeFunctionData({
|
|
852
1488
|
abi: detachAbi,
|
|
853
1489
|
functionName: "setNodeRemovalStatus",
|
|
@@ -1447,7 +2083,9 @@ var governorReadAbi = parseAbi([
|
|
|
1447
2083
|
"function proposalThreshold() view returns (uint256)",
|
|
1448
2084
|
"function proposalCount() view returns (uint256)",
|
|
1449
2085
|
"function clock() view returns (uint48)",
|
|
1450
|
-
"function proposalVotes(uint256 proposalId) view returns (uint256 againstVotes, uint256 forVotes, uint256 abstainVotes)"
|
|
2086
|
+
"function proposalVotes(uint256 proposalId) view returns (uint256 againstVotes, uint256 forVotes, uint256 abstainVotes)",
|
|
2087
|
+
"function hasVoted(uint256 proposalId, address account) view returns (bool)",
|
|
2088
|
+
"function proposalSnapshot(uint256 proposalId) view returns (uint256)"
|
|
1451
2089
|
]);
|
|
1452
2090
|
function splitAbiParamList(inner) {
|
|
1453
2091
|
const out = [];
|
|
@@ -1699,9 +2337,32 @@ async function buildEvmMultisignBodyContinuumDaoProposeDelta(args) {
|
|
|
1699
2337
|
);
|
|
1700
2338
|
return { ...built, proposalArgs };
|
|
1701
2339
|
}
|
|
2340
|
+
async function requireVoteEligible(args) {
|
|
2341
|
+
const st = await continuumDaoFetchProposalState({
|
|
2342
|
+
chainId: args.chainId,
|
|
2343
|
+
rpcUrl: args.rpcUrl,
|
|
2344
|
+
proposalId: args.proposalId,
|
|
2345
|
+
governor: args.governor
|
|
2346
|
+
});
|
|
2347
|
+
if (st.state !== 1) {
|
|
2348
|
+
throw new Error(`Proposal is not Active (on-chain state ${st.state}).`);
|
|
2349
|
+
}
|
|
2350
|
+
const elig = await continuumDaoFetchProposalVoteEligibility({
|
|
2351
|
+
chainId: args.chainId,
|
|
2352
|
+
rpcUrl: args.rpcUrl,
|
|
2353
|
+
proposalId: args.proposalId,
|
|
2354
|
+
account: args.executorAddress,
|
|
2355
|
+
governor: args.governor
|
|
2356
|
+
});
|
|
2357
|
+
if (elig.hasVoted) throw new Error("This KeyGen has already voted on this proposal.");
|
|
2358
|
+
if (BigInt(elig.snapshotVotes) === 0n) {
|
|
2359
|
+
throw new Error("No veCTM voting power at snapshot for this KeyGen.");
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
1702
2362
|
async function buildEvmMultisignBodyContinuumDaoCastVoteBravo(args) {
|
|
1703
2363
|
const gov = requireGovernor(args.chainId, args.governor);
|
|
1704
2364
|
const proposalId = BigInt(String(args.proposalId).trim());
|
|
2365
|
+
await requireVoteEligible(args);
|
|
1705
2366
|
const data = encodeFunctionData({
|
|
1706
2367
|
abi: governorWriteAbi,
|
|
1707
2368
|
functionName: "castVote",
|
|
@@ -1720,6 +2381,10 @@ async function buildEvmMultisignBodyContinuumDaoCastVoteBravo(args) {
|
|
|
1720
2381
|
async function buildEvmMultisignBodyContinuumDaoCastVoteDelta(args) {
|
|
1721
2382
|
const gov = requireGovernor(args.chainId, args.governor);
|
|
1722
2383
|
const proposalId = BigInt(String(args.proposalId).trim());
|
|
2384
|
+
if (args.weights.length < 3) {
|
|
2385
|
+
throw new Error("Delta vote must include one weight per option plus a NOTA slot.");
|
|
2386
|
+
}
|
|
2387
|
+
await requireVoteEligible(args);
|
|
1723
2388
|
const params = encodeDeltaVoteParams(args.weights);
|
|
1724
2389
|
const data = encodeFunctionData({
|
|
1725
2390
|
abi: governorWriteAbi,
|
|
@@ -1772,7 +2437,7 @@ async function buildEvmMultisignBodyContinuumDaoCancel(args) {
|
|
|
1772
2437
|
"continuum-dao cancel"
|
|
1773
2438
|
);
|
|
1774
2439
|
}
|
|
1775
|
-
function
|
|
2440
|
+
function publicClient2(rpcUrl, chainId) {
|
|
1776
2441
|
return createPublicClient({
|
|
1777
2442
|
chain: defineChain({
|
|
1778
2443
|
id: chainId,
|
|
@@ -1786,7 +2451,7 @@ function publicClient(rpcUrl, chainId) {
|
|
|
1786
2451
|
async function continuumDaoHashProposal(args) {
|
|
1787
2452
|
const chainId = Number(args.chainId);
|
|
1788
2453
|
const gov = requireGovernor(chainId, args.governor);
|
|
1789
|
-
const client =
|
|
2454
|
+
const client = publicClient2(args.rpcUrl, chainId);
|
|
1790
2455
|
const id = await client.readContract({
|
|
1791
2456
|
address: gov,
|
|
1792
2457
|
abi: governorReadAbi,
|
|
@@ -1798,7 +2463,7 @@ async function continuumDaoHashProposal(args) {
|
|
|
1798
2463
|
async function continuumDaoFetchVotingPower(args) {
|
|
1799
2464
|
const chainId = Number(args.chainId);
|
|
1800
2465
|
const gov = requireGovernor(chainId, args.governor);
|
|
1801
|
-
const client =
|
|
2466
|
+
const client = publicClient2(args.rpcUrl, chainId);
|
|
1802
2467
|
const clock = await client.readContract({ address: gov, abi: governorReadAbi, functionName: "clock" });
|
|
1803
2468
|
const timepoint = clock > 0 ? clock - 1 : 0;
|
|
1804
2469
|
const [votes, threshold] = await Promise.all([
|
|
@@ -1815,22 +2480,74 @@ async function continuumDaoFetchVotingPower(args) {
|
|
|
1815
2480
|
async function continuumDaoFetchProposalState(args) {
|
|
1816
2481
|
const chainId = Number(args.chainId);
|
|
1817
2482
|
const gov = requireGovernor(chainId, args.governor);
|
|
1818
|
-
const client =
|
|
2483
|
+
const client = publicClient2(args.rpcUrl, chainId);
|
|
1819
2484
|
const proposalId = BigInt(String(args.proposalId).trim());
|
|
1820
|
-
const
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
2485
|
+
const state = await client.readContract({
|
|
2486
|
+
address: gov,
|
|
2487
|
+
abi: governorReadAbi,
|
|
2488
|
+
functionName: "state",
|
|
2489
|
+
args: [proposalId]
|
|
2490
|
+
});
|
|
2491
|
+
let againstVotes = "0";
|
|
2492
|
+
let forVotes = "0";
|
|
2493
|
+
let abstainVotes = "0";
|
|
2494
|
+
let votesPartial = false;
|
|
2495
|
+
try {
|
|
2496
|
+
const votes = await client.readContract({
|
|
2497
|
+
address: gov,
|
|
2498
|
+
abi: governorReadAbi,
|
|
2499
|
+
functionName: "proposalVotes",
|
|
2500
|
+
args: [proposalId]
|
|
2501
|
+
});
|
|
2502
|
+
againstVotes = votes[0].toString();
|
|
2503
|
+
forVotes = votes[1].toString();
|
|
2504
|
+
abstainVotes = votes[2].toString();
|
|
2505
|
+
} catch {
|
|
2506
|
+
votesPartial = true;
|
|
2507
|
+
}
|
|
1824
2508
|
return {
|
|
1825
2509
|
state: Number(state),
|
|
1826
|
-
againstVotes
|
|
1827
|
-
forVotes
|
|
1828
|
-
abstainVotes
|
|
2510
|
+
againstVotes,
|
|
2511
|
+
forVotes,
|
|
2512
|
+
abstainVotes,
|
|
2513
|
+
votesPartial
|
|
2514
|
+
};
|
|
2515
|
+
}
|
|
2516
|
+
async function continuumDaoFetchProposalVoteEligibility(args) {
|
|
2517
|
+
const chainId = Number(args.chainId);
|
|
2518
|
+
const gov = requireGovernor(chainId, args.governor);
|
|
2519
|
+
const client = publicClient2(args.rpcUrl, chainId);
|
|
2520
|
+
const proposalId = BigInt(String(args.proposalId).trim());
|
|
2521
|
+
const account = getAddress(args.account);
|
|
2522
|
+
const [hasVoted, snapshotBlock] = await Promise.all([
|
|
2523
|
+
client.readContract({
|
|
2524
|
+
address: gov,
|
|
2525
|
+
abi: governorReadAbi,
|
|
2526
|
+
functionName: "hasVoted",
|
|
2527
|
+
args: [proposalId, account]
|
|
2528
|
+
}),
|
|
2529
|
+
client.readContract({
|
|
2530
|
+
address: gov,
|
|
2531
|
+
abi: governorReadAbi,
|
|
2532
|
+
functionName: "proposalSnapshot",
|
|
2533
|
+
args: [proposalId]
|
|
2534
|
+
})
|
|
2535
|
+
]);
|
|
2536
|
+
const snapshotVotes = await client.readContract({
|
|
2537
|
+
address: gov,
|
|
2538
|
+
abi: governorReadAbi,
|
|
2539
|
+
functionName: "getVotes",
|
|
2540
|
+
args: [account, snapshotBlock]
|
|
2541
|
+
});
|
|
2542
|
+
return {
|
|
2543
|
+
hasVoted,
|
|
2544
|
+
snapshotVotes: snapshotVotes.toString(),
|
|
2545
|
+
snapshotBlock: snapshotBlock.toString()
|
|
1829
2546
|
};
|
|
1830
2547
|
}
|
|
1831
2548
|
async function continuumDaoFetchProposalCount(args) {
|
|
1832
2549
|
const gov = requireGovernor(args.chainId, args.governor);
|
|
1833
|
-
const client =
|
|
2550
|
+
const client = publicClient2(args.rpcUrl, args.chainId);
|
|
1834
2551
|
const count = await client.readContract({ address: gov, abi: governorReadAbi, functionName: "proposalCount" });
|
|
1835
2552
|
return { count: count.toString() };
|
|
1836
2553
|
}
|
|
@@ -2165,7 +2882,7 @@ var continuumDaoProtocolModule = {
|
|
|
2165
2882
|
{ id: "continuum-dao.undelegate", protocolId: CONTINUUM_DAO_PROTOCOL_ID, chainCategory: "evm", description: "Return voting power to the KeyGen (delegate to self)", commonParams: ["keyGen", "purposeText", "useCustomGas"], params: {} },
|
|
2166
2883
|
{ id: "continuum-dao.fetch-delegates", protocolId: CONTINUUM_DAO_PROTOCOL_ID, chainCategory: "evm", description: "Read VotingEscrow.delegates(account)", commonParams: [], params: { account: { type: "string", required: true, description: "KeyGen or wallet address" } } },
|
|
2167
2884
|
{ id: "continuum-dao.transfer", protocolId: CONTINUUM_DAO_PROTOCOL_ID, chainCategory: "evm", description: "Transfer a veCTM NFT", commonParams: ["keyGen", "purposeText", "useCustomGas"], params: { tokenId: { type: "string", required: true, description: "veCTM token id" }, to: { type: "string", required: true, description: "Recipient" } } },
|
|
2168
|
-
{ id: "continuum-dao.attach", protocolId: CONTINUUM_DAO_PROTOCOL_ID, chainCategory: "evm", description: "Attach veCTM to the node (authority KeyGen)", commonParams: ["keyGen", "purposeText", "useCustomGas"], params: { nodeKey: { type: "string", required: true, description: "Node key" }, tokenId: { type: "string", required: true, description: "veCTM token id" }, mpaWallet: { type: "string", required: true, description: "MultiSignAgentWallet address" } } },
|
|
2885
|
+
{ id: "continuum-dao.attach", protocolId: CONTINUUM_DAO_PROTOCOL_ID, chainCategory: "evm", description: "Attach veCTM to the node (authority KeyGen). groupId is implicit from that KeyGen and binds the fee waiver. A second group attaches after rotating authority.", commonParams: ["keyGen", "purposeText", "useCustomGas"], params: { nodeKey: { type: "string", required: true, description: "Node key" }, tokenId: { type: "string", required: true, description: "veCTM token id" }, mpaWallet: { type: "string", required: true, description: "MultiSignAgentWallet address" } } },
|
|
2169
2886
|
{ id: "continuum-dao.request-detach", protocolId: CONTINUUM_DAO_PROTOCOL_ID, chainCategory: "evm", description: "Request governance detach", commonParams: ["keyGen", "purposeText", "useCustomGas"], params: { tokenId: { type: "string", required: true, description: "Attached token id" }, nodeProperties: { type: "string", required: true, description: "NodeProperties address" } } },
|
|
2170
2887
|
{ id: "continuum-dao.propose-bravo", protocolId: CONTINUUM_DAO_PROTOCOL_ID, chainCategory: "evm", description: "Propose a Bravo (For/Against/Abstain) proposal", commonParams: ["keyGen", "purposeText", "useCustomGas"], params: { title: { type: "string", required: true, description: "On-chain description (title)" }, forumKey: { type: "string", required: true, description: "Created forum thread URL from forum_create_topic" } } },
|
|
2171
2888
|
{ id: "continuum-dao.propose-delta", protocolId: CONTINUUM_DAO_PROTOCOL_ID, chainCategory: "evm", description: "Propose a Delta multi-option proposal", commonParams: ["keyGen", "purposeText", "useCustomGas"], params: { title: { type: "string", required: true, description: "On-chain description (title)" }, nWinners: { type: "number", required: true, description: "Winning options to execute" }, forumKey: { type: "string", required: true, description: "Created forum thread URL from forum_create_topic" } } },
|
|
@@ -2190,6 +2907,6 @@ var continuumDaoProtocolModule = {
|
|
|
2190
2907
|
};
|
|
2191
2908
|
registerProtocolModule(continuumDaoProtocolModule);
|
|
2192
2909
|
|
|
2193
|
-
export { ATTACH_VECTM_SIGNATURE, CONTINUUM_DAO_APPROVE_FALLBACK, CONTINUUM_DAO_ATTACH_FALLBACK, CONTINUUM_DAO_CANCEL_FALLBACK, CONTINUUM_DAO_CREATE_LOCK_FALLBACK, CONTINUUM_DAO_DELEGATE_FALLBACK, CONTINUUM_DAO_DEPLOYMENTS, CONTINUUM_DAO_DETACH_FALLBACK, CONTINUUM_DAO_ETHEREUM_CHAIN_ID, CONTINUUM_DAO_EVM_TYPES, CONTINUUM_DAO_EXECUTE_FALLBACK, CONTINUUM_DAO_GOVERNANCE_API, CONTINUUM_DAO_GOV_EVM_TYPES, CONTINUUM_DAO_INCREASE_AMOUNT_FALLBACK, CONTINUUM_DAO_INCREASE_UNLOCK_FALLBACK, CONTINUUM_DAO_LINEA_CHAIN_ID, CONTINUUM_DAO_LINEA_SEPOLIA_CHAIN_ID, CONTINUUM_DAO_LIQUIDATE_FALLBACK, CONTINUUM_DAO_MERGE_FALLBACK, CONTINUUM_DAO_PROPOSE_FALLBACK, CONTINUUM_DAO_PROTOCOL_ID, CONTINUUM_DAO_SPLIT_FALLBACK, CONTINUUM_DAO_TRANSFER_FALLBACK, CONTINUUM_DAO_VOTE_FALLBACK, CONTINUUM_DAO_WITHDRAW_FALLBACK, CTM_TOKEN_DECIMALS, CTM_TOKEN_NAME, CTM_TOKEN_SYMBOL, FORUM_GOVERNANCE_SECTIONS, FORUM_IDEA_SECTION, FORUM_LOGIN_DELIVERY_KIND, GOVERNOR_STATE, GOVERNOR_STATE_LABELS, MAXTIME_SECONDS, PROPOSAL_TYPE_LABELS, PROPOSAL_TYPE_TO_FORUM_SECTION, VECTM_NODE_INFO_TUPLE, VECTM_TOKEN_NAME, VECTM_TOKEN_SYMBOL, WEEK_SECONDS, applyForumSectionCheck, assertContinuumDaoForumDeleteInput, assertContinuumDaoForumMarkReadInput, assertContinuumDaoForumRecentInput, assertContinuumDaoForumSearchInput, assertContinuumDaoForumTopicKey, bucketGovernorState, buildBravoProposalArgs, buildDeltaProposalArgs, buildEvmMultisignBodyContinuumDaoAttachVeCtm, buildEvmMultisignBodyContinuumDaoAttachVeCtmFromMcp, buildEvmMultisignBodyContinuumDaoCancel, buildEvmMultisignBodyContinuumDaoCastVoteBravo, buildEvmMultisignBodyContinuumDaoCastVoteBravoFromMcp, buildEvmMultisignBodyContinuumDaoCastVoteDelta, buildEvmMultisignBodyContinuumDaoCreateLock, buildEvmMultisignBodyContinuumDaoDelegate, buildEvmMultisignBodyContinuumDaoExecute, buildEvmMultisignBodyContinuumDaoForumSignIn, buildEvmMultisignBodyContinuumDaoIncreaseAmount, buildEvmMultisignBodyContinuumDaoIncreaseUnlockTime, buildEvmMultisignBodyContinuumDaoLiquidate, buildEvmMultisignBodyContinuumDaoMerge, buildEvmMultisignBodyContinuumDaoProposeBravo, buildEvmMultisignBodyContinuumDaoProposeDelta, buildEvmMultisignBodyContinuumDaoProposeDeltaFromMcp, buildEvmMultisignBodyContinuumDaoRequestDetach, buildEvmMultisignBodyContinuumDaoSplit, buildEvmMultisignBodyContinuumDaoTransfer, buildEvmMultisignBodyContinuumDaoUndelegate, buildEvmMultisignBodyContinuumDaoWithdraw, continuumDaoDeployment, continuumDaoExplainProposal, continuumDaoFetchDelegates, continuumDaoFetchLiveProposals, continuumDaoFetchProposal, continuumDaoFetchProposalCount, continuumDaoFetchProposalState, continuumDaoFetchProposals, continuumDaoFetchVotingPower, continuumDaoForumCreateIdea, continuumDaoForumCreateTopic, continuumDaoForumDelete, continuumDaoForumFetchPost, continuumDaoForumFetchThread, continuumDaoForumMarkRead, continuumDaoForumMarkUnread, continuumDaoForumMe, continuumDaoForumReact, continuumDaoForumRecent, continuumDaoForumReply, continuumDaoForumReplyCount, continuumDaoForumResolve, continuumDaoForumSearch, continuumDaoForumSections, continuumDaoForumSignInEligible, continuumDaoForumSignInNonce, continuumDaoForumSignOut, continuumDaoForumUnread, continuumDaoForumUrl, continuumDaoForumUserPostIds, continuumDaoHashProposal, continuumDaoMpaFlag, continuumDaoMpaListing, continuumDaoMpaMailboxList, continuumDaoMpaMailboxOpen, continuumDaoMpaMailboxSend, continuumDaoMpaRetract, continuumDaoProtocolModule, continuumDaoRegisterProposal, continuumDaoTechnocoreBind, ctmTokenAddressOnEvmChain, encodeContinuumDaoNodeInfo, encodeDeltaMetadata, encodeDeltaVoteParams, encodeGovActionCalldata, expectedForumSectionForProposalType, explainGovAction, explainProposalRecord, governorAddressOnEvmChain, isConfiguredAddress, isContinuumDaoCtmOnAssetsChain, isContinuumDaoLockChain, isContinuumDaoSupportedChainId, isContinuumDaoVeCtmOnAssetsChain, isCtmTokenSymbol, isForumIdeaSection, isGovNoOpAction, isVotingEscrowContinuumName, listContinuumDaoDefaultAssets, proposalDescriptionHash, summarizeLiveProposals, votingEscrowAddressOnEvmChain };
|
|
2910
|
+
export { ATTACH_VECTM_SIGNATURE, CONTINUUM_DAO_APPROVE_FALLBACK, CONTINUUM_DAO_ATTACH_FALLBACK, CONTINUUM_DAO_CANCEL_FALLBACK, CONTINUUM_DAO_CREATE_LOCK_FALLBACK, CONTINUUM_DAO_DELEGATE_FALLBACK, CONTINUUM_DAO_DEPLOYMENTS, CONTINUUM_DAO_DETACH_FALLBACK, CONTINUUM_DAO_ETHEREUM_CHAIN_ID, CONTINUUM_DAO_EVM_TYPES, CONTINUUM_DAO_EXECUTE_FALLBACK, CONTINUUM_DAO_GOVERNANCE_API, CONTINUUM_DAO_GOV_EVM_TYPES, CONTINUUM_DAO_INCREASE_AMOUNT_FALLBACK, CONTINUUM_DAO_INCREASE_UNLOCK_FALLBACK, CONTINUUM_DAO_LINEA_CHAIN_ID, CONTINUUM_DAO_LINEA_SEPOLIA_CHAIN_ID, CONTINUUM_DAO_LIQUIDATE_FALLBACK, CONTINUUM_DAO_MERGE_FALLBACK, CONTINUUM_DAO_PROPOSE_FALLBACK, CONTINUUM_DAO_PROTOCOL_ID, CONTINUUM_DAO_SPLIT_FALLBACK, CONTINUUM_DAO_TRANSFER_FALLBACK, CONTINUUM_DAO_VOTE_FALLBACK, CONTINUUM_DAO_WITHDRAW_FALLBACK, CTM_TOKEN_ADDRESS, CTM_TOKEN_DECIMALS, CTM_TOKEN_NAME, CTM_TOKEN_SYMBOL, FORUM_GOVERNANCE_SECTIONS, FORUM_IDEA_SECTION, FORUM_LOGIN_DELIVERY_KIND, GOVERNOR_STATE, GOVERNOR_STATE_LABELS, INCREASE_UNLOCK_TIME_SIGNATURE, MAXTIME_SECONDS, PROPOSAL_TYPE_LABELS, PROPOSAL_TYPE_TO_FORUM_SECTION, VECTM_DEFAULT_MINIMUM_LOCK_WEI, VECTM_LIQ_MIN_LOCKED_WEI, VECTM_LIQ_PENALTY_DEN, VECTM_LIQ_PENALTY_NUM, VECTM_MAX_LOCK_WEEKS, VECTM_NODE_INFO_TUPLE, VECTM_TOKEN_NAME, VECTM_TOKEN_SYMBOL, VOTING_ESCROW_LINEA_ADDRESS, WEEK_SECONDS, applyForumSectionCheck, assertContinuumDaoForumDeleteInput, assertContinuumDaoForumMarkReadInput, assertContinuumDaoForumRecentInput, assertContinuumDaoForumSearchInput, assertContinuumDaoForumTopicKey, assertCreateLockAmount, assertCtmAmountAtLeastMinimum, assertDelegatee, assertExecutorOwnsLock, assertIncreaseAmount, assertIncreaseUnlockTime, assertLiquidate, assertLockDurationSeconds, assertMerge, assertSplitExtracted, assertTransfer, assertUndelegateNotAlreadySelf, assertWithdraw, bucketGovernorState, buildBravoProposalArgs, buildDeltaProposalArgs, buildEvmMultisignBodyContinuumDaoAttachVeCtm, buildEvmMultisignBodyContinuumDaoAttachVeCtmFromMcp, buildEvmMultisignBodyContinuumDaoCancel, buildEvmMultisignBodyContinuumDaoCastVoteBravo, buildEvmMultisignBodyContinuumDaoCastVoteBravoFromMcp, buildEvmMultisignBodyContinuumDaoCastVoteDelta, buildEvmMultisignBodyContinuumDaoCreateLock, buildEvmMultisignBodyContinuumDaoDelegate, buildEvmMultisignBodyContinuumDaoExecute, buildEvmMultisignBodyContinuumDaoForumSignIn, buildEvmMultisignBodyContinuumDaoIncreaseAmount, buildEvmMultisignBodyContinuumDaoIncreaseUnlockTime, buildEvmMultisignBodyContinuumDaoLiquidate, buildEvmMultisignBodyContinuumDaoMerge, buildEvmMultisignBodyContinuumDaoProposeBravo, buildEvmMultisignBodyContinuumDaoProposeDelta, buildEvmMultisignBodyContinuumDaoProposeDeltaFromMcp, buildEvmMultisignBodyContinuumDaoRequestDetach, buildEvmMultisignBodyContinuumDaoSplit, buildEvmMultisignBodyContinuumDaoTransfer, buildEvmMultisignBodyContinuumDaoUndelegate, buildEvmMultisignBodyContinuumDaoWithdraw, computeLiquidationAmounts, continuumDaoDeployment, continuumDaoExplainProposal, continuumDaoFetchDelegates, continuumDaoFetchLiveProposals, continuumDaoFetchProposal, continuumDaoFetchProposalCount, continuumDaoFetchProposalState, continuumDaoFetchProposalVoteEligibility, continuumDaoFetchProposals, continuumDaoFetchVotingPower, continuumDaoForumCreateIdea, continuumDaoForumCreateTopic, continuumDaoForumDelete, continuumDaoForumFetchPost, continuumDaoForumFetchThread, continuumDaoForumMarkRead, continuumDaoForumMarkUnread, continuumDaoForumMe, continuumDaoForumReact, continuumDaoForumRecent, continuumDaoForumReply, continuumDaoForumReplyCount, continuumDaoForumResolve, continuumDaoForumSearch, continuumDaoForumSections, continuumDaoForumSignInEligible, continuumDaoForumSignInNonce, continuumDaoForumSignOut, continuumDaoForumUnread, continuumDaoForumUrl, continuumDaoForumUserPostIds, continuumDaoHashProposal, continuumDaoMpaFlag, continuumDaoMpaListing, continuumDaoMpaMailboxList, continuumDaoMpaMailboxOpen, continuumDaoMpaMailboxSend, continuumDaoMpaRetract, continuumDaoProtocolModule, continuumDaoRegisterProposal, continuumDaoSignatureText, continuumDaoTechnocoreBind, ctmTokenAddressOnEvmChain, encodeContinuumDaoNodeInfo, encodeDeltaMetadata, encodeDeltaVoteParams, encodeGovActionCalldata, expectedForumSectionForProposalType, explainGovAction, explainProposalRecord, floorUnlockTimeSeconds, governorAddressOnEvmChain, isConfiguredAddress, isContinuumDaoCtmOnAssetsChain, isContinuumDaoLockChain, isContinuumDaoSupportedChainId, isContinuumDaoVeCtmOnAssetsChain, isCtmTokenSymbol, isForumIdeaSection, isGovNoOpAction, isVotingEscrowContinuumName, listContinuumDaoDefaultAssets, maxLockDurationSeconds, preflightAttachVeCtm, preflightCreateLock, preflightDelegate, preflightIncreaseAmount, preflightIncreaseUnlockTime, preflightLiquidate, preflightMerge, preflightRequestDetach, preflightSplit, preflightTransfer, preflightUndelegate, preflightWithdraw, proposalDescriptionHash, readCtmBalanceWei, readMinimumLockWei, readVeCtmLockState, resolveAttachGroupId, summarizeLiveProposals, votingEscrowAddressOnEvmChain };
|
|
2194
2911
|
//# sourceMappingURL=index.js.map
|
|
2195
2912
|
//# sourceMappingURL=index.js.map
|