ERC-3009 (TransferWithAuthorization) lets a token holder authorize a transfer with an off-chain signature instead of an on-chain transaction. Anyone can then submit that signature to the token contract and execute the transfer — the signer never needs ETH for gas. This page explains the standard and how SohoPay uses it to settle every payment. It assumes Solidity basics but no prior ERC-3009 experience.
The canonical spec is EIP-3009 on eips.ethereum.org. USDC implements it natively on every chain Circle deploys to, including Base.

Why permits

A standard ERC-20 transfer has two problems for agent payments:
  • The payer needs gas. An agent wallet would have to hold ETH on Base just to move USDC, and someone would have to keep it topped up.
  • Approve-then-transfer is two transactions. The approve/transferFrom pattern doubles latency and leaves standing allowances that are a well-known attack surface.
ERC-3009 solves both by separating authorization from execution: the payer signs an EIP-712 message saying “transfer X of my tokens to Y within this time window”, and any third party can submit it on-chain and pay the gas. The token contract verifies the signature and moves the funds directly — no allowance, no ETH in the payer’s wallet, one transaction. This is why USDC ships with ERC-3009 built in: it makes stablecoin payments feel like signing a check rather than operating a wallet.

The authorization message

transferWithAuthorization takes an EIP-712 signature over these fields:

Replay protection

Two mechanisms stop a signature from being reused:
  • Chain ID in the EIP-712 domain. The signature commits to the token contract’s address and chain ID via the domain separator, so a Base authorization is invalid on Base Sepolia, Ethereum mainnet, or any fork.
  • Random 32-byte nonce. Unlike sequential account nonces, ERC-3009 nonces are random and independent. The token contract records each (from, nonce) pair as used and rejects any second submission. Independence also means multiple authorizations can be signed concurrently without ordering — important for agents making parallel payments.
The validAfter/validBefore window adds a third bound: even an unsubmitted authorization dies at expiry. SohoPay sets this window to match its 10-minute order TTL.

How SohoPay uses it

Settlement in SohoPay is exactly the ERC-3009 split, with the MPC layer in between:
  1. The agent signs the authorization. When an agent pays an x402 challenge, the payment payload it signs is an ERC-3009 TransferWithAuthorization over USDC — payer is the agent’s wallet, recipient is SohoPay settlement, expiry matches the order TTL.
  2. The Policy Service co-signs. Under the 2-of-3 threshold scheme, the agent’s key share alone can’t produce a valid signature. The Policy Service checks the allowlist, credit, rate limits, and AML before contributing its share.
  3. SohoPay submits on-chain. SohoPay’s infrastructure calls transferWithAuthorization on the USDC contract on Base and pays the gas. The agent wallet never holds ETH.
This is why agents never need gas, private RPC access, or transaction management. Signing is the agent’s entire on-chain responsibility; submission is SohoPay’s.

Example: typed data for an authorization

The EIP-712 payload an agent signs for a 1 USDC payment on Base:
Typed data
On sandbox, the domain uses Base Sepolia’s chain ID (84532) and the Base Sepolia USDC contract — a signature made for one environment can never settle on the other.
You rarely construct this by hand. The SDKs build and sign the authorization for you, and the sandbox can auto-sign entirely (see the Quickstart). Build it manually only if you manage the agent key yourself — see Key Management.

Next steps

EIP-712 Signing

The typed-data signing standard that authorizations are encoded in.

MPC Signing

Why the agent’s signature alone can’t move funds.

Payment Flow

Where the signed authorization fits in the full payment lifecycle.