Supported Chains
Cross402 supports paying from one chain and settling on another, optionally in a different stablecoin. A payment intent declares two chains and (optionally) two assets:
payer_chain— where the payer sends stablecoins.target_chain— where the merchant receives stablecoins. Optional; defaults tobase.payer_asset— the token the payer signs against. Optional; defaults to"usdc". One of"usdc"|"usdt"|"usdt0".target_asset— the token the merchant receives. Optional; defaults to"usdc". Same allowed values.
Clients that omit payer_asset / target_asset continue to behave exactly as before — both fields default to "usdc" with no change in wire format. See
The set of target chains available to your integration is served by GET /api/chains. If a chain appears in that response, you can use it as a target_chain. Any chain supported as a payer can also be used as a target.
Status legend
- Live — callable. You can pass the identifier as
payer_chainortarget_chainand the call will succeed (assuming the usual validation). TreatGET /api/chainsas authoritative for the exact set exposed by your deployment.
Payer chains
TRON is USDT-only (no USDC is wired) and is gated behind the TRON_ENABLED deployment flag — intents naming a TRON chain return 400 while the gate is off. Pass the string literal "tron" as the chain identifier. A Nile testnet variant (tron-nile) mirrors the mainnet wiring.
Per-chain notes for BSC, Monad, SKALE Base, MegaETH, and TRON appear at the end of this page under
Target chains
Every payer chain is also a valid target. One caveat applies:
GET /api/chainsis authoritative. It lists the chains accepted as atarget_chainfor your deployment. Target chains are validated at intent creation; if you pass an unsupported value, the API returns400with an error that lists the currently accepted target chains.
GET /api/chains
Response:
{
"chains": ["base", "ethereum", "hyperevm", "polygon", "solana", "skale-base", "megaeth"],
"target_chains": ["base", "ethereum", "hyperevm", "polygon", "solana"]
}
The two lists are independent — chains enumerates valid payer_chain values and target_chains enumerates valid target_chain values. Payer-only chains (e.g. skale-base, megaeth) appear in chains but not in target_chains. The lists are stable per deployment.
Payer × target matrix
Any payer chain can pair with any target chain exposed by your deployment, including same-chain routes (e.g. base → base). The table below covers common combinations. The Payer sig and Target sig columns show the x402 signing flavor used on each leg — the SDK picks these automatically from payment_requirements.
Callers do not choose the signing flavor; the SDK derives it from payment_requirements and the (payer, target) pair. Both legs run through the x402 protocol. See Multi-Chain Settlement for the mechanics.
Token × Chain matrix
Every EVM and Solana chain ships at minimum with USDC. USDT0 (LayerZero's omni-Tether) is wired on the chains where it has a verified deployment; legacy native USDT is wired on Ethereum / BSC / Base / Solana (gated behind USDT_ENABLED), on Polygon / Arbitrum (live, no gate), and on TRON (USDT-only, gated behind TRON_ENABLED). The matrix below is authoritative.
Legend: ✅ Live = the (chain, asset) pair is callable today. 🚩 gated = wired but gated behind a deployment flag — USDT_ENABLED for Ethereum / BSC / Base / Solana USDT, TRON_ENABLED for TRON; calls return 400 invalid payer_asset (or 400 on the TRON chain itself) while the gate is off. — = not wired; 400 payer_asset not configured on chain if requested.
Chain-specific caveats
These are the details you need at signing and display time.
Token contracts and decimals
Always read extra.decimals from the payment_requirements object on the CreateIntent response. Do not hardcode 6. Mainnet token addresses are pinned by the backend per chain.
USDC (default, payerAsset omitted or "usdc")
USDT (payerAsset: "usdt")
USDT payers on BSC, Ethereum, and Base must hold native gas (BNB / ETH) to land a one-time
approve(Permit2, amount)before payment. TRON payers need TRX for the same one-time approve. Polygon USDT uses EIP-2612 with a salted domain and Arbitrum USDT uses EIP-3009 — no gas required on either. See USDT Signing for details.
EIP-712 domain values per asset
Always read extra.name, extra.version, and (where present) extra.domainType from payment_requirements. Never hardcode these — they vary per (chain, asset) deployment:
- USDC — defaults
name = "USD Coin",version = "2". Exceptions: SKALE Base uses"Bridged USDC (SKALE Bridge)", MegaETH native USDm uses"MegaUSD"+version = "1". - USDT0 —
nameis"USD₮0"(Tugrik U+20AE) on Arbitrum / HyperEVM; plain ASCII"USDT0"on Monad / MegaETH / Polygon.version = "1"everywhere. - USDT (native) — on Polygon (alias of USDT0, same contract) it signs via Permit2 + EIP-2612 against the salted EIP-712 domain (
name = "USDT0",version = "1"); on Arbitrum it signs EIP-3009 against the USD₮0 domain. Ethereum / BSC / Base USDT settle via Permit2 with the approval extension and have no token EIP-712 domain at all — the only typed data signed is Permit2's ownPermit2domain.
Hardcoding "USD Coin" v2 on SKALE Base or MegaETH produces signatures the on-chain contract rejects.
Per-asset transfer scheme
TransferScheme is per-(chain, asset), not per-chain. The same chain can use different schemes for different assets:
- EIP-3009
TransferWithAuthorization— Circle USDC on Base / Ethereum / Polygon / HyperEVM / Arbitrum / SKALE Base; USDT0 on Arbitrum / Monad / HyperEVM / MegaETH; native USDT on Arbitrum (same USD₮0 contract). - Permit2 + EIP-2612 — USDC on Monad / MegaETH; Polygon USDT / USDT0 (same contract
0xc2132D..., uses EIP-2612 against a salted EIP-712 domain — no EIP-3009). The payer signs the permit off-chain (attached as theeip2612GasSponsoringextension) and the facilitator submits it — gas is sponsored by Cross402. - Permit2 + approval extension — legacy native USDT on Ethereum / BSC / Base, and BSC Binance-Peg USDC (no EIP-2612 on any of them). Signalled as
assetTransferMethod: "permit2"withextensions: ["eip2612GasSponsoring", "erc20ApprovalGasSponsoring"]. The first payment from a (chain, wallet) pair needs a one-timeapprove(Permit2, max)— a payer-signed raw transaction relayed by the facilitator via theerc20ApprovalGasSponsoringextension, or an on-chain approve sent by the payer's wallet directly; either way the payer holds native gas. Subsequent payments are signature-only. USDT on these chains is gated behind theUSDT_ENABLEDdeployment flag. - TRON Permit2 — USDT on TRON (SUN.io Permit2 deployment). One-time on-chain
approve(Permit2, max)prepared/broadcast by the TRON Signing helper API (costs TRX), then off-chainPermitWitnessTransferFromsignatures per payment. Gated behindTRON_ENABLED. - Solana SPL — USDC and USDT on Solana; partial-signed VersionedTransaction v0.
The payment_requirements.extra.assetTransferMethod field signals the scheme:
See USDT Signing for the full USDT flow.
Polygon: salted EIP-712 domain
Polygon's native Tether contract (0xc2132D05D31c914a87C6611C10748AEb04B58e8F, used as both USDT0 and native USDT) signs against the non-standard Polygon-PoS layout:
EIP712Domain(string name, string version, address verifyingContract, bytes32 salt)
where salt = bytes32(chainID) — chainId moves out of the domain type and into a salt field. The intent response's payment_requirements.extra.domainType == "salted" is the explicit signal to switch the signer's DOMAIN_SEPARATOR construction. Standard chainId-in-domain EIP-712 signatures fail verification against these contracts.
Usage
Specify chains with SDK constants, not hardcoded strings. targetChain is optional.
import { Chain } from '@cross402/usdc';
const intent = await client.createIntent({
email: "merchant@example.com",
amount: "100.50",
payerChain: Chain.Base,
targetChain: Chain.Ethereum,
});
resp, err := client.CreateIntent(ctx, &pay.CreateIntentRequest{
Email: "merchant@example.com",
Amount: "100.50",
PayerChain: pay.ChainBase,
TargetChain: pay.ChainEthereum,
})
Per-chain notes
Caveats for chains whose token, signing flavor, or role differ from the default (Circle-native USDC, EIP-3009, payer + target).
BSC
- Token: Binance-Peg USDC at
0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d, 18 decimals. - Signing: Permit2
PermitWitnessTransferFrom. Binance-Peg USDC has no EIP-2612, so a one-timeapprove(Permit2)must land on-chain first — payer-signed and relayed via theerc20ApprovalGasSponsoringextension, or sent by the payer's wallet directly (BNB gas from the payer either way). - Role: payer and target.
- See BSC Signing for the full flow.
Monad
- Token: USDC at
0x754704Bc059F8C67012fEd69BC8A327a5aafb603, 6 decimals. - Signing: Permit2
PermitWitnessTransferFrom+ EIP-2612Permit. Gas is sponsored by Cross402. - Role: payer and target.
SKALE Base
- Token: USDC.e (Bridged USDC) at
0x85889c8c714505E0c94b30fcfcF64fE3Ac8FCb20, 6 decimals. Not Circle-native USDC. - Signing: EIP-3009
TransferWithAuthorization. EIP-712 domainname = "Bridged USDC (SKALE Bridge)",version = "2". Hardcoding"USD Coin"will fail signature verification. - Role: payer-only. Cannot be used as
target_chain.
MegaETH
- Token: USDm (MegaUSD, native) at
0xFAfDdbb3FC7688494971a79cc65DCa3EF82079E7, 18 decimals. Not USDC. - Signing: Permit2
PermitWitnessTransferFrom+ EIP-2612Permit. EIP-712 domainname = "MegaUSD",version = "1". Gas is sponsored by Cross402. - Role: payer-only. Cannot be used as
target_chain.
TRON
- Token: USDT-only — TRC-20 USDT at
TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t, 6 decimals. No USDC is wired; requestingpayer_asset: "usdc"on TRON fails. - Addresses: base58check (
T…prefix), not EVM hex. This applies to wallet addresses, the token contract, and thepayToinpayment_requirements. - Signing: Permit2
PermitWitnessTransferFromagainst the SUN.io Permit2 deployment (TTJxU3P8rHycAyFY4kVtGNfmnMH4ezcuM9on mainnet).payment_requirements.extracarriespermit2Address(the EIP-712verifyingContract) andspender(the x402ExactPermit2Proxy). A one-time on-chainapprove(Permit2, max)is required per wallet — prepared and broadcast via the TRON Signing helper API, costing TRX from the payer's wallet. Payments after that are gasless off-chain signatures. - Gating: behind the
TRON_ENABLEDdeployment flag — intents naming a TRON payer or target chain return400while the gate is off. TreatGET /api/chainsas authoritative for whether your deployment exposes TRON. - Networks: mainnet (
tron) plus the Nile testnet (tron-nile), mirroring the Base / Base-Sepolia split. - Role: payer and target (when the deployment wires a TRON proxy wallet).