Skip to content

Latest commit

 

History

History

base-mainnet

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 

README.md

Example: base-mainnet (REAL FUNDS)

The same gateway, settling real USDC on Base. Read docs/configuration.md first. It explains what is refused and why, and this file assumes it.

Two things are structurally different from the local and testnet examples:

  • No signerPrivateKey, and no way to have one. facilitator.mode: local is refused on mainnet: it signs with a key this process holds, which is a hot wallet inside the resource server. A remote facilitator broadcasts and pays the gas.
  • Almost nothing is defaulted. Only the port, the receipt store path and the RPC URL have fallbacks. Every other ${VAR} has none, so a missing one fails config loading rather than resolving to something plausible.

What you need

MERCHANT_WALLET your wallet. Address only: the gateway never takes a merchant key. See Paying into a contract.
ALLOW_X402_MAINNET=true the explicit opt-in. Never a default.
CDP credentials, or a bearer token cdp uses your CDP API key and the configured CDP URL. For bearer, set the facilitator's HTTPS url. Neither needs an extra peer.
ADMIN_TOKEN required here: it guards the receipt routes, which carry your ledger.
A Base RPC health checks only. Use a dedicated endpoint: the public one's outages become your readiness failures.

To run without a facilitator credential, see base-mainnet-payai, which sets allowUnauthenticatedFacilitator.

Paying into a contract address

MERCHANT_WALLET sets the x402 payTo address, which can be a contract such as a revenue splitter on Base. USDC's EIP-3009 transferWithAuthorization transfers tokens without invoking a recipient callback, so the contract needs no receive handler.

Before using a contract:

  • Confirm it can send USDC onward, for example through its distribute function. Otherwise, the funds stay at that address.
  • Verify its deployment on each offer's network. A contract exists only on the chain where it was deployed, while an EOA's key controls its address on every EVM chain. Do not reuse the address on another network unless the intended contract is also deployed there. docs/configuration.md reuses ${MERCHANT_WALLET} for Base and Polygon offers. Setting it to a Base contract would send Polygon payments to an address nobody controls there.

Validate before anything else

ALLOW_X402_MAINNET=true \
MERCHANT_WALLET=0xYourWallet \
GATEWAY_PUBLIC_BASE_URL=https://your.gateway \
ADMIN_TOKEN=... \
MERCHANT_API_BASE_URL=http://localhost:3000 \
CDP_API_KEY_ID=... CDP_API_KEY_SECRET=... \
  npm run agent-commerce -- validate --config examples/base-mainnet/config.yaml

Drop any one of those and it fails, naming the path:

FAIL  CONFIG_INVALID: Unresolved environment variable "${ALLOW_X402_MAINNET}"
      referenced at config path "$.payments.x402.allowMainnet"

With every value set, doctor reports the deployment as LIVE MAINNET MODE - REAL FUNDS.

Proving it settles

export ALLOW_X402_MAINNET=true
export X402_SMOKE_TARGETS=base
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=...   # optional; or X402_FACILITATOR_TOKEN
npm run test:mainnet

Each successful x402 smoke run spends X402_SMOKE_AMOUNT (default 0.01) of real USDC. The x402 suite skips itself, naming missing settings, unless X402_SMOKE_TARGETS names base; a named target then fails without the opt-in, buyer key, merchant address or facilitator URL. The facilitator credential is optional. Setting ALLOW_MPP_MAINNET=true also enables the separate, paid MPP smoke suite.

It proves, in order: the guard refuses a config that has not opted in · the deployment reports itself as live mainnet · a payment settles on Base · the receipt carries the settlement reference and a 2xx backend status · the resource is delivered exactly once · the same authorization presented again is refused, and the backend is not called again · no credential appears in anything logged. The USDC Transfer is read back from the transaction receipt. See smoke tests.

It never runs in CI; see the main README for why.