@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.
@@ -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 emitActionCalls(batch, indent) {
1403
- const lines = [];
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
- lines.push(`${indent}// signaling no-op (skipped)`);
1617
+ if (leg.noOp || leg.raw) {
1618
+ calls.push(" // signaling no-op (skipped)");
1407
1619
  return;
1408
1620
  }
1409
- const value = BigInt(leg.value);
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
- lines.push(`${indent}{`);
1413
- lines.push(`${indent} (bool ok, ) = payable(${addrLit(leg.target)}).call${valuePart}(${hexLiteral(leg.data)});`);
1414
- lines.push(`${indent} require(ok, ${label});`);
1415
- lines.push(`${indent}}`);
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
- return lines.join("\n");
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
- return [
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
- ].join("\n");
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 proposeBody = [
1438
- " function run() external {",
1439
- ` address proposer = ${addrLit(proposer)};`,
1440
- ` address governor = ${addrLit(governor)};`,
1441
- " vm.startPrank(proposer, proposer);",
1442
- ` (bool ok, ) = governor.call(${hexLiteral(args.proposeData)});`,
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
- proposeBody,
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: "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."
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, signatures) {
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 signature = signatures[i] ?? "";
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: value.toString(),
1869
+ value: valueStr,
1571
1870
  data,
1572
1871
  signature,
1573
- noOp: isGovNoOpAction({ value: value.toString(), signature, calldata: data })
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 proposalArgs2 = buildBravoProposalArgs(args.actions ?? [], title, noOp);
1582
- const signatures = (args.actions ?? []).map((a) => (a.signature ?? "").trim());
1583
- if (signatures.length === 0) signatures.push("");
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
- batches: [
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 encoded = buildBravoProposalArgs(opt.actions ?? [], title, noOp);
1598
- const signatures = (opt.actions ?? []).map((a) => (a.signature ?? "").trim());
1599
- if (signatures.length === 0) signatures.push("");
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, signatures)
1901
+ legs: legsFromEncoded(encoded.targets, encoded.values, encoded.calldatas, aligned)
1604
1902
  };
1605
1903
  });
1606
- return { proposalArgs, batches };
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 run the Foundry forge scripts in `foundry.files` on a Linea fork (no --broadcast) so action N sees state from action N-1."
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. 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."
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. Run the Foundry forge scripts in foundry.files on a Linea fork (foundry__forge_script, no --broadcast) before submitting.";
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
- proposeData,
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 splitAbiParamList(inner) {
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 splitAbiParamList(inner).map((t) => ({ type: t }));
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 = splitAbiParamList(type.slice(1, -1));
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 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.",
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 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.",
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
  }