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