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
| Chain | Chain ID | Native | USDC | USDT | Confirmations | Min order |
|---|---|---|---|---|---|---|
| Ethereum | 1 | ETH | ✓ (ERC-20) | ✓ (ERC-20) | 12 | $5.00 |
| Base | 8453 | ETH | ✓ (ERC-20) | — | 10 | $1.00 |
| Polygon | 137 | POL | ✓ (ERC-20) | ✓ (ERC-20) | 5 | $1.00 |
| BSC | 56 | BNB | ✓ (18-decimal) | ✓ (18-decimal) | 5 | $1.00 |
| Arbitrum | 42161 | ETH | ✓ (ERC-20) | ✓ (ERC-20) | 5 | $1.00 |
| Optimism | 10 | ETH | ✓ (ERC-20) | ✓ (ERC-20) | 5 | $1.00 |
| ZKsync Era | 324 | ETH | — | — | 5 | $1.00 |
| Mantle | 5000 | MNT | — | — | 5 | $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
| Chain | Chain ID | Native | Confirmations | Min order |
|---|---|---|---|---|
| Ethereum Sepolia | 11155111 | ETH | 1 | $0.50 |
| Base Sepolia | 84532 | ETH | 1 | $0.50 |
| Polygon Amoy | 80002 | POL | 1 | $0.50 |
| BNB Smart Chain Testnet | 97 | tBNB | 1 | $0.50 |
| Arbitrum Sepolia | 421614 | ETH | 1 | $0.50 |
| OP Sepolia | 11155420 | ETH | 1 | $0.50 |
| Mantle Sepolia | 5003 | MNT | 1 | $0.50 |
| ZKsync Sepolia | 300 | ETH | 1 | $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 valueIsSupportedCurrencyreturns true for inpayment-service. BothPOST /v1/ordersandPOST /b2b/v1/checkout-sessions/quickreject anything else withINVALID_INPUT(“unsupported currency”). The advertised list lives atGET /v1/supported/currenciesso 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:
- KYB completion
- 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_addressreturned 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
- Concepts → Sessions — how chain/asset selection happens at checkout time.
- Get started → Introduction — the big-picture model.