2026-11-13

Does USDC Have Gas Fees? Who Pays Them on a Payment

Circle doesn't charge anything for moving USDC. There's no transfer fee built into the token, and sending 10 USDC delivers 10 USDC. What does cost money is the transaction that moves it: every blockchain charges gas to include a transaction in a block, and on almost every network that gas is paid in the chain's own token, not in USDC.

So the short answer is yes, USDC has gas fees, and the person sending it pays them. If you're accepting USDC as a merchant there's a second layer: getting the money from wherever the customer sent it to your wallet takes transactions too, and somebody pays for those as well.


What the customer pays to send

A customer paying you 49 USDC on Base needs 49 USDC and a little ETH on Base. The USDC goes to you, and the ETH pays the network. On Polygon the gas is POL, on Avalanche it's AVAX, on BNB Chain it's BNB. On Ethereum mainnet it's ETH again, and it's much more expensive than on the L2s.

This is the most common reason a first stablecoin payment fails. The customer has exactly the USDC, the wallet says "insufficient funds for gas", and they have no idea why, because they were never told they'd need a second token.

Arc and some newer wallets get around it.

Arc pays gas in USDC. Arc is Circle's own chain, and USDC is its gas token. A customer with USDC on Arc has everything they need to pay, with no second balance to top up. We wrote about accepting USDC on Arc when we added it.

Some wallets cover gas for the user. Smart-contract wallets can use a paymaster, a contract that pays the gas and either absorbs the cost or takes it out of the user's USDC. Circle runs one, and some wallets sponsor gas for their users outright. It depends entirely on the wallet the customer already has, so you can't count on it at checkout.


What it costs to get the money to you

With a custodial processor you never see this part. The customer pays the processor's address, the processor keeps a balance for you, and its own gas is folded into whatever it charges.

With direct on-chain settlement, the money has to actually move to you. On Klap, every charge gets its own address, and that address is a split contract that only exists on-chain once it's deployed. After the customer pays, two transactions happen: one deploys the split, and one pays it out to your wallet, any split recipients and the fee. An escrow charge adds a third, the release.

Those transactions cost gas on the same network the customer paid on. Klap passes that cost to the payer in a field called gasFeeAmount, quoted when the charge is created and already included in the amount they're asked to send.


How Klap quotes it

At creation, Klap reads the current gas price on each network the charge accepts and the native token's USD price, then works out what the deploy and payout transactions would cost. The quote is twice that cost, rounded up to six decimals, with a floor of one cent. The margin is there because gas can move between the moment the charge is created and the moment the payout runs, up to an hour later.

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

requestedAmount is the 49 you asked for. gasFeeAmount is the quoted gas. amount is what the customer pays, which includes it. If the charge accepts more than one network, the gas quote is the one for the most expensive network, so the customer sees one price no matter which network they pick.

If Klap can't read gas prices at that moment, the request fails with 503 gas_quote_unavailable instead of guessing. Retry after a moment. Gas fees are only quoted on EVM networks.

The gas fee is separate from Klap's own fee, which is 0.5% with no monthly charge. You choose who pays that one with feePayer. The gas fee always goes on the payer, because it's the cost of their payment specifically.


Why Ethereum gets its own charge

Taking the most expensive network's quote works well when the networks are close in cost. The L2s and the other cheap chains are close enough that the difference is usually small.

Ethereum mainnet isn't. If a charge accepted Ethereum next to Base, the quote would have to cover Ethereum gas, and a customer paying on Base would pay mainnet prices for a transaction that never touches mainnet. So Klap doesn't allow it. A charge that accepts Ethereum accepts only Ethereum, and mixing it with another network returns a 400.

The minimum charge on Ethereum is also higher, $5 live against $0.10 elsewhere. A $1 payment on mainnet doesn't make sense once gas is counted. If you want to offer Ethereum, create it as a separate option:

const mainnet = await klap.charges.create({
amount: 49.0,
expiresIn: 1800,
acceptedPayments: [{ token: 'USDC', network: 'ethereum' }],
})

Plenty of merchants skip it. Customers who hold USDC on mainnet can usually bridge it or send from an exchange that supports Base or Polygon, and if you're not sure which networks to offer, USDC vs USDT for merchants has the network-by-network picture.


What to show the customer

Show amount on the checkout as the total, gas included, and say it includes the network fee. If you only show 49 and the wallet asks for 49.01, customers think something is wrong.

Then mention the native token. A line like "You'll also need a little ETH on Base to send this" saves a support ticket for every customer who'd otherwise be stuck on "insufficient funds". On Arc you can skip it, since there's nothing else to hold. If you'd rather not build that screen at all, crypto payment links give you a hosted page to send instead.