@continuumdao/ctm-mpc-defi 0.2.53 → 0.2.55

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.
@@ -27,7 +27,7 @@ One contract: `ContinuumDAO.propose`. Bravo vs Delta is encoding, not a separate
27
27
  - **Delta vote:** `weights` must be **nOptions + 1**. The last slot is baked-in NOTA. Do not ship nOptions-only params.
28
28
  - **Queue is not implemented.** `queue()` reverts. Execute from `Succeeded`.
29
29
 
30
- After a propose multi-sign request is **mined**, call `ctm_continuum_dao_register_proposal` (`POST /proposals/create`) so [app.continuumdao.org/governance](https://app.continuumdao.org/governance) stays in sync. If the POST fails, retry — do not revert the chain tx. `onchainId` comes from `hashProposal` or `ProposalCreated`.
30
+ `build_propose_bravo_multisign` and `build_propose_delta_multisign` POST `/proposals/create` (Ed25519, `pendingOnChain`) **before** the multi-sign request is submitted. Pass `description`, `type` (0–4), and a `name` on every action input so the catalog matches the forum section. If that POST fails, or the created id cannot be read back, the sign request is not created. `ctm_continuum_dao_register_proposal` retries the same POST and, when the onchainId already exists, updates that row instead of creating a second proposal. Do not wait for the propose tx to be mined before the catalog write.
31
31
 
32
32
  `load_defi_protocol({ protocolId: "continuum-dao" })` loads escrow, attach, and governance together.
33
33
 
@@ -68,8 +68,8 @@ When a proposal has a `forumKey`, fetch that thread (`ctm_continuum_dao_forum_re
68
68
  2. If it is only an idea: `forum_create_idea`. Stop (no propose).
69
69
  3. If it is a proposal: `forum_create_topic` with the matching Governance `section` (proposal-threshold veCTM). Keep the returned `url`.
70
70
  4. Compose actions with `ctm_continuum_dao_compose_proposal_action` (prefer ABI from `etherscan__get_contract_abi` with chainid 59144/59141, type-check values, preview table). Then offer `ctm_continuum_dao_simulate_proposal` — pass `account` for another EOA, or `keyGenId` / omit `account` for the preferred KeyGen. Write `foundry.files` and run `foundry__forge_script` on a Linea fork with no `--broadcast`. Do **not** treat isolated `eth_call` as success. Do **not** submit if `ok` is false or a forge script reverts.
71
- 5. `build_propose_bravo_multisign` or `build_propose_delta_multisign` with that `url` as `forumKey` (KeyGen submit only). These builders refuse a reverting `propose()` or (when the RPC supports it) sequential `eth_simulateV1` execute. Sequential execute still requires the forge scripts above.
72
- 6. After the propose tx is mined, `register_proposal` with the same `forumKey`.
71
+ 5. `build_propose_bravo_multisign` or `build_propose_delta_multisign` with that `url` as `forumKey`, plus `description` and `type` (KeyGen submit only). The tool writes the governance catalog, then submits the multi-sign request. It refuses a reverting `propose()` or (when the RPC supports it) sequential `eth_simulateV1` execute. Sequential execute still requires the forge scripts above.
72
+ 6. If the catalog POST failed, retry with `register_proposal` and the same `forumKey`, `description`, `type`, input names, and `onchainId`. A 409 updates the existing row. Do not create a second propose.
73
73
 
74
74
  `forumKey` must match `/topic/:tid` or `/t/:tid` on forum.continuumdao.org. Builders and `register_proposal` throw otherwise.
75
75
 
@@ -40,15 +40,6 @@ var init_chainIdParse = __esm({
40
40
  "src/chains/evm/chainIdParse.ts"() {
41
41
  }
42
42
  });
43
- function isContinuumDaoCommitteeProposer(addr2) {
44
- if (!addr2 || !isConfiguredAddress(addr2)) return false;
45
- try {
46
- const a = viem.getAddress(addr2);
47
- return exports.CONTINUUM_DAO_COMMITTEE_PROPOSERS.some((c) => c === a);
48
- } catch {
49
- return false;
50
- }
51
- }
52
43
  function isConfiguredAddress(addr2) {
53
44
  if (!addr2?.trim()) return false;
54
45
  try {
@@ -140,7 +131,7 @@ function listContinuumDaoDefaultAssets() {
140
131
  }
141
132
  return out;
142
133
  }
143
- exports.CONTINUUM_DAO_PROTOCOL_ID = void 0; exports.CONTINUUM_DAO_ETHEREUM_CHAIN_ID = void 0; exports.CONTINUUM_DAO_LINEA_CHAIN_ID = void 0; exports.CONTINUUM_DAO_LINEA_SEPOLIA_CHAIN_ID = void 0; exports.CTM_TOKEN_NAME = void 0; exports.CTM_TOKEN_SYMBOL = void 0; exports.VECTM_TOKEN_NAME = void 0; exports.VECTM_TOKEN_SYMBOL = void 0; exports.CTM_TOKEN_DECIMALS = void 0; exports.WEEK_SECONDS = void 0; exports.MAXTIME_SECONDS = void 0; exports.VECTM_MAX_LOCK_WEEKS = void 0; exports.VECTM_DEFAULT_MINIMUM_LOCK_WEI = void 0; exports.VECTM_LIQ_MIN_LOCKED_WEI = void 0; exports.VECTM_LIQ_PENALTY_NUM = void 0; exports.VECTM_LIQ_PENALTY_DEN = void 0; var UNSET; exports.CTM_TOKEN_ADDRESS = void 0; exports.VOTING_ESCROW_LINEA_ADDRESS = void 0; exports.MULTISIGN_AGENT_WALLET_LINEA_ADDRESS = void 0; exports.CTM_FEE_RATE_SCALE = void 0; exports.VOTING_ESCROW_MARKETPLACE_LINEA_ADDRESS = void 0; exports.VOTING_ESCROW_MARKETPLACE_START_BLOCK = void 0; exports.MARKETPLACE_WETH_LINEA_ADDRESS = void 0; exports.MARKETPLACE_USDC_LINEA_ADDRESS = void 0; exports.CONTINUUM_DAO_MARKETPLACE_PROTOCOL_ID = void 0; exports.CONTINUUM_DAO_LINEA_GOVERNOR_ADDRESS = void 0; exports.CONTINUUM_DAO_LINEA_GOVERNOR_LEGACY_ADDRESS = void 0; exports.CONTINUUM_DAO_COMMITTEE_PROPOSERS = void 0; exports.CONTINUUM_DAO_DEPLOYMENTS = void 0; exports.CONTINUUM_DAO_GOVERNANCE_API = void 0; exports.CONTINUUM_DAO_MARKETPLACE_API = void 0;
134
+ exports.CONTINUUM_DAO_PROTOCOL_ID = void 0; exports.CONTINUUM_DAO_ETHEREUM_CHAIN_ID = void 0; exports.CONTINUUM_DAO_LINEA_CHAIN_ID = void 0; exports.CONTINUUM_DAO_LINEA_SEPOLIA_CHAIN_ID = void 0; exports.CTM_TOKEN_NAME = void 0; exports.CTM_TOKEN_SYMBOL = void 0; exports.VECTM_TOKEN_NAME = void 0; exports.VECTM_TOKEN_SYMBOL = void 0; exports.CTM_TOKEN_DECIMALS = void 0; exports.WEEK_SECONDS = void 0; exports.MAXTIME_SECONDS = void 0; exports.VECTM_MAX_LOCK_WEEKS = void 0; exports.VECTM_DEFAULT_MINIMUM_LOCK_WEI = void 0; exports.VECTM_LIQ_MIN_LOCKED_WEI = void 0; exports.VECTM_LIQ_PENALTY_NUM = void 0; exports.VECTM_LIQ_PENALTY_DEN = void 0; var UNSET; exports.CTM_TOKEN_ADDRESS = void 0; exports.VOTING_ESCROW_LINEA_ADDRESS = void 0; exports.MULTISIGN_AGENT_WALLET_LINEA_ADDRESS = void 0; exports.CTM_FEE_RATE_SCALE = void 0; exports.VOTING_ESCROW_MARKETPLACE_LINEA_ADDRESS = void 0; exports.VOTING_ESCROW_MARKETPLACE_START_BLOCK = void 0; exports.MARKETPLACE_WETH_LINEA_ADDRESS = void 0; exports.MARKETPLACE_USDC_LINEA_ADDRESS = void 0; exports.CONTINUUM_DAO_MARKETPLACE_PROTOCOL_ID = void 0; exports.CONTINUUM_DAO_LINEA_GOVERNOR_ADDRESS = void 0; exports.CONTINUUM_DAO_LINEA_GOVERNOR_LEGACY_ADDRESS = void 0; exports.CONTINUUM_DAO_DEPLOYMENTS = void 0; exports.CONTINUUM_DAO_GOVERNANCE_API = void 0; exports.CONTINUUM_DAO_MARKETPLACE_API = void 0;
144
135
  var init_constants = __esm({
145
136
  "src/protocols/evm/continuum-dao/constants.ts"() {
146
137
  init_chainIdParse();
@@ -178,11 +169,6 @@ var init_constants = __esm({
178
169
  exports.CONTINUUM_DAO_MARKETPLACE_PROTOCOL_ID = "continuum-dao-marketplace";
179
170
  exports.CONTINUUM_DAO_LINEA_GOVERNOR_ADDRESS = viem.getAddress("0x76FF2CB03175900F1D83328C82D27EA9aeaF2355");
180
171
  exports.CONTINUUM_DAO_LINEA_GOVERNOR_LEGACY_ADDRESS = viem.getAddress("0x4800F9f1dC1b6daCA841B71E0531F547D374168E");
181
- exports.CONTINUUM_DAO_COMMITTEE_PROPOSERS = [
182
- viem.getAddress("0xd23eecBe0362F36b254F774C274823Cbfc482a10"),
183
- viem.getAddress("0x482cdCbdd72ef307997153Ee7eb627B7a2348d34"),
184
- viem.getAddress("0x14C189354702b7f2aC7b19e61522A41632226BD8")
185
- ];
186
172
  exports.CONTINUUM_DAO_DEPLOYMENTS = {
187
173
  [exports.CONTINUUM_DAO_LINEA_CHAIN_ID]: {
188
174
  ...UNSET,
@@ -200,6 +186,160 @@ var init_constants = __esm({
200
186
  exports.CONTINUUM_DAO_MARKETPLACE_API = exports.CONTINUUM_DAO_GOVERNANCE_API;
201
187
  }
202
188
  });
189
+ function docsBaseUrl() {
190
+ const raw = (typeof process !== "undefined" ? process.env.CONTINUUM_DOCS_BASE_URL : "")?.trim() || DEFAULT_DOCS_BASE;
191
+ return raw.replace(/\/+$/, "");
192
+ }
193
+ function parseCommitteeAddressesFromConstitution(markdown) {
194
+ const text = String(markdown || "");
195
+ const start = text.search(/^### Committee List\s*$/m);
196
+ if (start < 0) return [];
197
+ const lines = text.slice(start).split("\n");
198
+ const addresses = [];
199
+ for (let i = 1; i < lines.length; i += 1) {
200
+ const line = lines[i] ?? "";
201
+ if (/^#{1,6}\s/.test(line)) break;
202
+ const trimmed = line.trim();
203
+ if (!trimmed.startsWith("|")) continue;
204
+ if (/^\|\s*[-:]+\s*\|/.test(trimmed)) continue;
205
+ if (/^\|\s*Name\s*\|/i.test(trimmed)) continue;
206
+ const match = trimmed.match(/0x[a-fA-F0-9]{40}/);
207
+ if (!match) continue;
208
+ try {
209
+ addresses.push(viem.getAddress(match[0]));
210
+ } catch {
211
+ }
212
+ }
213
+ return addresses;
214
+ }
215
+ function addressInCommitteeList(addr2, addresses) {
216
+ if (!addr2 || addresses.length === 0) return false;
217
+ try {
218
+ const checksummed = viem.getAddress(addr2);
219
+ return addresses.some((item) => viem.getAddress(item) === checksummed);
220
+ } catch {
221
+ return false;
222
+ }
223
+ }
224
+ async function fetchContinuumDaoCommitteeAddresses() {
225
+ const now = Date.now();
226
+ if (cache && cache.expires > now) return cache.addresses;
227
+ const url = `${docsBaseUrl()}/${CONSTITUTION_PATH}`;
228
+ try {
229
+ const res = await fetch(url, { cache: "no-store" });
230
+ if (!res.ok) throw new Error(`constitution: ${res.status}`);
231
+ const markdown = await res.text();
232
+ const addresses = parseCommitteeAddressesFromConstitution(markdown);
233
+ cache = { expires: now + CACHE_TTL_MS, addresses };
234
+ return addresses;
235
+ } catch {
236
+ return [];
237
+ }
238
+ }
239
+ function resetContinuumDaoCommitteeCache() {
240
+ cache = null;
241
+ }
242
+ var DEFAULT_DOCS_BASE, CONSTITUTION_PATH, CACHE_TTL_MS, cache;
243
+ var init_committeeList = __esm({
244
+ "src/protocols/evm/continuum-dao/committeeList.ts"() {
245
+ DEFAULT_DOCS_BASE = "https://docs.continuumdao.org";
246
+ CONSTITUTION_PATH = "ContinuumDAO/Governance/Constitution.md";
247
+ CACHE_TTL_MS = 5 * 60 * 1e3;
248
+ cache = null;
249
+ }
250
+ });
251
+ function stableJson(value) {
252
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
253
+ if (Array.isArray(value)) return `[${value.map((item) => stableJson(item)).join(",")}]`;
254
+ const record = value;
255
+ const keys = Object.keys(record).sort();
256
+ return `{${keys.map((key) => `${JSON.stringify(key)}:${stableJson(record[key])}`).join(",")}}`;
257
+ }
258
+ function checksumOrEmpty(value) {
259
+ const raw = String(value || "").trim();
260
+ if (!raw) return "";
261
+ return viem.getAddress(raw);
262
+ }
263
+ function normalizeInput(inp, index) {
264
+ const explicit = String(inp?.name || "").trim();
265
+ return {
266
+ name: explicit || `arg${index}`,
267
+ type: String(inp?.type || ""),
268
+ value: String(inp?.value ?? "")
269
+ };
270
+ }
271
+ function normalizeAction(action) {
272
+ const network = action.network || {};
273
+ return {
274
+ network: {
275
+ name: String(network.name || ""),
276
+ label: String(network.label || ""),
277
+ chainId: String(network.chainId || ""),
278
+ c3governor: checksumOrEmpty(network.c3governor)
279
+ },
280
+ target: checksumOrEmpty(action.target),
281
+ value: String(action.value || "0"),
282
+ signature: String(action.signature || ""),
283
+ inputs: (action.inputs || []).map((inp, index) => normalizeInput(inp, index))
284
+ };
285
+ }
286
+ function assertProposalCatalogConstants() {
287
+ if (!exports.CONTINUUM_DAO_GOVERNANCE_API.startsWith("https://")) {
288
+ throw new Error("CONTINUUM_DAO_GOVERNANCE_API is not configured.");
289
+ }
290
+ if (!isConfiguredAddress(exports.CONTINUUM_DAO_LINEA_GOVERNOR_ADDRESS)) {
291
+ throw new Error("Linea governor constant is not configured.");
292
+ }
293
+ const network = exports.CONTINUUM_DAO_LINEA_CATALOG_NETWORK;
294
+ if (!network.name || !network.label || !network.chainId || !isConfiguredAddress(network.c3governor)) {
295
+ throw new Error("Linea catalog network constant is not configured.");
296
+ }
297
+ }
298
+ function proposalCatalogDocument(body) {
299
+ const doc = {
300
+ onchainId: String(body.onchainId || ""),
301
+ title: String(body.title || ""),
302
+ description: String(body.description || ""),
303
+ proposer: checksumOrEmpty(body.proposer),
304
+ forumKey: String(body.forumKey || ""),
305
+ type: Number(body.type ?? 0),
306
+ configuration: body.configuration,
307
+ pendingOnChain: body.pendingOnChain === true
308
+ };
309
+ if (body.configuration === 1) {
310
+ doc.nWinners = Number(body.nWinners ?? 0);
311
+ doc.options = (body.options || []).map((option) => ({
312
+ label: String(option.label || ""),
313
+ actions: (option.actions || []).map(normalizeAction)
314
+ }));
315
+ } else {
316
+ doc.actions = (body.actions || []).map(normalizeAction);
317
+ }
318
+ return doc;
319
+ }
320
+ function proposalCatalogCanonical(body) {
321
+ return stableJson(proposalCatalogDocument(body));
322
+ }
323
+ function stampLineaCatalogActions(actions) {
324
+ return (actions || []).map(
325
+ (action) => normalizeAction({
326
+ ...action,
327
+ network: { ...exports.CONTINUUM_DAO_LINEA_CATALOG_NETWORK }
328
+ })
329
+ );
330
+ }
331
+ exports.CONTINUUM_DAO_LINEA_CATALOG_NETWORK = void 0;
332
+ var init_proposalCatalog = __esm({
333
+ "src/protocols/evm/continuum-dao/proposalCatalog.ts"() {
334
+ init_constants();
335
+ exports.CONTINUUM_DAO_LINEA_CATALOG_NETWORK = {
336
+ name: "linea",
337
+ label: "Linea",
338
+ chainId: "59144",
339
+ c3governor: viem.getAddress("0x58B610a359c870E0fc941139821a51F5aa23f14E")
340
+ };
341
+ }
342
+ });
203
343
  function defiEvmTransport(rpcUrl, opts) {
204
344
  const url = rpcUrl.trim();
205
345
  return viem.http(url, opts);
@@ -1110,7 +1250,7 @@ function typeLabel(type) {
1110
1250
  const n = Number(type);
1111
1251
  return Number.isFinite(n) && exports.PROPOSAL_TYPE_LABELS[n] ? exports.PROPOSAL_TYPE_LABELS[n] : str(type, "Unknown");
1112
1252
  }
1113
- function explainProposalRecord(raw) {
1253
+ function explainProposalRecord(raw, committeeAddresses) {
1114
1254
  const row = asRecord(raw);
1115
1255
  if (!row) throw new Error("explain_proposal: empty proposal");
1116
1256
  const onchainId = str(row.onchainId ?? row.onchain_id ?? row.proposalId);
@@ -1142,7 +1282,7 @@ function explainProposalRecord(raw) {
1142
1282
  risks.push("Empty description with on-chain actions.");
1143
1283
  }
1144
1284
  const proposer = str(row.proposer);
1145
- const committeeProposer = isContinuumDaoCommitteeProposer(proposer);
1285
+ const committeeProposer = addressInCommitteeList(proposer, committeeAddresses ?? []);
1146
1286
  const lines = [];
1147
1287
  lines.push(`#${row.id ?? "\u2014"} ${str(row.title, "Untitled")} (${t}, ${configuration === "delta" ? "Delta" : "Bravo"}).`);
1148
1288
  if (committeeProposer) {
@@ -1205,9 +1345,10 @@ async function withForumSection(out, forumUrl) {
1205
1345
  }
1206
1346
  }
1207
1347
  async function continuumDaoExplainProposal(args) {
1348
+ const committeeAddresses = await fetchContinuumDaoCommitteeAddresses();
1208
1349
  if (args.id != null && String(args.id).trim() !== "") {
1209
1350
  const { proposal } = await continuumDaoFetchProposal({ id: args.id });
1210
- return withForumSection(explainProposalRecord(proposal), args.forumUrl);
1351
+ return withForumSection(explainProposalRecord(proposal, committeeAddresses), args.forumUrl);
1211
1352
  }
1212
1353
  const want = (args.onchainId ?? "").trim();
1213
1354
  if (!want) throw new Error("explain_proposal: pass id or onchainId");
@@ -1221,14 +1362,14 @@ async function continuumDaoExplainProposal(args) {
1221
1362
  const id = asRecord(hit)?.id;
1222
1363
  if (id != null) {
1223
1364
  const { proposal } = await continuumDaoFetchProposal({ id: Number(id) });
1224
- return withForumSection(explainProposalRecord(proposal), args.forumUrl);
1365
+ return withForumSection(explainProposalRecord(proposal, committeeAddresses), args.forumUrl);
1225
1366
  }
1226
- return withForumSection(explainProposalRecord(hit), args.forumUrl);
1367
+ return withForumSection(explainProposalRecord(hit, committeeAddresses), args.forumUrl);
1227
1368
  }
1228
1369
  exports.PROPOSAL_TYPE_LABELS = void 0; var APPROVE_RE, DENY_SIG_RE, LINEA_HOME_CHAIN_IDS;
1229
1370
  var init_explainProposal = __esm({
1230
1371
  "src/protocols/evm/continuum-dao/explainProposal.ts"() {
1231
- init_constants();
1372
+ init_committeeList();
1232
1373
  init_forum();
1233
1374
  init_governance();
1234
1375
  exports.PROPOSAL_TYPE_LABELS = ["Admin", "Constitution", "Decision", "Election", "Treasury"];
@@ -1574,7 +1715,7 @@ function summarizeNote(args) {
1574
1715
  return parts.join(" ");
1575
1716
  }
1576
1717
  function votingPowerShortfallBody(args) {
1577
- const committee = isContinuumDaoCommitteeProposer(args.account) ? " This is a ContinuumDAO Committee address \u2014 Committee is not exempt from proposalThreshold()." : "";
1718
+ const committee = addressInCommitteeList(args.account, args.committeeAddresses ?? []) ? " This is a ContinuumDAO Committee address \u2014 Committee is not exempt from proposalThreshold()." : "";
1578
1719
  return `${args.account} has veCTM voting power ${args.votes}, below live proposalThreshold() ${args.threshold}. On-chain rule: max(1000e18 ve power, 1% of total voting power). Delegation applies at this address (fetch_delegates).` + committee;
1579
1720
  }
1580
1721
  function votingPowerWarning(args) {
@@ -1705,7 +1846,13 @@ async function continuumDaoSimulateProposal(args) {
1705
1846
  }
1706
1847
  const executeOk = source !== "foundry_required" && batchResults.every((b) => b.ok);
1707
1848
  const ok = canPropose && proposeOk && executeOk;
1708
- const warning = votingPowerWarning({ account, votes: power.votes, threshold: power.threshold });
1849
+ const committeeAddresses = await fetchContinuumDaoCommitteeAddresses();
1850
+ const warning = votingPowerWarning({
1851
+ account,
1852
+ votes: power.votes,
1853
+ threshold: power.threshold,
1854
+ committeeAddresses
1855
+ });
1709
1856
  const foundry = buildFoundryProposalSimulatePlan({
1710
1857
  chainId,
1711
1858
  governor: gov,
@@ -1721,6 +1868,7 @@ async function continuumDaoSimulateProposal(args) {
1721
1868
  votes: power.votes,
1722
1869
  threshold: power.threshold,
1723
1870
  canPropose,
1871
+ committeeAddresses,
1724
1872
  ...warning ? { warning } : {},
1725
1873
  propose: proposeOk ? { ok: true } : { ok: false, error: proposeError },
1726
1874
  execute: { ok: executeOk, source, batches: batchResults },
@@ -1730,7 +1878,7 @@ async function continuumDaoSimulateProposal(args) {
1730
1878
  }
1731
1879
  var init_simulateProposal = __esm({
1732
1880
  "src/protocols/evm/continuum-dao/simulateProposal.ts"() {
1733
- init_constants();
1881
+ init_committeeList();
1734
1882
  init_explainProposal();
1735
1883
  init_foundryProposalScript();
1736
1884
  init_governance();
@@ -1928,6 +2076,56 @@ async function finalizeGov(args, to, data, fallbackGas, evmType, extra, purposeS
1928
2076
  buildBatchMeta: ({ gasLimit }) => meta2(evmType, args.chainId, to, extra, gasLimit)
1929
2077
  });
1930
2078
  }
2079
+ async function assertProposalTypeMatchesForum(forumKey, type, forumUrl) {
2080
+ const thread = await continuumDaoForumFetchThread({ url: forumKey, index: 0, forumUrl });
2081
+ const section = String(thread?.section || "").trim().toLowerCase();
2082
+ const expected = FORUM_SECTION_TO_PROPOSAL_TYPE[section];
2083
+ if (expected == null) {
2084
+ throw new Error(
2085
+ `Forum thread is not in a Governance proposal section (section: ${section || "unknown"}).`
2086
+ );
2087
+ }
2088
+ if (expected !== type) {
2089
+ throw new Error(`Proposal type ${type} does not match forum section ${section} (expected type ${expected}).`);
2090
+ }
2091
+ }
2092
+ async function buildProposeCatalog(args) {
2093
+ assertProposalCatalogConstants();
2094
+ if (!args.description && args.description !== "") {
2095
+ throw new Error("description is required to register the proposal catalog.");
2096
+ }
2097
+ if (!Number.isInteger(args.type) || args.type < 0 || args.type > 4) {
2098
+ throw new Error("type must be 0\u20134 (Admin, Constitution, Decision, Election, Treasury).");
2099
+ }
2100
+ await assertProposalTypeMatchesForum(args.forumKey, args.type, args.forumUrl);
2101
+ const hashed = await continuumDaoHashProposal({
2102
+ chainId: args.chainId,
2103
+ rpcUrl: args.rpcUrl,
2104
+ governor: args.governor,
2105
+ targets: args.targets,
2106
+ values: args.values,
2107
+ calldatas: args.calldatas,
2108
+ description: args.descriptionHashInput
2109
+ });
2110
+ const payload = proposalCatalogDocument({
2111
+ onchainId: hashed.proposalId,
2112
+ title: args.title.trim(),
2113
+ description: args.description,
2114
+ proposer: args.proposer,
2115
+ forumKey: args.forumKey,
2116
+ type: args.type,
2117
+ configuration: args.configuration,
2118
+ pendingOnChain: true,
2119
+ ...args.configuration === 1 ? {
2120
+ nWinners: args.nWinners,
2121
+ options: args.catalogOptions ?? (args.options || []).map((option) => ({
2122
+ label: option.label,
2123
+ actions: stampLineaCatalogActions(option.actions)
2124
+ }))
2125
+ } : { actions: args.catalogActions ?? stampLineaCatalogActions(args.actions) }
2126
+ });
2127
+ return { payload, canonical: proposalCatalogCanonical(payload) };
2128
+ }
1931
2129
  async function requireProposalThreshold(args) {
1932
2130
  const power = await continuumDaoFetchVotingPower({
1933
2131
  chainId: args.chainId,
@@ -1937,11 +2135,13 @@ async function requireProposalThreshold(args) {
1937
2135
  });
1938
2136
  if (BigInt(power.votes) < BigInt(power.threshold)) {
1939
2137
  const { votingPowerRefuse: votingPowerRefuse2 } = await Promise.resolve().then(() => (init_simulateProposal(), simulateProposal_exports));
2138
+ const committeeAddresses = await fetchContinuumDaoCommitteeAddresses();
1940
2139
  throw new Error(
1941
2140
  votingPowerRefuse2({
1942
2141
  account: args.executorAddress,
1943
2142
  votes: power.votes,
1944
- threshold: power.threshold
2143
+ threshold: power.threshold,
2144
+ committeeAddresses
1945
2145
  })
1946
2146
  );
1947
2147
  }
@@ -1984,7 +2184,25 @@ async function buildEvmMultisignBodyContinuumDaoProposeBravo(args) {
1984
2184
  },
1985
2185
  "continuum-dao propose bravo"
1986
2186
  );
1987
- return { ...built, proposalArgs };
2187
+ const proposalCatalog = await buildProposeCatalog({
2188
+ configuration: 0,
2189
+ title: args.title,
2190
+ description: args.description ?? "",
2191
+ type: Number(args.type),
2192
+ forumKey,
2193
+ forumUrl: args.forumUrl,
2194
+ proposer: args.executorAddress,
2195
+ chainId: args.chainId,
2196
+ rpcUrl: args.rpcUrl,
2197
+ governor: gov,
2198
+ targets: proposalArgs.targets,
2199
+ values: proposalArgs.values,
2200
+ calldatas: proposalArgs.calldatas,
2201
+ descriptionHashInput: proposalArgs.description,
2202
+ actions: args.actions,
2203
+ catalogActions: args.catalogActions
2204
+ });
2205
+ return { ...built, proposalArgs, proposalCatalog };
1988
2206
  }
1989
2207
  async function buildEvmMultisignBodyContinuumDaoProposeDelta(args) {
1990
2208
  const forumKey = assertContinuumDaoForumTopicKey(args.forumKey, args.forumUrl);
@@ -2025,7 +2243,26 @@ async function buildEvmMultisignBodyContinuumDaoProposeDelta(args) {
2025
2243
  },
2026
2244
  "continuum-dao propose delta"
2027
2245
  );
2028
- return { ...built, proposalArgs };
2246
+ const proposalCatalog = await buildProposeCatalog({
2247
+ configuration: 1,
2248
+ title: args.title,
2249
+ description: args.description ?? "",
2250
+ type: Number(args.type),
2251
+ forumKey,
2252
+ forumUrl: args.forumUrl,
2253
+ proposer: args.executorAddress,
2254
+ chainId: args.chainId,
2255
+ rpcUrl: args.rpcUrl,
2256
+ governor: gov,
2257
+ targets: proposalArgs.targets,
2258
+ values: proposalArgs.values,
2259
+ calldatas: proposalArgs.calldatas,
2260
+ descriptionHashInput: proposalArgs.description,
2261
+ options: args.options,
2262
+ catalogOptions: args.catalogOptions,
2263
+ nWinners: Number(args.nWinners)
2264
+ });
2265
+ return { ...built, proposalArgs, proposalCatalog };
2029
2266
  }
2030
2267
  async function requireVoteEligible(args) {
2031
2268
  const st = await continuumDaoFetchProposalState({
@@ -2327,7 +2564,7 @@ function bucketGovernorState(state) {
2327
2564
  function asRecord2(value) {
2328
2565
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
2329
2566
  }
2330
- function parseBackendProposal(raw) {
2567
+ function parseBackendProposal(raw, committeeAddresses) {
2331
2568
  const row = asRecord2(raw);
2332
2569
  if (!row) return null;
2333
2570
  const onchainId = String(row.onchainId ?? row.onchain_id ?? row.proposalId ?? "").trim();
@@ -2344,7 +2581,7 @@ function parseBackendProposal(raw) {
2344
2581
  configuration: typeof row.configuration === "number" ? row.configuration : void 0,
2345
2582
  governorAddress: isConfiguredAddress(governorRaw) ? viem.getAddress(governorRaw) : void 0,
2346
2583
  proposer,
2347
- committeeProposer: proposer ? isContinuumDaoCommitteeProposer(proposer) : void 0
2584
+ committeeProposer: proposer ? addressInCommitteeList(proposer, committeeAddresses ?? []) : void 0
2348
2585
  };
2349
2586
  }
2350
2587
  function isLegacyContinuumDaoGovernor(addr2) {
@@ -2378,7 +2615,8 @@ async function continuumDaoFetchLiveProposals(args) {
2378
2615
  const completedLimit = Math.min(Math.max(Number(args.completedLimit) || 8, 0), 20);
2379
2616
  const listed = await continuumDaoFetchProposals();
2380
2617
  const rawList = Array.isArray(listed.proposals) ? listed.proposals : [];
2381
- const parsed = rawList.map(parseBackendProposal).filter((p) => p != null);
2618
+ const committeeAddresses = await fetchContinuumDaoCommitteeAddresses();
2619
+ const parsed = rawList.map((row) => parseBackendProposal(row, committeeAddresses)).filter((p) => p != null);
2382
2620
  const ordered = [...parsed].sort((a, b) => (a.id ?? 0) - (b.id ?? 0));
2383
2621
  const latest = ordered.length > limit ? ordered.slice(ordered.length - limit) : ordered;
2384
2622
  const chainId = args.chainId != null && String(args.chainId).trim() !== "" ? Number(args.chainId) : NaN;
@@ -2447,21 +2685,35 @@ async function buildEvmMultisignBodyContinuumDaoCastVoteBravoFromMcp(args) {
2447
2685
  }
2448
2686
  async function continuumDaoRegisterProposal(payload) {
2449
2687
  assertContinuumDaoForumTopicKey(payload.forumKey);
2688
+ const body = JSON.stringify(payload);
2450
2689
  const res = await fetch(`${exports.CONTINUUM_DAO_GOVERNANCE_API}/proposals/create`, {
2451
2690
  method: "POST",
2452
2691
  headers: { "Content-Type": "application/json" },
2453
- body: JSON.stringify(payload)
2692
+ body
2454
2693
  });
2455
- if (!res.ok) throw new Error(`proposals/create: ${res.status} ${await res.text()}`);
2456
- return { ok: true, result: await res.json() };
2694
+ const text = await res.text();
2695
+ if (res.status === 409) {
2696
+ const repair = await fetch(`${exports.CONTINUUM_DAO_GOVERNANCE_API}/proposals/repair`, {
2697
+ method: "POST",
2698
+ headers: { "Content-Type": "application/json" },
2699
+ body
2700
+ });
2701
+ const repairText = await repair.text();
2702
+ if (!repair.ok) throw new Error(`proposals/repair: ${repair.status} ${repairText}`);
2703
+ return { ok: true, result: JSON.parse(repairText) };
2704
+ }
2705
+ if (!res.ok) throw new Error(`proposals/create: ${res.status} ${text}`);
2706
+ return { ok: true, result: JSON.parse(text) };
2457
2707
  }
2458
- exports.CONTINUUM_DAO_PROPOSE_FALLBACK = void 0; exports.CONTINUUM_DAO_VOTE_FALLBACK = void 0; exports.CONTINUUM_DAO_EXECUTE_FALLBACK = void 0; exports.CONTINUUM_DAO_CANCEL_FALLBACK = void 0; exports.CONTINUUM_DAO_GOV_EVM_TYPES = void 0; exports.governorWriteAbi = void 0; var governorReadAbi; exports.GOVERNOR_STATE = void 0; exports.GOVERNOR_STATE_LABELS = void 0;
2708
+ exports.CONTINUUM_DAO_PROPOSE_FALLBACK = void 0; exports.CONTINUUM_DAO_VOTE_FALLBACK = void 0; exports.CONTINUUM_DAO_EXECUTE_FALLBACK = void 0; exports.CONTINUUM_DAO_CANCEL_FALLBACK = void 0; exports.CONTINUUM_DAO_GOV_EVM_TYPES = void 0; exports.governorWriteAbi = void 0; var governorReadAbi, FORUM_SECTION_TO_PROPOSAL_TYPE; exports.GOVERNOR_STATE = void 0; exports.GOVERNOR_STATE_LABELS = void 0;
2459
2709
  var init_governance = __esm({
2460
2710
  "src/protocols/evm/continuum-dao/governance.ts"() {
2461
2711
  init_defiEvmClient();
2462
2712
  init_buildBatch();
2463
2713
  init_constants();
2714
+ init_committeeList();
2464
2715
  init_forum();
2716
+ init_proposalCatalog();
2465
2717
  exports.CONTINUUM_DAO_PROPOSE_FALLBACK = 550000n;
2466
2718
  exports.CONTINUUM_DAO_VOTE_FALLBACK = 220000n;
2467
2719
  exports.CONTINUUM_DAO_EXECUTE_FALLBACK = 800000n;
@@ -2490,6 +2742,13 @@ var init_governance = __esm({
2490
2742
  "function hasVoted(uint256 proposalId, address account) view returns (bool)",
2491
2743
  "function proposalSnapshot(uint256 proposalId) view returns (uint256)"
2492
2744
  ]);
2745
+ FORUM_SECTION_TO_PROPOSAL_TYPE = {
2746
+ admin: 0,
2747
+ constitution: 1,
2748
+ decision: 2,
2749
+ election: 3,
2750
+ treasury: 4
2751
+ };
2493
2752
  exports.GOVERNOR_STATE = {
2494
2753
  Pending: 0,
2495
2754
  Active: 1,
@@ -2566,6 +2825,8 @@ var continuumDaoMarketplaceProtocolModule = {
2566
2825
 
2567
2826
  // src/protocols/evm/continuum-dao/index.ts
2568
2827
  init_constants();
2828
+ init_committeeList();
2829
+ init_proposalCatalog();
2569
2830
 
2570
2831
  // src/protocols/evm/continuum-dao/escrowRules.ts
2571
2832
  init_constants();
@@ -5796,8 +6057,8 @@ function emptyMarketplaceIndexCache() {
5796
6057
  function getMarketplaceIndexMemoryCache(chainId) {
5797
6058
  return memoryCache.get(chainId) ?? null;
5798
6059
  }
5799
- function setMarketplaceIndexMemoryCache(chainId, cache) {
5800
- memoryCache.set(chainId, cache);
6060
+ function setMarketplaceIndexMemoryCache(chainId, cache2) {
6061
+ memoryCache.set(chainId, cache2);
5801
6062
  }
5802
6063
  function arg(args, key, index) {
5803
6064
  const value = args[key] ?? args[index];
@@ -5947,9 +6208,9 @@ async function scanMarketplaceLogs(args) {
5947
6208
  { listings: prior.listings, offerings: prior.offerings, auctions: prior.auctions },
5948
6209
  now2
5949
6210
  );
5950
- const cache2 = { lastScannedBlock: head.toString(), ...derived2 };
5951
- setMarketplaceIndexMemoryCache(args.chainId, cache2);
5952
- return cache2;
6211
+ const cache3 = { lastScannedBlock: head.toString(), ...derived2 };
6212
+ setMarketplaceIndexMemoryCache(args.chainId, cache3);
6213
+ return cache3;
5953
6214
  }
5954
6215
  const logs = await getLogsChunked({
5955
6216
  client,
@@ -5966,9 +6227,9 @@ async function scanMarketplaceLogs(args) {
5966
6227
  });
5967
6228
  const now = Number((await client.getBlock({ blockTag: "latest" })).timestamp);
5968
6229
  const derived = applyDerivedStatuses(folded, now);
5969
- const cache = { lastScannedBlock: head.toString(), ...derived };
5970
- setMarketplaceIndexMemoryCache(args.chainId, cache);
5971
- return cache;
6230
+ const cache2 = { lastScannedBlock: head.toString(), ...derived };
6231
+ setMarketplaceIndexMemoryCache(args.chainId, cache2);
6232
+ return cache2;
5972
6233
  }
5973
6234
 
5974
6235
  // src/protocols/evm/continuum-dao/marketplaceCatalog.ts
@@ -6990,6 +7251,7 @@ exports.MARKET_ORDER_STATUS = MARKET_ORDER_STATUS;
6990
7251
  exports.VECTM_NODE_INFO_TUPLE = VECTM_NODE_INFO_TUPLE;
6991
7252
  exports.VOTE_CAST_EVENT = VOTE_CAST_EVENT;
6992
7253
  exports.VOTE_CAST_WITH_PARAMS_EVENT = VOTE_CAST_WITH_PARAMS_EVENT;
7254
+ exports.addressInCommitteeList = addressInCommitteeList;
6993
7255
  exports.applyDerivedStatuses = applyDerivedStatuses;
6994
7256
  exports.applyForumSectionCheck = applyForumSectionCheck;
6995
7257
  exports.applyMarketplaceEvent = applyMarketplaceEvent;
@@ -7009,6 +7271,7 @@ exports.assertIncreaseUnlockTime = assertIncreaseUnlockTime;
7009
7271
  exports.assertLiquidate = assertLiquidate;
7010
7272
  exports.assertLockDurationSeconds = assertLockDurationSeconds;
7011
7273
  exports.assertMerge = assertMerge;
7274
+ exports.assertProposalCatalogConstants = assertProposalCatalogConstants;
7012
7275
  exports.assertSplitExtracted = assertSplitExtracted;
7013
7276
  exports.assertTransfer = assertTransfer;
7014
7277
  exports.assertUndelegateNotAlreadySelf = assertUndelegateNotAlreadySelf;
@@ -7116,6 +7379,7 @@ exports.expectedForumSectionForProposalType = expectedForumSectionForProposalTyp
7116
7379
  exports.explainGovAction = explainGovAction;
7117
7380
  exports.explainProposalRecord = explainProposalRecord;
7118
7381
  exports.explorerBase = explorerBase;
7382
+ exports.fetchContinuumDaoCommitteeAddresses = fetchContinuumDaoCommitteeAddresses;
7119
7383
  exports.fetchContinuumDaoMarketplaceActivity = fetchContinuumDaoMarketplaceActivity;
7120
7384
  exports.fetchContinuumDaoMarketplaceAuctions = fetchContinuumDaoMarketplaceAuctions;
7121
7385
  exports.fetchContinuumDaoMarketplaceConfig = fetchContinuumDaoMarketplaceConfig;
@@ -7132,7 +7396,6 @@ exports.getMarketplaceIndexMemoryCache = getMarketplaceIndexMemoryCache;
7132
7396
  exports.governorAddressOnEvmChain = governorAddressOnEvmChain;
7133
7397
  exports.howVoted = howVoted;
7134
7398
  exports.isConfiguredAddress = isConfiguredAddress;
7135
- exports.isContinuumDaoCommitteeProposer = isContinuumDaoCommitteeProposer;
7136
7399
  exports.isContinuumDaoCtmOnAssetsChain = isContinuumDaoCtmOnAssetsChain;
7137
7400
  exports.isContinuumDaoLockChain = isContinuumDaoLockChain;
7138
7401
  exports.isContinuumDaoSupportedChainId = isContinuumDaoSupportedChainId;
@@ -7159,6 +7422,7 @@ exports.monthsOfFees = monthsOfFees;
7159
7422
  exports.normalizeGovConfiguration = normalizeGovConfiguration;
7160
7423
  exports.normalizeNodeInfo = normalizeNodeInfo;
7161
7424
  exports.orderStatusName = orderStatusName;
7425
+ exports.parseCommitteeAddressesFromConstitution = parseCommitteeAddressesFromConstitution;
7162
7426
  exports.pickFeeAlternativeExample = pickFeeAlternativeExample;
7163
7427
  exports.planMarketplaceSteps = planMarketplaceSteps;
7164
7428
  exports.preflightAttachVeCtm = preflightAttachVeCtm;
@@ -7173,6 +7437,8 @@ exports.preflightSplit = preflightSplit;
7173
7437
  exports.preflightTransfer = preflightTransfer;
7174
7438
  exports.preflightUndelegate = preflightUndelegate;
7175
7439
  exports.preflightWithdraw = preflightWithdraw;
7440
+ exports.proposalCatalogCanonical = proposalCatalogCanonical;
7441
+ exports.proposalCatalogDocument = proposalCatalogDocument;
7176
7442
  exports.proposalDescriptionHash = proposalDescriptionHash;
7177
7443
  exports.publicClient = publicClient2;
7178
7444
  exports.quoteContinuumDaoMarketplaceFeeAlternative = quoteContinuumDaoMarketplaceFeeAlternative;
@@ -7183,12 +7449,14 @@ exports.readMarketplaceFeePreviewFromChain = readMarketplaceFeePreviewFromChain;
7183
7449
  exports.readMinimumLockWei = readMinimumLockWei;
7184
7450
  exports.readVeCtmLockState = readVeCtmLockState;
7185
7451
  exports.requireGovernor = requireGovernor;
7452
+ exports.resetContinuumDaoCommitteeCache = resetContinuumDaoCommitteeCache;
7186
7453
  exports.resolveAttachGroupId = resolveAttachGroupId;
7187
7454
  exports.resolveGovernorProposalRef = resolveGovernorProposalRef;
7188
7455
  exports.resolveProposalCatalogRange = resolveProposalCatalogRange;
7189
7456
  exports.scanMarketplaceLogs = scanMarketplaceLogs;
7190
7457
  exports.searchContinuumDaoMarketplace = searchContinuumDaoMarketplace;
7191
7458
  exports.setMarketplaceIndexMemoryCache = setMarketplaceIndexMemoryCache;
7459
+ exports.stampLineaCatalogActions = stampLineaCatalogActions;
7192
7460
  exports.summarizeLiveProposals = summarizeLiveProposals;
7193
7461
  exports.summarizeProposalVotes = summarizeProposalVotes;
7194
7462
  exports.throwIfProposalSimulationFailed = throwIfProposalSimulationFailed;