Public networks
Base mainnet
Real funds on Base, with every guardrail spelled out and nothing defaulted.
Real fundsRead Networks & facilitators first. Every
${VAR} here has no fallback, so a missing one fails config loading instead of resolving to something plausible.version: 1
merchant:
id: base-mainnet-example
name: Base Mainnet Example
publicBaseUrl: ${GATEWAY_PUBLIC_BASE_URL}
server:
port: ${GATEWAY_PORT:-8080}
host: 0.0.0.0
# Operator routes carry the commerce ledger. Without this they 404.
adminToken: ${GATEWAY_ADMIN_TOKEN}
allowedOrigins: []
storage:
receipts:
driver: sqlite
path: ${RECEIPT_STORE_PATH:-./data/receipts.sqlite}
protocols:
http:
enabled: true
mcp:
enabled: true
mountPath: /mcp
resources:
premium_report:
name: Premium Report
description: One paid endpoint, settled in USDC on Base.
input:
type: object
properties: {}
additionalProperties: false
backend:
type: http
method: GET
url: ${MERCHANT_API_BASE_URL}/api/report
timeoutMs: 10000
pricing:
type: fixed
amount: "0.01"
currency: USDC
expose: [http, mcp]
payments: [x402]
payments:
x402:
enabled: true
network: eip155:8453 # required. Base mainnet.
rpcUrl: ${X402_RPC_URL:-https://base.drpc.org} # health checks only
asset: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" # USDC on Base
assetName: "USD Coin" # the EIP-712 domain, not "USDC"
assetVersion: "2"
assetDecimals: 6
payTo: ${MERCHANT_WALLET} # required. Your wallet.
maxTimeoutSeconds: 600
allowMainnet: ${ALLOW_X402_MAINNET} # required, and required to be true
facilitator:
mode: remote # required. `local` is refused.
url: ${X402_FACILITATOR_URL} # required, and must be https
auth:
type: cdp # needs @coinbase/x402
apiKeyId: ${CDP_API_KEY_ID}
apiKeySecret: ${CDP_API_KEY_SECRET}
# For any facilitator that takes a static token:
# auth:
# type: bearer
# token: ${X402_FACILITATOR_TOKEN}What you need
| Variable | |
|---|---|
MERCHANT_WALLET | your wallet — address only |
ALLOW_X402_MAINNET=true | the explicit opt-in; never a default |
X402_FACILITATOR_URL | https, and authenticated |
CDP_API_KEY_ID, CDP_API_KEY_SECRET | or a bearer token with auth.type: bearer |
GATEWAY_ADMIN_TOKEN | without it you cannot read your own ledger |
GATEWAY_PUBLIC_BASE_URL | where agents reach you |
Validate before anything else
ALLOW_X402_MAINNET=true \
MERCHANT_WALLET=0xYourWallet \
GATEWAY_PUBLIC_BASE_URL=https://your.gateway \
GATEWAY_ADMIN_TOKEN=... \
MERCHANT_API_BASE_URL=http://localhost:3000 \
X402_FACILITATOR_URL=https://... \
CDP_API_KEY_ID=... CDP_API_KEY_SECRET=... \
npm run agent-commerce -- validate --config examples/base-mainnet/config.yamlFAIL CONFIG_INVALID: Unresolved environment variable "${ALLOW_X402_MAINNET}"
referenced at config path "$.payments.x402.allowMainnet"doctor then reports the deployment as LIVE MAINNET MODE.
Prove it settles
export ALLOW_X402_MAINNET=true
export X402_MAINNET_BUYER_PRIVATE_KEY=0x... # funded with USDC on Base
export X402_MAINNET_MERCHANT_ADDRESS=0x...
export X402_FACILITATOR_URL=https://...
export CDP_API_KEY_ID=... CDP_API_KEY_SECRET=... # or X402_FACILITATOR_TOKEN
npm run test:mainnetEvery run spends X402_MAINNET_AMOUNT (default 0.01) of real USDC. It never runs in CI — a workflow would mean a mainnet key in repository secrets.
Source: examples/base-mainnet