@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.
@@ -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 emitActionCalls(batch, indent) {
1401
- const lines = [];
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
- lines.push(`${indent}// signaling no-op (skipped)`);
1615
+ if (leg.noOp || leg.raw) {
1616
+ calls.push(" // signaling no-op (skipped)");
1405
1617
  return;
1406
1618
  }
1407
- const value = BigInt(leg.value);
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
- lines.push(`${indent}{`);
1411
- lines.push(`${indent} (bool ok, ) = payable(${addrLit(leg.target)}).call${valuePart}(${hexLiteral(leg.data)});`);
1412
- lines.push(`${indent} require(ok, ${label});`);
1413
- lines.push(`${indent}}`);
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
- return lines.join("\n");
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
- return [
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
- ].join("\n");
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 proposeBody = [
1436
- " function run() external {",
1437
- ` address proposer = ${addrLit(proposer)};`,
1438
- ` address governor = ${addrLit(governor)};`,
1439
- " vm.startPrank(proposer, proposer);",
1440
- ` (bool ok, ) = governor.call(${hexLiteral(args.proposeData)});`,
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
- proposeBody,
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: "Load MCP servers etherscan (official V2, https://mcp.etherscan.io/mcp) and foundry before compose/simulate. Write each file with foundry__create_solidity_file, then foundry__forge_script with the Linea fork RPC and no --broadcast. Execute scripts are sequential: later actions see state from earlier ones in the same run(). Do not use isolated eth_call or cast call as the execute sim. Never broadcast these scripts."
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, signatures) {
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 signature = signatures[i] ?? "";
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: value.toString(),
1867
+ value: valueStr,
1569
1868
  data,
1570
1869
  signature,
1571
- noOp: isGovNoOpAction({ value: value.toString(), signature, calldata: data })
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 proposalArgs2 = buildBravoProposalArgs(args.actions ?? [], title, noOp);
1580
- const signatures = (args.actions ?? []).map((a) => (a.signature ?? "").trim());
1581
- if (signatures.length === 0) signatures.push("");
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
- batches: [
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 encoded = buildBravoProposalArgs(opt.actions ?? [], title, noOp);
1596
- const signatures = (opt.actions ?? []).map((a) => (a.signature ?? "").trim());
1597
- if (signatures.length === 0) signatures.push("");
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, signatures)
1899
+ legs: legsFromEncoded(encoded.targets, encoded.values, encoded.calldatas, aligned)
1602
1900
  };
1603
1901
  });
1604
- return { proposalArgs, batches };
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 run the Foundry forge scripts in `foundry.files` on a Linea fork (no --broadcast) so action N sees state from action N-1."
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. Run the Foundry forge scripts in `foundry.files` on a Linea fork (no --broadcast)." : "One or more actions reverted as the governor. Submitting now would waste the voting period \u2014 fix the reverting signature first."
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. Run the Foundry forge scripts in foundry.files on a Linea fork (foundry__forge_script, no --broadcast) before submitting.";
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
- proposeData,
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 splitAbiParamList(inner) {
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 splitAbiParamList(inner).map((t) => ({ type: t }));
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 = splitAbiParamList(type.slice(1, -1));
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 the returned Foundry forge scripts (foundry__forge_script, no --broadcast). Isolated eth_call is not sufficient." : "Some parameter values failed type checks. Fix the rows marked no before simulating.",
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 the returned Foundry forge scripts (foundry__forge_script, no --broadcast). Isolated eth_call is not sufficient." : "Some parameter values failed type checks. Fix the rows marked no before simulating.",
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
  }