zcash_verify_evm
Verify a ZAP1 Merkle proof on-chain using EVM contracts to confirm a leaf hash belongs to a registered Zcash anchor root, supporting Sepolia, Base, and Arbitrum.
Instructions
Verify a ZAP1 Merkle proof on-chain via the EVM ZAP1Verifier contract. Checks that a leaf hash is included in a registered Zcash anchor root. Supports Sepolia (testnet), Base, and Arbitrum.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| chain | Yes | EVM chain to verify on | |
| leaf_hash | Yes | 64-char hex leaf hash (no 0x prefix) | |
| siblings | Yes | Ordered hex sibling hashes for the Merkle proof | |
| positions | Yes | Bit-packed position flags (0 = left, 1 = right per level) | |
| expected_root | Yes | 64-char hex expected Merkle root |
Implementation Reference
- src/index.ts:26-26 (registration)Import of registerVerifyEvmTool registration function
import { registerVerifyEvmTool } from "./tools/verify-evm.js"; - src/index.ts:51-51 (registration)Registration call for the zcash_verify_evm tool on the MCP server
registerVerifyEvmTool(server); - src/tools/verify-evm.ts:25-107 (handler)Full handler and registration of the zcash_verify_evm tool - defines schema, verifier addresses, ABI, and the async handler that returns proof verification instructions
export function registerVerifyEvmTool(server: McpServer) { server.tool( "zcash_verify_evm", "Verify a ZAP1 Merkle proof on-chain via the EVM ZAP1Verifier contract. " + "Checks that a leaf hash is included in a registered Zcash anchor root. " + "Supports Sepolia (testnet), Base, and Arbitrum.", { chain: z .enum(["sepolia", "base", "arbitrum"]) .describe("EVM chain to verify on"), leaf_hash: z .string() .regex(/^[0-9a-fA-F]{64}$/) .describe("64-char hex leaf hash (no 0x prefix)"), siblings: z .array(z.string().regex(/^[0-9a-fA-F]{64}$/)) .describe("Ordered hex sibling hashes for the Merkle proof"), positions: z .number() .int() .describe("Bit-packed position flags (0 = left, 1 = right per level)"), expected_root: z .string() .regex(/^[0-9a-fA-F]{64}$/) .describe("64-char hex expected Merkle root"), }, async ({ chain, leaf_hash, siblings, positions, expected_root }) => { try { const deployment = VERIFIER_ADDRESSES[chain]; if (!deployment?.address) { return { content: [{ type: "text" as const, text: JSON.stringify({ error: `ZAP1Verifier not yet deployed on ${chain}. Use 'sepolia' for testing.`, available: Object.entries(VERIFIER_ADDRESSES) .filter(([, v]) => v.address) .map(([k]) => k), }, null, 2), }], isError: true, }; } const result = { verification: { chain, contract: deployment.address, rpc: deployment.rpc, leaf_hash: `0x${leaf_hash}`, siblings: siblings.map((s) => `0x${s}`), positions, expected_root: `0x${expected_root}`, }, abi: VERIFIER_ABI, call: { method: "verifyProofStateless", args: [ `0x${leaf_hash}`, siblings.map((s) => `0x${s}`), positions, `0x${expected_root}`, ], note: "This is a view call (no gas cost). Returns true if the proof is valid.", }, anchor_check: { method: "isAnchorRegistered", args: [`0x${expected_root}`], note: "Check if this root is registered as a Zcash anchor. Returns (exists, zcashHeight).", }, how_to_call: `cast call ${deployment.address} 'verifyProofStateless(bytes32,bytes32[],uint256,bytes32)(bool)' 0x${leaf_hash} '[${siblings.map((s) => `0x${s}`).join(",")}]' ${positions} 0x${expected_root} --rpc-url ${deployment.rpc}`, }; return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }], }; } catch (err) { const msg = err instanceof Error ? err.message : String(err); return { content: [{ type: "text" as const, text: `Error: ${msg}` }], isError: true }; } } ); } - src/tools/verify-evm.ts:31-50 (schema)Zod schema for zcash_verify_evm inputs: chain, leaf_hash, siblings, positions, expected_root
{ chain: z .enum(["sepolia", "base", "arbitrum"]) .describe("EVM chain to verify on"), leaf_hash: z .string() .regex(/^[0-9a-fA-F]{64}$/) .describe("64-char hex leaf hash (no 0x prefix)"), siblings: z .array(z.string().regex(/^[0-9a-fA-F]{64}$/)) .describe("Ordered hex sibling hashes for the Merkle proof"), positions: z .number() .int() .describe("Bit-packed position flags (0 = left, 1 = right per level)"), expected_root: z .string() .regex(/^[0-9a-fA-F]{64}$/) .describe("64-char hex expected Merkle root"), },