@continuumdao/ctm-mpc-defi 0.2.63 → 0.2.64
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 +1 -1
- package/dist/agent/catalog.cjs.map +1 -1
- package/dist/agent/catalog.js +1 -1
- package/dist/agent/catalog.js.map +1 -1
- package/dist/agent/skills/aerodrome/SKILL.md +8 -6
- package/dist/agent/skills/continuum-dao/SKILL.md +2 -2
- package/dist/protocols/evm/continuum-dao/index.cjs +397 -70
- package/dist/protocols/evm/continuum-dao/index.cjs.map +1 -1
- package/dist/protocols/evm/continuum-dao/index.d.ts +27 -3
- package/dist/protocols/evm/continuum-dao/index.js +398 -71
- package/dist/protocols/evm/continuum-dao/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { parseAbi, parseAbiItem, getAddress, zeroAddress, encodeFunctionData, keccak256, encodePacked, padHex, toHex, createPublicClient, defineChain, parseUnits, isAddress, formatUnits, hashTypedData, http, decodeEventLog, stringToHex, parseGwei, serializeTransaction } from 'viem';
|
|
1
|
+
import { parseAbi, parseAbiItem, getAddress, zeroAddress, encodeFunctionData, keccak256, encodePacked, padHex, toHex, createPublicClient, defineChain, parseUnits, isAddress, formatUnits, hashTypedData, http, decodeEventLog, stringToHex, parseGwei, serializeTransaction, decodeFunctionData } from 'viem';
|
|
2
2
|
import { getClientIdFromKeyGenResult, fetchChainFeeParams, gasLimitFromEstimateAndChainConfig, gweiToDecimalString, proposalTxParamsToFeeSnapshot, alignEip1559FeesWithLatestBase } from '@continuumdao/continuum-node-sdk';
|
|
3
3
|
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
@@ -1379,7 +1379,7 @@ var init_explainProposal = __esm({
|
|
|
1379
1379
|
function hexLiteral(data) {
|
|
1380
1380
|
const t = data.trim();
|
|
1381
1381
|
const h = (t.startsWith("0x") ? t.slice(2) : t).toLowerCase();
|
|
1382
|
-
if (!h) return '""';
|
|
1382
|
+
if (!h) return 'hex""';
|
|
1383
1383
|
if (h.length % 2 !== 0) throw new Error("calldata hex must be even length");
|
|
1384
1384
|
return `hex"${h}"`;
|
|
1385
1385
|
}
|
|
@@ -1391,87 +1391,363 @@ function solIdent(raw, fallback) {
|
|
|
1391
1391
|
const start = cleaned.match(/^[A-Za-z_]/) ? cleaned : `_${cleaned}`;
|
|
1392
1392
|
return start.slice(0, 40) || fallback;
|
|
1393
1393
|
}
|
|
1394
|
+
function solString(value) {
|
|
1395
|
+
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\r/g, "\\r").replace(/\n/g, "\\n")}"`;
|
|
1396
|
+
}
|
|
1397
|
+
function splitAbiParamList(inner) {
|
|
1398
|
+
const out = [];
|
|
1399
|
+
let buf = "";
|
|
1400
|
+
let depth = 0;
|
|
1401
|
+
for (const ch of inner) {
|
|
1402
|
+
if (ch === "(") depth += 1;
|
|
1403
|
+
if (ch === ")") depth -= 1;
|
|
1404
|
+
if (ch === "," && depth === 0) {
|
|
1405
|
+
if (buf.trim()) out.push(buf.trim());
|
|
1406
|
+
buf = "";
|
|
1407
|
+
continue;
|
|
1408
|
+
}
|
|
1409
|
+
buf += ch;
|
|
1410
|
+
}
|
|
1411
|
+
if (buf.trim()) out.push(buf.trim());
|
|
1412
|
+
return out;
|
|
1413
|
+
}
|
|
1414
|
+
function resolveSolType(type, env) {
|
|
1415
|
+
const t = type.trim();
|
|
1416
|
+
const fixed = t.match(/^(.*)\[(\d+)\]$/);
|
|
1417
|
+
if (fixed && !t.endsWith(")")) return `${resolveSolType(fixed[1], env)}[${fixed[2]}]`;
|
|
1418
|
+
if (t.endsWith("[]")) return `${resolveSolType(t.slice(0, -2), env)}[]`;
|
|
1419
|
+
if (t.startsWith("(") && t.endsWith(")")) {
|
|
1420
|
+
const existing = env.structs.get(t);
|
|
1421
|
+
if (existing) return existing.name;
|
|
1422
|
+
const parts = splitAbiParamList(t.slice(1, -1));
|
|
1423
|
+
const fieldTypes = parts.map((part) => resolveSolType(part, env));
|
|
1424
|
+
const name = `SimTup${env.next++}`;
|
|
1425
|
+
const fields = fieldTypes.map((field, i) => ` ${field} f${i};`).join("\n");
|
|
1426
|
+
env.structs.set(t, { name, def: `struct ${name} {
|
|
1427
|
+
${fields}
|
|
1428
|
+
}` });
|
|
1429
|
+
return name;
|
|
1430
|
+
}
|
|
1431
|
+
return t;
|
|
1432
|
+
}
|
|
1433
|
+
function needsDataLocation(sol) {
|
|
1434
|
+
if (/^bytes\d+$/.test(sol)) return false;
|
|
1435
|
+
if (sol === "address" || sol === "bool") return false;
|
|
1436
|
+
if (/^uint\d*$/.test(sol) || /^int\d*$/.test(sol)) return false;
|
|
1437
|
+
return true;
|
|
1438
|
+
}
|
|
1439
|
+
function signatureParts(signature) {
|
|
1440
|
+
const sig = signature.trim();
|
|
1441
|
+
const open = sig.indexOf("(");
|
|
1442
|
+
const close = sig.lastIndexOf(")");
|
|
1443
|
+
if (open <= 0 || close !== sig.length - 1 || close < open) return null;
|
|
1444
|
+
const name = sig.slice(0, open).trim();
|
|
1445
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) return null;
|
|
1446
|
+
const inner = sig.slice(open + 1, close).trim();
|
|
1447
|
+
return { name, types: inner ? splitAbiParamList(inner) : [] };
|
|
1448
|
+
}
|
|
1449
|
+
function hexBody(value) {
|
|
1450
|
+
const s = String(value ?? "").trim();
|
|
1451
|
+
const h = (s.startsWith("0x") ? s.slice(2) : s).toLowerCase();
|
|
1452
|
+
if (h.length % 2 !== 0) throw new Error("hex length");
|
|
1453
|
+
return h;
|
|
1454
|
+
}
|
|
1455
|
+
function asList(value) {
|
|
1456
|
+
if (typeof value === "string") {
|
|
1457
|
+
const trimmed = value.trim();
|
|
1458
|
+
if (trimmed.startsWith("[")) {
|
|
1459
|
+
const parsed = JSON.parse(trimmed);
|
|
1460
|
+
if (!Array.isArray(parsed)) throw new Error("array value must be a JSON array");
|
|
1461
|
+
return parsed;
|
|
1462
|
+
}
|
|
1463
|
+
return trimmed ? trimmed.split(",").map((part) => part.trim()) : [];
|
|
1464
|
+
}
|
|
1465
|
+
if (Array.isArray(value)) return [...value];
|
|
1466
|
+
throw new Error("array value");
|
|
1467
|
+
}
|
|
1468
|
+
function asTuple(value, size) {
|
|
1469
|
+
if (typeof value === "string") return asTuple(JSON.parse(value.trim() || "[]"), size);
|
|
1470
|
+
if (Array.isArray(value)) {
|
|
1471
|
+
if (value.length !== size) throw new Error("tuple length");
|
|
1472
|
+
return [...value];
|
|
1473
|
+
}
|
|
1474
|
+
if (value && typeof value === "object") {
|
|
1475
|
+
const record = value;
|
|
1476
|
+
const keys = Object.keys(record).filter((key) => /^\d+$/.test(key)).sort((a, b) => Number(a) - Number(b));
|
|
1477
|
+
if (keys.length === size) return keys.map((key) => record[key]);
|
|
1478
|
+
}
|
|
1479
|
+
throw new Error("tuple value");
|
|
1480
|
+
}
|
|
1481
|
+
function scalarExpr(type, value) {
|
|
1482
|
+
const t = type.trim();
|
|
1483
|
+
if (t === "address") return `address(${getAddress(String(value))})`;
|
|
1484
|
+
if (t.startsWith("uint") || t.startsWith("int")) {
|
|
1485
|
+
const n = typeof value === "bigint" ? value : BigInt(String(value).trim() || "0");
|
|
1486
|
+
return `${t}(${n.toString()})`;
|
|
1487
|
+
}
|
|
1488
|
+
if (t === "bool") {
|
|
1489
|
+
if (typeof value === "boolean") return value ? "true" : "false";
|
|
1490
|
+
const s = String(value).trim();
|
|
1491
|
+
return s === "true" || s === "1" ? "true" : "false";
|
|
1492
|
+
}
|
|
1493
|
+
if (t === "string") return solString(typeof value === "string" ? value : String(value));
|
|
1494
|
+
if (t === "bytes") return `bytes(hex"${hexBody(value)}")`;
|
|
1495
|
+
const bytesN = t.match(/^bytes(\d+)$/);
|
|
1496
|
+
if (bytesN) {
|
|
1497
|
+
const width = Number(bytesN[1]) * 2;
|
|
1498
|
+
const h = hexBody(value);
|
|
1499
|
+
if (h.length > width) throw new Error(`${t} is longer than ${bytesN[1]} bytes`);
|
|
1500
|
+
return `${t}(hex"${h.padEnd(width, "0")}")`;
|
|
1501
|
+
}
|
|
1502
|
+
throw new Error(`unsupported ABI type ${t}`);
|
|
1503
|
+
}
|
|
1504
|
+
function emitTyped(type, value, fresh, env) {
|
|
1505
|
+
const t = type.trim();
|
|
1506
|
+
const fixed = t.match(/^(.*)\[(\d+)\]$/);
|
|
1507
|
+
if (fixed && !t.endsWith(")")) {
|
|
1508
|
+
const base = fixed[1];
|
|
1509
|
+
const items = asList(value);
|
|
1510
|
+
if (items.length !== Number(fixed[2])) throw new Error(`${t} length`);
|
|
1511
|
+
return emitArray(base, items, Number(fixed[2]), fresh, env);
|
|
1512
|
+
}
|
|
1513
|
+
if (t.endsWith("[]")) {
|
|
1514
|
+
const base = t.slice(0, -2);
|
|
1515
|
+
return emitArray(base, asList(value), null, fresh, env);
|
|
1516
|
+
}
|
|
1517
|
+
if (t.startsWith("(") && t.endsWith(")")) {
|
|
1518
|
+
const sol = resolveSolType(t, env);
|
|
1519
|
+
const parts = splitAbiParamList(t.slice(1, -1));
|
|
1520
|
+
const items = asTuple(value, parts.length);
|
|
1521
|
+
const emitted = parts.map((part, i) => emitTyped(part, items[i], fresh, env));
|
|
1522
|
+
return {
|
|
1523
|
+
expr: `${sol}(${emitted.map((item) => item.expr).join(", ")})`,
|
|
1524
|
+
prelude: emitted.flatMap((item) => item.prelude)
|
|
1525
|
+
};
|
|
1526
|
+
}
|
|
1527
|
+
return { expr: scalarExpr(t, value), prelude: [] };
|
|
1528
|
+
}
|
|
1529
|
+
function emitArray(base, items, fixedLen, fresh, env) {
|
|
1530
|
+
const elem = resolveSolType(base, env);
|
|
1531
|
+
const name = fresh();
|
|
1532
|
+
const emitted = items.map((item) => emitTyped(base, item, fresh, env));
|
|
1533
|
+
const prelude = emitted.flatMap((item) => item.prelude);
|
|
1534
|
+
if (fixedLen == null) {
|
|
1535
|
+
prelude.push(` ${elem}[] memory ${name} = new ${elem}[](${items.length});`);
|
|
1536
|
+
} else {
|
|
1537
|
+
prelude.push(` ${elem}[${fixedLen}] memory ${name};`);
|
|
1538
|
+
}
|
|
1539
|
+
emitted.forEach((item, i) => {
|
|
1540
|
+
prelude.push(` ${name}[${i}] = ${item.expr};`);
|
|
1541
|
+
});
|
|
1542
|
+
return { expr: name, prelude };
|
|
1543
|
+
}
|
|
1544
|
+
function valuesForLeg(leg) {
|
|
1545
|
+
const parts = signatureParts(leg.signature);
|
|
1546
|
+
if (!parts) return null;
|
|
1547
|
+
if (leg.inputs && leg.inputs.length === parts.types.length) {
|
|
1548
|
+
return leg.inputs.map((input) => input.value);
|
|
1549
|
+
}
|
|
1550
|
+
if (!leg.data || leg.data === "0x") return parts.types.length === 0 ? [] : null;
|
|
1551
|
+
try {
|
|
1552
|
+
const fragment = `function ${leg.signature.trim()}`;
|
|
1553
|
+
const decoded = decodeFunctionData({
|
|
1554
|
+
abi: parseAbi([fragment]),
|
|
1555
|
+
data: leg.data
|
|
1556
|
+
});
|
|
1557
|
+
const args = [...decoded.args ?? []];
|
|
1558
|
+
if (args.length !== parts.types.length) return null;
|
|
1559
|
+
return args;
|
|
1560
|
+
} catch {
|
|
1561
|
+
return null;
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
function buildCalldataFn(leg, id) {
|
|
1565
|
+
const parts = signatureParts(leg.signature);
|
|
1566
|
+
if (!parts) return null;
|
|
1567
|
+
const values = valuesForLeg(leg);
|
|
1568
|
+
if (!values) return null;
|
|
1569
|
+
let n = 0;
|
|
1570
|
+
const fresh = () => `v${n++}`;
|
|
1571
|
+
const env = { structs: /* @__PURE__ */ new Map(), next: 0 };
|
|
1572
|
+
let prelude = [];
|
|
1573
|
+
let exprs = [];
|
|
1574
|
+
try {
|
|
1575
|
+
const emitted = parts.types.map((type, i) => emitTyped(type, values[i], fresh, env));
|
|
1576
|
+
prelude = emitted.flatMap((item) => item.prelude);
|
|
1577
|
+
exprs = emitted.map((item) => item.expr);
|
|
1578
|
+
} catch {
|
|
1579
|
+
return null;
|
|
1580
|
+
}
|
|
1581
|
+
const fnName = `_calldata${id}`;
|
|
1582
|
+
const argList = exprs.join(", ");
|
|
1583
|
+
const lines = [
|
|
1584
|
+
` function ${fnName}() internal pure returns (bytes memory) {`,
|
|
1585
|
+
...prelude,
|
|
1586
|
+
` return abi.encodeCall(ISimAction${id}.${parts.name}, (${argList}));`,
|
|
1587
|
+
" }"
|
|
1588
|
+
];
|
|
1589
|
+
const params = parts.types.map((type, i) => {
|
|
1590
|
+
const sol = resolveSolType(type, env);
|
|
1591
|
+
const loc = needsDataLocation(sol) ? " calldata" : "";
|
|
1592
|
+
return `${sol}${loc} a${i}`;
|
|
1593
|
+
}).join(",");
|
|
1594
|
+
const structs = [...env.structs.values()].map((item) => item.def);
|
|
1595
|
+
const iface = [
|
|
1596
|
+
`interface ISimAction${id} {`,
|
|
1597
|
+
` function ${parts.name}(${params}) external;`,
|
|
1598
|
+
"}"
|
|
1599
|
+
].join("\n");
|
|
1600
|
+
const interfaceSrc = [...structs, iface].filter((part) => part.length > 0).join("\n\n");
|
|
1601
|
+
return { interfaceSrc, fnSrc: lines.join("\n"), fnName };
|
|
1602
|
+
}
|
|
1394
1603
|
function actionRequireLabel(batch, index) {
|
|
1395
1604
|
const leg = batch.legs[index];
|
|
1396
1605
|
const where = batch.label ? `${batch.label} action ${index + 1}` : `action ${index + 1}`;
|
|
1397
1606
|
const sig = leg?.signature?.trim() ? ` ${leg.signature.trim()}` : "";
|
|
1398
1607
|
return `${where}${sig} reverted`;
|
|
1399
1608
|
}
|
|
1400
|
-
function
|
|
1401
|
-
const
|
|
1609
|
+
function emitExecuteBody(batch, governor) {
|
|
1610
|
+
const interfaces = [];
|
|
1611
|
+
const helpers = [];
|
|
1612
|
+
const calls = [];
|
|
1613
|
+
let simId = 0;
|
|
1402
1614
|
batch.legs.forEach((leg, i) => {
|
|
1403
|
-
if (leg.noOp) {
|
|
1404
|
-
|
|
1615
|
+
if (leg.noOp || leg.raw) {
|
|
1616
|
+
calls.push(" // signaling no-op (skipped)");
|
|
1405
1617
|
return;
|
|
1406
1618
|
}
|
|
1407
|
-
const
|
|
1619
|
+
const built = buildCalldataFn(leg, simId);
|
|
1620
|
+
const indent = " ";
|
|
1621
|
+
const value = BigInt(leg.value || "0");
|
|
1408
1622
|
const valuePart = value === 0n ? "" : `{value: ${value.toString()}}`;
|
|
1409
1623
|
const label = JSON.stringify(actionRequireLabel(batch, i));
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1624
|
+
if (built) {
|
|
1625
|
+
interfaces.push(built.interfaceSrc);
|
|
1626
|
+
helpers.push(built.fnSrc);
|
|
1627
|
+
calls.push(`${indent}{`);
|
|
1628
|
+
calls.push(
|
|
1629
|
+
`${indent} (bool ok, bytes memory ret) = payable(${addrLit(leg.target)}).call${valuePart}(${built.fnName}());`
|
|
1630
|
+
);
|
|
1631
|
+
calls.push(`${indent} if (!ok) _bubble(ret, ${label});`);
|
|
1632
|
+
calls.push(`${indent}}`);
|
|
1633
|
+
simId += 1;
|
|
1634
|
+
return;
|
|
1635
|
+
}
|
|
1636
|
+
calls.push(`${indent}// ${leg.signature.trim() || "calldata"} (raw)`);
|
|
1637
|
+
calls.push(`${indent}{`);
|
|
1638
|
+
calls.push(
|
|
1639
|
+
`${indent} (bool ok, bytes memory ret) = payable(${addrLit(leg.target)}).call${valuePart}(${hexLiteral(leg.data)});`
|
|
1640
|
+
);
|
|
1641
|
+
calls.push(`${indent} if (!ok) _bubble(ret, ${label});`);
|
|
1642
|
+
calls.push(`${indent}}`);
|
|
1414
1643
|
});
|
|
1415
|
-
|
|
1644
|
+
const body = [
|
|
1645
|
+
...helpers,
|
|
1646
|
+
BUBBLE_FN,
|
|
1647
|
+
" function run() external {",
|
|
1648
|
+
` address governor = ${addrLit(governor)};`,
|
|
1649
|
+
" vm.startPrank(governor, governor);",
|
|
1650
|
+
...calls,
|
|
1651
|
+
" vm.stopPrank();",
|
|
1652
|
+
" }"
|
|
1653
|
+
].join("\n");
|
|
1654
|
+
return { body, interfaces };
|
|
1655
|
+
}
|
|
1656
|
+
function emitProposeBody(args) {
|
|
1657
|
+
const interfaces = [];
|
|
1658
|
+
const helpers = [];
|
|
1659
|
+
const assigns = [];
|
|
1660
|
+
let simId = 0;
|
|
1661
|
+
args.legs.forEach((leg, i) => {
|
|
1662
|
+
const comment = leg.raw ? "Delta metadata" : leg.noOp ? "signaling no-op" : leg.signature.trim() || "call";
|
|
1663
|
+
assigns.push(` // ${i + 1}. ${comment.replace(/\*\//g, "* /")}`);
|
|
1664
|
+
assigns.push(` targets[${i}] = ${addrLit(leg.target)};`);
|
|
1665
|
+
assigns.push(` values[${i}] = ${BigInt(leg.value || "0").toString()};`);
|
|
1666
|
+
if (leg.raw || leg.noOp || !leg.signature.trim()) {
|
|
1667
|
+
assigns.push(` calldatas[${i}] = ${hexLiteral(leg.data)};`);
|
|
1668
|
+
return;
|
|
1669
|
+
}
|
|
1670
|
+
const built = buildCalldataFn(leg, simId);
|
|
1671
|
+
if (!built) {
|
|
1672
|
+
assigns.push(` calldatas[${i}] = ${hexLiteral(leg.data)};`);
|
|
1673
|
+
return;
|
|
1674
|
+
}
|
|
1675
|
+
interfaces.push(built.interfaceSrc);
|
|
1676
|
+
helpers.push(built.fnSrc);
|
|
1677
|
+
assigns.push(` calldatas[${i}] = ${built.fnName}();`);
|
|
1678
|
+
simId += 1;
|
|
1679
|
+
});
|
|
1680
|
+
const n = args.legs.length;
|
|
1681
|
+
const body = [
|
|
1682
|
+
...helpers,
|
|
1683
|
+
" function run() external {",
|
|
1684
|
+
` address proposer = ${addrLit(args.proposer)};`,
|
|
1685
|
+
` address governor = ${addrLit(args.governor)};`,
|
|
1686
|
+
` address[] memory targets = new address[](${n});`,
|
|
1687
|
+
` uint256[] memory values = new uint256[](${n});`,
|
|
1688
|
+
` bytes[] memory calldatas = new bytes[](${n});`,
|
|
1689
|
+
...assigns,
|
|
1690
|
+
" vm.startPrank(proposer, proposer);",
|
|
1691
|
+
` IContinuumGovernor(governor).propose(targets, values, calldatas, ${solString(args.description)});`,
|
|
1692
|
+
" vm.stopPrank();",
|
|
1693
|
+
" }"
|
|
1694
|
+
].join("\n");
|
|
1695
|
+
return { body, interfaces };
|
|
1416
1696
|
}
|
|
1417
|
-
function wrapScript(contractName, body, headerComment) {
|
|
1418
|
-
|
|
1697
|
+
function wrapScript(contractName, body, headerComment, interfaces, includeGovernor) {
|
|
1698
|
+
const blocks = [...interfaces];
|
|
1699
|
+
if (includeGovernor) blocks.push(GOVERNOR_INTERFACE);
|
|
1700
|
+
const lines = [
|
|
1419
1701
|
"// SPDX-License-Identifier: MIT",
|
|
1420
1702
|
"pragma solidity ^0.8.24;",
|
|
1421
1703
|
"",
|
|
1422
1704
|
'import {Script} from "forge-std/Script.sol";',
|
|
1423
|
-
"",
|
|
1424
|
-
headerComment,
|
|
1425
|
-
`contract ${contractName} is Script {`,
|
|
1426
|
-
body,
|
|
1427
|
-
"}",
|
|
1428
1705
|
""
|
|
1429
|
-
]
|
|
1706
|
+
];
|
|
1707
|
+
if (blocks.length) {
|
|
1708
|
+
lines.push(blocks.join("\n\n"), "");
|
|
1709
|
+
}
|
|
1710
|
+
lines.push(headerComment, `contract ${contractName} is Script {`, body, "}", "");
|
|
1711
|
+
return lines.join("\n");
|
|
1430
1712
|
}
|
|
1431
1713
|
function buildFoundryProposalSimulatePlan(args) {
|
|
1432
1714
|
const governor = getAddress(args.governor);
|
|
1433
1715
|
const proposer = getAddress(args.proposer);
|
|
1716
|
+
const proposalLegs = args.proposalLegs ?? args.batches.flatMap((batch) => batch.legs);
|
|
1434
1717
|
const files = [];
|
|
1435
|
-
const
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
' require(ok, "ContinuumDAO.propose reverted");',
|
|
1442
|
-
" vm.stopPrank();",
|
|
1443
|
-
" }"
|
|
1444
|
-
].join("\n");
|
|
1718
|
+
const propose = emitProposeBody({
|
|
1719
|
+
governor,
|
|
1720
|
+
proposer,
|
|
1721
|
+
description: args.description,
|
|
1722
|
+
legs: proposalLegs
|
|
1723
|
+
});
|
|
1445
1724
|
files.push({
|
|
1446
1725
|
path: "script/ContinuumDaoSimulatePropose.s.sol",
|
|
1447
1726
|
contractName: "ContinuumDaoSimulatePropose",
|
|
1448
1727
|
source: wrapScript(
|
|
1449
1728
|
"ContinuumDaoSimulatePropose",
|
|
1450
|
-
|
|
1451
|
-
"/// Dry-run ContinuumDAO.propose from the draft proposer. Do not --broadcast."
|
|
1729
|
+
propose.body,
|
|
1730
|
+
"/// Dry-run ContinuumDAO.propose from the draft proposer. Signatures are abi.encodeCall, not raw calldata. Do not --broadcast.",
|
|
1731
|
+
propose.interfaces,
|
|
1732
|
+
true
|
|
1452
1733
|
)
|
|
1453
1734
|
});
|
|
1454
1735
|
args.batches.forEach((batch, bi) => {
|
|
1455
1736
|
const suffix2 = batch.label != null ? solIdent(batch.label, `Option${(batch.optionIndex ?? bi) + 1}`) : "Bravo";
|
|
1456
1737
|
const contractName = `ContinuumDaoSimulateExecute${suffix2}`;
|
|
1457
1738
|
const path = `script/${contractName}.s.sol`;
|
|
1458
|
-
const body = [
|
|
1459
|
-
" function run() external {",
|
|
1460
|
-
` address governor = ${addrLit(governor)};`,
|
|
1461
|
-
" vm.startPrank(governor, governor);",
|
|
1462
|
-
emitActionCalls(batch, " "),
|
|
1463
|
-
" vm.stopPrank();",
|
|
1464
|
-
" }"
|
|
1465
|
-
].join("\n");
|
|
1466
1739
|
const label = batch.label != null ? `Delta option ${(batch.optionIndex ?? bi) + 1} (${batch.label})` : "Bravo execute";
|
|
1740
|
+
const execute = emitExecuteBody(batch, governor);
|
|
1467
1741
|
files.push({
|
|
1468
1742
|
path,
|
|
1469
1743
|
contractName,
|
|
1470
1744
|
label,
|
|
1471
1745
|
source: wrapScript(
|
|
1472
1746
|
contractName,
|
|
1473
|
-
body,
|
|
1474
|
-
`/// Sequential governor execute dry-run (${label}). msg.sender = governor. Do not --broadcast
|
|
1747
|
+
execute.body,
|
|
1748
|
+
`/// Sequential governor execute dry-run (${label}). Each action is abi.encodeCall on its signature. msg.sender = governor. Do not --broadcast.`,
|
|
1749
|
+
execute.interfaces,
|
|
1750
|
+
false
|
|
1475
1751
|
)
|
|
1476
1752
|
});
|
|
1477
1753
|
});
|
|
@@ -1486,7 +1762,7 @@ function buildFoundryProposalSimulatePlan(args) {
|
|
|
1486
1762
|
broadcast: false,
|
|
1487
1763
|
forkUrlFromRegistry: true
|
|
1488
1764
|
},
|
|
1489
|
-
note:
|
|
1765
|
+
note: FOUNDRY_NOTE
|
|
1490
1766
|
};
|
|
1491
1767
|
}
|
|
1492
1768
|
function foundryPlanHasSequentialExecute(plan) {
|
|
@@ -1495,10 +1771,31 @@ function foundryPlanHasSequentialExecute(plan) {
|
|
|
1495
1771
|
function foundryScriptContainsCall(source, target, data) {
|
|
1496
1772
|
return source.includes(getAddress(target)) && source.toLowerCase().includes(data.slice(2).toLowerCase());
|
|
1497
1773
|
}
|
|
1498
|
-
var FOUNDRY_PROPOSAL_REQUIRED_SERVERS;
|
|
1774
|
+
var FOUNDRY_PROPOSAL_REQUIRED_SERVERS, FOUNDRY_NOTE, BUBBLE_FN, GOVERNOR_INTERFACE;
|
|
1499
1775
|
var init_foundryProposalScript = __esm({
|
|
1500
1776
|
"src/protocols/evm/continuum-dao/foundryProposalScript.ts"() {
|
|
1501
1777
|
FOUNDRY_PROPOSAL_REQUIRED_SERVERS = ["etherscan", "foundry"];
|
|
1778
|
+
FOUNDRY_NOTE = "Load MCP servers etherscan (official V2, https://mcp.etherscan.io/mcp) and foundry before compose/simulate. foundry.files source calls each action with abi.encodeCall on its Solidity signature (typed arguments, not raw hex calldata) and bubbles reverts. Write each file unchanged with foundry__create_solidity_file, then foundry__forge_script every file on the Linea fork RPC with no --broadcast, and report each forge result before the turn ends. Execute scripts are sequential: later actions see state from earlier ones in the same run(). Do not replace the source with hex calldata or use isolated eth_call or cast call as the execute sim. Never broadcast these scripts.";
|
|
1779
|
+
BUBBLE_FN = [
|
|
1780
|
+
" function _bubble(bytes memory ret, string memory why) internal pure {",
|
|
1781
|
+
" if (ret.length > 0) {",
|
|
1782
|
+
" assembly {",
|
|
1783
|
+
" revert(add(ret, 0x20), mload(ret))",
|
|
1784
|
+
" }",
|
|
1785
|
+
" }",
|
|
1786
|
+
" revert(why);",
|
|
1787
|
+
" }"
|
|
1788
|
+
].join("\n");
|
|
1789
|
+
GOVERNOR_INTERFACE = [
|
|
1790
|
+
"interface IContinuumGovernor {",
|
|
1791
|
+
" function propose(",
|
|
1792
|
+
" address[] calldata targets,",
|
|
1793
|
+
" uint256[] calldata values,",
|
|
1794
|
+
" bytes[] calldata calldatas,",
|
|
1795
|
+
" string calldata description",
|
|
1796
|
+
" ) external returns (uint256);",
|
|
1797
|
+
"}"
|
|
1798
|
+
].join("\n");
|
|
1502
1799
|
}
|
|
1503
1800
|
});
|
|
1504
1801
|
|
|
@@ -1557,18 +1854,21 @@ function isSimulateUnsupportedError(err) {
|
|
|
1557
1854
|
}
|
|
1558
1855
|
return false;
|
|
1559
1856
|
}
|
|
1560
|
-
function legsFromEncoded(targets, values, calldatas,
|
|
1857
|
+
function legsFromEncoded(targets, values, calldatas, actions) {
|
|
1561
1858
|
return targets.map((target, i) => {
|
|
1562
1859
|
const data = calldatas[i] ?? "0x";
|
|
1563
1860
|
const value = values[i] ?? 0n;
|
|
1564
|
-
const
|
|
1861
|
+
const action = actions[i];
|
|
1862
|
+
const signature = (action?.signature ?? "").trim();
|
|
1863
|
+
const valueStr = value.toString();
|
|
1565
1864
|
return {
|
|
1566
1865
|
index: i,
|
|
1567
1866
|
target,
|
|
1568
|
-
value:
|
|
1867
|
+
value: valueStr,
|
|
1569
1868
|
data,
|
|
1570
1869
|
signature,
|
|
1571
|
-
|
|
1870
|
+
inputs: action?.inputs,
|
|
1871
|
+
noOp: isGovNoOpAction({ value: valueStr, signature, calldata: data })
|
|
1572
1872
|
};
|
|
1573
1873
|
});
|
|
1574
1874
|
}
|
|
@@ -1576,32 +1876,58 @@ function executableGovBatches(args) {
|
|
|
1576
1876
|
const title = args.title.trim() || "draft";
|
|
1577
1877
|
const noOp = args.noOpTarget;
|
|
1578
1878
|
if (args.configuration === "bravo") {
|
|
1579
|
-
const
|
|
1580
|
-
const
|
|
1581
|
-
|
|
1879
|
+
const actions = args.actions ?? [];
|
|
1880
|
+
const proposalArgs2 = buildBravoProposalArgs(actions, title, noOp);
|
|
1881
|
+
const aligned = actions.length === 0 ? [void 0] : actions;
|
|
1882
|
+
const legs = legsFromEncoded(proposalArgs2.targets, proposalArgs2.values, proposalArgs2.calldatas, aligned);
|
|
1582
1883
|
return {
|
|
1583
1884
|
proposalArgs: proposalArgs2,
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
legs: legsFromEncoded(proposalArgs2.targets, proposalArgs2.values, proposalArgs2.calldatas, signatures)
|
|
1587
|
-
}
|
|
1588
|
-
]
|
|
1885
|
+
proposalLegs: legs,
|
|
1886
|
+
batches: [{ legs }]
|
|
1589
1887
|
};
|
|
1590
1888
|
}
|
|
1591
1889
|
const options = args.options ?? [];
|
|
1592
1890
|
const nWinners = Number(args.nWinners);
|
|
1593
1891
|
const proposalArgs = buildDeltaProposalArgs(options, nWinners, title, noOp);
|
|
1594
1892
|
const batches = options.map((opt, optionIndex) => {
|
|
1595
|
-
const
|
|
1596
|
-
const
|
|
1597
|
-
|
|
1893
|
+
const acts = opt.actions ?? [];
|
|
1894
|
+
const encoded = buildBravoProposalArgs(acts, title, noOp);
|
|
1895
|
+
const aligned = acts.length === 0 ? [void 0] : acts;
|
|
1598
1896
|
return {
|
|
1599
1897
|
label: opt.label,
|
|
1600
1898
|
optionIndex,
|
|
1601
|
-
legs: legsFromEncoded(encoded.targets, encoded.values, encoded.calldatas,
|
|
1899
|
+
legs: legsFromEncoded(encoded.targets, encoded.values, encoded.calldatas, aligned)
|
|
1602
1900
|
};
|
|
1603
1901
|
});
|
|
1604
|
-
|
|
1902
|
+
const proposalLegs = [
|
|
1903
|
+
{
|
|
1904
|
+
index: 0,
|
|
1905
|
+
target: proposalArgs.targets[0],
|
|
1906
|
+
value: (proposalArgs.values[0] ?? 0n).toString(),
|
|
1907
|
+
data: proposalArgs.calldatas[0] ?? "0x",
|
|
1908
|
+
signature: "",
|
|
1909
|
+
noOp: false,
|
|
1910
|
+
raw: true
|
|
1911
|
+
}
|
|
1912
|
+
];
|
|
1913
|
+
let cursor = 1;
|
|
1914
|
+
for (const opt of options) {
|
|
1915
|
+
const acts = opt.actions ?? [];
|
|
1916
|
+
const count = acts.length === 0 ? 1 : acts.length;
|
|
1917
|
+
for (let j = 0; j < count; j++) {
|
|
1918
|
+
const action = acts.length === 0 ? void 0 : acts[j];
|
|
1919
|
+
const leg = legsFromEncoded(
|
|
1920
|
+
[proposalArgs.targets[cursor]],
|
|
1921
|
+
[proposalArgs.values[cursor] ?? 0n],
|
|
1922
|
+
[proposalArgs.calldatas[cursor] ?? "0x"],
|
|
1923
|
+
[action]
|
|
1924
|
+
)[0];
|
|
1925
|
+
leg.index = cursor;
|
|
1926
|
+
proposalLegs.push(leg);
|
|
1927
|
+
cursor += 1;
|
|
1928
|
+
}
|
|
1929
|
+
}
|
|
1930
|
+
return { proposalArgs, proposalLegs, batches };
|
|
1605
1931
|
}
|
|
1606
1932
|
async function simulateSequential(client, account, legs) {
|
|
1607
1933
|
if (legs.length === 0) return { kind: "ok", gasUsed: [] };
|
|
@@ -1688,7 +2014,7 @@ function summarizeNote(args) {
|
|
|
1688
2014
|
const parts = [];
|
|
1689
2015
|
if (args.canPropose && args.proposeOk && args.executeOk) {
|
|
1690
2016
|
parts.push(
|
|
1691
|
-
"RPC sequential simulate succeeded. Still
|
|
2017
|
+
"RPC sequential simulate succeeded. Still write foundry.files unchanged (abi.encodeCall on each function signature, not raw hex calldata) and run foundry__forge_script on a Linea fork (no --broadcast) to a result for every file before the turn ends."
|
|
1692
2018
|
);
|
|
1693
2019
|
} else {
|
|
1694
2020
|
if (!args.canPropose) {
|
|
@@ -1700,7 +2026,7 @@ function summarizeNote(args) {
|
|
|
1700
2026
|
}
|
|
1701
2027
|
if (!args.executeOk) {
|
|
1702
2028
|
parts.push(
|
|
1703
|
-
args.source === "foundry_required" ? "RPC cannot run sequential eth_simulateV1. Isolated eth_call is not sufficient.
|
|
2029
|
+
args.source === "foundry_required" ? "RPC cannot run sequential eth_simulateV1. Isolated eth_call is not sufficient. Write foundry.files unchanged (abi.encodeCall on each signature) and run foundry__forge_script on a Linea fork (no --broadcast) to a result for every file." : "One or more actions reverted as the governor. Submitting now would waste the voting period \u2014 fix the reverting signature first."
|
|
1704
2030
|
);
|
|
1705
2031
|
}
|
|
1706
2032
|
}
|
|
@@ -1733,7 +2059,7 @@ function formatProposalSimulationFailure(result) {
|
|
|
1733
2059
|
}
|
|
1734
2060
|
if (!result.execute.ok) {
|
|
1735
2061
|
if (result.execute.source === "foundry_required") {
|
|
1736
|
-
return "RPC does not support sequential eth_simulateV1. Isolated eth_call cannot carry state between actions.
|
|
2062
|
+
return "RPC does not support sequential eth_simulateV1. Isolated eth_call cannot carry state between actions. Write the Foundry forge scripts in foundry.files unchanged (abi.encodeCall on each signature, not raw hex) and run foundry__forge_script on a Linea fork (no --broadcast) to a result before submitting.";
|
|
1737
2063
|
}
|
|
1738
2064
|
for (const batch of result.execute.batches) {
|
|
1739
2065
|
const failed = batch.actions.find((a) => !a.ok);
|
|
@@ -1780,7 +2106,7 @@ async function continuumDaoSimulateProposal(args) {
|
|
|
1780
2106
|
throw new Error("Delta nWinners must be >= 1 and < nOptions.");
|
|
1781
2107
|
}
|
|
1782
2108
|
}
|
|
1783
|
-
const { proposalArgs, batches } = executableGovBatches({
|
|
2109
|
+
const { proposalArgs, proposalLegs, batches } = executableGovBatches({
|
|
1784
2110
|
configuration,
|
|
1785
2111
|
title,
|
|
1786
2112
|
actions: args.actions,
|
|
@@ -1855,7 +2181,8 @@ async function continuumDaoSimulateProposal(args) {
|
|
|
1855
2181
|
chainId,
|
|
1856
2182
|
governor: gov,
|
|
1857
2183
|
proposer: account,
|
|
1858
|
-
|
|
2184
|
+
description: proposalArgs.description,
|
|
2185
|
+
proposalLegs,
|
|
1859
2186
|
batches
|
|
1860
2187
|
});
|
|
1861
2188
|
return {
|
|
@@ -1882,7 +2209,7 @@ var init_simulateProposal = __esm({
|
|
|
1882
2209
|
init_governance();
|
|
1883
2210
|
}
|
|
1884
2211
|
});
|
|
1885
|
-
function
|
|
2212
|
+
function splitAbiParamList2(inner) {
|
|
1886
2213
|
const out = [];
|
|
1887
2214
|
let buf = "";
|
|
1888
2215
|
let depth = 0;
|
|
@@ -1918,14 +2245,14 @@ function signatureToAbiInputs(signature) {
|
|
|
1918
2245
|
if (end === -1) return [];
|
|
1919
2246
|
const inner = signature.slice(start + 1, end);
|
|
1920
2247
|
if (!inner.trim()) return [];
|
|
1921
|
-
return
|
|
2248
|
+
return splitAbiParamList2(inner).map((t) => ({ type: t }));
|
|
1922
2249
|
}
|
|
1923
2250
|
function coerceAbiValue(type, value) {
|
|
1924
2251
|
const trimmed = (value ?? "").trim();
|
|
1925
2252
|
if (type.startsWith("(") && type.endsWith(")")) {
|
|
1926
2253
|
const parsed = JSON.parse(trimmed || "[]");
|
|
1927
2254
|
if (!Array.isArray(parsed)) throw new Error("tuple value must be a JSON array");
|
|
1928
|
-
const parts =
|
|
2255
|
+
const parts = splitAbiParamList2(type.slice(1, -1));
|
|
1929
2256
|
return parts.map((part, i) => {
|
|
1930
2257
|
const child = parsed[i];
|
|
1931
2258
|
if (typeof child === "string") return coerceAbiValue(part, child);
|
|
@@ -4480,7 +4807,7 @@ async function continuumDaoComposeProposalAction(args) {
|
|
|
4480
4807
|
table: table2,
|
|
4481
4808
|
rows: rows2,
|
|
4482
4809
|
error: errors.length ? errors.join("; ") : void 0,
|
|
4483
|
-
note: ok ? "Draft inputs are type-valid. Present this table, then ask whether to dry-run with ctm_continuum_dao_simulate_proposal and run
|
|
4810
|
+
note: ok ? "Draft inputs are type-valid. Present this table, then ask whether to dry-run with ctm_continuum_dao_simulate_proposal. Write foundry.files unchanged (abi.encodeCall on each function signature, not raw hex calldata) and run foundry__forge_script on a Linea fork (no --broadcast) to a result for every file. Isolated eth_call is not sufficient." : "Some parameter values failed type checks. Fix the rows marked no before simulating.",
|
|
4484
4811
|
next: ok ? "Show the table. Load etherscan and foundry first. Ask if the operator wants to simulate via forge script (pass account for another EOA, or keyGenId / omit account for the preferred KeyGen). Do not call build_propose_* until they confirm, the forge script succeeded, and, for KeyGen submit, a Governance forum topic exists." : "Re-ask the invalid parameters and call this tool again to preview."
|
|
4485
4812
|
};
|
|
4486
4813
|
}
|
|
@@ -4507,7 +4834,7 @@ async function continuumDaoComposeProposalAction(args) {
|
|
|
4507
4834
|
table,
|
|
4508
4835
|
rows: rows2,
|
|
4509
4836
|
error: errors.length ? errors.join("; ") : void 0,
|
|
4510
|
-
note: ok ? "Draft inputs are type-valid. Present this table, then ask whether to dry-run with ctm_continuum_dao_simulate_proposal and run
|
|
4837
|
+
note: ok ? "Draft inputs are type-valid. Present this table, then ask whether to dry-run with ctm_continuum_dao_simulate_proposal. Write foundry.files unchanged (abi.encodeCall on each function signature, not raw hex calldata) and run foundry__forge_script on a Linea fork (no --broadcast) to a result for every file. Isolated eth_call is not sufficient." : "Some parameter values failed type checks. Fix the rows marked no before simulating.",
|
|
4511
4838
|
next: ok ? "Show the table. Load etherscan and foundry first. Ask if the operator wants to simulate via forge script (pass account for another EOA, or keyGenId / omit account for the preferred KeyGen). Do not call build_propose_* until they confirm, the forge script succeeded, and, for KeyGen submit, a Governance forum topic exists." : "Re-ask the invalid parameters and call this tool again to preview."
|
|
4512
4839
|
};
|
|
4513
4840
|
}
|