# EIP-3009 payments Source: https://docs.mrdn.finance/ai-skills/eip-3009 Step-by-step Meridian integration guide for native transferWithAuthorization chains Use this guide to integrate Meridian x402 payments on EVM chains where the payment token exposes native EIP-3009 `transferWithAuthorization`. It is written for sellers, buyers, and AI coding agents implementing either side of the payment flow. This path applies to chains such as Base, Ink, HyperEVM, Optimism, Polygon, and Unichain when the selected payment token natively supports EIP-3009. Do not use Permit2 on these chains for the normal USDC x402 flow. Permit2 is only for chains whose payment token does not expose EIP-3009, such as MegaETH, BSC, and BOT Chain. For USDC routes supported by Across, this same EIP-3009 path can settle cross-chain. The seller exposes one `paymentRequirements` entry per source chain the buyer can pay from, and uses `extra.destinationChainId` when the recipient should be paid on a different chain. ## How the flow works ```text theme={"system"} seller creates payment requirements buyer signs an EIP-3009 transferWithAuthorization buyer sends paymentPayload to seller seller settles through Meridian POST /v1/settle Meridian facilitator relays transferWithAuthorization Meridian distributes proceeds locally or via Across to the destination chain ``` Meridian-specific rules: * `paymentRequirements.payTo` is the Meridian facilitator, not the seller wallet * `paymentRequirements.asset` is the token that implements EIP-3009 * `paymentRequirements.network` is the source chain the buyer pays from * `authorization.to` is the Meridian facilitator * The EIP-712 verifying contract is `paymentRequirements.asset` * The EIP-712 primary type is `TransferWithAuthorization` * `paymentRequirements.extra.destinationChainId` enables an Across-backed destination chain when it differs from the source chain * `paymentRequirements.extra.creditedRecipient` can carry the seller's final recipient address when it differs from the facilitator-bound `payTo` * The buyer does not approve Permit2, the facilitator, or any proxy * The buyer does not sign a Permit2 witness payload * The seller keeps the Meridian API key on the backend ## Prerequisites Seller: * A Meridian organization * An organization API key from `https://mrdn.finance/dev/api-keys` * A backend endpoint that can receive `paymentPayload` from the buyer Buyer: * A wallet on the selected chain * Balance of the selected EIP-3009 payment token Install the common client dependency: ```bash theme={"system"} npm install viem ``` Do not install `@uniswap/permit2-sdk` for this flow. ## Network constants The token addresses below are Meridian's default EIP-3009 payment tokens for common networks. If your application accepts another token on the same chain, only use this flow after confirming that token implements EIP-3009 `transferWithAuthorization`. Otherwise use the non-EIP-3009 Permit2 guide. ```ts theme={"system"} export const MERIDIAN_API_BASE = "https://api.mrdn.finance/v1"; export const EIP_3009_NETWORKS = { base: { chainId: 8453, token: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", tokenName: "USD Coin", tokenVersion: "2", decimals: 6, facilitator: "0x8E7769D440b3460b92159Dd9C6D17302b036e2d6", }, ink: { chainId: 57073, token: "0x2D270e6886d130D724215A266106e6832161EAEd", tokenName: "USD Coin", tokenVersion: "2", decimals: 6, facilitator: "0x8E7769D440b3460b92159Dd9C6D17302b036e2d6", }, hyperevm: { chainId: 999, token: "0xb88339CB7199b77E23DB6E890353E22632Ba630f", tokenName: "USD Coin", tokenVersion: "2", decimals: 6, facilitator: "0x8E7769D440b3460b92159Dd9C6D17302b036e2d6", }, optimism: { chainId: 10, token: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85", tokenName: "USD Coin", tokenVersion: "2", decimals: 6, facilitator: "0x8E7769D440b3460b92159Dd9C6D17302b036e2d6", }, polygon: { chainId: 137, token: "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", tokenName: "USD Coin", tokenVersion: "2", decimals: 6, facilitator: "0x8E7769D440b3460b92159Dd9C6D17302b036e2d6", }, unichain: { chainId: 130, token: "0x078d782b760474a361dda0af3839290b0ef57ad6", tokenName: "USD Coin", tokenVersion: "2", decimals: 6, facilitator: "0x8E7769D440b3460b92159Dd9C6D17302b036e2d6", }, "base-sepolia": { chainId: 84532, token: "0x036CbD53842c5426634e7929541eC2318f3dCF7e", tokenName: "USDC", tokenVersion: "2", decimals: 6, facilitator: "0x8e633dBf31adCc7D41BE3e95B7c8DD3526B5235A", }, "arc-testnet": { chainId: 5042002, token: "0x3600000000000000000000000000000000000000", tokenName: "USDC", tokenVersion: "1", decimals: 6, facilitator: "0x8e633dBf31adCc7D41BE3e95B7c8DD3526B5235A", }, } as const; ``` ## Seller setup ### 1. Create a Meridian API key Seller-side setup starts in Meridian: 1. Connect the seller wallet at `https://mrdn.finance` 2. Open `https://mrdn.finance/dev/api-keys` 3. Create an organization-scoped API key 4. Store the public `pk_...` key on the server Use the key in the `Authorization` header when calling Meridian: ```ts theme={"system"} const meridianHeaders = { Authorization: `Bearer ${process.env.MERIDIAN_API_KEY}`, "Content-Type": "application/json", }; ``` The API key can also be created programmatically with a valid SIWE session cookie: ```bash theme={"system"} curl -X POST https://api.mrdn.finance/v1/api_keys \ -H "Content-Type: application/json" \ -H "Cookie: siwe-session=your_session" \ -d '{ "name": "My Application Key", "test_net": true }' ``` ### 2. Choose the network and amount Amounts must use the selected source token's base units. USDC-style EIP-3009 tokens usually have 6 decimals, but still bind the amount to the configured source token. ```ts theme={"system"} const network = "base"; const config = EIP_3009_NETWORKS[network]; const amountInTokenBaseUnits = 1_000_000n; ``` ### 3. Build `paymentRequirements` Use the EIP-3009 token address as `asset`. Keep `payTo` pointed at the Meridian facilitator. ```ts theme={"system"} const paymentRequirements = { scheme: "exact", network, asset: config.token, payTo: config.facilitator, maxAmountRequired: amountInTokenBaseUnits.toString(), resource: "https://seller.example/api/tool", description: "Paid access to agent action", mimeType: "application/json", maxTimeoutSeconds: 300, extra: { name: config.tokenName, version: config.tokenVersion, }, }; ``` If you expose a standard HTTP 402 challenge, return: ```ts theme={"system"} return Response.json( { x402Version: 1, accepts: [paymentRequirements], }, { status: 402 }, ); ``` ### 4. Expose multiple source-chain routes If the seller wants to accept payments from several chains, return one `paymentRequirements` object per accepted source chain in `accepts`. The buyer or SDK selects the entry matching the buyer's connected wallet chain. For a seller that wants to receive on Base but accept payment from Base, Ink, and Optimism: ```ts theme={"system"} const destinationNetwork = "base"; const destinationChainId = EIP_3009_NETWORKS[destinationNetwork].chainId; const sellerBaseRecipient = "0x1111111111111111111111111111111111111111"; const sourceNetworks = ["base", "ink", "optimism"] as const; const accepts = sourceNetworks.map((sourceNetwork) => { const source = EIP_3009_NETWORKS[sourceNetwork]; return { scheme: "exact", network: sourceNetwork, asset: source.token, payTo: source.facilitator, maxAmountRequired: amountInTokenBaseUnits.toString(), resource: "https://seller.example/api/tool", description: "Paid access to agent action", mimeType: "application/json", maxTimeoutSeconds: 300, extra: { name: source.tokenName, version: source.tokenVersion, creditedRecipient: sellerBaseRecipient, destinationChainId, }, }; }); return Response.json({ x402Version: 1, accepts }, { status: 402 }); ``` In this model, an Ink-funded buyer pays the `network: "ink"` requirement. The buyer signs on Ink against Ink USDC, Meridian pulls funds into the Ink facilitator, and Across routes the output to Base before final settlement to `creditedRecipient`. Do not return only a Base requirement if you expect buyers to pay from Ink, Optimism, or another chain. A `paymentRequirements` object is source-chain specific because the EIP-712 signature domain includes the source chain and source token contract. Across currently uses exact-input routing in this flow. If the seller must receive an exact destination-chain amount, quote the route and set each source-chain `maxAmountRequired` high enough to cover Across fees. ### 5. Receive `paymentPayload` The buyer sends raw JSON, not a transaction they submit on-chain. Build or retrieve the matching `paymentRequirements` server-side; do not trust buyer-supplied requirements for pricing, recipient, network, or token selection. ```json theme={"system"} { "x402Version": 1, "scheme": "exact", "network": "base", "payload": { "signature": "0x...", "authorization": { "from": "0x2222222222222222222222222222222222222222", "to": "0x8E7769D440b3460b92159Dd9C6D17302b036e2d6", "value": "1000000", "validAfter": "0", "validBefore": "1741885200", "nonce": "0x..." } } } ``` ### 6. Settle with Meridian Call `POST /v1/settle` from the seller backend. Do not expose the Meridian API key to an untrusted client. ```ts theme={"system"} const response = await fetch(`${MERIDIAN_API_BASE}/settle`, { method: "POST", headers: meridianHeaders, body: JSON.stringify({ paymentPayload, paymentRequirements, }), }); const result = await response.json(); if (!response.ok || !result.success) { throw new Error(result.errorReason ?? "Meridian settlement failed"); } ``` ## Buyer setup ### 1. Select a payment requirement Use the seller-provided requirement for the network and token the buyer will pay with. If the seller returned several `accepts` entries, choose the route whose `network` matches the buyer's connected source chain. ```ts theme={"system"} const buyerNetwork = "ink"; const paymentRequirements = accepts.find((entry) => entry.network === buyerNetwork) ?? accepts[0]; const network = paymentRequirements.network as keyof typeof EIP_3009_NETWORKS; const config = EIP_3009_NETWORKS[network]; ``` For cross-chain routes, do not change the signing domain to the destination chain. The buyer always signs on the source chain selected by `paymentRequirements.network`. ### 2. Sign `TransferWithAuthorization` The buyer signs typed data against the token contract. There is no ERC-20 approval, no Permit2 allowance, and no witness object. ```ts theme={"system"} import { bytesToHex } from "viem"; const transferWithAuthorizationTypes = { TransferWithAuthorization: [ { name: "from", type: "address" }, { name: "to", type: "address" }, { name: "value", type: "uint256" }, { name: "validAfter", type: "uint256" }, { name: "validBefore", type: "uint256" }, { name: "nonce", type: "bytes32" }, ], } as const; const randomNonce32 = () => bytesToHex(crypto.getRandomValues(new Uint8Array(32))); const now = BigInt(Math.floor(Date.now() / 1000)); const authorization = { from: account.address, to: paymentRequirements.payTo as `0x${string}`, value: BigInt(paymentRequirements.maxAmountRequired), validAfter: 0n, validBefore: now + BigInt(paymentRequirements.maxTimeoutSeconds), nonce: randomNonce32(), }; const signature = await walletClient.signTypedData({ account, domain: { name: paymentRequirements.extra?.name ?? config.tokenName, version: paymentRequirements.extra?.version ?? config.tokenVersion, chainId: config.chainId, verifyingContract: paymentRequirements.asset as `0x${string}`, }, types: transferWithAuthorizationTypes, primaryType: "TransferWithAuthorization", message: authorization, }); ``` ### 3. Send `paymentPayload` to the seller Serialize bigint values as decimal strings. Keep the nonce as the original 32-byte hex string. ```ts theme={"system"} const paymentPayload = { x402Version: 1, scheme: "exact", network: paymentRequirements.network, payload: { signature, authorization: { from: authorization.from, to: authorization.to, value: authorization.value.toString(), validAfter: authorization.validAfter.toString(), validBefore: authorization.validBefore.toString(), nonce: authorization.nonce, }, }, }; await fetch("https://seller.example/api/tool", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ paymentPayload }), }); ``` ## Checklist for AI agents * Read the seller's existing route structure before adding payment handling * Keep settlement on the backend * Use this guide for Base, Ink, HyperEVM, and other EIP-3009 token chains * Use `asset = EIP-3009 token address` * Use `payTo = Meridian facilitator` * For multi-chain sellers, return one `accepts` entry per source chain * For Across routes, keep `network`, `asset`, and `payTo` source-chain based * For Across routes, set `extra.destinationChainId` to the settlement chain id * Put the final seller recipient in organization settings or `extra.creditedRecipient` * Use `authorization.to = Meridian facilitator` * Use `domain.verifyingContract = paymentRequirements.asset` * Use `primaryType = TransferWithAuthorization` * Generate a fresh 32-byte nonce per payment * Convert every bigint in `paymentPayload` to a string before JSON encoding * Do not add Permit2 approval, Permit2 SDK code, or Permit2 witness fields ## Common mistakes * Using Permit2 on Base, Ink, HyperEVM, or another native EIP-3009 token chain * Setting `payTo` to the seller wallet * Setting `authorization.to` to the seller wallet * Returning only a Base requirement when buyers need to pay from Ink or another source chain * Signing with the destination chain id for a cross-chain payment * Signing with the facilitator as the EIP-712 verifying contract * Using the wrong token name or version in the EIP-712 domain * Reusing an EIP-3009 nonce * Sending raw `bigint` values through JSON * Putting the Meridian API key in browser code ## References * [Supported Networks](/api-reference/supported-networks) * [Payment Types](/api-reference/payment-types/overview) * [Settle x402 Payment](/api-reference/endpoint/settle-payment) * [Smart Contracts](/payments/smart-contracts) * [EIP-3009](https://eips.ethereum.org/EIPS/eip-3009) # Non-EIP-3009 payments Source: https://docs.mrdn.finance/ai-skills/non-eip-3009 Step-by-step Meridian integration guide for Permit2-based payment chains Use this guide to integrate Meridian x402 payments on EVM chains where the payment token does not expose native EIP-3009 `transferWithAuthorization`. It is written for sellers, buyers, and AI coding agents implementing either side of the payment flow. This current path uses Permit2 and applies to MegaETH, BSC, BOT Chain, BOT Chain testnet, and Tempo. Permit2 settlement is same-chain in Meridian's current flow. Do not add `extra.destinationChainId` to create an Across route for Permit2 payments. Use the EIP-3009 guide for cross-chain USDC routes where the source token supports `transferWithAuthorization`. ## How the flow works ```text theme={"system"} seller creates payment requirements buyer approves Permit2 buyer signs a Permit2 witness authorization buyer sends paymentPayload to seller seller settles through Meridian POST /v1/settle Meridian facilitator pulls funds and distributes proceeds ``` Meridian-specific rules: * `paymentRequirements.payTo` is the Meridian facilitator, not the seller wallet * `paymentRequirements.asset` is the ERC-20 token the buyer pays with * `paymentRequirements.network` is the source and settlement chain * The buyer approves Permit2, not the facilitator and not the exact proxy * The buyer signs with `x402ExactPermit2Proxy` as the Permit2 spender * `witness.to` is the Meridian facilitator * The seller keeps the Meridian API key on the backend ## Prerequisites Seller: * A Meridian organization * An organization API key from `https://mrdn.finance/dev/api-keys` * A backend endpoint that can receive `paymentPayload` from the buyer Buyer: * A wallet on the selected chain * Balance of the selected ERC-20 token * Permit2 allowance for that token Install the common client dependencies: ```bash theme={"system"} npm install viem @uniswap/permit2-sdk ``` ## Network constants The token addresses below are Meridian's default payment tokens for each network. If your application accepts another transferable ERC-20 on the same chain, use that token address in `paymentRequirements.asset` and use that token's real name, version, and decimals. ```ts theme={"system"} export const MERIDIAN_API_BASE = "https://api.mrdn.finance/v1"; export const PERMIT2 = "0x000000000022D473030F116dDEE9F6B43aC78BA3"; export const X402_EXACT_PERMIT2_PROXY = "0x402085c248EeA27D92E8b30b2C58ed07f9E20001"; // Spender for the "upto" scheme (usage-based pricing); same address on every chain. export const X402_UPTO_PERMIT2_PROXY = "0x402015c795ecb48A360bDC6e35a2EaEb313a0002"; export const NON_EIP_3009_NETWORKS = { megaeth: { chainId: 4326, token: "0xFAfDdbb3FC7688494971a79cc65DCa3EF82079E7", tokenName: "USDm", tokenVersion: "1", facilitator: "0x8E7769D440b3460b92159Dd9C6D17302b036e2d6", }, bsc: { chainId: 56, token: "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d", tokenName: "USDC", tokenVersion: "1", facilitator: "0x8E7769D440b3460b92159Dd9C6D17302b036e2d6", }, "bot-chain": { chainId: 677, token: "0xaBabc7Ddc03e501d190C676BF3d92ef0e6e87a3C", tokenName: "USDT", tokenVersion: "1", facilitator: "0x8E7769D440b3460b92159Dd9C6D17302b036e2d6", }, "bot-chain-testnet": { chainId: 968, token: "0x75edC9335175Fc0552D51D48439F229c10420fe3", tokenName: "USDT", tokenVersion: "1", facilitator: "0x8e633dBf31adCc7D41BE3e95B7c8DD3526B5235A", }, tempo: { chainId: 4217, token: "0x20C000000000000000000000b9537d11c60E8b50", tokenName: "USDC.e", tokenVersion: "1", facilitator: "0x8E7769D440b3460b92159Dd9C6D17302b036e2d6", }, } as const; ``` ## Seller setup ### 1. Create a Meridian API key Seller-side setup starts in Meridian: 1. Connect the seller wallet at `https://mrdn.finance` 2. Open `https://mrdn.finance/dev/api-keys` 3. Create an organization-scoped API key 4. Store the public `pk_...` key on the server Use the key in the `Authorization` header when calling Meridian: ```ts theme={"system"} const meridianHeaders = { Authorization: `Bearer ${process.env.MERIDIAN_API_KEY}`, "Content-Type": "application/json", }; ``` The API key can also be created programmatically with a valid SIWE session cookie: ```bash theme={"system"} curl -X POST https://api.mrdn.finance/v1/api_keys \ -H "Content-Type: application/json" \ -H "Cookie: siwe-session=your_session" \ -d '{ "name": "My Application Key", "test_net": true }' ``` ### 2. Choose the network and amount Amounts must use the selected token's base units. Do not assume every token has 6 decimals. ```ts theme={"system"} const network = "exampleNetwork"; const config = NON_EIP_3009_NETWORKS[network]; const amountInTokenBaseUnits = 1_000_000_000_000_000_000n; ``` ### 3. Build `paymentRequirements` Use the ERC-20 token address as `asset`. Keep `payTo` pointed at the Meridian facilitator. ```ts theme={"system"} const paymentRequirements = { scheme: "exact", network, asset: config.token, payTo: config.facilitator, maxAmountRequired: amountInTokenBaseUnits.toString(), resource: "https://seller.example/api/tool", description: "Paid access to agent action", mimeType: "application/json", maxTimeoutSeconds: 300, extra: { name: config.tokenName, version: config.tokenVersion, }, }; ``` If you expose a standard HTTP 402 challenge, return: ```ts theme={"system"} return Response.json( { x402Version: 1, accepts: [paymentRequirements], }, { status: 402 }, ); ``` ### 4. Receive `paymentPayload` The buyer sends raw JSON, not a transaction they submit on-chain. Build or retrieve the matching `paymentRequirements` server-side; do not trust buyer-supplied requirements for pricing, recipient, network, or token selection. ```json theme={"system"} { "x402Version": 1, "scheme": "exact", "network": "exampleNetwork", "payload": { "signature": "0x...", "owner": "0x...", "permit": { "permitted": { "token": "0x3333333333333333333333333333333333333333", "amount": "123456789" }, "nonce": "123", "deadline": "1741885200" }, "witness": { "to": "0x8E7769D440b3460b92159Dd9C6D17302b036e2d6", "validAfter": "0" } } } ``` ### 5. Settle with Meridian Call `POST /v1/settle` from the seller backend. Do not expose the Meridian API key to an untrusted client. ```ts theme={"system"} const response = await fetch(`${MERIDIAN_API_BASE}/settle`, { method: "POST", headers: meridianHeaders, body: JSON.stringify({ paymentPayload, paymentRequirements, }), }); const result = await response.json(); if (!response.ok || !result.success) { throw new Error(result.errorReason ?? "Meridian settlement failed"); } ``` ## Buyer setup ### 1. Select a payment requirement Use the seller-provided requirement for the network and token the buyer will pay with. ```ts theme={"system"} const paymentRequirements = accepts[0]; const network = paymentRequirements.network as keyof typeof NON_EIP_3009_NETWORKS; const config = NON_EIP_3009_NETWORKS[network]; ``` ### 2. Approve Permit2 The buyer approves the canonical Permit2 contract as spender for the token in `paymentRequirements.asset`. The buyer does not approve the x402ExactPermit2Proxy or the facilitator to spend their tokens! ```ts theme={"system"} import { erc20Abi, maxUint256 } from "viem"; const token = paymentRequirements.asset as `0x${string}`; await walletClient.writeContract({ address: token, abi: erc20Abi, functionName: "approve", args: [PERMIT2, maxUint256], }); ``` In production, check allowance first and only prompt for approval when needed. ### 3. Sign the Permit2 authorization Sign a Permit2 witness transfer. The EIP-712 verifying contract is Permit2, the Permit2 spender is `x402ExactPermit2Proxy`, and the witness destination is the Meridian facilitator. ```ts theme={"system"} import { randomBytes } from "node:crypto"; import { SignatureTransfer } from "@uniswap/permit2-sdk"; const nonce = BigInt(`0x${randomBytes(32).toString("hex")}`); const permit = { permitted: { token, amount: BigInt(paymentRequirements.maxAmountRequired), }, spender: X402_EXACT_PERMIT2_PROXY, nonce, deadline: BigInt( Math.floor(Date.now() / 1000) + paymentRequirements.maxTimeoutSeconds, ), }; const witness = { to: paymentRequirements.payTo as `0x${string}`, validAfter: 0n, }; const witnessType = { Witness: [ { name: "to", type: "address" }, { name: "validAfter", type: "uint256" }, ], }; const { domain, types, values } = SignatureTransfer.getPermitData( permit, PERMIT2, config.chainId, witness, witnessType, ); const signature = await walletClient.signTypedData({ account, domain, types, primaryType: "PermitWitnessTransferFrom", message: values, }); ``` ### 4. Send `paymentPayload` to the seller Serialize bigint values as decimal strings. ```ts theme={"system"} const paymentPayload = { x402Version: 1, scheme: "exact", network: paymentRequirements.network, payload: { signature, owner: account.address, permit: { permitted: { token: permit.permitted.token, amount: permit.permitted.amount.toString(), }, nonce: permit.nonce.toString(), deadline: permit.deadline.toString(), }, witness: { to: witness.to, validAfter: witness.validAfter.toString(), }, }, }; await fetch("https://seller.example/api/tool", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ paymentPayload }), }); ``` If the token supports ERC-2612 and you want a first-payment flow without a standing Permit2 approval, attach a verified `permit2612` object to `paymentPayload.payload`. Only use this shortcut for tokens you have confirmed implement `permit()`. ## Upto scheme (usage-based pricing) This playbook covers the `exact` scheme. For usage-based pricing, use `scheme: "upto"`: the buyer signs the Permit2 witness for the **maximum** amount with `X402_UPTO_PERMIT2_PROXY` as the spender, the witness becomes `{ to, facilitator, validAfter }` (both `to` and `facilitator` set to the Meridian facilitator, with witness type `[{ name: "to", type: "address" }, { name: "facilitator", type: "address" }, { name: "validAfter", type: "uint256" }]`), and the seller passes the actual amount at settle time via `paymentRequirements.extra.settleAmount`. Unlike exact Permit2, `upto` works on **every** supported network. See [Payment Types](/api-reference/payment-types/overview) and [Settle x402 Payment](/api-reference/endpoint/settle-payment). ## Checklist for AI agents * Read the seller's existing route structure before adding payment handling * Keep settlement on the backend * Use `asset = token address` * Use `payTo = Meridian facilitator` * Use `permit.spender = x402ExactPermit2Proxy` * Use `domain.verifyingContract = Permit2` * Use `witness.to = Meridian facilitator` * Keep Permit2 payments same-chain * Convert every bigint in `paymentPayload` to a string before JSON encoding ## Common mistakes * Setting `payTo` to the seller wallet * Approving the exact proxy or facilitator instead of Permit2 * Signing with Permit2 as spender instead of `x402ExactPermit2Proxy` * Setting `witness.to` to anything other than the Meridian facilitator * Adding `extra.destinationChainId` and expecting Permit2 to bridge through Across * Reusing hardcoded decimals from another chain * Sending raw `bigint` values through JSON * Putting the Meridian API key in browser code ## References * [Supported Networks](/api-reference/supported-networks) * [Payment Types](/api-reference/payment-types/overview) * [Settle x402 Payment](/api-reference/endpoint/settle-payment) * [Smart Contracts](/payments/smart-contracts) * [Permit2](https://github.com/Uniswap/permit2) * [x402ExactPermit2Proxy](https://github.com/x402-foundation/x402/tree/main/contracts/evm) # Solana payments Source: https://docs.mrdn.finance/ai-skills/solana Step-by-step Meridian integration guide for x402 USDC payments on Solana (SVM) Use this guide to integrate Meridian x402 payments on **Solana**, where the buyer pays USDC (an SPL token) and settlement runs through the Meridian `meridian_x402` program. It is written for sellers, buyers, and AI coding agents implementing either side of the payment flow. This path applies to the `solana` (mainnet-beta) and `solana-devnet` networks. It is **same-chain only** — there is no Across cross-chain routing on Solana in this version. Do not use the EIP-3009 or Permit2 flows here: Solana has no EIP-712 signing, no Permit2, and no token approvals. The buyer signs the whole settlement transaction instead, and that signature *is* the authorization. The integration needs only `@solana/web3.js` and `@solana/spl-token` — **no Anchor, no IDL, and no on-chain config reads**. Everything the buyer needs to build the transaction comes from a single API call: `GET /v1/solana/facilitator`. The Solana program is live on both `solana` (mainnet) and `solana-devnet`. Do not hardcode the program ID. Read it (and everything else) from `GET /v1/solana/facilitator` at runtime so your integration keeps working across any redeploys. See [Solana Program](/payments/solana-program). ## How the flow works ```text theme={"system"} seller creates payment requirements (asset = USDC mint, payTo = recipient wallet) buyer fetches GET /v1/solana/facilitator (fee payer, program id, mint, treasury, fees) buyer builds a transfer_with_authorization transaction, fee payer = facilitator buyer signs it once with their wallet (as USDC token authority) buyer sends the base64 transaction to the seller in paymentPayload seller settles through Meridian POST /v1/settle Meridian validates, co-signs as fee payer, submits, and confirms on-chain ``` Meridian-specific rules: * `paymentRequirements.asset` is the **USDC mint address**, not a token account * `paymentRequirements.payTo` is the seller's **recipient wallet** (the owner, not a token account); it must match the Solana recipient on your Meridian organization * `paymentRequirements.network` is `solana` or `solana-devnet` * `paymentRequirements.extra.feePayer` is the facilitator pubkey from `GET /v1/solana/facilitator`; it pays the transaction fee and must equal the on-chain `config.backendSigner` * `paymentRequirements.extra.name` is `"USDC"` and `extra.decimals` is `6` * The buyer signs the full transaction; there is **no** typed-data signature, no token approval, and no Permit2 * All USDC associated token accounts (payer, recipient, platform, treasury) must **already exist** — the settlement transaction never creates accounts * The buyer needs **no SOL**; the facilitator pays the transaction fee * The seller keeps the Meridian API key on the backend ## Prerequisites Seller: * A Meridian organization with a **Solana recipient wallet** configured * An organization API key from `https://mrdn.finance/dev/api-keys` * A backend endpoint that can receive `paymentPayload` from the buyer Buyer: * A Solana wallet (Phantom, Solflare, Backpack, …) * A USDC balance on the selected network — on `solana-devnet`, get test USDC from Circle's faucet at `https://faucet.circle.com` (select **Solana Devnet**) * An existing USDC associated token account (the recipient's must exist too) Install the client dependencies: ```bash theme={"system"} npm install @solana/web3.js @solana/spl-token ``` ## Network constants ```ts theme={"system"} export const MERIDIAN_API_BASE = "https://api.mrdn.finance/v1"; // The facilitator base URL that serves GET /v1/solana/facilitator and POST /v1/settle. export const FACILITATOR_URL = "https://api.mrdn.finance"; export const USDC_DECIMALS = 6; export const SOLANA_NETWORKS = { solana: { cluster: "mainnet-beta", rpcUrl: "https://api.mainnet-beta.solana.com", usdcMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", }, "solana-devnet": { cluster: "devnet", rpcUrl: "https://api.devnet.solana.com", usdcMint: "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU", }, } as const; ``` The public RPC endpoints above are heavily rate-limited. Use a dedicated devnet/mainnet RPC (Helius, QuickNode, Triton, …) for anything beyond a quick test. The buyer only needs the RPC for a recent blockhash. ## The facilitator info endpoint One GET returns everything needed to build the settlement transaction — the fee-payer pubkey, program ID, config PDA, USDC mint, treasury token account, protocol fee, and pause state (read on-chain by the facilitator and cached): ```ts theme={"system"} export interface SolanaFacilitatorInfo { network: string; facilitator: string; // fee payer pubkey (== on-chain config.backendSigner) programId: string; configPda: string; usdcMint?: string; // enriched fields: present when the on-chain treasury?: string; // config is readable (normal case) treasuryToken?: string; // treasury's USDC associated token account treasuryFeeBps?: number; paused?: boolean; } export async function getFacilitatorInfo( network: keyof typeof SOLANA_NETWORKS, ): Promise { const res = await fetch( `${FACILITATOR_URL}/v1/solana/facilitator?network=${network}`, { cache: "no-store" }, ); if (!res.ok) { throw new Error(`facilitator info failed (${res.status})`); } return (await res.json()) as SolanaFacilitatorInfo; } ``` The enriched fields (`usdcMint`, `treasury`, `treasuryToken`, `treasuryFeeBps`, `paused`) mirror the program's on-chain `Config` account, so clients no longer read it themselves. If they are missing, the facilitator could not reach the chain — fail closed and retry rather than guessing values. ## Seller setup ### 1. Create a Meridian API key Seller-side setup starts in Meridian: 1. Connect the seller wallet at `https://mrdn.finance` 2. Configure your organization's **Solana recipient wallet** 3. Open `https://mrdn.finance/dev/api-keys` and create an organization-scoped key 4. Store the public `pk_...` key on the server Use the key in the `Authorization` header when calling Meridian: ```ts theme={"system"} const meridianHeaders = { Authorization: `Bearer ${process.env.MERIDIAN_API_KEY}`, "Content-Type": "application/json", }; ``` ### 2. Build `paymentRequirements` Use the USDC mint as `asset` and your recipient wallet as `payTo`. Fetch the fee-payer pubkey from the facilitator and put it in `extra.feePayer`. ```ts theme={"system"} const network = "solana-devnet"; const config = SOLANA_NETWORKS[network]; const amountInUsdcBaseUnits = "10000"; // 0.01 USDC (6 decimals) const { facilitator } = await getFacilitatorInfo(network); const paymentRequirements = { scheme: "exact", network, asset: config.usdcMint, payTo: process.env.SOLANA_RECIPIENT_WALLET, // owner wallet, not a token account maxAmountRequired: amountInUsdcBaseUnits, resource: "https://seller.example/api/tool", description: "Paid access to agent action", mimeType: "application/json", maxTimeoutSeconds: 600, extra: { name: "USDC", decimals: USDC_DECIMALS, feePayer: facilitator, creditedRecipient: process.env.SOLANA_RECIPIENT_WALLET, }, }; ``` If you expose a standard HTTP 402 challenge, return: ```ts theme={"system"} return Response.json( { x402Version: 1, accepts: [paymentRequirements] }, { status: 402, headers: { "Cache-Control": "no-store, max-age=0" } }, ); ``` ### 3. Receive `paymentPayload` The buyer sends a base64-encoded, already-signed Solana transaction under `payload.transaction`. Build or retrieve the matching `paymentRequirements` server-side; do not trust buyer-supplied requirements for pricing, recipient, network, or asset. ```json theme={"system"} { "x402Version": 1, "scheme": "exact", "network": "solana-devnet", "payload": { "transaction": "" } } ``` Clients usually deliver this base64-encoded in an `X-PAYMENT` header; decode it before settling: ```ts theme={"system"} const header = request.headers.get("X-PAYMENT"); const paymentPayload = JSON.parse(Buffer.from(header, "base64").toString("utf8")); ``` ### 4. Settle with Meridian Call `POST /v1/settle` from the seller backend. Meridian validates the signed transaction, co-signs as fee payer, submits it, and confirms on-chain. Do not expose the Meridian API key to an untrusted client. ```ts theme={"system"} const response = await fetch(`${MERIDIAN_API_BASE}/settle`, { method: "POST", headers: meridianHeaders, body: JSON.stringify({ paymentPayload, paymentRequirements }), }); const result = await response.json(); if (!response.ok || !result.success) { throw new Error(result.error ?? result.errorReason ?? "Meridian settlement failed"); } // result.transaction is the confirmed Solana transaction signature. const signature = result.transaction; ``` ## Buyer setup ### 1. Select a payment requirement and fetch the facilitator info ```ts theme={"system"} const requirements = accepts[0]; const network = requirements.network as keyof typeof SOLANA_NETWORKS; const info = await getFacilitatorInfo(network); // Sanity-check the 402 against the facilitator's authoritative config. if (!info.usdcMint || !info.treasuryToken) { throw new Error("facilitator did not return on-chain config"); } if (info.paused) throw new Error("Solana x402 program is paused"); if (requirements.extra.feePayer !== info.facilitator) { throw new Error("402 fee payer does not match the facilitator"); } if (requirements.asset !== info.usdcMint) { throw new Error("402 asset does not match the USDC mint"); } ``` Because the info is fetched directly from the facilitator over HTTPS, it is the trust anchor for the payment — a tampered 402 from a malicious seller cannot redirect fees or swap the mint. (The paranoid can additionally verify the returned values against the on-chain `Config` PDA at `info.configPda`, but this is not required.) ### 2. Build the settlement transaction Build one `transfer_with_authorization` instruction with plain `@solana/web3.js` — no Anchor, no IDL. This helper is self-contained; copy-paste it as-is: ```ts theme={"system"} import { getAssociatedTokenAddressSync, TOKEN_PROGRAM_ID, } from "@solana/spl-token"; import { PublicKey, Transaction, TransactionInstruction } from "@solana/web3.js"; // transfer_with_authorization: anchor discriminator, stable across deployments. const SETTLE_DISCRIMINATOR = Uint8Array.from([241, 208, 6, 43, 81, 61, 213, 10]); export function buildSettleTransaction(params: { info: SolanaFacilitatorInfo; from: PublicKey; // buyer wallet recipient: PublicKey; // paymentRequirements.payTo (owner wallet) value: bigint; // USDC base units validAfter: bigint; // unix seconds, 0 = immediately valid validBefore: bigint; // unix seconds, e.g. now + maxTimeoutSeconds nonce: Uint8Array; // 32 random bytes recentBlockhash: string; platform?: PublicKey; // only in platform mode platformFeeBps?: number; }): Transaction { const { info, from, recipient, recentBlockhash, platform } = params; if (!info.usdcMint || !info.treasuryToken) { throw new Error("facilitator did not return on-chain config"); } if (info.paused) throw new Error("Solana settlement program is paused"); if (params.nonce.length !== 32) throw new Error("nonce must be 32 bytes"); const payer = new PublicKey(info.facilitator); const programId = new PublicKey(info.programId); const usdcMint = new PublicKey(info.usdcMint); // Instruction data: 66 bytes. const data = Buffer.alloc(66); data.set(SETTLE_DISCRIMINATOR, 0); data.writeBigUInt64LE(params.value, 8); data.writeBigInt64LE(params.validAfter, 16); data.writeBigInt64LE(params.validBefore, 24); data.set(params.nonce, 32); data.writeUInt16LE(params.platformFeeBps ?? 0, 64); // Recipient / platform / treasury owners may be off-curve (PDAs); the payer // signs, so their wallet must be a regular on-curve key. const instruction = new TransactionInstruction({ programId, keys: [ { pubkey: payer, isSigner: true, isWritable: true }, { pubkey: from, isSigner: true, isWritable: false }, { pubkey: new PublicKey(info.configPda), isSigner: false, isWritable: false }, { pubkey: usdcMint, isSigner: false, isWritable: false }, { pubkey: getAssociatedTokenAddressSync(usdcMint, from), isSigner: false, isWritable: true, }, { pubkey: getAssociatedTokenAddressSync(usdcMint, recipient, true), isSigner: false, isWritable: true, }, // Optional platform slot: Anchor expects the PROGRAM ID here as a // placeholder when there is no platform fee — always pass 9 accounts. platform ? { pubkey: getAssociatedTokenAddressSync(usdcMint, platform, true), isSigner: false, isWritable: true, } : { pubkey: programId, isSigner: false, isWritable: false }, { pubkey: new PublicKey(info.treasuryToken), isSigner: false, isWritable: true, }, { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false }, ], data, }); const tx = new Transaction({ feePayer: payer, recentBlockhash }); tx.add(instruction); return tx; } ``` Then use it: ```ts theme={"system"} import { Connection } from "@solana/web3.js"; const connection = new Connection(SOLANA_NETWORKS[network].rpcUrl, "confirmed"); const { blockhash } = await connection.getLatestBlockhash("confirmed"); const tx = buildSettleTransaction({ info, from: walletPublicKey, // the connected buyer wallet recipient: new PublicKey(requirements.payTo), value: BigInt(requirements.maxAmountRequired), validAfter: 0n, validBefore: BigInt(Math.floor(Date.now() / 1000) + requirements.maxTimeoutSeconds), nonce: crypto.getRandomValues(new Uint8Array(32)), recentBlockhash: blockhash, }); ``` If the monorepo package is available to you (Meridian's own apps), the same builder ships as `buildSettleTransaction` / `fetchSolanaFacilitatorInfo` from `@meridian/x402/shared/svm` — import it instead of copy-pasting. Confirm the recipient's USDC associated token account exists before paying — the settlement transaction does not create it. Getting a `TokenAccountNotFoundError` at settle time means the recipient (or platform) ATA is missing. ### 3. Sign the transaction with the buyer wallet The buyer signs once, as the USDC token authority. The facilitator adds the fee payer's signature later, so serialize without requiring all signatures. ```ts theme={"system"} const signed = await wallet.signTransaction(tx); // e.g. wallet-adapter const transactionB64 = Buffer.from( signed.serialize({ requireAllSignatures: false, verifySignatures: false }), ).toString("base64"); ``` ### 4. Send `paymentPayload` to the seller ```ts theme={"system"} const paymentPayload = { x402Version: 1, scheme: "exact", network: requirements.network, payload: { transaction: transactionB64 }, }; const paymentHeader = Buffer.from(JSON.stringify(paymentPayload)).toString( "base64", ); await fetch("https://seller.example/api/tool", { method: "GET", headers: { "X-PAYMENT": paymentHeader }, }); ``` ## Instruction reference For implementations in other languages, the layout (stable across deployments — it is derived from the instruction name): ```text theme={"system"} discriminator 8 bytes [241, 208, 6, 43, 81, 61, 213, 10] value u64 LE amount in USDC base units valid_after i64 LE unix seconds (0 = immediately valid) valid_before i64 LE unix seconds (now + maxTimeoutSeconds) nonce 32 bytes random platform_fee_bps u16 LE 0 unless in platform mode → total data length = 66 bytes ``` Account order — **always 9 accounts**, all non-signer/read-only unless noted: `payer` (signer, writable), `from` (signer), `config`, `usdc_mint`, `from_token` (writable), `recipient_token` (writable), `platform_token` (writable — or the **program ID** as a read-only placeholder when there is no platform fee), `treasury_token` (writable), `token_program`. Do not omit the `platform_token` slot when there is no platform fee. Anchor resolves optional accounts positionally: an 8-account list misaligns `treasury_token` and `token_program` and the facilitator rejects it. Pass the program ID in slot 6 as the "no platform" placeholder. ## Checklist for AI agents * Read the seller's existing route structure before adding payment handling * Keep settlement on the backend; never ship the Meridian API key to the client * Use `network = "solana"` or `"solana-devnet"` * Use `asset = USDC mint address` * Use `payTo = the seller's recipient wallet` (owner, not a token account), matching the org's configured Solana recipient * Fetch everything (`programId`, `facilitator`, `configPda`, `usdcMint`, `treasuryToken`, `paused`) from `GET /v1/solana/facilitator`; hardcode nothing * Set `extra.feePayer` to the facilitator pubkey and `tx.feePayer` to it too * Check `info.paused === false` and that the 402's `feePayer`/`asset` match the facilitator info before signing * Ensure the recipient (and platform) USDC associated token account exists first * Generate a fresh 32-byte nonce per payment * Always pass 9 accounts (program ID placeholder in the platform slot) * Have the buyer sign once and serialize with `requireAllSignatures: false` * Send `payload.transaction` as base64; keep amounts as decimal strings ## Common mistakes * Using EIP-3009 typed data, Permit2, or a token `approve` on Solana — none apply * Setting `asset` to a token account instead of the USDC **mint** * Setting `payTo` to a token account instead of the recipient **wallet owner** * Hardcoding the program ID instead of reading it from the facilitator endpoint * Omitting the `platform_token` slot (8 accounts) instead of passing the program ID placeholder — the account list must always be 9 long * Setting `tx.feePayer` to the buyer wallet instead of the facilitator * Paying to a recipient whose USDC associated token account does not exist yet * Expecting the settlement transaction to create token accounts (it never does) * Serializing with `requireAllSignatures: true` before the facilitator co-signs * Reusing a nonce, or expecting cross-chain (Across) routing on Solana ## References * [Solana Program](/payments/solana-program) — program architecture and accounts * [Supported Networks](/api-reference/supported-networks) * [Payment Types](/api-reference/payment-types/overview) * [Settle x402 Payment](/api-reference/endpoint/settle-payment) * [x402 SVM scheme](https://github.com/x402-foundation/x402) # Get Gateway Status Source: https://docs.mrdn.finance/api-reference/endpoint/batched-sample GET /v1/batched-sample Returns Circle Gateway client status, the per-network enablement map, and the upstream `getSupported()` payload from Circle. Useful to confirm Gateway connectivity from your environment. Diagnostic endpoint that reports the facilitator's Circle Gateway client status, the per-network enablement map, and the upstream `getSupported()` payload from Circle's Gateway API. Use it to confirm Gateway connectivity and check which networks are currently routable through the batched path before integrating. ## Authentication Requires `Authorization: Bearer `. The endpoint requires `req.auth.organization.id` to be present after the `apiSession` middleware runs; otherwise it returns HTTP 401. ## Response The literal response body returned by the facilitator: ```json theme={"system"} { "success": true, "data": { "message": "Circle Gateway batched nanopayments integration", "organization": "Acme", "organizationId": "org_123", "gatewayMainnetEnabled": true, "networkStatus": { "arc-testnet": true, "base-sepolia": true, "optimism-sepolia": true, "fluent-testnet": true, "base": true, "optimism": true, "polygon": true, "unichain": true, "ink": true, "hyperevm": true, "megaeth": true }, "gatewaySupported": { "...": "Pass-through of Circle Gateway's /supported response, or null if the upstream call failed" }, "endpoints": { "batchedVerify": "POST /v1/batched-verify", "batchedSettle": "POST /v1/batched-settle", "batchedSample": "GET /v1/batched-sample" }, "usage": { "description": "Use /v1/batched-settle and /v1/batched-verify as opt-in alternatives to /v1/settle and /v1/verify. The batched endpoints route payments through Circle Gateway for gas-free batched settlement. The default /v1/settle and /v1/verify endpoints continue to settle directly on-chain.", "note": "Circle recommends using settle() directly rather than verify() + settle() for production batched flows." }, "timestamp": "2026-04-30T18:24:11.502Z" } } ``` The `endpoints.batchedVerify` and `endpoints.batchedSettle` strings (`POST /v1/batched-verify` / `POST /v1/batched-settle`) and the `usage.description` text are returned verbatim by the facilitator but are misleading: those URLs do **not** exist as distinct routes. Batched payments are routed through the regular `POST /v1/verify` and `POST /v1/settle` endpoints, with detection driven by `paymentRequirements.extra.name === "GatewayWalletBatched"`. ### Field reference | Field | Description | | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `message` | Constant `"Circle Gateway batched nanopayments integration"`. | | `organization` | Organization name resolved from the API key, or `"Unknown"` if not set. | | `organizationId` | Organization ID resolved from the API key. | | `gatewayMainnetEnabled` | Mirrors the `GATEWAY_MAINNET_ENABLED` constant in `apps/facilitator/src/gateway/index.ts`. When `false`, only testnets route through Circle. | | `networkStatus` | Boolean per network: result of `isGatewayEnabledForNetwork(name)`. Only the ten networks shown above are reported. | | `gatewaySupported` | Pass-through of Circle Gateway's upstream `getSupported()` response, or `null` if the call to Circle threw. Internally uses the **mainnet** Circle client (no network arg). | | `endpoints` | Static map of names → strings as returned by the facilitator (see warning above for accuracy). | | `usage.description` | Static descriptive string returned by the facilitator (see warning above). | | `usage.note` | Constant: Circle recommends calling `settle()` directly for production batched flows. | | `timestamp` | ISO timestamp generated when the response is built. | ## Errors ### 401 Unauthorized Returned when the request has no organization context after `apiSession`: ```json theme={"system"} { "error": "No organization found for this request" } ``` ### 500 Internal Server Error Returned when the route handler throws (Circle unreachable for reasons other than the wrapped `getSupported()` call, etc.): ```json theme={"system"} { "error": "Internal server error", "message": "" } ``` # Settle Batched Payment Source: https://docs.mrdn.finance/api-reference/endpoint/batched-settle POST /v1/settle Settle an x402 payment with organization-specific authentication. This endpoint supports standard exact EIP-3009 EVM payloads, Circle Gateway batched payloads, and the Permit2 payload used on non-EIP-3009 EVM networks. Settle a Nanopayments Gateway (Circle Gateway batched) payment. This is the same `POST /v1/settle` endpoint used by the default x402 path. The facilitator inspects `paymentRequirements.extra` and forwards batched requests to Circle's Gateway API instead of settling on-chain. See [Nanopayments Gateway Overview](/api-reference/payment-types/circle-gateway) for the end-to-end flow, supported networks, and Gateway Wallet contract addresses. ## Routing A request is routed to Circle Gateway when **all** of the following are true: * `paymentRequirements.extra.name === "GatewayWalletBatched"` * `paymentRequirements.extra.version === "1"` * The network is Gateway-enabled (testnets always; mainnets gated by the facilitator's `GATEWAY_MAINNET_ENABLED` flag, currently `true`) If `extra` indicates batched but the network is not Gateway-enabled, the facilitator returns HTTP 403 with `errorReason: "gateway_not_enabled"`. ## Authentication Requires `Authorization: Bearer `. Recipient is taken from your organization settings or from `paymentRequirements.extra.creditedRecipient`. ## Request Body ```json theme={"system"} { "paymentPayload": { "x402Version": 1, "scheme": "exact", "network": "base", "payload": { "signature": "0x...", "authorization": { "from": "0x742d35Cc6634C0532925a3b8D0c4E5e6C2aE7A3e", "to": "0x77777777Dcc4d5A8B6E418Fd04D8997ef11000eE", "value": "10000", "validAfter": "1735689600", "validBefore": "1736035200", "nonce": "0x..." } } }, "paymentRequirements": { "scheme": "exact", "network": "base", "maxAmountRequired": "10000", "amount": "10000", "resource": "https://example.com/api/sample", "description": "Batched payment for 0.01 USDC", "mimeType": "application/json", "payTo": "0xRecipient...", "maxTimeoutSeconds": 345600, "asset": "0x77777777Dcc4d5A8B6E418Fd04D8997ef11000eE", "extra": { "name": "GatewayWalletBatched", "version": "1", "verifyingContract": "0x77777777Dcc4d5A8B6E418Fd04D8997ef11000eE", "creditedRecipient": "0xRecipient...", "platform": "0x0000000000000000000000000000000000000000", "platformFeeBps": 0, "destinationChainId": 8453 } } } ``` ### Field notes | Field | Notes | | --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `paymentRequirements.asset` | Set to the **Gateway Wallet contract** for this network (used as `verifyingContract` in EIP-712 signing). The facilitator swaps it back to USDC before forwarding to Circle. | | `paymentRequirements.maxTimeoutSeconds` | `345600` (4 days) is the recommended Circle Gateway nanopayment validity window. | | `payload.authorization.value` | USDC base units (6 decimals). `10000` = `0.01` USDC. | | `extra.name` + `extra.version` | Required exactly as `"GatewayWalletBatched"` and `"1"`. This is the routing trigger. | ## Response ### 200: Success The facilitator spreads Circle's response and adds Meridian-specific fields. The exact shape is `{ ...gatewayResponse, settlementMethod, network, testnet, authContext }` after `toJsonSafe()`: ```json theme={"system"} { "success": true, "transaction": "0xCircleSettlementTxHash...", "network": "base", "settlementMethod": "batched", "testnet": false, "authContext": { "authMethod": "middleware_handled", "timestamp": "2026-04-30T18:24:11.502Z" } } ``` * `success` and `transaction` (and any other fields) are forwarded from Circle. * `settlementMethod: "batched"` confirms the Circle Gateway path was taken. * `testnet` is `true` when `network` is one of `arc-testnet`, `base-sepolia`, `optimism-sepolia`, or `fluent-testnet`. * `authContext.authMethod` is the literal string `"middleware_handled"`. * The settle path's `authContext` does **not** include `organizationId` (unlike verify). ### 400: Error from Circle Gateway When Circle's response has `success: false`, the facilitator forwards it with HTTP 400. The shape is `{ ...gatewayResponse, settlementMethod, network, testnet }` (no `authContext`): ```json theme={"system"} { "success": false, "settlementMethod": "batched", "network": "base", "testnet": false, "...": "all other fields are forwarded from Circle (errorReason, message, etc.)" } ``` ### 403: Network not enabled ```json theme={"system"} { "success": false, "errorReason": "gateway_not_enabled", "error": "Circle Gateway is not supported for network \"\".", "transaction": "", "network": "" } ``` ### 500: Facilitator-side failure forwarding to Circle ```json theme={"system"} { "success": false, "errorReason": "batched_settle_error", "error": "", "transaction": "", "network": "" } ``` ## Error Codes * `gateway_not_enabled`: `paymentRequirements.extra` matched batched but `isGatewayEnabledForNetwork(network)` returned `false`. * `batched_settle_error`: forwarding to Circle threw before a response could be parsed (network error, SDK exception, etc.). * Any `errorReason` Circle returns: passed through verbatim alongside `settlementMethod: "batched"`. # Verify Batched Payment Source: https://docs.mrdn.finance/api-reference/endpoint/batched-verify POST /v1/verify Deprecated legacy endpoint for verifying an x402 payment with organization-specific authentication Verify a Nanopayments Gateway (Circle Gateway batched) payment without settling it. Routing happens on the same `POST /v1/verify` endpoint as the default x402 path; when `paymentRequirements.extra.name === "GatewayWalletBatched"`, the facilitator forwards the request to Circle's Gateway API. Circle recommends calling [Settle Batched Payment](/api-reference/endpoint/batched-settle) directly for production batched flows rather than the legacy `verify()` + `settle()` two-step. Use this endpoint mainly for diagnostics. ## Routing Identical to [Settle Batched Payment](/api-reference/endpoint/batched-settle): * `paymentRequirements.extra.name === "GatewayWalletBatched"` and `extra.version === "1"` * The network must be Gateway-enabled (testnets always; mainnets gated by `GATEWAY_MAINNET_ENABLED`) If the network is not Gateway-enabled, the facilitator returns HTTP 403 with `errorReason: "gateway_not_enabled"`. ## Request Body Same shape as the default verify endpoint, with the batched `extra` block: ```json theme={"system"} { "paymentPayload": { "x402Version": 1, "scheme": "exact", "network": "base", "payload": { "signature": "0x...", "authorization": { "from": "0x742d35Cc6634C0532925a3b8D0c4E5e6C2aE7A3e", "to": "0x77777777Dcc4d5A8B6E418Fd04D8997ef11000eE", "value": "10000", "validAfter": "1735689600", "validBefore": "1736035200", "nonce": "0x..." } } }, "paymentRequirements": { "scheme": "exact", "network": "base", "maxAmountRequired": "10000", "amount": "10000", "resource": "https://example.com/api/sample", "description": "Batched payment for 0.01 USDC", "mimeType": "application/json", "payTo": "0xRecipient...", "maxTimeoutSeconds": 345600, "asset": "0x77777777Dcc4d5A8B6E418Fd04D8997ef11000eE", "extra": { "name": "GatewayWalletBatched", "version": "1", "verifyingContract": "0x77777777Dcc4d5A8B6E418Fd04D8997ef11000eE", "creditedRecipient": "0xRecipient...", "platform": "0x0000000000000000000000000000000000000000", "platformFeeBps": 0, "destinationChainId": 8453 } } } ``` See the [Nanopayments Gateway Overview](/api-reference/payment-types/circle-gateway#extra-field-reference) for a full description of the `extra` fields. ## Response ### 200: Success The facilitator spreads Circle's response and adds Meridian-specific fields. The exact shape is `{ ...gatewayResponse, verificationMethod, testnet, authContext }`: ```json theme={"system"} { "isValid": true, "verificationMethod": "batched", "testnet": false, "authContext": { "organizationId": "org_123", "authMethod": "middleware_handled", "timestamp": "2026-04-30T18:24:11.502Z" } } ``` * `isValid` (and any other fields) come from Circle. * `verificationMethod: "batched"` confirms the Circle Gateway path was taken. * `testnet` is `true` when `paymentRequirements.network` is `arc-testnet`, `base-sepolia`, or `optimism-sepolia`. Note: the verify path does **not** mark `fluent-testnet` as testnet, which differs from the settle path. * `authContext.authMethod` is the literal string `"middleware_handled"`. The verify path's `authContext` includes `organizationId` (resolved by the `apiSession` middleware). ### 403: Network not enabled ```json theme={"system"} { "isValid": false, "invalidReason": "gateway_not_enabled", "error": "Circle Gateway is not enabled for network \"\".", "payer": "" } ``` ### 500: Facilitator-side failure forwarding to Circle ```json theme={"system"} { "isValid": false, "invalidReason": "batched_verify_error", "error": "", "payer": "" } ``` ### 400: Schema validation failures (apply to all `/v1/verify` requests) Verify validates `paymentPayload` and `paymentRequirements` against `PaymentPayloadSchema` and `PaymentRequirementsSchema` **before** the batched routing check. If either fails, the request never reaches the Circle path: ```json theme={"system"} { "isValid": false, "invalidReason": "invalid_payload", "payer": "" } ``` ```json theme={"system"} { "isValid": false, "invalidReason": "invalid_payment_requirements", "payer": "" } ``` ## Error Codes * `gateway_not_enabled`: `paymentRequirements.extra` matched batched but `isGatewayEnabledForNetwork(network)` returned `false`. * `batched_verify_error`: forwarding to Circle threw before a response could be parsed. * `invalid_payload`: `paymentPayload` failed `PaymentPayloadSchema.parse()`. * `invalid_payment_requirements`: `paymentRequirements` failed `PaymentRequirementsSchema.parse()`. * `unexpected_verify_error`: unhandled exception inside the verify handler (HTTP 500). # Execute ERC-20 Permit Source: https://docs.mrdn.finance/api-reference/endpoint/permit POST /v1/permit Submit a signed EIP-2612 permit and have the facilitator's signer execute `transferWithPermit` on the Meridian Proxy Facilitator contract. The user signs the permit off-chain; the facilitator pays the gas to apply the permit and transfer `value` tokens from `owner` to `recipient`. Execute a gasless ERC-20 token transfer using a signed [EIP-2612](https://eips.ethereum.org/EIPS/eip-2612) permit. The user signs off-chain; the facilitator pays the gas and settles on-chain. This endpoint is for ERC-2612 `permit()` transfers. Permit2 payments use [`POST /v1/settle`](/api-reference/endpoint/settle-payment) with the Permit2 payment payload. ## Authentication Requires an API key passed as `Authorization: Bearer `. ## Request Body ```json theme={"system"} { "network": "base-sepolia", "tokenAddress": "0x036CbD53842c5426634e7929541eC2318f3dCF7e", "owner": "0x742d35Cc6634C0532925a3b8D0c4E5e6C2aE7A3e", "spender": "0x8e633dBf31adCc7D41BE3e95B7c8DD3526B5235A", "value": "1000000", "deadline": "1893456000", "v": 27, "r": "0x...", "s": "0x...", "recipient": "0x742d35Cc6634C0532925a3b8D0c4E5e6C2aE7A3e" } ``` | Field | Required | Notes | | -------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `network` | yes | EVM network identifier (e.g. `base`, `avalanche`, `arbitrum`, `bsc`, `bot-chain`, `base-sepolia`, `arc-testnet`, `bot-chain-testnet`, `optimism`, `polygon`, `unichain`, `ink`, `sei`, `tempo`, `monad`, `robinhood`) | | `tokenAddress` | yes | ERC-20 token contract address | | `owner` | yes | Token owner address; must have signed the EIP-2612 permit | | `spender` | yes | Spender address authorized in the permit signature | | `value` | yes | Amount in the token's smallest unit as a decimal string (`1000000` = 1 USDC) | | `deadline` | yes | Unix timestamp after which the permit expires, as a decimal string | | `v` | yes\* | Signature recovery byte (27 or 28). Provide `v/r/s` OR `signature`, not both. | | `r` | yes\* | 32-byte hex signature component | | `s` | yes\* | 32-byte hex signature component | | `recipient` | yes | Address that receives the transferred tokens | ## Facilitator contract addresses | Network | Contract | | ------------------- | -------------------------------------------- | | `arc-testnet` | `0x8e633dBf31adCc7D41BE3e95B7c8DD3526B5235A` | | `bot-chain-testnet` | `0x8e633dBf31adCc7D41BE3e95B7c8DD3526B5235A` | | `bot-chain` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `base` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `bsc` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `base-sepolia` | `0x8e633dBf31adCc7D41BE3e95B7c8DD3526B5235A` | | `ethereum` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `avalanche` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `optimism` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `optimism-sepolia` | `0x8e633dBf31adCc7D41BE3e95B7c8DD3526B5235A` | | `arbitrum` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `polygon` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `unichain` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `ink` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `sonic` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `worldchain` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `sei` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `hyperevm` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `megaeth` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `tempo` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `monad` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `robinhood` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `fluent-testnet` | `0xB3Ac1B7871942bCdCD0bD6C65765272bBE70B8Da` | ## Response ### Success ```json theme={"system"} { "success": true, "transaction": "0x1234567890abcdef...", "network": "base-sepolia" } ``` ### Error ```json theme={"system"} { "success": false, "transaction": "", "network": "base-sepolia", "errorReason": "invalid_request" } ``` ## Error Codes | `errorReason` | HTTP | Description | | ------------------------- | ---- | ----------------------------------------------------------------- | | `invalid_request` | 400 | Missing or malformed fields in the request body | | `unsupported_network` | 400 | `network` is not a supported EVM network | | `network_not_configured` | 400 | The facilitator has no private key configured for this network | | `invalid_signature` | 400 | Could not resolve a valid `v`, `r`, `s` from the provided inputs | | `permit_execution_failed` | 500 | On-chain transaction failed (RPC error, revert, bad permit, etc.) | # Settle x402 Payment Source: https://docs.mrdn.finance/api-reference/endpoint/settle-payment POST /v1/settle Settle an x402 payment with organization-specific authentication. This endpoint supports standard exact EIP-3009 EVM payloads, Circle Gateway batched payloads, and the Permit2 payload used on non-EIP-3009 EVM networks. Settle an x402 payment on-chain after verification. This endpoint executes the actual blockchain transaction to transfer funds according to the x402 payment authorization. When `paymentRequirements.extra.name === "GatewayWalletBatched"`, this endpoint forwards the request to Circle's Gateway API instead of settling on-chain. See [Circle Gateway (Batched)](/api-reference/payment-types/circle-gateway) for the batched payment type and [Payment Types](/api-reference/payment-types/overview) for an overview of all routing paths. ## Authentication Requires API key authentication with organization context. ## Recipient For standard merchant integrations, the wallet that receives settled funds is configured in your organization settings and tied to your API key. Keep `paymentRequirements.payTo` set to the Meridian facilitator contract for the network; do not use `payTo` for your merchant wallet. Platform and marketplace integrations can route the end payout with `paymentRequirements.extra.creditedRecipient`. See [Marketplace Fees](/command-centre/marketplace) for that flow. ## Standard Request Body ```json theme={"system"} { "paymentPayload": { "x402Version": 1, "scheme": "exact", "network": "base-sepolia", "payload": { "signature": "0x...", "authorization": { "from": "0x742d35Cc6634C0532925a3b8D0c4E5e6C2aE7A3e", "to": "0x8e633dBf31adCc7D41BE3e95B7c8DD3526B5235A", "value": "1000000", "validAfter": "1234567890", "validBefore": "1234567899", "nonce": "0x..." } } }, "paymentRequirements": { "scheme": "exact", "network": "base-sepolia", "maxAmountRequired": "1000000", "resource": "https://example.com/resource", "description": "Payment for resource access", "mimeType": "application/json", "payTo": "0x8e633dBf31adCc7D41BE3e95B7c8DD3526B5235A", "maxTimeoutSeconds": 3600, "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e" } } ``` For EIP-3009 payloads, both `authorization.to` and `payTo` must be set to the facilitator contract address for the network. For Permit2 payloads, keep `payTo` and `witness.to` bound to the facilitator. ## Cross-Chain EIP-3009 Request Body Cross-chain USDC settlement uses the same `POST /v1/settle` endpoint and the same EIP-3009 payload shape. The payment payload is signed on the source chain where the buyer has USDC. Set `paymentRequirements.network`, `asset`, and `payTo` for that source chain, then set `paymentRequirements.extra.destinationChainId` to the destination chain where Meridian should settle after Across fills the bridge. For example, a buyer paying from Ink USDC to a seller receiving on Base uses `network: "ink"` and `destinationChainId: 8453`: ```json theme={"system"} { "paymentPayload": { "x402Version": 1, "scheme": "exact", "network": "ink", "payload": { "signature": "0x...", "authorization": { "from": "0x2222222222222222222222222222222222222222", "to": "0x8E7769D440b3460b92159Dd9C6D17302b036e2d6", "value": "1000000", "validAfter": "1234567890", "validBefore": "1234568190", "nonce": "0x..." } } }, "paymentRequirements": { "scheme": "exact", "network": "ink", "maxAmountRequired": "1000000", "resource": "https://seller.example/api/tool", "description": "Payment for resource access", "mimeType": "application/json", "payTo": "0x8E7769D440b3460b92159Dd9C6D17302b036e2d6", "maxTimeoutSeconds": 300, "asset": "0x2D270e6886d130D724215A266106e6832161EAEd", "extra": { "name": "USD Coin", "version": "2", "creditedRecipient": "0x1111111111111111111111111111111111111111", "destinationChainId": 8453 } } } ``` The settlement response transaction hash is the source-chain transaction that started the Across bridge. The seller receives the destination-chain output after Across fill and Meridian destination-side settlement. A seller that accepts payments from multiple source chains should expose one `paymentRequirements` object per route in the x402 `accepts` array. A Base-source requirement cannot be paid by a buyer who only has USDC on Ink. Return an Ink-source requirement with `extra.destinationChainId: 8453` for that route. Cross-chain EIP-3009 currently uses Across exact-input routing. If the seller needs to receive an exact destination-chain amount, quote the route and set `maxAmountRequired` to include bridge fees. ## Permit2 Request Body There is no separate `POST /v1/permit2` endpoint. Permit2 payments are settled through `POST /v1/settle` with the Permit2 payload shape below. ```json theme={"system"} { "paymentPayload": { "x402Version": 1, "scheme": "exact", "network": "exampleNetwork", "payload": { "signature": "0x...", "owner": "0x...", "permit": { "permitted": { "token": "0x...", "amount": "12345" }, "nonce": "123", "deadline": "12345678" }, "witness": { "to": "0x8E7769D440b3460b92159Dd9C6D17302b036e2d6", "validAfter": "0" } } }, "paymentRequirements": { "scheme": "exact", "network": "exampleNetwork", "maxAmountRequired": "12345", "resource": "https://example.com/resource", "description": "Payment for resource access", "mimeType": "application/json", "payTo": "0x8E7769D440b3460b92159Dd9C6D17302b036e2d6", "maxTimeoutSeconds": 3600, "asset": "0x...." } } ``` Permit2 payloads are accepted on every supported network. On `megaeth`, `bsc`, `bot-chain`, `bot-chain-testnet`, and `tempo` it is the only settlement path, since their payment tokens do not expose EIP-3009. If the buyer has not already approved Permit2 for the token, include the optional `paymentPayload.payload.permit2612` object for tokens that support ERC-2612: ```json theme={"system"} { "permit2612": { "value": "1000000", "deadline": "1893456000", "r": "0x0000000000000000000000000000000000000000000000000000000000000000", "s": "0x0000000000000000000000000000000000000000000000000000000000000000", "v": 27 } } ``` On chains where the payment token does not expose EIP-3009, use Permit2 through `x402ExactPermit2Proxy` `0x402085c248EeA27D92E8b30b2C58ed07f9E20001`. This is the canonical exact Permit2 proxy from the official [x402 repository](https://github.com/x402-foundation/x402/tree/main), deployed at the same address on supported EVM chains. This includes MegaETH, BSC, and BOT chain testnet and mainnet. New integrations should keep `payTo` pointed at the facilitator, set `paymentRequirements.asset` to the ERC-20 token address, approve Permit2 `0x000000000022D473030F116dDEE9F6B43aC78BA3`, and sign the Permit2 witness with the proxy as spender. Your backend should still call Meridian `POST /v1/settle`. ### Upto scheme For usage-based pricing, set `scheme` to `"upto"` in both the payment payload and the payment requirements. The buyer signs the Permit2 witness for the maximum chargeable amount with `x402UptoPermit2Proxy` `0x402015c795ecb48A360bDC6e35a2EaEb313a0002` as the spender (same address on every chain), and the witness gains a `facilitator` field: ```json theme={"system"} { "witness": { "to": "0x8E7769D440b3460b92159Dd9C6D17302b036e2d6", "facilitator": "0x8E7769D440b3460b92159Dd9C6D17302b036e2d6", "validAfter": "0" } } ``` At settle time, pass the actual usage amount in `paymentRequirements.extra.settleAmount` (base units). It must satisfy `0 < settleAmount ≤ permit.permitted.amount`; if omitted, the full signed maximum is settled. Fees are computed on the settled amount only. | Network | Facilitator Contract | | ------------------- | -------------------------------------------- | | `arc-testnet` | `0x8e633dBf31adCc7D41BE3e95B7c8DD3526B5235A` | | `bot-chain-testnet` | `0x8e633dBf31adCc7D41BE3e95B7c8DD3526B5235A` | | `bot-chain` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `base` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `bsc` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `base-sepolia` | `0x8e633dBf31adCc7D41BE3e95B7c8DD3526B5235A` | | `avalanche` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `optimism` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `arbitrum` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `optimism-sepolia` | `0x8e633dBf31adCc7D41BE3e95B7c8DD3526B5235A` | | `polygon` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `unichain` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `ink` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `worldchain` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `sei` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `hyperevm` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `megaeth` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `tempo` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `monad` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `robinhood` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | ## Response ### Success Response ```json theme={"system"} { "success": true, "transaction": "0x1234567890abcdef...", "network": "base-sepolia" } ``` ### Error Response ```json theme={"system"} { "success": false, "errorReason": "insufficient_funds", "transaction": "", "network": "base-sepolia" } ``` ## Error Codes
* `invalid_payload` - Payment payload format is invalid * `invalid_payment_requirements` - Payment requirements format is invalid * `insufficient_funds` - Insufficient balance for settlement * `unexpected_settle_error` - Unhandled exception during settlement * `invalid_transaction_state` - On-chain transaction failed
# Get Supported Payment Kinds Source: https://docs.mrdn.finance/api-reference/endpoint/supported-payments GET /v1/supported Get list of supported payment types and networks Get the list of supported payment types and networks for x402 payments. ## Response ```json theme={"system"} { "kinds": [ { "x402Version": 1, "scheme": "exact", "network": "avalanche" }, { "x402Version": 1, "scheme": "exact", "network": "base" }, { "x402Version": 1, "scheme": "exact", "network": "bsc" }, { "x402Version": 1, "scheme": "exact", "network": "bot-chain" }, { "x402Version": 1, "scheme": "exact", "network": "base-sepolia" }, { "x402Version": 1, "scheme": "exact", "network": "arc-testnet" }, { "x402Version": 1, "scheme": "exact", "network": "bot-chain-testnet" }, { "x402Version": 1, "scheme": "exact", "network": "fluent-testnet" }, { "x402Version": 1, "scheme": "exact", "network": "optimism" }, { "x402Version": 1, "scheme": "exact", "network": "arbitrum" }, { "x402Version": 1, "scheme": "exact", "network": "optimism-sepolia" }, { "x402Version": 1, "scheme": "exact", "network": "polygon" }, { "x402Version": 1, "scheme": "exact", "network": "unichain" }, { "x402Version": 1, "scheme": "exact", "network": "ink" }, { "x402Version": 1, "scheme": "exact", "network": "worldchain" }, { "x402Version": 1, "scheme": "exact", "network": "sei" }, { "x402Version": 1, "scheme": "exact", "network": "hyperevm" }, { "x402Version": 1, "scheme": "exact", "network": "megaeth" }, { "x402Version": 1, "scheme": "exact", "network": "tempo" }, { "x402Version": 1, "scheme": "exact", "network": "monad" }, { "x402Version": 1, "scheme": "exact", "network": "robinhood" } ] } ``` ## Payment Kinds Each payment kind specifies: * **x402Version**: Version of the x402 protocol (currently 1) * **scheme**: Payment scheme type (currently "exact") * **network**: Supported blockchain network ## Supported Networks Currently supported networks: ### Mainnet | Network ID | Chain | | ------------ | --------------- | | `avalanche` | Avalanche | | `base` | Base | | `bsc` | BNB Smart Chain | | `bot-chain` | BOT Chain | | `optimism` | Optimism | | `arbitrum` | Arbitrum One | | `polygon` | Polygon | | `unichain` | Unichain | | `ink` | Ink | | `worldchain` | World Chain | | `sei` | Sei | | `hyperevm` | HyperEVM | | `megaeth` | MegaETH | | `tempo` | Tempo | | `monad` | Monad | | `robinhood` | Robinhood Chain | ### Testnet | Network ID | Chain | | ------------------- | ----------------- | | `arc-testnet` | Arc Testnet | | `bot-chain-testnet` | BOT Chain Testnet | | `base-sepolia` | Base Sepolia | | `fluent-testnet` | Fluent Testnet | | `optimism-sepolia` | Optimism Sepolia | ## Usage ```javascript theme={"system"} const response = await fetch("/v1/supported"); const data = await response.json(); console.log("Supported payment kinds:", data.kinds); // Check if a specific network is supported const isOptimismSupported = data.kinds.some( (kind) => kind.network === "optimism", ); ``` # Verify x402 Payment (Deprecated) Source: https://docs.mrdn.finance/api-reference/endpoint/verify-payment POST /v1/verify Deprecated legacy endpoint for verifying an x402 payment with organization-specific authentication `POST /v1/verify` is deprecated and retained for legacy integrations only. Verify an x402 payment with organization-specific authentication. This legacy endpoint validates payment signatures and authorization data according to the x402 protocol specification. When `paymentRequirements.extra.name === "GatewayWalletBatched"`, verification is forwarded to Circle's Gateway API. See [Circle Gateway (Batched)](/api-reference/payment-types/circle-gateway) for that flow and [Payment Types](/api-reference/payment-types/overview) for an overview. ## Features * Organization-specific x402 authentication * Automatic fallback to default authentication * Authentication context in responses * Comprehensive error handling ## Request Example ```json theme={"system"} { "paymentPayload": { "x402Version": 1, "scheme": "exact", "network": "base-sepolia", "payload": { "signature": "0x...", "authorization": { "from": "0x742d35Cc6634C0532925a3b8D0c4E5e6C2aE7A3e", "to": "0x8e633dBf31adCc7D41BE3e95B7c8DD3526B5235A", "value": "1000000", "validAfter": "1234567890", "validBefore": "1234567899", "nonce": "0x..." } } }, "paymentRequirements": { "scheme": "exact", "network": "base-sepolia", "maxAmountRequired": "1000000", "resource": "https://example.com/resource", "description": "Payment for resource access", "mimeType": "application/json", "payTo": "0x8e633dBf31adCc7D41BE3e95B7c8DD3526B5235A", "maxTimeoutSeconds": 3600, "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e" } } ``` For EIP-3009 payloads, both `authorization.to` and `payTo` must be set to the facilitator contract address for the network. `POST /v1/verify` does not validate the Permit2 payload shape in the current facilitator. Permit2 payments are settled directly through [`POST /v1/settle`](/api-reference/endpoint/settle-payment). | Network | Facilitator Contract | | ------------------- | -------------------------------------------- | | `arc-testnet` | `0x8e633dBf31adCc7D41BE3e95B7c8DD3526B5235A` | | `bot-chain-testnet` | `0x8e633dBf31adCc7D41BE3e95B7c8DD3526B5235A` | | `bot-chain` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `base` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `bsc` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `base-sepolia` | `0x8e633dBf31adCc7D41BE3e95B7c8DD3526B5235A` | | `avalanche` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `optimism` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `arbitrum` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `optimism-sepolia` | `0x8e633dBf31adCc7D41BE3e95B7c8DD3526B5235A` | | `polygon` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `unichain` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `ink` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `worldchain` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `sei` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `hyperevm` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `megaeth` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `tempo` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `monad` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | | `robinhood` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | ## Response Example ```json theme={"system"} { "isValid": true, "payer": "0x742d35Cc6634C0532925a3b8D0c4E5e6C2aE7A3e", "authContext": { "organizationId": "org_123", "authMethod": "organization_specific", "timestamp": "2024-01-01T00:00:00.000Z" } } ``` # Chat Completion Source: https://docs.mrdn.finance/api-reference/inference/chat POST https://api.mrdn.finance/v1/inference/chat Paid chat completion, streamed as server-sent events Runs one chat completion against the selected Dolphin model. This endpoint is x402-protected: unpaid requests receive `402 Payment Required`, and paid requests must include the `X-PAYMENT` header. ## Headers Must be `application/json`. Base64-encoded signed x402 payment payload. Omit on the first request to receive the payment requirements via a 402 response. ## Body Conversation history as an array of `{ role, content }` objects. Roles are `user` and `assistant`. Must be non-empty. Model ID. Must be one of the IDs returned by [`GET /v1/inference/models`](/api-reference/inference/models). Invalid values return `400` with the supported list. Prompt template. Must be one of the names returned by [`GET /v1/inference/templates`](/api-reference/inference/templates). Invalid values return `400` with the supported list. ## Response On success (`200`), the completion is streamed as `text/event-stream`. Each event is an OpenAI-style chunk; concatenate `choices[0].delta.content` across chunks to build the full reply. Settlement details (including the transaction hash) are returned in the `X-PAYMENT-RESPONSE` header. ```bash Unpaid (get quote) theme={"system"} curl -X POST https://api.mrdn.finance/v1/inference/chat \ -H "Content-Type: application/json" \ -d '{ "messages": [{ "role": "user", "content": "hey" }], "model": "dolphinserver:24B", "template": "logical" }' ``` ```typescript x402-fetch (auto-pays) theme={"system"} import { privateKeyToAccount } from "viem/accounts"; import { wrapFetchWithPayment } from "@meridian/x402-fetch"; const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`); const fetchWithPayment = wrapFetchWithPayment(fetch, account); const response = await fetchWithPayment("https://api.mrdn.finance/v1/inference/chat", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ messages: [{ role: "user", content: "hey" }], model: "dolphinserver:24B", template: "logical", }), }); for await (const chunk of response.body) { process.stdout.write(new TextDecoder().decode(chunk)); } ``` ```json 402 Payment Required theme={"system"} { "x402Version": 1, "error": {}, "accepts": [ { "scheme": "exact", "network": "base", "maxAmountRequired": "10000", "resource": "https://api.mrdn.finance/v1/inference/chat", "description": "One chat completion from Dolphin AI", "mimeType": "text/event-stream", "payTo": "0x8E7769D440b3460b92159Dd9C6D17302b036e2d6", "maxTimeoutSeconds": 60, "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "extra": { "name": "USD Coin", "version": "2" } } ] } ``` ```text 200 OK (SSE stream) theme={"system"} data: {"id":"chatcmpl-...","object":"chat.completion.chunk","model":"24B","choices":[{"index":0,"delta":{"role":"assistant","content":"Hey"},"finish_reason":null}]} data: {"id":"chatcmpl-...","object":"chat.completion.chunk","model":"24B","choices":[{"index":0,"delta":{"content":" there!"},"finish_reason":null}]} data: [DONE] ``` ```json 400 Bad Request theme={"system"} { "error": "Unknown model \"gpt-4\"", "supported": ["dolphinserver:24B", "dolphinserver2:6b"] } ``` # List Models Source: https://docs.mrdn.finance/api-reference/inference/models GET https://api.mrdn.finance/v1/inference/models Get the available inference models Returns the models currently available for chat completions. Free — no payment required. Results are cached server-side for 5 minutes. ## Response List of available models. Model ID to pass as `model` in [`POST /v1/inference/chat`](/api-reference/inference/chat). Human-readable model name. Whether the model accepts image input. ```bash cURL theme={"system"} curl https://api.mrdn.finance/v1/inference/models ``` ```json 200 OK theme={"system"} { "data": [ { "id": "dolphinserver:24B", "label": "Dolphin 24B", "vision": false }, { "id": "dolphinserver2:6b", "label": "Dolphin X1 Nano", "vision": false } ] } ``` # Overview Source: https://docs.mrdn.finance/api-reference/inference/overview Pay-per-request AI inference over x402 The x402 Inference API exposes Dolphin AI chat completions behind an x402 paywall. There are no API keys and no subscriptions — each chat request is paid for with a single USDC micropayment, authorized by a wallet signature. ## Base URL The inference API is served by the Meridian facilitator: ``` https://api.mrdn.finance ``` ## Endpoints `POST /v1/inference/chat` — paid, streams SSE `GET /v1/inference/models` — free `GET /v1/inference/templates` — free ## How payment works `POST /v1/inference/chat` is protected by the standard x402 flow: 1. Call the endpoint with no payment — you receive `402 Payment Required` with the accepted payment options (price, network, asset, receiver). 2. Sign an EIP-3009 `TransferWithAuthorization` for the quoted amount (a gasless signature — no transaction, no gas needed). 3. Retry the same request with the signed payment base64-encoded in the `X-PAYMENT` header. 4. The server verifies and settles the payment through the Meridian facilitator, then streams the completion. Any x402 client handles this automatically — see [Programmatic API Access](/api-reference/programmatic-access) for `x402-fetch` / `x402-axios` examples that work against this API unchanged. ## Models | ID | Name | | ------------------- | --------------- | | `dolphinserver:24B` | Dolphin 24B | | `dolphinserver2:6b` | Dolphin X1 Nano | ## Templates | Name | Label | | --------------- | ------------- | | `logical` | Logical | | `creative` | Creative | | `summary` | Summarize | | `code-beginner` | Code Beginner | | `code-advanced` | Code Advanced | Models and templates are sourced live from the upstream provider — always fetch the current lists from `GET /v1/inference/models` and `GET /v1/inference/templates` rather than hardcoding them. # List Templates Source: https://docs.mrdn.finance/api-reference/inference/templates GET https://api.mrdn.finance/v1/inference/templates Get the available prompt templates Returns the prompt templates that shape the model's response style. Free — no payment required. Results are cached server-side for 5 minutes. ## Response List of available templates. Template name to pass as `template` in [`POST /v1/inference/chat`](/api-reference/inference/chat). Human-readable template name. ```bash cURL theme={"system"} curl https://api.mrdn.finance/v1/inference/templates ``` ```json 200 OK theme={"system"} { "data": [ { "name": "logical", "label": "Logical" }, { "name": "creative", "label": "Creative" }, { "name": "summary", "label": "Summarize" }, { "name": "code-beginner", "label": "Code Beginner" }, { "name": "code-advanced", "label": "Code Advanced" } ] } ``` # Overview Source: https://docs.mrdn.finance/api-reference/introduction Complete API documentation for Meridian x402 payments Welcome to the Meridian API documentation. This guide covers all endpoints for x402 payments, facilitator-backed settlement, and related organization features. ## Base URL All API requests are made to the following base URL: ``` https://api.mrdn.finance/v1 ``` ## Authentication Meridian Protocol supports API key authentication for server-to-server & client-to-server integrations: ```bash theme={"system"} curl -X POST https://api.mrdn.finance/v1/settle \ -H "Authorization: Bearer pk_..." \ -H "Content-Type: application/json" \ -d '{ "paymentPayload": { "...": "..." }, "paymentRequirements": { "...": "..." } }' ``` ## Core Endpoints ### x402 Payments `POST /v1/verify` is deprecated and should only be used by existing legacy integrations. `POST /v1/verify` - Legacy x402 payment verification endpoint `POST /v1/settle` - Settle verified payment `POST /v1/permit` - Execute an ERC-2612 permit-backed token transfer `GET /v1/supported` - Get supported payment types ## Error Handling All endpoints return consistent error responses with the HTTP status code mirrored in the body: ```json theme={"system"} { "type": "api_error", "code": 401, "message": "You are not authorized to access this resource." } ``` ### Common Error Codes * `400 Bad Request` - The request is unacceptable * `401 Not Authorized` - Missing or invalid authentication * `403 Forbidden` - You do not have permission to perform the request * `404 Not Found` - This object was not found * `409 Conflict` - There was an issue adding or updating this resource ## Examples ### Node.js (Backend) ```javascript theme={"system"} const response = await fetch("https://api.mrdn.finance/v1/settle", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${process.env.MERIDIAN_API_KEY}`, }, body: JSON.stringify({ paymentPayload: { /* ... */ }, paymentRequirements: { /* ... */ }, }), }); ``` ## Webhooks (Coming Soon) Meridian supports webhooks for payment events: * `payment.verified` - Payment successfully verified * `payment.failed` - Payment verification failed * `payment.settled` - Payment successfully settled Configure webhooks in your developer dashboard or via API. # Manual Integration Source: https://docs.mrdn.finance/api-reference/manual-integration Integrate x402 payments into your frontend with viem — no SDK required This guide shows the full x402 payment flow from a custom frontend using only `viem`. Your resource server owns pricing, builds the `accepts` challenge, and calls Meridian to settle the signed payment. The browser requests the resource, signs the selected payment requirement, and resubmits the signed payload. If you only need the shortest possible path on one network, start with the [Quickstart](/quickstart). This guide adds multi-chain challenges, requirement selection, and error handling on top of it. ## Prerequisites * A frontend with wallet connection (MetaMask, WalletConnect, etc.) * A protected resource endpoint that returns an x402 `402 Payment Required` challenge * A server-side Meridian API key for the resource server ## Installation ```bash theme={"system"} npm install viem ``` ## Environment Setup Keep the Meridian API key on your server. The browser only needs to know your resource URL. ```bash theme={"system"} # Browser-exposed client config NEXT_PUBLIC_RESOURCE_URL=https://your-api.example.com/api/premium # Server-side resource server config MERIDIAN_API_KEY=pk_test_... ``` ## Client Implementation ### 1. Wallet Connection ```typescript theme={"system"} import { createWalletClient, custom, type WalletClient } from "viem"; // Import the chain(s) your app supports, e.g. base, optimism, ink, ... import { base } from "viem/chains"; const connectWallet = async () => { const ethereum = (window as any).ethereum; if (!ethereum) { throw new Error("Please install MetaMask or another wallet extension"); } const accounts = (await ethereum.request({ method: "eth_requestAccounts", })) as `0x${string}`[]; if (accounts.length === 0) { throw new Error("No wallet account selected"); } const account = accounts[0]; const walletClient = createWalletClient({ account, transport: custom(ethereum), chain: base, // the chain the buyer pays from }); return { account, walletClient }; }; ``` ### 2. Request the Protected Resource Request the resource without payment first. A `402` response carries the challenge with one payment requirement per accepted source chain. ```typescript theme={"system"} interface PaymentRequirements { scheme: string; network: string; asset: string; payTo: string; maxAmountRequired: string; resource: string; description: string; mimeType: string; maxTimeoutSeconds: number; extra?: { name?: string; version?: string; creditedRecipient?: string; destinationChainId?: number; }; } interface X402Challenge { x402Version: number; accepts: PaymentRequirements[]; error?: string; } const RESOURCE_URL = process.env.NEXT_PUBLIC_RESOURCE_URL!; const requestResource = async ( walletClient: WalletClient, account: `0x${string}`, ) => { const response = await fetch(RESOURCE_URL); if (response.status === 402) { const challenge = (await response.json()) as X402Challenge; return payAndRetry(challenge, walletClient, account); } if (!response.ok) { throw new Error(`Resource request failed: ${response.status}`); } return response.json(); }; ``` ### 3. Select the Payment Requirement Pick the `accepts` entry whose `network` matches the buyer's connected chain. A requirement is source-chain specific — a Base entry cannot be paid from Ink, because the EIP-712 signature domain includes the source chain id and source token contract. ```typescript theme={"system"} // Chain ids for the networks your app supports const NETWORK_CHAIN_IDS: Record = { "base-sepolia": 84532, base: 8453, ink: 57073, optimism: 10, }; const selectRequirement = ( accepts: PaymentRequirements[], connectedChainId: number, ): PaymentRequirements => { const match = accepts.find( (entry) => NETWORK_CHAIN_IDS[entry.network] === connectedChainId, ); if (!match) { throw new Error("No payment route matches the connected wallet network"); } return match; }; ``` ### 4. Sign and Retry Sign the EIP-3009 `TransferWithAuthorization` as typed data and resubmit the request with the signed payload. The verifying contract is the token (`requirement.asset`), not the facilitator, and `authorization.to` is the facilitator (`requirement.payTo`). ```typescript theme={"system"} import { bytesToHex } from "viem"; const payAndRetry = async ( challenge: X402Challenge, walletClient: WalletClient, account: `0x${string}`, ) => { const chainId = walletClient.chain!.id; const requirement = selectRequirement(challenge.accepts, chainId); const now = BigInt(Math.floor(Date.now() / 1000)); const authorization = { from: account, to: requirement.payTo as `0x${string}`, value: BigInt(requirement.maxAmountRequired), validAfter: 0n, validBefore: now + BigInt(requirement.maxTimeoutSeconds), nonce: bytesToHex(crypto.getRandomValues(new Uint8Array(32))), }; const signature = await walletClient.signTypedData({ account, domain: { name: requirement.extra?.name ?? "USD Coin", version: requirement.extra?.version ?? "2", chainId, verifyingContract: requirement.asset as `0x${string}`, }, types: { TransferWithAuthorization: [ { name: "from", type: "address" }, { name: "to", type: "address" }, { name: "value", type: "uint256" }, { name: "validAfter", type: "uint256" }, { name: "validBefore", type: "uint256" }, { name: "nonce", type: "bytes32" }, ], }, primaryType: "TransferWithAuthorization", message: authorization, }); const paymentPayload = { x402Version: 1, scheme: "exact", network: requirement.network, payload: { signature, authorization: { from: authorization.from, to: authorization.to, value: authorization.value.toString(), validAfter: authorization.validAfter.toString(), validBefore: authorization.validBefore.toString(), nonce: authorization.nonce, }, }, }; const response = await fetch(RESOURCE_URL, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ paymentPayload }), }); if (response.status === 402) { const body = await response.json(); throw new Error(body?.error ?? "Payment was rejected"); } if (!response.ok) { throw new Error(`Paid resource request failed: ${response.status}`); } return response.json(); }; ``` On chains whose payment token does not support EIP-3009 (MegaETH, BSC, BOT chain, Tempo), the buyer signs a Permit2 witness instead of `TransferWithAuthorization`. That is a separate payload shape and signing flow, not a small variation of the code above. See the [non-EIP-3009 guide](/ai-skills/non-eip-3009) for the Permit2 implementation. ## Server Responsibilities Your resource server must not trust buyer-supplied pricing, recipient, network, or token data. Build or look up the matching `paymentRequirements` server-side, validate that the signed payload is bound to that requirement, then settle the buyer's `paymentPayload` through Meridian: ```typescript theme={"system"} const paymentRequirements = lookupRequirementForAcceptedNetwork( paymentPayload.network, ); if (!paymentRequirements) { throw new Error("Unsupported payment network"); } if (paymentPayload.network !== paymentRequirements.network) { throw new Error("Payment network does not match the server requirement"); } if ( paymentPayload.payload.authorization.to.toLowerCase() !== paymentRequirements.payTo.toLowerCase() ) { throw new Error("Payment must authorize the Meridian facilitator"); } if ( paymentPayload.payload.authorization.value !== paymentRequirements.maxAmountRequired ) { throw new Error("Payment amount does not match the server price"); } ``` ```typescript theme={"system"} const response = await fetch("https://api.mrdn.finance/v1/settle", { method: "POST", headers: { Authorization: `Bearer ${process.env.MERIDIAN_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ paymentPayload, paymentRequirements }), }); const result = await response.json(); if (!response.ok || !result.success) { throw new Error(result.errorReason ?? "Meridian settlement failed"); } ``` For multi-chain sellers, include one `paymentRequirements` object per source chain in `accepts`. Set `network`, `asset`, and `payTo` for the source chain, then set `extra.destinationChainId` to the destination chain where Meridian should settle after Across fills. Add `extra.creditedRecipient` when the payout should go to a specific merchant or marketplace recipient — see [Marketplace Fees](/command-centre/marketplace). The full request shape, including cross-chain and Permit2 payloads, is documented in [Settle x402 Payment](/api-reference/endpoint/settle-payment). # Overview Source: https://docs.mrdn.finance/api-reference/payment-types/circle-gateway Gas-free, batched USDC nanopayments routed through Circle's Gateway API The Nanopayments Gateway is Meridian's integration of [Circle Gateway nanopayments](https://developers.circle.com/gateway/nanopayments). The buyer funds a unified USDC balance once on any supported chain, then makes many gas-free, sub-cent (\$0.000001 minimum) payments that Circle batches into a single onchain settlement. Each individual x402 payment is verified instantly by signature, while Circle's Gateway System aggregates the signed authorizations and settles net positions in bulk. The Meridian facilitator wraps Circle's `@circle-fin/x402-batching/server` SDK and exposes it through the standard `/v1/verify` and `/v1/settle` endpoints. Deposit and withdraw USDC against the Gateway Wallet contract from a hosted UI. ## Endpoints `POST /v1/settle` settles a batched payment via Circle Gateway. `POST /v1/verify` verifies a batched payment without settling. `GET /v1/batched-sample` reports Gateway connectivity for diagnostics. The buyer never submits an on-chain transaction at payment time. Funds move from their pre-funded Gateway Wallet balance through Circle's batched settlement. Withdrawals from the Gateway Wallet are subject to a 7-day delay enforced by the Gateway Wallet contract. ## Detection A payment is routed through the Nanopayments Gateway when **both** of these are present in `paymentRequirements.extra`: ```json theme={"system"} { "extra": { "name": "GatewayWalletBatched", "version": "1" } } ``` This check is performed by `isBatchPayment()` from `@circle-fin/x402-batching/server` inside the facilitator's `/v1/verify` and `/v1/settle` handlers. There are **no separate URLs** for batched flows. Clients call the same endpoints used for default x402 settlement, and the facilitator inspects `extra` to decide where to forward the request. ## Supported networks `isGatewayEnabledForNetwork()` returns `true` for every network listed below. Testnets are always enabled, and mainnets are gated by the `GATEWAY_MAINNET_ENABLED` flag in the facilitator (currently `true`). The Meridian facilitator will accept requests for these networks and forward them to Circle. Networks listed in the facilitator's CAIP-2 map are **not** currently supported by Circle's Gateway API for nanopayments, including `bsc`, `fluent-testnet`, `ink`, `megaeth`, and `tempo`. The facilitator will forward the request, but Circle will reject it. See Circle's [Gateway Supported Blockchains](https://developers.circle.com/gateway/references/supported-blockchains) for the authoritative list (column `Nanopayments: Yes`). Networks marked with `[!]` below are configured locally but not yet supported by Circle. ### Testnets | Network | CAIP-2 chain ID | Circle domain | Gateway Wallet | USDC | | ---------------------- | ----------------- | :-----------: | -------------------------------------------- | -------------------------------------------- | | `arc-testnet` | `eip155:5042002` | 26 | `0x0077777d7EBA4688BDeF3E311b846F25870A19B9` | `0x3600000000000000000000000000000000000000` | | `base-sepolia` | `eip155:84532` | 6 | `0x0077777d7EBA4688BDeF3E311b846F25870A19B9` | `0x036CbD53842c5426634e7929541eC2318f3dCF7e` | | `optimism-sepolia` | `eip155:11155420` | 2 | `0x0077777d7EBA4688BDeF3E311b846F25870A19B9` | `0x5fd84259d66Cd46123540766Be93DFE6D43130D7` | | `fluent-testnet` `[!]` | `eip155:20994` | n/a | `0x0077777d7EBA4688BDeF3E311b846F25870A19B9` | n/a | ### Mainnets | Network | CAIP-2 chain ID | Circle domain | Gateway Wallet | USDC | | --------------- | --------------- | :-----------: | -------------------------------------------- | -------------------------------------------- | | `ethereum` | `eip155:1` | 0 | `0x77777777Dcc4d5A8B6E418Fd04D8997ef11000eE` | `0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48` | | `base` | `eip155:8453` | 6 | `0x77777777Dcc4d5A8B6E418Fd04D8997ef11000eE` | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` | | `optimism` | `eip155:10` | 2 | `0x77777777Dcc4d5A8B6E418Fd04D8997ef11000eE` | `0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85` | | `arbitrum` | `eip155:42161` | 3 | `0x77777777Dcc4d5A8B6E418Fd04D8997ef11000eE` | `0xaf88d065e77c8cC2239327C5EDb3A432268e5831` | | `avalanche` | `eip155:43114` | 1 | `0x77777777Dcc4d5A8B6E418Fd04D8997ef11000eE` | `0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E` | | `bsc` `[!]` | `eip155:56` | n/a | `0x77777777Dcc4d5A8B6E418Fd04D8997ef11000eE` | `0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d` | | `polygon` | `eip155:137` | 7 | `0x77777777Dcc4d5A8B6E418Fd04D8997ef11000eE` | `0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359` | | `unichain` | `eip155:130` | 10 | `0x77777777Dcc4d5A8B6E418Fd04D8997ef11000eE` | `0x078d782b760474a361dda0af3839290b0ef57ad6` | | `ink` `[!]` | `eip155:57073` | n/a | `0x77777777Dcc4d5A8B6E418Fd04D8997ef11000eE` | `0x2D270e6886d130D724215A266106e6832161EAEd` | | `sonic` | `eip155:146` | 13 | `0x77777777Dcc4d5A8B6E418Fd04D8997ef11000eE` | `0x29219dd400f2Bf60E5a23d13Be72B486D4038894` | | `worldchain` | `eip155:480` | 14 | `0x77777777Dcc4d5A8B6E418Fd04D8997ef11000eE` | `0x79A02482A880bCe3F13E09da970dC34dB4cD24D1` | | `sei` | `eip155:1329` | 16 | `0x77777777Dcc4d5A8B6E418Fd04D8997ef11000eE` | `0xe15fC38F6D8c56aF07bbCBe3BAf5708A2Bf42392` | | `hyperevm` | `eip155:999` | 19 | `0x77777777Dcc4d5A8B6E418Fd04D8997ef11000eE` | `0xb88339CB7199b77E23DB6E890353E22632Ba630f` | | `megaeth` `[!]` | `eip155:4326` | n/a | `0x77777777Dcc4d5A8B6E418Fd04D8997ef11000eE` | n/a | The Gateway Wallet addresses are deployed by Circle at the same address on every supported EVM chain (verified against [Circle's contract addresses page](https://developers.circle.com/gateway/references/contract-addresses)). The facilitator forwards batched requests to: * **Testnet:** `https://gateway-api-testnet.circle.com` (explicit URL passed to `BatchFacilitatorClient`). * **Mainnet:** the default URL baked into `@circle-fin/x402-batching/server`. The facilitator constructs `new BatchFacilitatorClient()` with no `url` override. Both clients are lazy-initialized singletons. ## Funding the Gateway Wallet Buyers fund the Gateway Wallet contract on each source chain they want to spend from. Available balance is unified across deposits, so a single signed payment can be settled from any chain that has been funded. ### Deposit flow 1. `USDC.approve(gatewayWallet, value)`. One-time per source chain. 2. `GatewayWallet.deposit(usdcAddress, value)`. Pulls USDC and credits the depositor's balance. Circle also exposes `depositFor`, `depositWithPermit`, and `depositWithAuthorization` on the wallet contract. 3. After the deposit transaction is finalized onchain, the credit becomes part of `availableBalance` and can be spent. **Time-to-finality is chain-specific**: | Chain family | Confirmations | Approx. time | | --------------------------------------------------- | --------------- | ----------------- | | Ethereum, Arbitrum, Base, OP, Unichain, World Chain | \~65 ETH blocks | \~13 – 19 minutes | | Avalanche, Sonic | \~1 | \~8 seconds | | HyperEVM, Sei | \~1 | \~5 seconds | | Polygon PoS | \~2 – 3 | \~8 seconds | Source: Circle's [Required block confirmations](https://developers.circle.com/gateway/references/supported-blockchains#required-block-confirmations) table. Sending USDC to the Gateway Wallet via a plain ERC-20 `transfer` instead of one of the deposit methods will result in **loss of funds**. Always call a `deposit*` method on the wallet contract. ### Withdrawal flow Circle's Gateway Wallet supports two withdrawal paths: * **Instant** (preferred): submit a same-chain transfer through the Gateway API. Funds round-trip through a burn on the source chain and a mint on the destination (same chain), so the withdrawal still requires a user-signed authorization. Pays only burn-tx gas. * **Trustless** (fallback for when Circle's API is unavailable): on-chain only, with a 7-day delay enforced by the `withdrawalBlock` value on the Gateway Wallet contract. The hosted [Nanopayments Gateway UI](https://nanopayments.mrdn.finance/) currently exposes the trustless path: 1. `GatewayWallet.initiateWithdrawal(usdc, value)`. Moves balance from `availableBalance` to `withdrawingBalance` and records `withdrawalBlock`. 2. After `block.number >= withdrawalBlock`, call `GatewayWallet.withdraw(usdc)`. Moves the unlocked amount from `withdrawableBalance` back to the buyer's wallet. Source: Circle's [Withdrawal documentation](https://developers.circle.com/gateway/references/technical-guide#withdrawal). ### Balance views The Gateway Wallet exposes per-token, per-depositor views the SDK reads: | View | Meaning | | --------------------- | ----------------------------------------------------------- | | `availableBalance` | Spendable balance for nanopayments | | `withdrawingBalance` | Amount in the 7-day delay window | | `withdrawableBalance` | Amount past the delay, ready to claim | | `totalBalance` | `available + withdrawing + withdrawable` | | `withdrawalBlock` | Block at which a pending withdrawal becomes withdrawable | | `isTokenSupported` | `true` if the Gateway Wallet accepts deposits of this token | The hosted [Nanopayments Gateway UI](https://nanopayments.mrdn.finance/) wraps these calls so end users do not have to call the contract directly. ## `extra` field reference In the Nanopayments Gateway path, only `extra.name` and `extra.version` are read by the Meridian facilitator. They are the routing trigger that `isBatchPayment()` checks. Everything else inside `extra` is **passed through** to Circle's Gateway API verbatim by `transformForGateway()`, which spreads `paymentRequirements` and overrides `network`, `asset`, and `amount` only. | Field | Required | Read by Meridian? | Description | | -------------------- | :------: | :---------------: | -------------------------------------------------------------------------------------------------------------------------------------- | | `name` | ✓ | Routing only | Must be the literal string `"GatewayWalletBatched"`. Together with `version`, it is the trigger for `isBatchPayment()`. | | `version` | ✓ | Routing only | Must be the literal string `"1"`. | | `verifyingContract` | | No (pass-through) | Gateway Wallet contract address used by the **client SDK** as the EIP-712 `verifyingContract` when signing. | | `creditedRecipient` | | No (pass-through) | Forwarded to Circle. Note: in the **default x402 path** Meridian reads this field, but the batched path returns before that code runs. | | `platform` | | No (pass-through) | Forwarded to Circle. The `platform` / `platformFeeBps` settlement logic in the facilitator is not executed in the batched path. | | `platformFeeBps` | | No (pass-through) | Forwarded to Circle. Gateway batched payments do not currently charge a Meridian platform fee. | | `destinationChainId` | | No (pass-through) | Forwarded to Circle. | The buyer-side SDK sets `paymentRequirements.asset` to the Gateway Wallet contract so the EIP-712 domain has the correct `verifyingContract`. `transformForGateway()` swaps that back to the network's USDC address (from `NETWORK_USDC`) before forwarding to Circle, since Circle expects the actual token address. ## Implementation notes * The facilitator-side glue lives in [`apps/facilitator/src/gateway/index.ts`](https://github.com/meridian-protocol/meridian-protocol/blob/main/apps/facilitator/src/gateway/index.ts). It owns the testnet/mainnet client routing, the Gateway Wallet ↔ USDC asset swap, and the CAIP-2 network mapping that Circle's API requires. * The deposit/withdraw UI lives in [`apps/circle-gateway`](https://github.com/meridian-protocol/meridian-protocol/tree/main/apps/circle-gateway) and is also hosted at [`nanopayments.mrdn.finance`](https://nanopayments.mrdn.finance/). * Circle recommends calling `settle()` directly for production batched flows rather than the legacy `verify()` + `settle()` two-step. ## See also ### Meridian * [Payment Types](/api-reference/payment-types/overview) * [Settle Batched Payment](/api-reference/endpoint/batched-settle) * [Verify Batched Payment](/api-reference/endpoint/batched-verify) * [Gateway Status](/api-reference/endpoint/batched-sample) * [Roadmap: Circle Nanopayments](/roadmap) ### Circle (authoritative source for the upstream API and contracts) * [Circle Nanopayments overview](https://developers.circle.com/gateway/nanopayments) * [Circle Gateway technical guide](https://developers.circle.com/gateway/references/technical-guide) * [Circle Gateway supported blockchains](https://developers.circle.com/gateway/references/supported-blockchains) * [Circle Gateway contract addresses](https://developers.circle.com/gateway/references/contract-addresses) # Payment Types Source: https://docs.mrdn.finance/api-reference/payment-types/overview Payment schemes and settlement paths supported by the Meridian facilitator The Meridian facilitator accepts several payment payload types through `POST /v1/settle`. The facilitator inspects each settle request and routes it to the correct settlement path automatically; clients do not call a separate endpoint for Permit2, Circle Gateway, or Solana settlement. ## Supported types Default scheme. Buyer signs an EIP-3009 `transferWithAuthorization` for USDC and Meridian settles through `X402ProxyFacilitator` on the destination chain. Use this path when the payment token natively exposes EIP-3009. Current path on MegaETH, BSC, and BOT chain when the token does not expose EIP-3009. Buyer approves Permit2 once, then signs a witness authorization that Meridian settles through `x402ExactPermit2Proxy`. Usage-based pricing on any supported network. Buyer signs a Permit2 witness authorization for a maximum amount; Meridian settles the actual usage amount (≤ the signed max) through `x402UptoPermit2Proxy`. Gas-free, batched USDC nanopayments backed by Circle's Gateway Wallet. Detected via `paymentRequirements.extra.name === "GatewayWalletBatched"`. USDC payments on Solana settled through the `meridian_x402` program. The buyer signs the settlement transaction and the facilitator co-signs as fee payer. Detected via `network === "solana"` / `"solana-devnet"`. ## Comparison | Capability | x402 (EIP-3009) | Permit2 fallback | Circle Gateway Batched | | ----------------------- | --------------------------------------------- | ----------------------------------------- | -------------------------------------------------------------------- | | Tokens | USDC | Any ERC-20 | USDC only | | Minimum payment | Token-decimal granularity | Token-decimal granularity | \$0.000001 USDC | | Settlement target | `X402ProxyFacilitator` | `x402ExactPermit2Proxy` (via facilitator) | Circle Gateway API | | Buyer one-time setup | None | `approve(Permit2, ∞)` | Deposit USDC into Gateway Wallet | | Per-payment signing | EIP-3009 typed data | Permit2 witness typed data | EIP-712 typed data over Gateway Wallet domain | | Gas paid by buyer | None | None | None | | Cross-chain | Same-chain or Across via `destinationChainId` | Same-chain | Unified balance across all funded chains | | Buyer experience | \< 2s same-chain; Across fill for cross-chain | \< 2s on-chain settlement | Instant signature verification; onchain settlement batched by Circle | | Platform fee / cashback | Supported | Supported | Not applied (Meridian fee logic is skipped on the batched path) | ## Upto scheme (usage-based pricing) The `upto` scheme decouples the signed authorization from the settled amount: the buyer signs a Permit2 witness authorization for a **maximum** amount before consuming the resource, and the seller settles the **actual** usage amount afterwards. It is available on every supported network, since it always settles through Permit2 (`x402UptoPermit2Proxy` at `0x402015c795ecb48A360bDC6e35a2EaEb313a0002`, the same address on every chain). Differences from the exact Permit2 flow: * `paymentRequirements.scheme` is `"upto"` and `maxAmountRequired` is the cap the buyer signs, not the final price. * The buyer signs with `x402UptoPermit2Proxy` as the Permit2 spender, and the witness carries an extra `facilitator` field bound to the Meridian facilitator contract: `{ to, facilitator, validAfter }`. * At settle time the seller passes the final amount as `paymentRequirements.extra.settleAmount` (base units, must be `0 < settleAmount ≤ maxAmountRequired`). If omitted, the full signed maximum is settled. Platform fees, treasury fees, and cashback all apply to the settled amount only. ## Accepting payments from multiple chains An x402 challenge can expose multiple payment routes in `accepts`. Each route is a full `paymentRequirements` object for the source chain the buyer will pay from. The buyer or SDK selects the requirement that matches the connected wallet chain, then signs against that source chain's token contract. For example, a seller that wants to receive settlement on Base while allowing buyers to pay from Base, Ink, or Optimism would return one requirement per source chain: ```json theme={"system"} { "x402Version": 1, "accepts": [ { "scheme": "exact", "network": "base", "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "payTo": "0x8E7769D440b3460b92159Dd9C6D17302b036e2d6", "maxAmountRequired": "1000000", "resource": "https://seller.example/api/tool", "description": "Paid access", "mimeType": "application/json", "maxTimeoutSeconds": 300, "extra": { "name": "USD Coin", "version": "2", "creditedRecipient": "0x1111111111111111111111111111111111111111", "destinationChainId": 8453 } }, { "scheme": "exact", "network": "ink", "asset": "0x2D270e6886d130D724215A266106e6832161EAEd", "payTo": "0x8E7769D440b3460b92159Dd9C6D17302b036e2d6", "maxAmountRequired": "1000000", "resource": "https://seller.example/api/tool", "description": "Paid access", "mimeType": "application/json", "maxTimeoutSeconds": 300, "extra": { "name": "USD Coin", "version": "2", "creditedRecipient": "0x1111111111111111111111111111111111111111", "destinationChainId": 8453 } }, { "scheme": "exact", "network": "optimism", "asset": "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85", "payTo": "0x8E7769D440b3460b92159Dd9C6D17302b036e2d6", "maxAmountRequired": "1000000", "resource": "https://seller.example/api/tool", "description": "Paid access", "mimeType": "application/json", "maxTimeoutSeconds": 300, "extra": { "name": "USD Coin", "version": "2", "creditedRecipient": "0x1111111111111111111111111111111111111111", "destinationChainId": 8453 } } ] } ``` For a cross-chain EIP-3009 payment, `network`, `asset`, and `payTo` describe the source chain. `extra.destinationChainId` describes the destination chain where Meridian settles after Across fills the bridge. A Base-only requirement cannot be paid from Ink; the seller must expose an Ink-source requirement for an Ink-funded buyer. Across uses exact-input routing in the current flow. If the seller must receive an exact destination-chain amount, quote the route and set the source-chain `maxAmountRequired` high enough to cover Across fees. ## How routing works `POST /v1/settle` follows this decision tree. It distinguishes between EIP-3009 and Permit2 by trying the standard payment payload first and falling back to the Permit2 payload: ```mermaid theme={"system"} flowchart TD A[Incoming payment] --> S{isSupportedSolanaNetwork(network)?
network == "solana" or "solana-devnet"} S -- Yes --> SV[Solana path
meridian_x402 program settlement] S -- No --> B{isBatchPayment(paymentRequirements)
extra.name == "GatewayWalletBatched"
and extra.version == "1"?} B -- Yes --> F{isGatewayEnabledForNetwork(network)?} F -- Yes --> H[Forward to Circle Gateway API
via BatchFacilitatorClient] F -- No --> G[HTTP 403
errorReason: gateway_not_enabled] B -- No --> C{Payload parses as
Permit2 payload?} C -- No --> D[Default x402 path
X402ProxyFacilitator on-chain] C -- Yes --> E[Permit2 path
x402ExactPermit2Proxy] ``` The Solana branch is checked first, keyed off `paymentPayload.network`; a `solana` / `solana-devnet` payment is routed to the `meridian_x402` program settlement path (the payload carries a base64 `transaction` instead of an EIP-3009 or Permit2 authorization). See [Solana Program](/payments/solana-program). The "is this a batched payment?" check is implemented via [`isBatchPayment`](https://www.npmjs.com/package/@circle-fin/x402-batching) from `@circle-fin/x402-batching/server`. It looks at `paymentRequirements.extra` only; the rest of the request body keeps the same x402 v1 shape for EIP-3009 and Circle Gateway paths. Inside the default EIP-3009 path, Meridian compares the source chain from `paymentPayload.network` with `paymentRequirements.extra.destinationChainId`. If they differ, the facilitator pulls source-chain USDC and routes the destination-chain settlement through Across. If they match or `destinationChainId` is omitted, settlement stays on the source chain. ## Choosing a payment type * **Building a typical x402 paywall on Base, Optimism, Arbitrum, Polygon, etc.** → Exact (EIP-3009). Nothing extra to set up for native EIP-3009 tokens; follow the [Quickstart](/quickstart). * **Accepting USDC from several source chains while settling to one destination chain** → Exact (EIP-3009) with one `accepts` entry per source chain and `extra.destinationChainId` set on routes that should settle elsewhere via Across. * **Integrating Avalanche, Sei, or Arc Testnet on the [Instant demo](https://instant.mrdn.finance)** → Exact (EIP-3009), **same-chain only**. Use matching source and destination network ids; Across cross-chain is not used on these chains. See [Supported Networks](/api-reference/supported-networks). * **Integrating MegaETH, BSC, or BOT chain testnet/mainnet when no EIP-3009 token is present** → Permit2 via `x402ExactPermit2Proxy` `0x402085c248EeA27D92E8b30b2C58ed07f9E20001`. Buyers approve Permit2 `0x000000000022D473030F116dDEE9F6B43aC78BA3`, sign with the proxy as spender, and keep facilitator fields bound to Meridian. See [Smart Contracts](/payments/smart-contracts) and [Supported Networks](/api-reference/supported-networks). * **Usage-based pricing (metered APIs, per-token inference, pay-per-result) where the final price is only known after serving the request** → Upto via `x402UptoPermit2Proxy` `0x402015c795ecb48A360bDC6e35a2EaEb313a0002`, on any supported network. Buyer signs the maximum; seller settles the metered amount via `extra.settleAmount`. * **High-frequency, sub-cent agent-to-agent payments where zero fees and unified balance matter more than per-tx settlement speed** → [Circle Gateway Batched](/api-reference/payment-types/circle-gateway). * **Accepting USDC on Solana** → Solana (SVM) via the `meridian_x402` program, same-chain only. The buyer signs the settlement transaction and the facilitator co-signs as fee payer. See [Solana Program](/payments/solana-program) and the [Solana payments](/ai-skills/solana) guide. Do not mix the signing flows. EIP-3009 routes use a `TransferWithAuthorization` authorization under `paymentPayload.payload`. Permit2 routes use `owner`, `permit`, and `witness` fields instead. Solana routes carry a base64-encoded, payer-signed `transaction` under `paymentPayload.payload`. All are submitted to `POST /v1/settle`, but the signing code and payload shape are different. ## Reference * [`POST /v1/verify`](/api-reference/endpoint/verify-payment): deprecated verification entrypoint for EIP-3009 and Circle Gateway payloads. * [`POST /v1/settle`](/api-reference/endpoint/settle-payment): settlement entrypoint for EIP-3009, Permit2, and Circle Gateway payloads. * [`GET /v1/supported`](/api-reference/endpoint/supported-payments): list of `{ x402Version, scheme, network }` triples the facilitator accepts. # Programmatic API Access Source: https://docs.mrdn.finance/api-reference/programmatic-access Call x402-protected APIs from scripts, backends, and AI agents ## Overview Any API protected by Meridian x402 can be consumed programmatically — no browser or manual wallet interaction required. Your client holds a funded wallet, and payments are authorized by signing an EIP-3009 message (a gasless signature, not a transaction). The flow is: 1. Call the endpoint normally. 2. Receive `402 Payment Required` with a list of accepted payment options. 3. Sign a payment authorization with your wallet. 4. Retry the request with the signed payment in the `X-PAYMENT` header. The SDKs below handle steps 2–4 automatically. ## Prerequisites * A wallet private key with USDC on a [supported network](/api-reference/supported-networks) * The URL of an x402-protected endpoint Payments are authorized via signature only — your client never submits a transaction and does not need native gas tokens. The facilitator settles on-chain on your behalf. ## Using fetch (`x402-fetch`) Wrap the native `fetch` so 402 responses are paid and retried automatically: ```bash theme={"system"} npm install @meridian/x402-fetch viem ``` ```typescript theme={"system"} import { privateKeyToAccount } from "viem/accounts"; import { wrapFetchWithPayment } from "@meridian/x402-fetch"; const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`); // maxValue caps how much a single request may cost (base units, 6 decimals) const fetchWithPayment = wrapFetchWithPayment( fetch, account, BigInt(0.1 * 10 ** 6), // allow up to 0.10 USDC per request ); const response = await fetchWithPayment("https://api.example.com/paid-endpoint", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ query: "hello" }), }); const data = await response.json(); ``` If the endpoint costs more than `maxValue`, the wrapper throws instead of paying. ## Using axios (`x402-axios`) ```bash theme={"system"} npm install @meridian/x402-axios viem ``` ```typescript theme={"system"} import axios from "axios"; import { privateKeyToAccount } from "viem/accounts"; import { withPaymentInterceptor } from "@meridian/x402-axios"; const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`); const api = withPaymentInterceptor( axios.create({ baseURL: "https://api.example.com" }), account, ); const { data } = await api.post("/paid-endpoint", { query: "hello" }); ``` ## Manual flow (any language) If an SDK is not available for your stack, implement the flow directly. ### 1. Trigger the 402 ```bash theme={"system"} curl -X POST https://api.example.com/paid-endpoint \ -H "Content-Type: application/json" \ -d '{"query":"hello"}' ``` **Response (402):** ```json theme={"system"} { "x402Version": 1, "accepts": [ { "scheme": "exact", "network": "base", "maxAmountRequired": "10000", "resource": "https://api.example.com/paid-endpoint", "payTo": "0x8E7769D440b3460b92159Dd9C6D17302b036e2d6", "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "maxTimeoutSeconds": 60, "extra": { "name": "USD Coin", "version": "2" } } ] } ``` ### 2. Sign the payment authorization Pick an entry from `accepts` and sign an EIP-712 `TransferWithAuthorization` (EIP-3009) message with your wallet: * **Domain**: `name` and `version` from `extra`, the network's `chainId`, and `asset` as `verifyingContract` * **Message**: `from` (your address), `to` (`payTo`), `value` (`maxAmountRequired`), `validAfter` (now − 600), `validBefore` (now + `maxTimeoutSeconds`), and a random 32-byte `nonce` ### 3. Retry with the `X-PAYMENT` header Base64-encode the signed payload and retry: ```json theme={"system"} { "x402Version": 1, "scheme": "exact", "network": "base", "payload": { "signature": "0x...", "authorization": { "from": "0xYourAddress", "to": "0x8E7769D440b3460b92159Dd9C6D17302b036e2d6", "value": "10000", "validAfter": "1750000000", "validBefore": "1750000060", "nonce": "0x..." } } } ``` ```bash theme={"system"} curl -X POST https://api.example.com/paid-endpoint \ -H "Content-Type: application/json" \ -H "X-PAYMENT: " \ -d '{"query":"hello"}' ``` A successful response includes an `X-PAYMENT-RESPONSE` header with the settlement result (including the transaction hash). ## AI agents Because the whole flow is signature-based and non-interactive, x402 works well for autonomous agents: give the agent a funded wallet key, wrap its HTTP client with `x402-fetch` or `x402-axios`, and set a conservative `maxValue` as a spending guardrail per request. ## Best practices * **Cap spend** — always set `maxValue` explicitly rather than relying on defaults. * **Protect keys** — load private keys from environment variables or a secrets manager; never commit them. * **Use a dedicated wallet** — fund a purpose-specific wallet with only the USDC your integration needs. * **Test first** — develop against a testnet (e.g. `base-sepolia`) before pointing at mainnet endpoints. ## Next steps Networks and assets you can pay with Protect your own endpoints with x402 Authenticate facilitator API calls Facilitator settlement endpoint reference # Supported Networks Source: https://docs.mrdn.finance/api-reference/supported-networks Networks supported by Meridian ## Overview Meridian supports multiple EVM networks with different levels of functionality for each network. This page documents the current network support and capabilities. ## Contract Addresses ### Mainnet | Network | Chain ID | Payment Token | Facilitator | Cross-chain | | ------------ | -------- | -------------------------------------------- | -------------------------------------------- | ----------- | | `avalanche` | `43114` | `0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | No | | `base` | `8453` | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | Across | | `bsc` | `56` | `0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | Across | | `bot-chain` | `677` | `0xaBabc7Ddc03e501d190C676BF3d92ef0e6e87a3C` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | No | | `optimism` | `10` | `0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | Across | | `arbitrum` | `42161` | `0xaf88d065e77c8cC2239327C5EDb3A432268e5831` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | Across | | `polygon` | `137` | `0x3c499c542cef5e3811e1192ce70d8cc03d5c3359` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | Across | | `unichain` | `130` | `0x078d782b760474a361dda0af3839290b0ef57ad6` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | Across | | `ink` | `57073` | `0x2D270e6886d130D724215A266106e6832161EAEd` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | Across | | `worldchain` | `480` | `0x79A02482A880bCe3F13E09da970dC34dB4cD24D1` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | Across | | `sei` | `1329` | `0xe15fC38F6D8c56aF07bbCBe3BAf5708A2Bf42392` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | No | | `hyperevm` | `999` | `0xb88339CB7199b77E23DB6E890353E22632Ba630f` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | Across | | `megaeth` | `4326` | `0xFAfDdbb3FC7688494971a79cc65DCa3EF82079E7` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | No | | `tempo` | `4217` | `0x20C000000000000000000000b9537d11c60E8b50` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | No | | `monad` | `143` | `0x754704Bc059F8C67012fEd69BC8A327a5aafb603` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | Across | | `robinhood` | `4663` | `0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168` | `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` | Across | #### Non-EIP-3009 Token Note Chains whose payment token does not natively support EIP-3009 use Permit2 via `x402ExactPermit2Proxy` `0x402085c248EeA27D92E8b30b2C58ed07f9E20001`. This is the canonical exact Permit2 proxy from the official [x402 repository](https://github.com/x402-foundation/x402/tree/main), deployed at the same address on supported EVM chains. This includes MegaETH, BSC, and BOT chain testnet and mainnet. For these integrations: * Keep `payTo` set to the facilitator `0x8E7769D440b3460b92159Dd9C6D17302b036e2d6` * Set `paymentRequirements.asset` to the ERC-20 token address the buyer will pay with * Use Permit2 `0x000000000022D473030F116dDEE9F6B43aC78BA3` as the approval target * Use `x402ExactPermit2Proxy` `0x402085c248EeA27D92E8b30b2C58ed07f9E20001` as the Permit2 spender #### Upto Scheme Note The `upto` scheme (usage-based pricing, buyer signs a maximum and the seller settles the actual amount) is available on **every** supported network, since it always settles via Permit2. Sign with `x402UptoPermit2Proxy` `0x402015c795ecb48A360bDC6e35a2EaEb313a0002` as the Permit2 spender (same address on every chain) and include the `facilitator` field in the witness. See [Payment Types](/api-reference/payment-types/overview) for details. #### Tempo Note Tempo has no native gas token. Transaction fees are paid in TIP-20 stablecoins such as USDC.e. Meridian uses USDC.e at `0x20C000000000000000000000b9537d11c60E8b50` for payment flows. The public RPC is `https://rpc.tempo.xyz`, the WebSocket endpoint is `wss://rpc.tempo.xyz`, the explorer is `https://explore.tempo.xyz`, and Circle CCTP/Gateway is not currently supported. ### Testnet | Network | Chain ID | Payment Token | Facilitator | Cross-chain | | ------------------- | ---------- | -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ----------- | | `arc-testnet` | `5042002` | `0x3600000000000000000000000000000000000000` | [`0x8e633dBf31adCc7D41BE3e95B7c8DD3526B5235A`](https://testnet.arcscan.app/address/0x8e633dBf31adCc7D41BE3e95B7c8DD3526B5235A) | No | | `bot-chain-testnet` | `968` | `0x75edC9335175Fc0552D51D48439F229c10420fe3` | `0x8e633dBf31adCc7D41BE3e95B7c8DD3526B5235A` | No | | `base-sepolia` | `84532` | `0x036CbD53842c5426634e7929541eC2318f3dCF7e` | `0x8e633dBf31adCc7D41BE3e95B7c8DD3526B5235A` | Across | | `fluent-testnet` | `20994` | `0xa5544e6DDe3d8927Bf3bD9556e84f49067E36bAf` | [`0xB3Ac1B7871942bCdCD0bD6C65765272bBE70B8Da`](https://testnet.fluentscan.xyz/address/0xB3Ac1B7871942bCdCD0bD6C65765272bBE70B8Da) | No | | `optimism-sepolia` | `11155420` | `0x5fd84259d66Cd46123540766Be93DFE6D43130D7` | `0x8e633dBf31adCc7D41BE3e95B7c8DD3526B5235A` | Across | Fluent testnet currently uses a mock USDC deployment, including function `mint(to, amount)` to receive test USDC. #### Arc Testnet Note Arc Testnet uses USDC as the native gas token. Meridian uses the ERC-20 USDC interface at `0x3600000000000000000000000000000000000000` for payment flows. The public RPC is `https://rpc.testnet.arc.network`, the WebSocket endpoint is `wss://rpc.testnet.arc.network`, the explorer is `https://testnet.arcscan.app`, and the Circle CCTP/Gateway domain is `26`. ### Solana (SVM) Meridian settles USDC payments on Solana through the `meridian_x402` program rather than an EVM facilitator. Payments are same-chain only (no Across cross-chain routing on Solana in this version). | Network | Cluster | USDC Mint | Program ID | | --------------- | ------------ | ---------------------------------------------- | --------------------------------------------- | | `solana` | mainnet-beta | `EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v` | `Ro6hz1smrm5zDh73849eDqKna9dE1EkPsWekAB5rBWm` | | `solana-devnet` | devnet | `4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU` | `Ro6hz1smrm5zDh73849eDqKna9dE1EkPsWekAB5rBWm` | The program is live on both `solana` and `solana-devnet`. Do not hardcode the program ID or the fee-payer pubkey — read everything at runtime from `GET /v1/solana/facilitator?network=`, which returns `{ network, facilitator, programId, configPda }` plus the on-chain program config (`usdcMint`, `treasury`, `treasuryToken`, `treasuryFeeBps`, `paused`) when the chain is reachable. Buyers need no Anchor, no IDL, and no on-chain config reads — this one call plus `@solana/web3.js` / `@solana/spl-token` is enough. For these integrations: * Set `paymentRequirements.asset` to the **USDC mint** address * Set `paymentRequirements.payTo` to the seller's **recipient wallet** (owner, not a token account), matching the org's configured Solana recipient * Set `paymentRequirements.extra.feePayer` to the facilitator pubkey; the buyer signs the settlement transaction and the facilitator co-signs as fee payer * Ensure all USDC associated token accounts exist before settlement See [Solana Program](/payments/solana-program) and the [Solana payments](/ai-skills/solana) integration guide. ## Network Monitoring * Monitor network status via `/v1/supported` endpoint * Implement fallback logic for network outages * Cache network information with appropriate TTL # Create Top-up Source: https://docs.mrdn.finance/api-reference/topup/create-topup POST https://api-antseed.mrdn.finance/v1/{merchant}/topups Pay with x402 and credit the merchant-side balance Creates (or resumes) a top-up. Unpaid requests receive `402 Payment Required` with the exact payment requirements to sign; paid requests settle the payment and perform the merchant deposit. Replaying the same `X-PAYMENT` returns the existing top-up idempotently. ## Path Merchant id. Currently `antseed`. Unsupported merchant paths return `404`. ## Headers Must be `application/json`. Base64-encoded signed x402 payment payload. Omit on the first request to receive the payment requirements via a 402 response. ## Body Address to credit on the merchant contract. Required on an unpaid request; on a paid request it defaults to the payment signer. It must equal the signer (`authorization.from`) — anything else is rejected with `400 buyer_mismatch`. Gross payment-token amount in 6-decimal base units (for example, `"5000000"` = 5 tokens). When omitted, the gateway uses the buyer's maximum available headroom. When paying, it must match `authorization.value` and be within the bounds returned by the challenge or quote endpoint. Signed x402 payment payload as JSON. Use either this field or the base64-encoded `X-PAYMENT` header. ## Responses | Status | Meaning | | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `200` | Deposited. Body is the public top-up row (`state: "deposited"`, `grossAmount`, `netAmount`, source `settlementTx`, optional Base `fillTx`, and Base `depositTx`); settlement details are echoed base64-encoded in the `X-PAYMENT-RESPONSE` header. | | `402` | Payment challenge or clean settlement failure. A clean failure can be retried with the same valid payment. The response also contains a fresh challenge. | | `400` | Rejected before settlement: `invalid_request`, `buyer_mismatch`, `wrong_network`, `wrong_recipient`, `invalid_signature`, `authorization_not_yet_valid`, `authorization_expired`, `amount_mismatch`, `amount_too_low`, `amount_too_high`, … (`errorReason` names the cause). | | `409` | `authorization_reused` (payment already used), `credit_limit_reached`, or `deposit_failed` (deposit impossible after settlement — the settled amount was refunded, see `refundTx`). | | `503` | Settlement, bridging, deposit, or refund is pending. Poll the returned `topupId` or retry with the same `X-PAYMENT` after `Retry-After` seconds. | | `500` | `needs_review` — an inconsistent, expired, or timed-out bridge state that requires operator attention. | ```bash Unpaid (get challenge) theme={"system"} curl -X POST https://api-antseed.mrdn.finance/v1/antseed/topups \ -H "Content-Type: application/json" \ -d '{ "buyer": "0xYourAddress", "amount": "5000000" }' ``` ```bash Paid theme={"system"} curl -X POST https://api-antseed.mrdn.finance/v1/antseed/topups \ -H "Content-Type: application/json" \ -H "X-PAYMENT: " \ -d '{ "buyer": "0xYourAddress", "amount": "5000000" }' ``` The challenge below abbreviates the enabled source list for readability. The live response contains one `accepts[]` and `topup.sources[]` entry per source that can accept the requested amount. ```json 402 Challenge theme={"system"} { "x402Version": 1, "error": "X-PAYMENT header required", "accepts": [ { "scheme": "exact", "network": "base", "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "payTo": "0x…facilitator", "maxAmountRequired": "5000000", "resource": "https://api-antseed.mrdn.finance/v1/antseed/topups?buyer=0xYourAddress", "description": "Top up AntSeed buyer deposits for 0xYourAddress…", "mimeType": "application/json", "maxTimeoutSeconds": 300, "extra": { "name": "USD Coin", "version": "2", "creditedRecipient": "0x…relayer" } }, { "scheme": "exact", "network": "optimism", "asset": "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85", "payTo": "0x…facilitator", "maxAmountRequired": "5000000", "extra": { "name": "USD Coin", "version": "2", "creditedRecipient": "0x…relayer", "destinationChainId": 8453 }, "…": "…" }, { "scheme": "exact", "network": "ink", "asset": "0x2D270e6886d130D724215A266106e6832161EAEd", "extra": { "name": "USDC", "version": "2", "destinationChainId": 8453 }, "…": "…" } ], "topup": { "buyer": "0xYourAddress", "amount": "5000000", "minAmount": "1250000", "maxAmount": "10000000", "note": "The credited deposit is the settled amount net of Meridian facilitator fees.", "sources": [ { "network": "base", "evmChainId": 8453, "minAmount": "1250000", "maxAmount": "10000000" }, { "network": "optimism", "evmChainId": 10, "minAmount": "1750000", "maxAmount": "10000000" }, { "network": "ink", "evmChainId": 57073, "minAmount": "1750000", "maxAmount": "10000000" } ] } } ``` ```json 200 Deposited theme={"system"} { "success": true, "topupId": "0xf6cd…d723", "state": "deposited", "buyer": "0xYourAddress", "payer": "0xYourAddress", "network": "optimism", "sourceNetwork": "optimism", "grossAmount": "5000000", "netAmount": "4900000", "settlementTx": "0xeb80…e5a8", "fillTx": "0xa91c…44fe", "depositTx": "0x8186…1234", "refundTx": null, "error": null, "createdAt": 1783656257928, "updatedAt": 1783656258080 } ``` # Gateway Health Source: https://docs.mrdn.finance/api-reference/topup/health GET https://api-antseed.mrdn.finance/healthz Read the running gateway's merchant and network configuration Returns the gateway's relayer, facilitator, merchant contracts, and enabled payment sources. ## Response fields | Field | Meaning | | --------------------- | ---------------------------------------------------------------------- | | `ok` | The gateway process completed startup and is serving requests. | | `relayer` | Base wallet that submits merchant deposits and refunds. | | `facilitator` | Meridian facilitator contract used for Base settlement. | | `merchants` | Configured merchant runtimes and their settlement chains. | | `merchants[].sources` | Enabled payment sources, token addresses, facilitators, and chain ids. | ```bash Request theme={"system"} curl "https://api-antseed.mrdn.finance/healthz" ``` ```json 200 theme={"system"} { "ok": true, "relayer": "0x…relayer", "facilitator": "0x8E7769D440b3460b92159Dd9C6D17302b036e2d6", "merchants": [ { "id": "antseed", "chainId": "base-mainnet", "evmChainId": 8453, "network": "base", "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "contract": "0x0F7a3a8f4Da01637d1202bb5443fcF7F88F99fD2", "relayer": "0x…relayer", "sources": [ { "network": "base", "evmChainId": 8453, "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "facilitator": "0x8E7769D440b3460b92159Dd9C6D17302b036e2d6", "sameChain": true } ] } ] } ``` # Overview Source: https://docs.mrdn.finance/api-reference/topup/overview Gasless merchant top-ups over x402 The Merchant Top-up Gateway turns an x402 payment into an onchain action on a merchant contract. The buyer signs a gasless EIP-3009 authorization on a supported source chain. Meridian settles the payment to a Base relayer wallet, using Across for cross-chain payments. The relayer then calls the merchant contract. The buyer does not need ETH. The first supported merchant is **AntSeed**: topping up `AntseedDeposits` buyer balances on Base — the one gas-requiring step in AntSeed's otherwise gasless buyer flow. Once the deposit lands onchain, the balance appears in all AntSeed tooling automatically. ## Base URL ``` https://api-antseed.mrdn.finance ``` All endpoints are scoped by merchant id. The only merchant currently available is `antseed`. ## Endpoints `POST /v1/{merchant}/topups` — x402-paid `GET /v1/{merchant}/topups/quote` — free `GET /v1/{merchant}/topups/{id}` — free `GET /healthz` — free ## How it works 1. Send `POST /v1/antseed/topups` with a `buyer` and no payment. The `402 Payment Required` response contains one entry per eligible source in `accepts[]` and source bounds in `topup.sources[]`. If `amount` is omitted, the gateway uses the buyer's full headroom. 2. Choose a source and sign an EIP-3009 `TransferWithAuthorization` using its chain id and token domain. Cross-chain entries include `extra.destinationChainId: 8453`. 3. Retry the same request with the signed payment base64-encoded in the `X-PAYMENT` header. 4. The gateway settles through Meridian. Payments from sources other than Base enter `bridging` while Across delivers to Base. The gateway measures the exact Base credit and calls the merchant contract (`deposit(buyer, net)` for AntSeed) from its relayer wallet. 5. The response (and `GET /v1/antseed/topups/{id}`) reports the source `settlementTx`, optional Base `fillTx`, Base `depositTx`, and exact credited amount. ## Payment sources Most sources use USDC. Robinhood uses USDG. Cross-chain payments deliver USDC on Base. Use the token address and EIP-712 domain returned in the payment requirements. | Source | Chain id | Payment token / EIP-712 name / version | Settlement | | --------------- | -------: | -------------------------------------- | -------------- | | Base | `8453` | USDC / `USD Coin` / `2` | Same-chain | | Arbitrum | `42161` | USDC / `USD Coin` / `2` | Across to Base | | BSC | `56` | USDC / `USD Coin` / `1` | Across to Base | | HyperEVM | `999` | USDC / `USD Coin` / `2` | Across to Base | | Ink | `57073` | USDC / `USDC` / `2` | Across to Base | | Monad | `143` | USDC / `USD Coin` / `2` | Across to Base | | Optimism | `10` | USDC / `USD Coin` / `2` | Across to Base | | Polygon | `137` | USDC / `USD Coin` / `2` | Across to Base | | Robinhood Chain | `4663` | USDG / `Global Dollar` / `1` | Across to Base | | Unichain | `130` | USDC / `USD Coin` / `2` | Across to Base | | World Chain | `480` | USDC / `USD Coin` / `2` | Across to Base | ## Trust assumptions * **Signed payment.** The EIP-3009 authorization fixes the token, amount, facilitator, validity window, payer, and nonce. The merchant and credited recipient are settlement parameters supplied by the gateway. The gateway enforces `buyer = payer` and requests the merchant relayer as the credited recipient. * **Facilitator contract.** Payments pass through the verified [`X402ProxyFacilitator`](https://basescan.org/address/0x8E7769D440b3460b92159Dd9C6D17302b036e2d6#code) contract. * **Relayer.** The merchant relayer holds the settled funds until it completes the merchant deposit or refunds the payer. Its transactions and the final credited amount are recorded in the top-up status. * **Cross-chain payments.** Payments from other chains use Across to deliver funds to Base before merchant fulfillment. ## Rules worth knowing * **Buyer = payer.** v1 only allows self top-ups: the `buyer` must equal the address that signed the payment. Requests with a different `buyer` are rejected (`buyer_mismatch`). * **Fees.** The merchant receives the amount delivered on Base after Meridian and Across fees. Responses show `grossAmount` and `netAmount` in 6-decimal base units. * **Idempotent.** Replaying the same `X-PAYMENT` returns the existing top-up. Each EIP-3009 authorization `(payer, nonce)` is accepted exactly once. * **Pending requests.** A `503` means the payment is still being reconciled. Retry with the same `X-PAYMENT` or poll its `topupId`. * **Refund path.** If the deposit can no longer succeed after settlement (e.g. credit limit lost to a concurrent deposit), the delivered amount is automatically refunded to the payer on Base. If an Across deposit itself expires, Meridian operations recover the source-proxy refund manually in v1. The gateway temporarily holds funds between settlement and merchant fulfillment. Merchant contracts remain public and can support other gateways. # Quote Bounds Source: https://docs.mrdn.finance/api-reference/topup/quote GET https://api-antseed.mrdn.finance/v1/{merchant}/topups/quote Read the current top-up bounds and payment requirements for a buyer Returns the buyer's merchant balance, credit limit, headroom, accepted amount range, and x402 payment requirements. This endpoint does not move funds. ## Path Merchant id. Currently `antseed`. ## Query Address to quote for (checksummed or lowercase). Gross payment-token amount in 6-decimal base units. Defaults to the maximum available headroom. Invalid amounts return `400 amount_too_low`, `400 amount_too_high`, or `409 credit_limit_reached`. Optional payment source. Supported values are `base`, `arbitrum`, `bsc`, `hyperevm`, `ink`, `monad`, `optimism`, `polygon`, `robinhood`, `unichain`, and `worldchain`. Defaults to Base. Unknown sources return `400 wrong_network`. ## Response fields All amounts are 6-decimal base-unit strings. `amount`, `minAmount`, and `maxAmount` use the selected source token. Merchant balance fields use Base USDC. | Field | Meaning | | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | | `minAmount` / `maxAmount` | Accepted gross range for the selected source. Cross-chain minimums include a buffered Across fee quote (or the configured static fallback). | | `balance` | Buyer's current merchant-side balance (available + reserved). | | `creditLimit` | Merchant-side credit limit for the buyer. | | `headroom` | Remaining credit capacity (`creditLimit − balance`). | | `minBuyerDeposit` | Merchant-side minimum applying to this top-up (`"0"` once the buyer has a balance). | | `requirements` | The x402 payment requirements for the selected source. It corresponds to that source's entry in a subsequent challenge. | | `sources` | Sources that can accept the requested amount, as `{ network, evmChainId, minAmount, maxAmount }`. | ```bash Request theme={"system"} curl "https://api-antseed.mrdn.finance/v1/antseed/topups/quote?buyer=0xYourAddress&amount=5000000&network=optimism" ``` The source list below is abbreviated; the live response includes every enabled source that can accept the requested amount. ```json 200 theme={"system"} { "buyer": "0xYourAddress", "amount": "5000000", "minAmount": "1750000", "maxAmount": "10000000", "balance": "0", "creditLimit": "10000000", "headroom": "10000000", "minBuyerDeposit": "1000000", "requirements": { "scheme": "exact", "network": "optimism", "extra": { "destinationChainId": 8453, "…": "…" }, "…": "…" }, "sources": [ { "network": "base", "evmChainId": 8453, "minAmount": "1250000", "maxAmount": "10000000" }, { "network": "optimism", "evmChainId": 10, "minAmount": "1750000", "maxAmount": "10000000" }, { "network": "ink", "evmChainId": 57073, "minAmount": "1750000", "maxAmount": "10000000" } ] } ``` # Top-up Status Source: https://docs.mrdn.finance/api-reference/topup/status GET https://api-antseed.mrdn.finance/v1/{merchant}/topups/{id} Poll the state of a top-up Free, public status of a top-up. The `topupId` comes from the `POST` response. The row never exposes the payment signature. ## Path Merchant id. Currently `antseed`. Returns `404` for ids that belong to a different merchant. Top-up id (`topupId` from the create response). ## States ``` same-chain: settling ───────────→ settled → depositing → deposited cross-chain: settling → bridging → settled → depositing → deposited └→ refunding → refunded any → failed | needs_review terminal ``` Poll until the state is terminal: `deposited`, `refunded`, `failed`, or `needs_review`. A status request advances non-terminal top-ups. The gateway also resumes them in the background and after restarts. Use the `Retry-After` value from a pending `POST` (currently 5 seconds). A clean settlement failure appears as `failed`. It is terminal for status polling, but the same `X-PAYMENT` can be resubmitted while its authorization is still unused and unexpired. `bridge_expired`, `bridge_timeout`, or any `needs_review` state requires Meridian operations review. ```bash Request theme={"system"} curl "https://api-antseed.mrdn.finance/v1/antseed/topups/0xf6cd…d723" ``` ```json 200 theme={"system"} { "topupId": "0xf6cd…d723", "state": "deposited", "buyer": "0xYourAddress", "payer": "0xYourAddress", "network": "optimism", "sourceNetwork": "optimism", "grossAmount": "5000000", "netAmount": "4900000", "settlementTx": "0xeb80…e5a8", "fillTx": "0xa91c…44fe", "depositTx": "0x8186…1234", "refundTx": null, "error": null, "createdAt": 1783656257928, "updatedAt": 1783656258080 } ``` # Brandkit Source: https://docs.mrdn.finance/brandkit Meridian brand assets and guidelines ## Colors
#34D399
Mint Green
#171719
Deep Black
#1B1B1C
Main Black
#1B2824
Dark Emerald
#F0F0F0
Light Gray
## Typography [Funnel Display](https://fonts.google.com/specimen/Funnel+Display) from Google Fonts. 400 for logo.
Meridian
Larger font sizes prioritized for readability. ## Logos Meridian Token Logo Meridian Logo SVG # Bring Your Own Agent Source: https://docs.mrdn.finance/byoa The vision for integrating any EVM agent with Meridian's payment infrastructure ## Agents Un-chained AI agents today face dual constraints: they're chained to specific networks **and** locked into the platforms that created them. Agent-to-agent interactions are limited to others built on the same platform, creating isolated silos. **Meridian is actively building the infrastructure to remove these barriers.** ## The Current Problem Agents restricted to single blockchain networks, limiting their reach and functionality. Agents can only interact with others from the same creation platform, preventing cross-ecosystem collaboration. Complex payment integrations required for each network and platform combination. No standardized way for agents to communicate and transact across different infrastructures. ## Meridian's Agent-Ready Infrastructure ### Built for Agent Interoperability Meridian's payment infrastructure was designed from the ground up to solve agent ecosystem fragmentation: * **X402ProxyFacilitator Contract**: Proxy architecture that enables any agent to receive payments without direct wallet management * **Multichain Foundation**: Live on Base with multi-network deployment ready * **Organization Management**: API-driven system perfect for managing multiple agents under one umbrella * **Transparent Fee Structure**: Platform and treasury fees are accounted for at settlement time ### Current Payment Flow ``` Agent/User → x402 Authorization → Meridian Facilitator → Settlement ``` ## B.Y.O.A: The Next Layer ### Building on Proven Infrastructure Our payment architecture already handles the core challenge: enabling any address to receive payments through relayed facilitator settlement without requiring the recipient to submit every transaction themselves. The organization management system provides the framework for grouping multiple payment recipients under unified control. This maps naturally to agent ecosystems where multiple agents operate under shared management. Multichain foundation means agents won't be locked to single networks. The same payment flow that works on Base extends across any EVM chain. ## The Foundation is Ready ### Current Infrastructure Enables Agent Payments The payment verification system already handles authorization validation and settlement, including Permit2 proxy settlement on non-EIP-3009 chains such as MegaETH, BSC, and BOT chain. Organization management provides the access control framework that scales from single developers to multi-agent ecosystems. Facilitator-based settlement and fee accounting create the operational layer that agents need to collect payments reliably across supported networks. The fee structure provides sustainable funding for continued development and network effects. ## Meridian Scope **Meridian Scope:** * Payment authorization and settlement * Multichain transaction coordination * Economic validation through payment proofs * Fee management and treasury operations ## Understanding the Architecture ### How Current Infrastructure Becomes Agent Infrastructure See how proxy architecture and relayed settlement create the foundation for agent payments. Understand how multi-user organizations scale to multi-agent ecosystems. Explore the network architecture that prevents agent platform lock-in. # Analytics Source: https://docs.mrdn.finance/command-centre/analytics Understand payment performance and trends Analytics helps you understand payment performance across time, receivers, and products. Use it to spot trends and measure throughput at a glance. Command Centre analytics dashboard ## Metrics and calculations Below is every metric shown in Analytics and how it is calculated for the selected time range. ### Overview * **Total transactions**: Count of all transactions in the time range. * **Total volume**: Sum of transaction amounts in the time range. * **Average transaction value**: Average of transaction amounts in the time range. * **Success rate**: `(settledTransactions / totalTransactions) × 100`, where “settled” means `status = SETTLED`. * **Volume trend %**: Percentage change in total volume vs the previous period of the same length (previous period ends one day before the current start). ### Breakdowns * **By status**: Count and percentage of total transactions per status. `percentage = (statusCount / totalTransactions) × 100`. * **By network**: Count, volume, and percentage of total transactions per network. `percentage = (networkCount / totalTransactions) × 100`. * **Daily**: Count and volume aggregated per day, with missing days shown as 0. * **Hourly distribution**: Count and volume aggregated per hour of day. * **Weekly trends**: Count and volume aggregated per week. ## What you can analyze * **Volume and totals** by time range. * **Top receivers** by activity. * **Success vs refund rates** for operational health. # Balances Source: https://docs.mrdn.finance/command-centre/balances View merchant balances and payout readiness Balances shows merchant-level and receiver-level balances derived from settled payments. These balances represent **available funds in Meridian's receiver ledger**, not live on-chain wallet balances. Use this view to understand what is ready for withdrawal, what is pending, and how balances roll up across receiver wallets. Command Centre balances view ## How balances are calculated All balances are pulled directly from on-chain reads and contract events. ### Merchant revenue (by network) * **Source**: `InstantSettlement` events from the Meridian contract. * **Calculation**: Sum of `netAmount` for all `InstantSettlement` events where `recipient = your wallet`, per network. * **Networks**: Base, Optimism, Arbitrum, Polygon, Unichain, Ink, MegaETH, HyperEVM, BNB Smart Chain (BSC), Monad. ### Ready-to-withdraw balance (protocol balance) * **Source**: `balanceReadyToWithdrawal(recipient, usdcAddress)` on the Meridian contract. * **Calculation**: Direct contract read returning the amount available to withdraw for the selected recipient and token. ### Wallet balance (USDC on each network) * **Source**: ERC-20 `balanceOf(wallet)` for the network’s USDC address. * **Calculation**: Direct token balance read on each supported network. ### MRDN balances * **Current MRDN balance**: ERC-20 `balanceOf(wallet)` on Base mainnet. * **Total MRDN earned**: Sum of `mrdnAmount` from `CashbackPaid` events where `recipient = your wallet` (Base mainnet). ### Total revenue (top-line) * **Calculation**: Sum of merchant revenue across **mainnet** networks, plus current MRDN balance. Testnet balances are excluded. ## What you can monitor * **Available balance**: Funds from settled payments that can be withdrawn. * **Pending balance**: Payments not yet settled. * **Receiver wallet totals**: Balance breakdown by receiver. * **Merchant rollups**: Aggregate totals across all receivers in the org. # Marketplace Source: https://docs.mrdn.finance/command-centre/marketplace Configure marketplace fees and route payouts with creditedRecipient Marketplace mode lets your organization collect fees on payments routed through Meridian while sending the remaining payout to a separate recipient wallet. Command Centre settings page showing Marketplace toggle, Marketplace Fee input, and Save Changes button ## Enable marketplace mode Sign in to Meridian Command Centre and open **Settings** under **Financial Services**. Toggle **Marketplace** on. Enter your fee in basis points. Example: `500` bps = `5%`. Click **Save Changes** to apply the setting to your organization. Older internal messages may call this "Platform" mode. In Command Centre the setting is labeled **Marketplace**. ## Update your x402 payment requirements Keep `payTo` pointed at the Meridian facilitator address for the target network. Use `extra.creditedRecipient` to tell Meridian which wallet should receive the net payout. Below is a `paymentRequirements` example for the recommended Permit2-based flow on a chain without native EIP-3009 support. The example uses MegaETH; use the same pattern on BSC and BOT chain with that network's token and chain id. ```ts theme={"system"} const paymentRequirements = { amount: value.toString(), recipient: address, network: "megaeth", scheme: "exact", maxAmountRequired: value.toString(), resource: `${FACILITATOR_URL}/v1/samples`, description: `Payment for ${formatUnits(value, TOKEN_DECIMALS)} ${TOKEN_SYMBOL}`, mimeType: "application/json", payTo: MEGAETH_FACILITATOR_ADDRESS, asset: MEGAETH_TOKEN_ADDRESS, extra: { creditedRecipient: recipientAddress, name: TOKEN_SYMBOL, version: "1", destinationChainId: MEGAETH_CHAIN_ID, }, }; ``` On chains without EIP-3009 support, use the ERC-20 token address in `asset`, approve Permit2 `0x000000000022D473030F116dDEE9F6B43aC78BA3`, and sign with `x402ExactPermit2Proxy` `0x402085c248EeA27D92E8b30b2C58ed07f9E20001`. See [Payment Types](/api-reference/payment-types/overview). ## How payouts work * `payTo` remains the Meridian facilitator, not the seller wallet. * On Permit2 networks such as MegaETH, BSC, and BOT chain, `asset` should be the ERC-20 token address for the current Permit2 flow. * On Permit2 networks, `extra.name` and `extra.version` should describe the token used in the Permit2 flow. * `extra.creditedRecipient` receives the payment amount remaining after fees. * Your marketplace fee accrues inside the facilitator contract as a platform balance for your organization. * Fees do **not** automatically land in your wallet. Withdraw them from the Settings page using **Withdraw Marketplace Fees**, or withdraw them directly on-chain from the proxy contract. If your marketplace fee is `500` bps, Meridian records `5%` of each payment as marketplace fees. In the simple case where no other fee applies, the remaining `95%` is settled to `creditedRecipient`. Marketplace fees are tracked on-chain inside the facilitator and can later be withdrawn to your connected wallet from Command Centre. ## Withdraw marketplace fees on-chain You can also withdraw accumulated marketplace fees directly from the Meridian proxy contract by calling: ```solidity theme={"system"} withdrawPlatformFees(address token) ``` Call this function on the network's Meridian proxy/facilitator address from the same wallet address that is configured as the marketplace platform address. The platform address is inferred from `msg.sender`, so it is **not** passed as a function argument. ```ts theme={"system"} await facilitator.write.withdrawPlatformFees([tokenAddress]); ``` Use the token address for the asset in which fees accrued on that network, for example USDC on BSC or USDm on MegaETH. ## Related guides * [Smart Contracts](/payments/smart-contracts) * [Balances](/command-centre/balances) # Refunds Source: https://docs.mrdn.finance/command-centre/refunds Permanent receipt generation for transaction refunds ## Overview The refund service generates permanent, cryptographically-verifiable receipts when transactions are refunded. Merchants pay for receipt storage using x402. ## Refund Process When a merchant refunds a transaction: 1. Refund executed on-chain 2. Receipt generated with transaction details 3. Merchant pays for permanent IPFS storage 4. Receipt hash logged to Meridian database 5. Receipt retrievable by transaction ID or IPFS hash ## Receipt Contents * Original transaction hash * Refund transaction hash * Amount refunded * Currency and network * Merchant wallet address * Customer wallet address * Timestamp * Block explorer link Refund receipt details ### Example Receipt ```json theme={"system"} { "transactionId": "pmt_abc123xyz", "originalTxHash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", "refundTxHash": "0xfedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321", "amount": "10.50", "currency": "USDC", "network": "base", "refundedTo": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e", "refundedBy": "0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199", "refundedAt": "2026-01-22T10:30:00.000Z", "explorerUrl": "https://basescan.org/tx/0xfedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321", "type": "meridian_refund_receipt", "version": "1.0" } ``` ## Merchant-Paid Storage Receipts use x402 protocol via [Pinata](https://docs.pinata.cloud/api-reference/endpoint/x402/pin) on Base mainnet. Merchants pay \~\$0.001 to permanently store refund proof. **Why merchant-paid:** Ensures receipts remain accessible indefinitely without platform dependency. Customers receive permanent proof of refund. **Pricing:** `$0.10/GB/month × 12 months = $1.20/GB total` **Network:** Base (x402 protocol requirement) ## Networks Refunds can be processed on all Meridian-supported networks: **Mainnet:** Base, Optimism, Arbitrum, Polygon, Unichain, Ink, BNB Smart Chain (BSC), Monad **Testnet:** Base Sepolia, Optimism Sepolia Merchants need USDC on Base to pay for receipt storage via Pinata x402, regardless of which network the original transaction occurred on. # Transactions Source: https://docs.mrdn.finance/command-centre/transactions Inspect payment events, status, and metadata The Transactions view is the source of truth for payment activity across your organization. Use it to trace a payment from authorization through verification, settlement, or refund, and to inspect the metadata you need for support and reconciliation. Command Centre transactions view ## Key fields * **Lifecycle status**: Authorized, verified, settled, or refunded. * **Identifiers**: Payment ID and associated receiver wallet. * **Amounts and timing**: Amount, currency, and timestamps. * **Settlement + refund links**: Jump to related settlement or refund records. ## Filters * **Status** to isolate in-flight or completed activity. * **Time range** for daily, weekly, or custom audits. * **Receiver wallet** to review individual balances. # API Keys Source: https://docs.mrdn.finance/essentials/api-keys Create and manage API keys for Meridian x402 payment APIs ## Overview API keys are required to authenticate Meridian x402 payment API requests. They provide secure, programmatic access to Meridian's payment infrastructure for your applications. ## Key Types Meridian Protocol uses **public keys** for API authentication: * **Public Key (pk\_)**: Used for API authentication * **Test Mode Keys**: Prefixed with `pk_test_` for development * **Production Keys**: Prefixed with `pk_` for live transactions Secret keys are generated but only shown once during creation for security. The public key is used for all API requests. ## Creating API Keys ### Via Developer Dashboard Connect your wallet and authenticate using SIWE Go to the API Keys section in your developer dashboard Click "Create API Key" and provide a descriptive name Select test mode for development or production for live transactions Copy and securely store both keys - the secret is only shown once! ### Via API You can also create API keys programmatically: ```bash theme={"system"} curl -X POST https://api.mrdn.finance/v1/api_keys \ -H "Content-Type: application/json" \ -H "Cookie: siwe-session=your_session" \ -d '{ "name": "My Application Key", "test_net": true }' ``` **Response:** ```json theme={"system"} { "id": "key_1234567890abcdef", "name": "My Application Key", "organization": "org_456", "api_key": "pk_test_1234567890abcdef...", "api_secret_key": "sk_test_abcdef1234567890...", "test_net": true, "created_at": "2024-01-01T00:00:00.000Z", "updated_at": "2024-01-01T00:00:00.000Z" } ``` The `api_secret_key` is only returned once during creation. Store it securely! ## Using API Keys ### Authentication Header Include your public key in the Authorization header: `POST /v1/verify` is deprecated. Use `POST /v1/settle` for current API examples and new integrations. ```bash theme={"system"} curl -X POST https://api.mrdn.finance/v1/settle \ -H "Authorization: Bearer pk_1234567890abcdef..." \ -H "Content-Type: application/json" \ -d '{ "paymentPayload": {...}, "paymentRequirements": {...} }' ``` ### JavaScript/Node.js ```javascript theme={"system"} const apiKey = "pk_test_1234567890abcdef..."; const response = await fetch("https://api.mrdn.finance/v1/settle", { method: "POST", headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json", }, body: JSON.stringify({ paymentPayload: { /* ... */ }, paymentRequirements: { /* ... */ }, }), }); ``` ### Python ```python theme={"system"} import requests api_key = 'pk_test_1234567890abcdef...' headers = { 'Authorization': f'Bearer {api_key}', 'Content-Type': 'application/json' } response = requests.post( 'https://api.mrdn.finance/v1/settle', headers=headers, json={ 'paymentPayload': { /* ... */ }, 'paymentRequirements': { /* ... */ } } ) ``` ## Managing API Keys ### List All Keys ```bash theme={"system"} curl https://api.mrdn.finance/v1/api_keys \ -H "Cookie: siwe-session=your_session" ``` **Response:** ```json theme={"system"} { "apiKeys": [ { "id": "key_1234567890abcdef", "name": "My Application Key", "organization": "org_456", "api_key": "pk_test_1234567890abcdef...", "test_net": true, "created_at": "2024-01-01T00:00:00.000Z", "updated_at": "2024-01-01T00:00:00.000Z", "last_used_at": "2024-01-01T12:00:00.000Z" } ] } ``` ### Get Single Key ```bash theme={"system"} curl https://api.mrdn.finance/v1/api_keys/key_1234567890abcdef \ -H "Cookie: siwe-session=your_session" ``` ## Test vs Production Keys ### Test Mode Keys (`pk_test_`) * Used for development and testing * Process payments on testnets (Base Sepolia, etc.) * No real money involved * Separate analytics and transaction history ### Production Keys (`pk_`) * Used for live applications * Process real payments on mainnets * Handle actual value transfers * Production-level monitoring and support Always use test keys during development. Switch to production keys only when your integration is complete and tested. ## Security Best Practices ### Key Storage * **Never** commit API keys to version control * Use environment variables or secure key management services * Rotate keys regularly * Use different keys for different environments ### Access Control * API keys are scoped to your organization * Each key tracks usage and last access time * Keys can be individually managed and revoked ### Environment Variables ```bash theme={"system"} # .env file MERIDIAN_API_KEY=pk_test_1234567890abcdef... # In your application const apiKey = process.env.MERIDIAN_API_KEY ``` ## API Key Endpoints `POST /v1/api_keys` - Create a new API key pair `GET /v1/api_keys` - Get all keys for your organization `GET /v1/api_keys/:id` - Get details for a specific key `DELETE /v1/api_keys/:id` - Delete an API key ## Troubleshooting ### Common Errors **401 Unauthorized** * Check that your API key is correct * Ensure you're using the right environment (test vs production) * Verify the Authorization header format: `Bearer pk_...` **403 Forbidden** * API key may be revoked or expired * Check if you're accessing the correct organization's resources ### Testing Your API Key ```bash theme={"system"} # Test authentication curl https://api.mrdn.finance/v1/supported \ -H "Authorization: Bearer pk_test_your_key_here" # Should return supported payment kinds { "kinds": [ { "x402Version": 1, "scheme": "exact", "network": "base-sepolia" } ] } ``` ## Next Steps Start settling x402 payments with your API key Monitor your payment transactions Access detailed payment analytics Manage keys in the web interface # Overview Source: https://docs.mrdn.finance/index Build onchain applications with x402 payments and facilitator-based settlement Meridian is a **layer built on top of Coinbase's infrastructure** that introduces **proxy facilitator architecture** for x402 payments. Unlike direct wallet-to-wallet transfers, Meridian routes payments through a facilitator that settles same-chain transfers immediately, tracks fees onchain, and supports cross-chain execution where needed. ### Key Innovation: Proxy Facilitator Architecture **Traditional x402 (Coinbase):** Direct transfers from payer to recipient wallet ``` Payer Wallet → Recipient Wallet (immediate transfer) ``` **Meridian's Architecture:** Proxy facilitator settlement ``` Payer Wallet → Meridian Facilitator → Settlement / Bridge → Recipient ``` This enables: * **Organization-scoped payment management** with multi-user access * **Same-chain instant settlement** through the facilitator * **Onchain fee accounting** for platform and treasury balances * **Cross-chain routing** through Across when required * **Enhanced security** with organization-scoped access control ## How x402 Works with Meridian Meridian x402 Payment Flow Sequence Diagram The sequence diagram above shows how Meridian implements the x402 payment protocol with facilitator-based settlement for secure, authenticated transactions. ## Build onchain apps Use Meridian's backend and frontend suites of crypto payment services. Accept your first x402 payment in minutes. Complete API documentation for all endpoints. ## Key Features Everything you need to build secure payment applications with facilitator-based settlement and multichain execution. Proxy-mediated payment settlement with onchain accounting and relayed execution. Platform and treasury fee accounting handled inside the facilitator. Multi-user organizations with shared payment infrastructure. Organization-scoped access control with API key management. ## Resources Explore the codebase and contribute to Meridian's development. # Mpay Source: https://docs.mrdn.finance/mpay/overview Gasless multi-token transfers with x402 and ERC-20 Permit flows Mpay is Meridian's gasless multi-token transfer product. It lets users send supported tokens without paying gas directly, while keeping the signing flow simple and human readable. Mpay transfer interface ## How Mpay works Mpay uses one of two signing standards depending on the token being transferred: ### 1. x402 using EIP-3009 For tokens that support EIP-3009, Mpay uses an x402-style flow where the user, or an agent acting on the user's behalf, signs an off-chain payment authorization. The transfer is then executed from that signed message without requiring the user to submit an onchain transaction themselves. This path is efficient, but support is still limited across the ERC-20 ecosystem. In practice, the main supported token today is USDC. ### 2. ERC20Permit using EIP-2612 For other supported tokens, Mpay uses ERC20Permit under EIP-2612. In this flow, the user signs a permit message that grants approval to the Meridian protocol contract, and the contract executes the token transfer. The standard is different, but the user experience is the same: sign once, then the tokens move. ## Shared security guarantees Both flows rely on the same core protections: * **EIP-712 typed data** for human-readable signing prompts. * **Nonces** to prevent replay attacks. * **Expiry windows** so signatures are only valid for a limited time. * **No private key exposure**, since signing happens client-side and keys never leave the wallet. ## Supported destinations Mpay supports gasless transfers to: * **ENS names** * **Wallet addresses** ## Supported chains Mpay is available on Meridian-supported deployments. ### Mainnet | Network ID | Chain | | ------------ | --------------- | | `avalanche` | Avalanche | | `base` | Base | | `bsc` | BNB Smart Chain | | `bot-chain` | BOT Chain | | `optimism` | Optimism | | `arbitrum` | Arbitrum One | | `polygon` | Polygon | | `unichain` | Unichain | | `ink` | Ink | | `worldchain` | World Chain | | `sei` | Sei | | `hyperevm` | HyperEVM | | `megaeth` | MegaETH | | `tempo` | Tempo | | `monad` | Monad | | `robinhood` | Robinhood Chain | ### Testnet | Network ID | Chain | | ------------------- | ----------------- | | `arc-testnet` | Arc Testnet | | `bot-chain-testnet` | BOT Chain Testnet | | `base-sepolia` | Base Sepolia | | `fluent-testnet` | Fluent Testnet | | `optimism-sepolia` | Optimism Sepolia | # Smart Contracts Source: https://docs.mrdn.finance/payments/smart-contracts x402 payment protocol and Meridian facilitator architecture ## Overview Meridian extends x402 with a **proxy facilitator architecture**. Standard x402 transfers funds directly from payer to recipient; Meridian routes settlement through a facilitator contract instead: ``` Standard x402: Payer signs authorization → Direct transfer to recipient wallet Meridian: Payer signs authorization → Facilitator receives funds → Facilitator settles recipient and fees ``` This one change is what enables fee handling, batch settlement, cross-chain routing, cashback, and organization-scoped payment management on top of the plain x402 flow. On chains with native EIP-3009 support, Meridian settles standard x402 exact payloads through the facilitator; on chains without an EIP-3009-compatible token (MegaETH, BSC, BOT chain), Meridian settles via Permit2 through `x402ExactPermit2Proxy`. The `upto` scheme (usage-based pricing) settles via Permit2 through `x402UptoPermit2Proxy` on every supported chain. See [Payment Types](/api-reference/payment-types/overview) for how the facilitator routes each payload type. ## X402ProxyFacilitator.sol (V6) ```solidity theme={"system"} 0x8E7769D440b3460b92159Dd9C6D17302b036e2d6 ``` The facilitator is a UUPS-upgradeable contract, currently at version 6. * **Payment Methods**: EIP-3009 `transferWithAuthorization` (USDC), ERC-2612 `transferWithPermit`, and Permit2 `transferWithPermit2` / `transferWithPermit2Upto` * **Instant Settlement**: Same-chain payments are settled to the recipient during payment execution * **Batch Settlement**: One signed authorization can settle multiple payments in a single transaction (`batchTransferWithAuthorization` / `batchTransferWithPermit2`), each item with its own recipient, platform, and fee * **Fee Handling**: Platform and treasury fees accrue inside the facilitator and are withdrawn via `withdrawPlatformFees` / `withdrawTreasuryFees` * **MRDN Cashback**: On Base, USDC payments earn MRDN cashback priced via Aerodrome TWAP — the rate starts at 2% and decays as total cashback is distributed, capped at \$5 per transaction * **Cross-Chain Support**: The same entrypoint initiates cross-chain settlement through Across on supported networks, authenticated by a backend-signed EIP-712 message ## x402ExactPermit2Proxy.sol (Non-EIP-3009 Chains) Integrations on chains without EIP-3009-compatible tokens (MegaETH, BSC, BOT chain) use Permit2 settlement through the Meridian facilitator: ```solidity theme={"system"} Facilitator: 0x8E7769D440b3460b92159Dd9C6D17302b036e2d6 x402ExactPermit2Proxy: 0x402085c248EeA27D92E8b30b2C58ed07f9E20001 x402UptoPermit2Proxy: 0x402015c795ecb48A360bDC6e35a2EaEb313a0002 Permit2: 0x000000000022D473030F116dDEE9F6B43aC78BA3 ``` * **Flow**: Users approve Permit2 once, then sign a Permit2 witness authorization off-chain for each payment * **Token model**: `paymentRequirements.asset` is the ERC-20 token address being paid * **Facilitator binding**: `payTo` and `witness.to` stay bound to the Meridian facilitator * **Settlement**: Meridian settles through `transferWithPermit2`, not through the buyer submitting a transaction ### x402UptoPermit2Proxy (upto scheme) The `upto` scheme uses the same Permit2 flow with `x402UptoPermit2Proxy` as the signed spender (same address on every chain). The buyer authorizes a maximum amount; Meridian settles the actual usage amount through `transferWithPermit2Upto`. Differences from the exact proxy: * **Witness shape**: `{ to, facilitator, validAfter }` — the extra `facilitator` field binds which contract may choose the settled amount; both `to` and `facilitator` stay bound to the Meridian facilitator * **Settled amount**: `0 < settleAmount ≤ permit.permitted.amount`, passed by the seller at settle time (`paymentRequirements.extra.settleAmount`); fees and cashback apply to the settled amount only ```mermaid theme={"system"} sequenceDiagram participant User participant Token as ERC20 Token participant Permit2 participant Facilitator participant ExactProxy as x402ExactPermit2Proxy participant Recipient Note over User: One-time setup on Permit2 chains User->>Token: approve(Permit2, amount or max) Token-->>User: Approval recorded Note over User,Facilitator: Every payment User->>User: Signs Permit2 witness authorization User->>Facilitator: Sends signed payment payload Facilitator->>ExactProxy: transferWithPermit2(...) ExactProxy->>Permit2: settle signed transfer Permit2->>Token: transferFrom(user, facilitator, amount) Token-->>Facilitator: Tokens moved Facilitator->>Recipient: Settle recipient amount and fees Facilitator-->>User: Payment complete ``` ## X402PaymentRouter.sol (Pay with Any Token) The payment router lets buyers fund an exact USDC payment with another ERC-20 token. The buyer signs one Permit2 witness over the input token; the router pulls that token, swaps `tokenIn → WETH → USDC` (or `WETH → USDC`), refunds unused input, and pushes the exact USDC output into Facilitator V6\_1. USDC never passes through the buyer's wallet and no USDC signature is required. The router is scoped to **Base** and **Robinhood Chain** and uses CREATE2 with the same address on both. Its new V6\_1 address will be published after deployment. Use `previewAmountIn(tokenIn, usdcAmount)` to quote the input requirement before signing. The signed witness fixes the router, facilitator, exact USDC amount, recipient, platform fee, destination chain, payment ID, and start time. Anyone may submit the signed payment, but none of those terms can be changed. Permit2's nonce prevents replay. * On **Base**, V6\_1 distributes the USDC locally to the payment recipient after fees. * On **Robinhood Chain**, V6\_1 deposits the USDC into Across and forwards it to the recipient on Base. * The intended first integration is the top-up gateway: its Base relayer receives the net USDC and deposits it into the selected inference provider. The input token must have approved canonical Permit2 once. Input tokens with transfer taxes are unsupported because they break exact swap and refund accounting. Native ETH is not accepted; wrap it to WETH first. ## Solana (SVM) On Solana, Meridian settles through the `meridian_x402` program instead of a Solidity facilitator. Solana needs no EIP-3009: a transaction can carry multiple signers, so the payer signs the settlement transaction as their USDC token authority and the facilitator co-signs as fee payer and submits it. Settlement is atomic — the program splits fees and the recipient's net directly from the payer's token account in one transaction, with no escrow hop. The program is live on both `solana` (mainnet) and `solana-devnet`. See [Solana Program](/payments/solana-program) for the architecture, accounts, and fee math, and [Solana payments](/ai-skills/solana) for an end-to-end integration guide. ## How a Payment Flows 1. **Authorization Signing**: Payer signs a payment authorization with amount, recipient context, and network details. 2. **Verification**: Server verifies signature and authorization parameters. 3. **Transfer Execution**: Meridian executes settlement through the facilitator — `transferWithAuthorization` for native EIP-3009 tokens, `transferWithPermit2` via the proxy elsewhere. 4. **Fee Application**: Recipient and fee amounts are handled during settlement processing. Payments are linked to your organization: API keys authenticate verification and settlement, the recipient wallet is configured in organization settings, and every payment lands in your organization's transaction history in [Command Centre](/command-centre/transactions). # Solana Program Source: https://docs.mrdn.finance/payments/solana-program Meridian x402 payment facilitator on Solana (SVM) ## Overview `meridian_x402` is the Solana (SVM) port of the Meridian facilitator. It is the equivalent of [`X402ProxyFacilitator.transferWithAuthorization`](/payments/smart-contracts) on EVM: it pulls USDC from a payer, subtracts platform and treasury fees, and settles the remainder to the recipient — atomically, in a single transaction. This first version is intentionally minimal. It settles same-chain USDC payments on Solana and nothing else: no cross-chain (Across) routing, no MRDN cashback, and no batched settlement (those live only on the EVM facilitator today). Integrations should read the program ID, facilitator pubkey, and on-chain config from `GET /v1/solana/facilitator` at runtime rather than hardcoding them, so they keep working across any redeploys. ## Deployment | Network | Cluster | Program ID | Status | | --------------- | ------------ | --------------------------------------------- | ------ | | `solana` | mainnet-beta | `Ro6hz1smrm5zDh73849eDqKna9dE1EkPsWekAB5rBWm` | Live | | `solana-devnet` | devnet | `Ro6hz1smrm5zDh73849eDqKna9dE1EkPsWekAB5rBWm` | Live | Built with **Anchor 0.32.1**. The config account is a PDA at seeds `["config"]`, derived from the program ID. USDC mints the program settles against: | Network | USDC Mint | | --------------- | ---------------------------------------------- | | `solana` | `EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v` | | `solana-devnet` | `4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU` | ## Why it looks different from the EVM facilitator On EVM, the payer cannot join the settlement transaction without paying gas, so x402 relies on EIP-3009: the payer signs an off-chain authorization, and the facilitator submits it on-chain where the token contract verifies the signature. Solana needs none of that machinery: 1. **A transaction can have multiple signers, and any of them can pay the fee.** The payer (`from`) signs the settlement transaction as the authority of their USDC token account. The backend signer co-signs as `payer` (fee payer) and submits it. The payer's transaction signature *is* the authorization — no in-program signature verification required. 2. **Settlement is atomic within one transaction**, so there is no escrow hop. The EVM contract first pulls the full amount into itself and then distributes; here the program splits funds directly from the payer's token account via up to three `transfer_checked` CPIs (platform fee, treasury fee, recipient net). From the client's perspective the x402 flow is unchanged: ```mermaid theme={"system"} sequenceDiagram participant User as User (payer) participant Seller participant Facilitator as Meridian Facilitator participant Program as meridian_x402 Seller->>User: HTTP 402 + payment requirements User->>Facilitator: GET /v1/solana/facilitator
(fee payer, program id, mint, treasury, fees, paused) User->>User: Build transfer_with_authorization tx, sign as token authority User->>Seller: X-PAYMENT: base64(paymentPayload { transaction }) Seller->>Facilitator: POST /v1/settle (paymentPayload + requirements) Facilitator->>Facilitator: Validate the signed transaction Facilitator->>Program: Co-sign as fee payer, submit Program->>Program: Enforce window, split fees, settle net atomically Program-->>Facilitator: InstantSettlement event Facilitator-->>Seller: { success, transaction: signature } ``` What is kept for parity with the EVM `transferWithAuthorization`: * `valid_after` / `valid_before` settlement window, checked against the on-chain clock * a 32-byte `nonce` for x402 payment correlation and idempotency * identical fee economics (platform fee off gross, treasury fee off the remainder) ## Architecture ### Config account A single global PDA (`seeds = ["config"]`) holds the program's configuration: | Field | Type | Purpose | | ------------------ | -------- | ----------------------------------------------------- | | `authority` | `Pubkey` | Admin: fee updates, pause, treasury / signer rotation | | `backend_signer` | `Pubkey` | Backend signer allowed to submit settlements | | `treasury` | `Pubkey` | Wallet that owns the treasury-fee token account | | `usdc_mint` | `Pubkey` | The only mint the program settles | | `treasury_fee_bps` | `u16` | Protocol fee in basis points (max `1000` = 10%) | | `paused` | `bool` | Circuit breaker for the settlement entrypoint | | `bump` | `u8` | Config PDA bump | ### Instructions | Instruction | Access | Purpose | | ---------------------------------------------------------------------------------------- | --------------------------- | -------------------------- | | `initialize(backend_signer, treasury, treasury_fee_bps)` | one-time | Create the `Config` PDA | | `transfer_with_authorization(value, valid_after, valid_before, nonce, platform_fee_bps)` | payer + backend signer sign | Settle a payment | | `set_treasury_fee_bps(bps)` | authority | Update the protocol fee | | `set_treasury(pubkey)` | authority | Rotate the treasury wallet | | `set_backend_signer(pubkey)` | authority | Rotate the backend signer | | `set_paused(bool)` | authority | Pause / unpause settlement | `transfer_with_authorization` account list: | Account | Signer | Notes | | ----------------- | ------ | ---------------------------------------------------------- | | `payer` | ✓ | Backend signer (facilitator): pays the tx fee | | `from` | ✓ | The paying user (USDC token authority) | | `config` | | Config PDA | | `usdc_mint` | | Must match `config.usdc_mint` | | `from_token` | | Payer's USDC associated token account (authority = `from`) | | `recipient_token` | | Recipient's USDC associated token account | | `platform_token` | | *Optional* — omit for payments without a platform fee | | `treasury_token` | | USDC token account owned by `config.treasury` | | `token_program` | | SPL Token / Token-2022 program | The settlement transaction does **not** create any token accounts. The payer, recipient, platform (if used), and treasury USDC associated token accounts must already exist, or settlement fails. The backend signer pays only the transaction fee, never rent. ### Fee math Identical to the EVM facilitator's `_settleAndDistribute`: ``` platform_fee = value * platform_fee_bps / 10_000 (0 if no platform account) remainder = value - platform_fee treasury_fee = remainder * treasury_fee_bps / 10_000 (skipped if platform == treasury) net_amount = remainder - treasury_fee (→ recipient) ``` Both fees are capped at `MAX_FEE_BPS = 1000` (10%). Multiplication is widened to `u128` so it cannot overflow. Each settlement emits an `InstantSettlement` event with the full breakdown: ```rust theme={"system"} InstantSettlement { from, // payer wallet recipient, // recipient token account platform, // Option nonce, // [u8; 32] — x402 payment correlation id gross_amount, // value platform_fee, treasury_fee, net_amount, // credited to the recipient } ``` ### Replay protection & the nonce Replay protection is **transaction-level**, matching the reference x402 SVM scheme. The paying user signs the settlement transaction itself, which embeds a recent blockhash: the runtime rejects duplicate signatures, and the transaction expires with the blockhash. Settling the same payment again would require a fresh signature from the payer. No nonce account is created on-chain, so no rent is locked per payment. The 32-byte `nonce` argument is carried into the `InstantSettlement` event for x402 payment correlation, and the facilitator additionally refuses to co-sign a nonce it has already settled (a settlement cache in the payments table). ### Errors | Error | Meaning | | -------------------------- | ---------------------------------------------------- | | `Paused` | The settlement entrypoint is paused | | `Unauthorized` | Caller is not the config authority / backend signer | | `FeeTooHigh` | A fee exceeds `MAX_FEE_BPS` (10%) | | `InvalidValue` | `value` (or net after fees) is not greater than zero | | `AuthorizationNotYetValid` | `now <= valid_after` | | `AuthorizationExpired` | `now >= valid_before` | | `InvalidMint` | Mint does not match `config.usdc_mint` | (Treasury-token ownership is enforced by an Anchor account constraint, which surfaces as a generic constraint error rather than a program-specific code.) ## How settlement is validated The client builds and payer-signs the settlement transaction; the facilitator's `POST /v1/settle` acts as the co-signing safety boundary. Before it adds the backend signer's signature and submits, it checks that the transaction does exactly one thing the backend signer is willing to pay for: * the fee payer is the backend signer, and the transaction contains **exactly one** `transfer_with_authorization` instruction against the Meridian program * `payer` (account 0) is the backend signer; `from` (account 1) is a different signer * `config` is the expected Config PDA and the token program is the SPL Token program * the mint equals `paymentRequirements.asset`, and `value` equals `maxAmountRequired` * the validity window is still open (`valid_after <= now`, `valid_before >= now + 6s`) * `from_token`, `recipient_token`, and any `platform_token` are the correct associated token accounts for the resolved recipient / platform, and `platform_fee_bps` matches the organization's configured fee * the payer's wallet signature is present and valid, and the backend signer has not already signed Because the program only moves USDC with the payer's signature and never creates accounts, a structurally valid transaction means the backend signer risks only the transaction fee. The recipient (and platform, in platform mode) is resolved from the caller's Meridian **organization settings**, not blindly trusted from the request. ## Deliberately omitted (vs. EVM V6) Settlement is instant to every party, so there are no fee-accrual balances or withdraw functions. Also out of scope for this version, all of which remain EVM-only for now: * MRDN cashback * Cross-chain (Across) settlement * Batch payments ## Building & running locally The program lives in [`contracts-sol/`](https://github.com/meridian-protocol/meridian/tree/main/contracts-sol) in the Meridian monorepo. Requires the Solana CLI (Agave) and Anchor 0.32.1 (`avm use 0.32.1`). ```bash theme={"system"} cd contracts-sol anchor build # compile + generate the IDL (target/idl/meridian_x402.json) anchor test # spin up a local validator and run the test suite ``` The test suite covers the exact fee splits (with and without a platform fee), nonce replay rejection, the validity window, the fee cap, a missing payer signature, the treasury-as-platform skip, pause / unpause, and admin access control. See `contracts-sol/README.md` and `contracts-sol/SETUP.md` for deploying to devnet and running the facilitator + pay app against it. ## References * [Solana payments (AI skill)](/ai-skills/solana) — end-to-end integration guide * [Smart Contracts](/payments/smart-contracts) — the EVM facilitator * [Supported Networks](/api-reference/supported-networks) * [Payment Types](/api-reference/payment-types/overview) * [Settle x402 Payment](/api-reference/endpoint/settle-payment) # Quickstart Source: https://docs.mrdn.finance/quickstart Accept your first x402 payment in minutes This guide takes you from zero to accepting USDC payments through Meridian on any supported chain. The only dependency is `viem` — your server returns an x402 challenge, the buyer signs a payment authorization off-chain, and your server settles it through Meridian's API. No transaction is submitted by the buyer and no gas is paid by either side. ```text theme={"system"} 1. Your server returns 402 + paymentRequirements 2. Buyer signs an EIP-3009 authorization (off-chain, gasless) 3. Buyer sends the signed payload to your server 4. Your server settles via Meridian POST /v1/settle 5. Meridian's facilitator executes the transfer on-chain ``` The code below is identical on every chain whose payment token supports EIP-3009 (Base, Optimism, Arbitrum, Polygon, Ink, and more) — only the network constants change. For chains without an EIP-3009 token (MegaETH, BSC, BOT chain, Tempo), the buyer signs a Permit2 witness instead; see the [non-EIP-3009 guide](/ai-skills/non-eip-3009). Sign in with your wallet at [mrdn.finance/dev/api-keys](https://mrdn.finance/dev/api-keys) and create a key. Use a `pk_test_` key for development and a `pk_` key for production. Store it server-side: ```bash theme={"system"} MERIDIAN_API_KEY=pk_test_... ``` See [API Keys](/essentials/api-keys) for details. In your organization settings, configure the wallet that receives settled funds. The recipient is tied to your API key — in the payment requirements, `payTo` always stays pointed at the Meridian facilitator contract, never at your own wallet. Take the payment token and facilitator address for your chain from [Supported Networks](/api-reference/supported-networks) and keep them in one config object — everything below reads from it: ```typescript theme={"system"} // Example: Base mainnet. Swap in any supported network's values. const NETWORK = { id: "base", chainId: 8453, token: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // USDC tokenName: "USD Coin", // EIP-712 domain name of the token tokenVersion: "2", // EIP-712 domain version of the token facilitator: "0x8E7769D440b3460b92159Dd9C6D17302b036e2d6", }; ``` For development, use a testnet (`pk_test_` key), e.g. Base Sepolia: `chainId: 84532`, token `0x036CbD53842c5426634e7929541eC2318f3dCF7e` (`tokenName: "USDC"`), facilitator `0x8e633dBf31adCc7D41BE3e95B7c8DD3526B5235A`. Test USDC is available from [Circle's faucet](https://faucet.circle.com). A minimal Base Sepolia test should use: * `MERIDIAN_API_KEY=pk_test_...` * a buyer wallet connected to Base Sepolia (`84532`) * Base Sepolia USDC from Circle's faucet * `paymentRequirements.network: "base-sepolia"` * `paymentRequirements.payTo: "0x8e633dBf31adCc7D41BE3e95B7c8DD3526B5235A"` When an unpaid request hits your paid endpoint, respond with HTTP 402 and the payment requirements: ```typescript theme={"system"} const paymentRequirements = { scheme: "exact", network: NETWORK.id, asset: NETWORK.token, payTo: NETWORK.facilitator, maxAmountRequired: "10000", // $0.01 in USDC base units (6 decimals) resource: "https://your-api.example.com/api/premium", description: "Access to premium content", mimeType: "application/json", maxTimeoutSeconds: 300, extra: { name: NETWORK.tokenName, version: NETWORK.tokenVersion }, }; return Response.json( { x402Version: 1, accepts: [paymentRequirements] }, { status: 402 }, ); ``` To accept payments from more than one chain, return one requirement per source chain in `accepts` — see [Payment Types](/api-reference/payment-types/overview). The buyer signs an EIP-3009 `TransferWithAuthorization` as typed data — no approval, no transaction: ```typescript theme={"system"} import { bytesToHex } from "viem"; const now = BigInt(Math.floor(Date.now() / 1000)); const authorization = { from: account.address, to: paymentRequirements.payTo as `0x${string}`, value: BigInt(paymentRequirements.maxAmountRequired), validAfter: 0n, validBefore: now + BigInt(paymentRequirements.maxTimeoutSeconds), nonce: bytesToHex(crypto.getRandomValues(new Uint8Array(32))), }; const signature = await walletClient.signTypedData({ account, domain: { name: paymentRequirements.extra.name, version: paymentRequirements.extra.version, chainId: NETWORK.chainId, verifyingContract: paymentRequirements.asset as `0x${string}`, }, types: { TransferWithAuthorization: [ { name: "from", type: "address" }, { name: "to", type: "address" }, { name: "value", type: "uint256" }, { name: "validAfter", type: "uint256" }, { name: "validBefore", type: "uint256" }, { name: "nonce", type: "bytes32" }, ], }, primaryType: "TransferWithAuthorization", message: authorization, }); ``` Then send the payload to your server (bigints as decimal strings): ```typescript theme={"system"} const paymentPayload = { x402Version: 1, scheme: "exact", network: paymentRequirements.network, payload: { signature, authorization: { from: authorization.from, to: authorization.to, value: authorization.value.toString(), validAfter: authorization.validAfter.toString(), validBefore: authorization.validBefore.toString(), nonce: authorization.nonce, }, }, }; ``` Your server settles the payment through Meridian, then serves the paid response. Never expose the API key to the browser, and do not trust buyer-supplied pricing, recipient, token, or network values. Rebuild or look up the matching `paymentRequirements` on the server before settlement: ```typescript theme={"system"} if (paymentPayload.network !== paymentRequirements.network) { throw new Error("Payment network does not match the server requirement"); } if ( paymentPayload.payload.authorization.to.toLowerCase() !== paymentRequirements.payTo.toLowerCase() ) { throw new Error("Payment must authorize the Meridian facilitator"); } if ( paymentPayload.payload.authorization.value !== paymentRequirements.maxAmountRequired ) { throw new Error("Payment amount does not match the server price"); } ``` ```typescript theme={"system"} const response = await fetch("https://api.mrdn.finance/v1/settle", { method: "POST", headers: { Authorization: `Bearer ${process.env.MERIDIAN_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ paymentPayload, paymentRequirements }), }); const result = await response.json(); if (!response.ok || !result.success) { throw new Error(result.errorReason ?? "Settlement failed"); } ``` Open [Command Centre](/command-centre/transactions) to see the payment, its settlement status, and your balance. ## Next steps The full client-side flow: multi-chain challenges, requirement selection, and error handling. EIP-3009, Permit2, and Circle Gateway — and when to use which. Chains, tokens, and contract addresses. Self-contained integration instructions for AI coding agents. # Roadmap Source: https://docs.mrdn.finance/roadmap Meridian is a payments facilitator. We're also building toward trustless refunds, cross-chain settlement, and **mrUSD**, a USD stablecoin issued via M0. The sections below follow those three tracks through 2026. Meridian Protocol roadmap: UMA arbitration (in progress), Circle CCTP nano-payments (completed), and mrUSD stablecoin via M0 (planned) ## 01. UMA Arbitration **Status:** In progress Today, refunds on Meridian are issued manually by merchants. UMA's optimistic oracle, the same dispute system used by Polymarket, adds a trustless escalation path for when refund requests are ignored or denied in bad faith. For [EIP-8183](https://eips.ethereum.org/EIPS/eip-8183) agentic commerce jobs, UMA can serve as the evaluator: the independent party that attests whether work should be completed and paid out, or rejected and refunded. * Buyer-initiated dispute escalation * Neutral on-chain arbitration * Automatic payout on resolution This brings consumer-grade refund protection to x402 payments without requiring either party to trust a centralized arbiter. ## 02. Circle Nanopayments **Status:** Completed A new x402 settlement path built on top of Circle's [CCTP](https://www.circle.com/cross-chain-transfer-protocol) (Cross-Chain Transfer Protocol). Instead of routing USDC across chains through a bridge, CCTP burns USDC on the source chain and mints native USDC on the destination, giving Meridian a gas-free, capital-efficient cross-chain rail for small payments. | | **Default path** | **Circle path** | | ----------- | ---------------- | ------------------- | | Speed | \< 2s settlement | Batched | | Tokens | Multi-token | USDC only | | Economics | Fee + cashback | No fee, no cashback | | Cross-chain | Across bridge | Burn & mint | Both paths remain **gas-free** for the payer. The new Circle path is optimized for high-frequency, small-dollar agent payments where zero fees and native USDC matter more than cashback. ## 03. mrUSD **Status:** Planned Meridian's native USD stablecoin, fully backed and issued via the [M0](https://m0.org/) network. mrUSD brings institutional-grade stability together with on-chain programmability purpose-built for agent-driven commerce. * EIP-3009 authorization transfers built in * Onramps, swaps, and cross-chain rails out of the box * Powers agentic commerce flows natively across the Meridian stack ## Timeline Q2 to Q4 2026. Dates are directional. Scope and sequencing may shift as integrations land. The roadmap is intentionally opinionated about *what* ships, less so about *when*. # Tokenomics Source: https://docs.mrdn.finance/tokenomics $MRDN token distribution, emissions, and cashback mechanics ## Token Distribution The \$MRDN token has a carefully designed distribution model to ensure sustainable growth and proper incentive alignment across all stakeholders. **Total Supply: 1,000,000,000 \$MRDN** **Distribution Breakdown:**
**70% Emissions** - 700M tokens
**10% Treasury** - 100M tokens
**10% Team** - 100M tokens
**5% Liquidity & Listings** - 50M tokens
**3% Pool2** - 30M tokens
**1% Closed Round** - 10M tokens
**1% uOS Airdrop** - 10M tokens
**70%** of total supply for \$MRDN cashback emissions. Cashback starts at 2%, decays over time, and is capped at \$5 of cashback value per eligible transaction. **10%** of total supply Protocol development and ecosystem growth **10%** of total supply 4-year linear vesting schedule **5%** of total supply for \$MRDN/USDC trading liquidity & reserve for future listings **3%** of total supply for additional liquidity incentives **1%** of total supply to boot strap initial liquidity pool **1%** of total supply Community distribution ## Emission Schedule

Exponential Decay Emission Model

**How it works:** * **Continuous Decay:** Cashback rate decreases exponentially with total network transaction volume * **Volume-Based:** Higher network usage naturally reduces emission rate over time * **Smooth Transition:** Emissions decay continuously leading to halving events * **Economic Alignment:** Early adopters receive higher rates while maintaining long-term sustainability

Decay Function

The cashback rate follows an exponential decay based on cumulative transaction volume: **Cashback Rate Formula:** $y(x) = 0.02 \cdot e^{-13.9 \times 10^{-9} \cdot x}$ Where: * $y(x)$ = Cashback percentage at volume $x$ * $x$ = Cumulative transaction volume processed (USD) * $0.02$ = Initial cashback rate (2%) * $13.9 \times 10^{-9}$ = Decay constant (adjustable parameter)

Emission Milestones

| Tokens Emitted | Current Rate | | -------------- | ------------- | | **0** | **2.0%** | | **50M** | **1.0%** | | **100M** | **0.5%** | | **150M** | **0.25%** | | **200M** | **0.125%** | | **250M** | **0.0625%** | | **300M** | **0.03125%** | | **350M** | **0.015625%** |
**Key Parameters:** * **Total Emission Supply:** 700M \$MRDN tokens * **Per-Transaction Cap:** $5 of cashback value, paid in $MRDN * **Halving Interval:** Every 50M tokens emitted * **Decay Constant:** 13.9e-9 * **Volume Dependency:** Transaction volume requirements scale with token price ## Cashback Mechanism

How Cashback Works

Cashback rewards are paid in \$MRDN tokens for eligible x402 payments settled on Base. The reward value follows the current cashback rate, but each transaction is capped at **\$5 of cashback value** before conversion into \$MRDN. ``` Cashback Formula: Total Payment × Current Cashback Rate = Cashback Amount (USD) min(Cashback Amount, $5.00) = Capped Cashback Amount (USD) Capped Cashback Amount ÷ $MRDN Price = $MRDN Tokens Received ``` \$MRDN is distributed to the cashback contract every few minutes as rewards. The contract pays cashback FIFO: the first eligible transactions receive cashback first, until the newly distributed rewards for that window are used. Cashback is not paid for self-payments where the payer and recipient are the same address.

Variable Token Payouts

Token payouts depend on market price: * **Higher \$MRDN Price:** Fewer tokens received as cashback * **Lower \$MRDN Price:** More tokens received as cashback

Example Calculation

``` Service Payment: $1000 USDC on Base x402 Cashback Calculation: $1000 × 0.02 = $20.00 USDC value Per-Transaction Cap: min($20.00, $5.00) = $5.00 USDC value $MRDN/USDC Price: $0.50 $MRDN Tokens Queued: $5.00 ÷ $0.50 = 10 $MRDN ``` ## Fee Structure

Transaction Fees

A **1% protocol fee** is collected for x402 transactions on Base: * **Fee Timing:** Collected during Base x402 settlement * **Fee Application:** Applies only to x402 transactions on Base * **No Fee on Permit Transfers:** Apps such as `pay.mrdn.finance` use permit for gasless token transfers; the 1% protocol fee is not subtracted from those transfers * **No Fee on Non-Base x402:** The 1% protocol fee is not collected on x402 transactions outside Base * **Fee Destination:** Collected by protocol treasury * **Purpose:** Sustainable protocol revenue and ecosystem development funding ### Fee Distribution Example ``` Base x402 payment processed: $1000 USDC ├── Protocol Fee: -$10 USDC (1%) ├── Net to Recipient: $990 USDC └── Cashback to Payer: up to $5 value in $MRDN, paid FIFO from reward distributions ``` ## Token Utility

Primary Use Cases

* **Cashback Rewards:** Base x402 rewards paid in \$MRDN * **Network Participation:** Incentivizes usage of Meridian payment infrastructure * **Governance:** Future governance rights for protocol decisions * **Real Yield:** Protocol-generated revenue provides sustainable returns

Economic Model

The tokenomics create a deflationary pressure through: - **Decreasing Emissions:** Halving cycles reduce new token supply over time - **Usage-Based Rewards:** Higher network usage increases token demand - **Fee Collection:** Protocol revenue provides sustainable funding model ## Trading & Liquidity

Primary Trading Pair

The \$MRDN token will be primarily traded against USDC: * **Main Pair:** \$MRDN/USDC * **Liquidity Pool:** 5% of total supply allocated for initial \$MRDN/USDC liquidity * **Price Discovery:** Market-driven pricing through decentralized exchanges * **Stability:** USDC pairing provides stable reference point for cashback calculations * **Auto Liquidity Provision:** USDC collected from Base x402 fees adds protocol owned liquidity ## Long-term Sustainability

Balanced Incentives

* **Early Adopters:** Higher cashback rates in early cycles reward pioneers - **Long-term Users:** Sustained value through decreasing supply inflation - **Protocol Growth:** Treasury and fee structure fund continuous development

Emission Flexibility

The dynamic emission model ensures: - **Early Adopters:** Higher absolute emissions reward network pioneers - **Long-term Sustainability:** Emission rate decreases with remaining supply - **Adaptive Schedule:** Halving triggered by time or economic milestones provides flexibility