Skip to Content
ConceptsChains & assets

Chains & assets

InfraIO Pay is EVM-first and stablecoin-first. Every supported chain is configured in the payment-service’s networks.json registry; the matrix below mirrors what’s live in test and mainnet today.

Mainnet

ChainChain IDNativeUSDCUSDTConfirmationsMin order
Ethereum1ETH✓ (ERC-20)✓ (ERC-20)12$5.00
Base8453ETH✓ (ERC-20)10$1.00
Polygon137POL✓ (ERC-20)✓ (ERC-20)5$1.00
BSC56BNB✓ (18-decimal)✓ (18-decimal)5$1.00
Arbitrum42161ETH✓ (ERC-20)✓ (ERC-20)5$1.00
Optimism10ETH✓ (ERC-20)✓ (ERC-20)5$1.00
ZKsync Era324ETH5$1.00
Mantle5000MNT5$1.00

BSC USDC + USDT use 18 decimals, not 6 like everywhere else. If you’re computing amounts client-side for some reason, account for this — otherwise the SDK’s amount fields handle it.

“Min order” is the USD floor enforced at payment-intent create time. Defaults shipped today are $1.00 mainnet / $0.50 testnet, with Ethereum bumped to $5 because L1 gas dwarfs smaller orders. Admin ops can override per-network — orders below the floor get rejected with AMOUNT_BELOW_MINIMUM (HTTP 400), and details.floor_usd carries the actual configured value so your FE can display it.

Testnet

ChainChain IDNativeConfirmationsMin order
Ethereum Sepolia11155111ETH1$0.50
Base Sepolia84532ETH1$0.50
Polygon Amoy80002POL1$0.50
BNB Smart Chain Testnet97tBNB1$0.50
Arbitrum Sepolia421614ETH1$0.50
OP Sepolia11155420ETH1$0.50
Mantle Sepolia5003MNT1$0.50
ZKsync Sepolia300ETH1$0.50

Test mode uses real testnets, not a mock chain. To trigger a test payment you need actual testnet funds — pull from a faucet (e.g., sepoliafaucet.com , coinbase.com/faucets ) and send to the deposit address the checkout page generates.

Currency codes used in the API

currency (on the top-level body and each line item) is the order’s display currency — what the buyer sees as the amount. Today the platform accepts a single code at create time:

  • "USD" — the only value IsSupportedCurrency returns true for in payment-service. Both POST /v1/orders and POST /b2b/v1/checkout-sessions/quick reject anything else with INVALID_INPUT (“unsupported currency”). The advertised list lives at GET /v1/supported/currencies so the server’s enum and your client never drift.

The on-chain asset the buyer settles in (USDC, USDT, …) is not the currency field. It’s chosen by the buyer on the checkout page from the chain × token combinations you’ve enabled in your merchant config, and surfaces back to you on the PaymentIntent + webhook payload as settlement_token (symbol + chain). Native gas tokens (ETH, BNB, POL, MNT) are chain metadata only — not valid payment currencies.

The chain itself isn’t specified at create time either — same reason: the buyer picks the combination on the checkout page.

Confirmations vs finality

The “Confirmations” column above is the number of blocks the chain watcher waits before flipping a PaymentIntent to SETTLED and firing payment.settled. The defaults are tuned to give probabilistic finality on each chain at typical reorg depths:

  • Mainnet Ethereum’s 12 is the value most exchanges use post-Merge.
  • L2s settle faster but inherit Ethereum’s finality eventually — the per-chain count reflects the L2’s own reorg risk, not the L1.
  • Testnets sit at 1 conf to keep the dev loop fast; don’t extrapolate to mainnet-grade safety in your dev environment.

If you need stricter (or looser) confirmations for a specific chain, the per-chain count is configurable on the merchant account — talk to your account contact.

Enabling chains for your merchant

By default a new merchant has a permissive set of chains on testnet and a narrower set on mainnet. The mainnet set is gated on:

  1. KYB completion
  2. Treasury wallet attestation per chain (you sign a message proving custody)

Enable additional chains from Settings → Networks in the merchant dashboard.

Deterministic deposit addresses

EVM deposit addresses are deterministic per checkout: the platform derives them ahead of time from internal parameters bound to the merchant, the order, and the intent. You don’t pre-create or fund anything — you just get the address back on the PaymentIntent and hand it to the buyer.

  • Two intents for the same merchant get different addresses — no collisions across buyers.
  • The address shows up on the checkout page before any on-chain setup, so the buyer sees a destination immediately.
  • Buyers never pay setup gas. Funds settle straight into the merchant treasury without a separate sweep step the merchant has to track.

Practical implications for integrators:

  • The deposit_address returned on a PaymentIntent is a regular EVM address — you can show it as a QR code, scan it, send to it from any wallet. No special client support needed.
  • Sending to the address before the intent expires settles the order. Sending after expiry is recovered on a best-effort basis — the platform can re-sweep late deposits in some cases, but never assume late funds are safe.
  • Each address is single-use per intent. If a buyer needs a second attempt, mint a new checkout session — you’ll get a fresh address.

What’s NOT supported today

  • Tron / TRC-20. Frequently requested for USDT-TRC20; on the roadmap, not live.
  • Solana / SPL. On the roadmap.
  • Bitcoin / Lightning. Not on the roadmap.
  • Layer-1 native stablecoins (USDe, FDUSD, …). Add via dashboard request; the underlying chain just needs to be enabled.

What’s next