When a server uses price: "$0.10" syntax (USD string pricing), x402 needs to know which stablecoin to use on that chain. Each SDK maintains a map of chain → default stablecoin. This document explains how to add or update these defaults.
For chains without a default, servers can use registerMoneyParser() or specify prices directly as a TokenAmount with amountInAtomicUnits.
x402 supports two methods for transferring assets on EVM:
| Method | Use Case | Notes |
|---|---|---|
| EIP-3009 (default) | Tokens with transferWithAuthorization (e.g., USDC) |
Simplest — single signature, no approval step |
| Permit2 | Any ERC-20 token | Universal fallback — requires one-time Permit2 approval |
If no transfer method is specified, the system defaults to EIP-3009.
For Permit2 tokens, also check whether the token implements EIP-2612 permit():
- Yes → set
supportsEip2612: trueso clients can use gasless permits for Permit2 approval - No → omit the field; clients fall back to ERC-20 approval gas sponsoring
- Get the stablecoin's contract address on your chain
- Read the
name()andversion()functions from the token contract (these are the EIP-712 domain values) - Check whether the token supports EIP-3009 (
transferWithAuthorization) - If not, check whether it supports EIP-2612 (
permit())
Add an entry in each SDK's constants file. All three must use the same CAIP-2 key, token address, EIP-712 name/version, decimals, and transfer method.
TypeScript — typescript/packages/mechanisms/evm/src/shared/defaultAssets.ts
Add to the DEFAULT_STABLECOINS map:
"eip155:YOUR_CHAIN_ID": {
address: "0xYOUR_STABLECOIN_ADDRESS",
name: "Token Name", // EIP-712 domain name
version: "1", // EIP-712 domain version
decimals: 6,
// assetTransferMethod: "permit2", // Only if token lacks EIP-3009
// supportsEip2612: true, // Only for Permit2 tokens with EIP-2612
},Go — go/mechanisms/evm/constants.go
Add to the NetworkConfigs map:
"eip155:YOUR_CHAIN_ID": {
ChainID: big.NewInt(YOUR_CHAIN_ID),
DefaultAsset: AssetInfo{
Address: "0xYOUR_STABLECOIN_ADDRESS",
Name: "Token Name", // EIP-712 domain name
Version: "1", // EIP-712 domain version
Decimals: 6,
// AssetTransferMethod: AssetTransferMethodPermit2, // Only if token lacks EIP-3009
// SupportsEip2612: true, // Only for Permit2 tokens with EIP-2612
},
},Python — python/x402/mechanisms/evm/constants.py
Add to the NETWORK_CONFIGS dict:
"eip155:YOUR_CHAIN_ID": NetworkConfig(
chain_id=YOUR_CHAIN_ID,
default_asset=AssetInfo(
address="0xYOUR_STABLECOIN_ADDRESS",
name="Token Name", # EIP-712 domain name
version="1", # EIP-712 domain version
decimals=6,
# asset_transfer_method=AssetTransferMethod.PERMIT2, # Only if token lacks EIP-3009
# supports_eip2612=True, # Only for Permit2 tokens with EIP-2612
),
),The HTTP paywall formats human-readable amounts using each chain's default stablecoin decimals. The generated map (typescript/packages/http/paywall/src/evm/gen/decimals.ts) only includes chains whose default asset does not use 6 decimals; everything else assumes 6.
If your new or updated default uses any value other than 6 for decimals, run the paywall build from typescript/ and commit the generated artifacts (including decimals.ts):
cd typescript && pnpm --filter @x402/paywall build:paywallSee CONTRIBUTING.md — Paywall Changes for the full list of files this command updates. Skip this step when the default asset stays at 6 decimals.
Include the chain name and rationale for the asset selection. If the chain team has officially endorsed a stablecoin, mention that.
The paywall renders a "Need {token} on {chain}? Request some here." link on testnet payment requirements. Without a configured faucet URL, the paywall renders "No faucet configured." instead.
To provide a working faucet link for your testnet, add one line to typescript/packages/http/paywall/src/faucetUrls.ts:
"eip155:YOUR_TESTNET_CHAIN_ID": "https://your-faucet-url",Paywall-only file; recommended for testnet entries; N/A for mainnet (paywall faucet UI is testnet-gated). No cross-SDK lockstep required — the map is rendered exclusively by the TypeScript paywall bundle and is read by the Python and Go paywall handlers through the bundled template.
The default asset is chosen per chain based on:
- Chain-endorsed stablecoin — If the chain has officially selected or endorsed a stablecoin, use it.
- No official stance — We encourage the chain team to make the selection and submit a PR.
- Community PRs welcome — Chain teams and community members may submit PRs, provided the EIP-712 domain parameters are correct and the selection aligns with the chain's ecosystem.
| SDK | File | Map/Dict |
|---|---|---|
| TypeScript | typescript/packages/mechanisms/evm/src/shared/defaultAssets.ts |
DEFAULT_STABLECOINS |
| Go | go/mechanisms/evm/constants.go |
NetworkConfigs |
| Python | python/x402/mechanisms/evm/constants.py |
NETWORK_CONFIGS |