2026-10-14

Accept USDC on Arc With Klappay

Arc is Circle's own L1, and it's built around the token most Klappay merchants already settle in. On Arc you pay gas in USDC. For a payments API that only ever asks a customer to send stablecoins, that removes a step.

Klappay now supports Arc on mainnet and on Arc Testnet, on the same official 0xSplits contracts we use on every other EVM network. Getting there took a detour through a fork of our own.


How we got here

Every Klappay charge address is a 0xSplits contract, predicted with CREATE2 before anyone pays (the deterministic addresses post explains why). That only works on a chain where 0xSplits is deployed, and until recently Arc wasn't one of them.

We wanted Arc anyway, so we did two things. We backed the community request for official support (0xSplits issue #77), and we built klap-arc in the meantime: a self-hosted fork of the 0xSplits contracts deployed to Arc, with its own SDK. It worked, but it was a stopgap. A fork has a different factory address, so an Arc charge couldn't share its address with Base or Arbitrum and Arc had to sit in its own isolated family.

On October 2, 0xSplits closed the issue. SplitsWarehouse and the v2.2 Push and Pull split factories are now deployed on Arc mainnet and testnet, at the same addresses they use everywhere else, and the official SDK supports them. Three days later we moved Arc onto them and retired klap-arc. The fork's SDK is deprecated on npm, pointing people to the official one.


What changes for you

Arc mixes with the rest of your networks. The official v2.2 factory predicts the same split address on Arc that it does on Base and Arbitrum. We checked that live before shipping. So a single charge can accept USDC on Arc next to USDC on Base, and whichever one the customer pays with lands at the same contract address.

const charge = await klap.charges.create({
amount: 49.00,
expiresIn: 1800,
acceptedPayments: [
{ token: 'USDC', network: 'arc' },
{ token: 'USDC', network: 'base' },
],
})

Nothing else in the integration moves. Webhooks, splits, the charge lifecycle and the SDK calls are the same as on any other network.

One confirmation. On every other chain Klappay waits a set number of blocks (5 on Avalanche, 15 on Base, 150 on Polygon) before marking a payment confirmed, because a block can still be reorganized away. Arc uses Malachite, a Tendermint-style BFT consensus, so a block is final the moment two thirds of validators commit to it. Circle's own integration docs say a single confirmation is enough and that's what we require. Arc produces a block about every half second.

USDC is the gas token. A customer paying on Arc needs USDC and nothing else. There's no separate native coin to hold just for fees. On Arc, USDC is native: there's one system address with an ERC-20 interface on top of the native balance, and no wrapped USDC. Klappay talks to that ERC-20 interface, at 6 decimals, the same way it reads USDC everywhere else, so amounts in your charges and webhooks look exactly like they do on Base.


Testing it

Arc Testnet works like every other test network in Klappay. Create a charge with a test API key, put { token: 'USDC', network: 'arc' } in acceptedPayments, and pay it with testnet USDC from Circle's faucet. The guide to testing without real funds covers the rest of the sandbox, including simulated webhook events.


One known limitation

0xSplits' distribute() is public: anyone can call it, not just Klappay (here's why that matters). On Arc, someone can call it with the native-token sentinel address instead of the USDC ERC-20 address. Recipients still get paid correctly, but the transfer log comes from a different emitter than the one we watch, so that distribution can show as pending in Klappay even though the money has already moved.

If you already take payments with Klappay, Arc is one more line in acceptedPayments. If you're just starting, the developer docs are the place to begin.