← Blog
July 12, 2026 · 11 min

EIP-3009: the gasless transfer under every agent payment

x402 gets the headlines. But when an agent pays for an API call, the money does not move because of HTTP. It moves because of one function on a token contract: transferWithAuthorization. That function is defined by EIP-3009, and it is worth reading closely.

We have written about x402 as the HTTP-native payment rail and about when to reach for it versus a bearer key. Those posts stay at the protocol layer. This one goes down one level, to the crypto primitive that makes any of it work. If you operate agents that pay per call, this is the mechanism your money passes through, so you should know how it behaves and where it breaks.

The problem: approve, transferFrom, and the gas-token trap

Before EIP-3009, paying with an ERC-20 token was a two-transaction dance. First the payer sent an approve transaction to let a contract spend its tokens. Then someone sent a transferFrom to actually move them. Two on-chain transactions, two gas fees, for one payment.

The deeper problem was the gas token itself. To spend USDC on Ethereum, you had to hold ETH to pay for gas. A wallet with a hundred dollars of USDC and zero ETH could not move a cent. For a human this is an onboarding nuisance. For an autonomous agent it is a structural failure: the agent would need to manage a native-token balance on every chain it touches, top it up before it runs dry, and price gas volatility into every decision. That is a second treasury to babysit, and it defeats the point of an agent that just wants to pay for a model call.

EIP-3009 removes both problems by turning a transfer into a signed message.

How transferWithAuthorization works

Instead of sending an on-chain approve, the payer signs an off-chain authorization: a structured message that says "move this much of my balance, to this address, valid within this time window, identified by this nonce." The signature is produced with the payer's key but broadcasts nothing. A third party — a relayer, or in x402 terms a facilitator — takes that signed message and submits it to the token contract, paying the gas.

The contract does the verification itself. It reconstructs the EIP-712 typed-data hash, runs ecrecover to confirm the signature belongs to the from address, checks the nonce has never been used, and confirms the current block time falls inside the validity window. If every check passes, it moves the tokens. The payer never touched gas; the relayer did.

Here is the function, straight from the EIP-3009 specification:

function transferWithAuthorization(
    address from, address to, uint256 value,
    uint256 validAfter, uint256 validBefore,
    bytes32 nonce, uint8 v, bytes32 r, bytes32 s
) external;

Two design choices matter for agents. The nonce is a random 32-byte value, not a sequential counter. That means a wallet can sign many authorizations in parallel without worrying that they will fail out of order — critical for an agent firing concurrent paid requests. And the pair of validAfter / validBefore timestamps lets an authorization be scheduled: sign now, payable in two weeks, expired in three. Subscriptions, escrow, and milestone payments fall out of that window for free.

The message the payer actually signs is a typed struct. Its type hash is fixed in the spec:

// keccak256 of the TransferWithAuthorization type string
// TransferWithAuthorization(address from,address to,uint256 value,
//   uint256 validAfter,uint256 validBefore,bytes32 nonce)
bytes32 TYPEHASH =
  0x7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267;

EIP-3009 shipped as a Draft ERC in 2020, authored by Peter Jihoon Kim, Kevin Britz, and David Knott. Circle implemented it in the USDC v2 contract alongside EIP-2612 (permit). The two standards overlap but differ: permit authorizes an allowance that still needs a follow-up transfer, and it uses sequential nonces. EIP-3009 authorizes the entire transfer atomically and uses random nonces. For a payment rail, atomic-and-parallel wins.

The front-running trap: transfer vs receive

There is a second function with the same parameters, receiveWithAuthorization, and the difference between the two is a security boundary, not a convenience.

The traptransferWithAuthorization can be submitted by anyone. If a contract wraps it — say, "call transferWithAuthorization, then credit the deposit" — an attacker watching the mempool can extract the signed authorization and call the bare transferWithAuthorization directly, front-running the wrapper. The transfer executes, but the wrapper's follow-up logic never runs. The deposit lands and is never credited.

The spec's answer is receiveWithAuthorization, which adds one check: msg.sender must equal the payee. That makes the authorization useless to any front-runner, because only the intended recipient can submit it. The rule from the spec is blunt: use the receive variant whenever you call from another contract. If your settlement logic lives in a contract wrapper, and for agent billing it eventually will, this is not optional.

How x402 rides on EIP-3009

x402 is the HTTP layer; EIP-3009 is the settlement layer. When a resource server answers a request with 402 Payment Required, the client constructs an EIP-3009 authorization, signs it, and retries the request with the signed payload in an X-PAYMENT header. The server hands that payload to a facilitator, and the facilitator does two distinct jobs.

Verify is read-only. Per the exact-scheme EVM spec, the facilitator confirms the signature recovers to authorization.from, checks the payer holds enough balance, validates that the authorization's amount and recipient match what the server demanded, confirms the token and network line up, and simulates the transferWithAuthorization call to be sure it would succeed. No money has moved yet.

Settle is the write. The facilitator submits the real transferWithAuthorization transaction and pays the gas. The payload it carries is the same authorization the client signed:

// payload.authorization inside the X-PAYMENT header
{
  "from":        "0xAgentWallet...",
  "to":          "0xResourceServer...",
  "value":       "10000",        // 0.01 USDC, 6 decimals
  "validAfter":  "0",
  "validBefore": "1752345600",
  "nonce":       "0x9f2c...random32bytes"
}

The split matters. Verify lets the server decide whether to serve the response before paying to settle. Settle is where gas is spent and the transfer becomes final. Because the facilitator can only broadcast the signed authorization — not alter its amount or destination — the payer's risk is bounded by what they signed. That is the whole security argument for the model: the facilitator is trusted with liveness, not with funds.

x402's default asset method is EIP-3009 for tokens that support it, falling back to Permit2 for those that do not. Coinbase's hosted facilitator settles EIP-3009 payments in USDC and EURC across Base, Polygon, Arbitrum, World, and Solana, with a free tier of 1,000 transactions per month and $0.001 per transaction beyond it. Those are small numbers, and they are supposed to be — a payment primitive that costs a tenth of a cent to settle is what makes per-call billing plausible at all. We covered how a gateway turns that into usable balances in the reserve-proxy-settle billing internals.

Where the model runs out of room

EIP-3009 is elegant, but it is narrow, and an operator should know the edges before betting a fleet on it.

The first edge is token support. EIP-3009 is a contract feature, not a network feature, so it only works if the specific token implements it. USDC and EURC do. Tether's USDT, the largest stablecoin by supply, does not implement it on Ethereum — which is exactly why x402 falls back to Permit2 for non-3009 tokens and why USDC dominates agent payments today. There are also implementation gaps within the same brand: some bridged deployments, notably Polygon's bridged USDC, do not expose the full EIP-3009 surface. Always check that your target token on your target chain actually supports the method, and prefer receiveWithAuthorization where it exists.

The second edge is deeper: EIP-3009 is just a transfer. It moves value from A to B and stops. It cannot express "pay, then execute business logic, then mint access, then split revenue" as one atomic operation. For a single API call that is fine. For metered subscriptions, credits, escrow, or revenue splits, you need settlement logic in a contract, and that logic must use the receive variant to stay front-running safe. This is the frontier the ecosystem is now pushing on — moving from bare transfers to programmable settlement without giving up the gasless, signature-first model that made agents able to pay in the first place.

What it means for LLM4Agents

LLM4Agents charges agents per use in stablecoins over an OpenAI-compatible gateway. EIP-3009 is the primitive underneath that charge. Every design choice in the spec maps onto something we care about.

Random nonces mean an agent can sign many paid requests in parallel without serial ordering — exactly the concurrency profile of a fleet hitting the gateway. Gasless settlement means an agent funds itself once in USDC and never has to hold ETH, MATIC, or SOL to pay for inference; the one-treasury model we argued for is only possible because the transfer carries its own gas relay. The validity window gives us a native handle for authorization expiry and scheduled billing without inventing our own. And the verify/settle split is the seam where the gateway decides to serve a completion before the transfer finalizes — the same seam our own reserve-and-settle flow is built around.

The front-running rule is the sharpest lesson. Any settlement contract we or our facilitators run must use receiveWithAuthorization, not the bare transfer. A gateway that credits balance off a transferWithAuthorization call in a wrapper is one mempool watcher away from crediting nothing. This is not a theoretical concern; it is a code-review checklist item.

Staying on the frontier

Concrete steps, in order.

First, treat EIP-3009 support as a per-token, per-chain capability check, not an assumption. Maintain a matrix of which stablecoins on which chains expose transferWithAuthorization and, critically, receiveWithAuthorization — and route settlement accordingly, falling back to Permit2 only when forced. Do not let a Polygon bridged-USDC edge case surface as a failed agent payment.

Second, audit every settlement path against the front-running trap. Anywhere a contract wraps an authorization, require the receive variant and verify the payee check on-chain. Make it a gate in the deploy pipeline, not a comment in a design doc.

Third, build toward programmable settlement without abandoning the signature-first model. The next phase is "pay plus execute" as one atomic step — metering, credits, revenue splits — expressed in a contract that consumes receive-authorizations. Start with the read side: expose verify-style checks so agents can confirm a payment would clear before committing to it.

Fourth, stay token-agnostic at the interface and opinionated underneath. Agents should sign one authorization shape; the gateway should decide whether that becomes an EIP-3009 transfer, a Permit2 flow, or something newer, and absorb the difference. The primitive will keep evolving. The agent's experience should not.

Pay per call in stablecoins, no gas token required

LLM4Agents settles agent inference on the same gasless primitive x402 rides on. One USDC balance, an OpenAI-compatible endpoint, no ETH to babysit.

Register your agent