diff --git a/README.md b/README.md index e3810ea..2876846 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ rome facts programs devnet # Solana program ids for a network rome cookbook cpi-recipe # the CPI account-rules + SDK encoders (grounded addresses) rome cookbook patterns lending # which example repo + guide fits a goal rome cookbook errors "Custom(1)" # decode a Rome failure → cause + fix (the error taxonomy) +rome preset foundry hadrian # ready Rome network config for foundry / hardhat + the quirks rome call hadrian 0x… "balanceOf(address) returns (uint256)" 0x… # read a contract (no key) rome doctor hadrian --address 0x… # preflight: chain live? RPC reachable? program set? wallet funded? rome tx hadrian 0x… # diagnose a tx: EVM receipt + the Solana settlement tx(s) + a Via link diff --git a/src/core/capabilities.ts b/src/core/capabilities.ts index a24d9d1..6c7ce83 100644 --- a/src/core/capabilities.ts +++ b/src/core/capabilities.ts @@ -5,6 +5,7 @@ import { fundHandler, bridgeHandler } from "./bridge.js"; import { doctor } from "./doctor.js"; import { diagnoseTx } from "./tx.js"; import { verifyHandler } from "./verify.js"; +import { getPreset } from "./presets.js"; import { type Deps } from "./deps.js"; export interface ArgSpec { @@ -171,6 +172,15 @@ export const CAPABILITIES: Capability[] = [ (a) => diagnoseTx(a.chain, a.hash), ), + // ── preset: ready Rome toolchain config (read; CLI + MCP) ── + verbCap( + "preset", + "preset", + "Emit a ready Rome network config for your toolchain + the Rome quirks. e.g. rome preset foundry hadrian", + [{ name: "tool", required: true, description: "foundry or hardhat" }, chainArg], + (a) => getPreset(a.tool, a.chain), + ), + // ── fund / bridge: the "from home" on-ramp (CCTP USDC inbound). CLI-only actions. ── verbAction( "fund", diff --git a/src/core/presets.ts b/src/core/presets.ts new file mode 100644 index 0000000..d5034e5 --- /dev/null +++ b/src/core/presets.ts @@ -0,0 +1,47 @@ +import { getChainFacts } from "./facts.js"; + +// `preset ` — emit a ready Rome network config for the EVM +// toolchain, plus the Rome quirks. Extend-don't-duplicate: builders keep Foundry/Hardhat; +// this just injects the right RPC/chainId + the caveats. Read-only, sourced from the registry. + +export interface PresetResult { + tool: "foundry" | "hardhat"; + chainId: number; + filename: string; + config: string; + notes: string[]; +} + +const romeNotes = (chainId: number): string[] => [ + "Deploy scripts: run `forge script --skip-simulation` — Rome's execution diverges from forge's local simulation. `forge create` and `cast` work as-is.", + "Gas: Rome charges the EXACT gas used; `eth_estimateGas` over-predicts (often 10-50×). Don't hard-fail or size budgets off a high estimate — a native transfer is ~1.48M gas, not 21k.", + "App writes go through `submitRomeTx` (@rome-protocol/sdk) for the correct Rome fee path, not a raw signed tx.", + `Chain id: ${chainId}.`, +]; + +export function getPreset(tool: string, chain: string | number): PresetResult { + const t = tool.trim().toLowerCase(); + const c = getChainFacts(chain); + const slug = c.name.toLowerCase().split(/\s+/).pop() ?? String(c.chainId); // "hadrian" + const camel = `rome${slug.charAt(0).toUpperCase()}${slug.slice(1)}`; // "romeHadrian" + + if (t === "foundry") { + return { + tool: "foundry", + chainId: c.chainId, + filename: "foundry.toml", + config: `[rpc_endpoints]\nrome-${slug} = "${c.rpcUrl}"\n\n# deploy: forge create --rpc-url rome-${slug} ... (scripts: add --skip-simulation)`, + notes: romeNotes(c.chainId), + }; + } + if (t === "hardhat") { + return { + tool: "hardhat", + chainId: c.chainId, + filename: "hardhat.config.ts", + config: `networks: {\n ${camel}: {\n url: "${c.rpcUrl}",\n chainId: ${c.chainId},\n },\n}`, + notes: romeNotes(c.chainId), + }; + } + throw new Error(`Unknown preset tool "${tool}". Supported: foundry, hardhat.`); +} diff --git a/test/presets.test.ts b/test/presets.test.ts new file mode 100644 index 0000000..5347ad6 --- /dev/null +++ b/test/presets.test.ts @@ -0,0 +1,42 @@ +import { describe, it, expect } from "vitest"; +import { getPreset } from "../src/core/presets.js"; +import { CAPABILITIES } from "../src/core/capabilities.js"; +import { buildMcpTools } from "../src/mcp.js"; + +// `preset ` — a read that emits a ready Rome network config +// (RPC + chainId) plus the Rome quirks a builder needs (forge --skip-simulation, the +// estimateGas caveat). Sourced from the registry; on CLI + MCP. + +describe("getPreset — Rome toolchain config", () => { + it("foundry: emits an rpc_endpoints entry with the real RPC + the skip-simulation quirk", () => { + const p = getPreset("foundry", "hadrian"); + expect(p.tool).toBe("foundry"); + expect(p.chainId).toBe(200010); + expect(p.filename).toBe("foundry.toml"); + expect(p.config).toContain("https://hadrian.testnet.romeprotocol.xyz/"); + expect(p.config).toContain("rpc_endpoints"); + expect(p.notes.join(" ")).toMatch(/skip-simulation/i); + }); + + it("hardhat: emits a networks entry with url + chainId", () => { + const p = getPreset("hardhat", "hadrian"); + expect(p.tool).toBe("hardhat"); + expect(p.filename).toMatch(/hardhat\.config/); + expect(p.config).toContain("chainId: 200010"); + expect(p.config).toContain("https://hadrian.testnet.romeprotocol.xyz/"); + }); + + it("resolves the chain by id/name/slug and errors on an unknown tool", () => { + expect(getPreset("foundry", 200010).chainId).toBe(200010); + expect(() => getPreset("truffle", "hadrian")).toThrow(/foundry|hardhat/i); + }); +}); + +describe("preset is a read capability on MCP", () => { + it("registered as a read verb + present on the MCP surface", () => { + const cap = CAPABILITIES.find((c) => c.id === "preset.preset"); + expect(cap?.kind).toBe("read"); + expect(cap?.requiresKey).toBe(false); + expect(new Set(buildMcpTools().map((t) => t.name)).has("preset")).toBe(true); + }); +});