Skip to content
onchain-engineeringusdc
Joshua C.

How to Charge USDC Onchain Without Gas in the Customer Wallet

Learn to charge USDC onchain when your customer does not hold native gas. Covers EIP-3009, sponsored transfers, and practical integration tips.

How to Charge USDC Onchain Without Gas in the Customer Wallet

Payments onchain usually require the sender to pay a fee in native gas. This is a hurdle for users who only hold USDC. If you want to charge a customer onchain, but they do not have ETH, MATIC or another network token, you need another way. Sponsored transfers solve this, letting you pay gas for your user. This guide covers how sponsored USDC transfers work, the EIP-3009 standard, and when to use this pattern.

Why Native Gas is a Problem for USDC Payments

Most users expect to pay with USDC. But onchain transfers require gas, paid in the chain’s native token. If the customer’s wallet is empty of ETH or MATIC, the transaction fails. This is a common onboarding issue. You do not want to make users buy or bridge gas tokens just to pay you.

Sponsored transfers let you cover the gas. The customer signs an authorization, but a relayer submits the transaction and pays the network fee. The user never touches native gas.

EIP-3009: Transfer With Authorization

The EIP-3009 standard lets a customer sign a message granting permission to transfer USDC from their wallet. The message is not a transaction. It is an offchain signature. Anyone can submit this signature onchain, triggering the transfer. This pattern is sometimes called a meta-transaction.

The key fields in a transferWithAuthorization message are:

  • from: the customer’s address
  • to: the merchant’s receiving address
  • value: the USDC amount
  • validAfter/validBefore: a time window for validity
  • nonce: prevents replay
  • v, r, s: the ECDSA signature

The relayer wraps this data into a transaction and pays the network gas.

Flow: Charging USDC Without Customer Gas

Here is the general sequence:

  1. Build the authorization: Show the customer the details (amount, destination, validity window). They sign it with their wallet.
  2. Collect the signature: You (the merchant/server) receive the signed message.
  3. Relay the transaction: You or a relayer submit a transferWithAuthorization call to the USDC contract onchain, passing the signature. The relayer pays the gas.
  4. Settlement: The USDC transfers from the user’s address to yours. The relayer cannot intercept the funds. The user never had to hold native gas.

Worked Example: Charging 50 USDC Using EIP-3009

Suppose you want to charge 50 USDC from Alice, who has no ETH. You control the relayer.

  • Alice: 0xA...CE
  • Merchant: 0xM...NT
  • USDC Contract: 0xA0b...6e (on Base)

1. Alice Signs the Authorization

You display the payment details. Alice signs using her wallet. Here’s a TypeScript snippet for building and signing the message using ethers.js:

import { ethers } from "ethers";

const domain = {
  name: "USD Coin",
  version: "2",
  chainId: 8453, // Base mainnet
  verifyingContract: "0xA0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
};

const types = {
  TransferWithAuthorization: [
    { name: "from", type: "address" },
    { name: "to", type: "address" },
    { name: "value", type: "uint256" },
    { name: "validAfter", type: "uint256" },
    { name: "validBefore", type: "uint256" },
    { name: "nonce", type: "bytes32" }
  ]
};

const value = {
  from: "0xA...CE",
  to: "0xM...NT",
  value: ethers.utils.parseUnits("50", 6), // 50 USDC, 6 decimals
  validAfter: 0,
  validBefore: Math.floor(Date.now() / 1000) + 600, // valid for 10 mins
  nonce: ethers.utils.hexlify(ethers.utils.randomBytes(32)),
};

const signature = await signer._signTypedData(domain, types, value);

You collect the signature from Alice.

2. Relayer Submits the Transaction

Now your backend (or relayer) calls the USDC contract:

const usdcAbi = [
  "function transferWithAuthorization(address from, address to, uint256 value, uint256 validAfter, uint256 validBefore, bytes32 nonce, uint8 v, bytes32 r, bytes32 s)"
];

const contract = new ethers.Contract(
  "0xA0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
  usdcAbi,
  relayerSigner
);

const { v, r, s } = ethers.utils.splitSignature(signature);

await contract.transferWithAuthorization(
  value.from,
  value.to,
  value.value,
  value.validAfter,
  value.validBefore,
  value.nonce,
  v, r, s
);

The transaction is sent by the relayer, who pays the gas. Once confirmed, 50 USDC move from Alice to you. Alice did not need ETH.

3. Handling Failures

  • Signature rejected: If the signature is invalid, or the nonce is reused, the transaction reverts. Check that the details match what Alice saw.
  • Expired: If the relayer waits too long, validBefore will have passed. The transfer fails, but Alice keeps her funds.
  • Insufficient funds: If Alice has less than 50 USDC, the transfer fails. You cannot overdraft.

When to Use a Sponsored Transfer

This pattern is useful when:

  • Your users are new and do not have chain native tokens.
  • You want to abstract away gas fees.
  • You want to bill from a non-custodial wallet.

It does not fit if:

  • You need recurring, automated withdrawals without user action. EIP-3009 requires a fresh signature for each payment.
  • Your use case is not supported by USDC’s contract. Not all ERC-20 tokens have transferWithAuthorization.

Security Considerations

  • Always show the user the exact amount and destination before signing.
  • Set a short validity window. This limits risk if a signature leaks.
  • Never accept an unlimited approval or blank destination.
  • Store nonces to avoid replay.
  • Do not ask for a private key or seed phrase. Only collect signatures for the specific payment.

How We Use Sponsored Transfers at Reclack

We support gasless USDC payments for merchants who want to charge users without asking them to acquire ETH or MATIC. When a merchant chooses this mode, we generate a bounded authorization for the customer to sign. Our relayer settles the transaction and pays the gas. The transfer is onchain, non-custodial, and final. See more at reclack.com.

onchain-engineeringusdc