Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions src/core/capabilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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",
Expand Down
47 changes: 47 additions & 0 deletions src/core/presets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { getChainFacts } from "./facts.js";

// `preset <foundry|hardhat> <chain>` — 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.`);
}
42 changes: 42 additions & 0 deletions test/presets.test.ts
Original file line number Diff line number Diff line change
@@ -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 <foundry|hardhat> <chain>` — 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);
});
});
Loading