ERC-2612 is an extension to the ERC-20 token standard that lets users approve token spending through a signed off-chain message rather than an onchain transaction. The standard adds a single function, permit(), that accepts an EIP-712 typed-data signature and grants an allowance in the same call as the spender's transferFrom. Two transactions collapse into one. The user pays no gas to approve.
The standard was finalized in March 2020 as EIP-2612 and is implemented natively by USDC, DAI, USDS, and most ERC-20s deployed since 2021. Uniswap's Permit2 contract generalized the pattern to every ERC-20 in 2022, and meta-transaction relayers, account-abstraction wallets, and gasless DEX aggregators now rely on permit signatures as a baseline. This article covers what ERC-2612 does, the EIP-712 mechanics behind it, how Permit2 differs, where adoption stands, and the security tradeoffs every integrator should understand before accepting a signed permit.
What Is ERC-2612?
ERC-2612 is an optional extension to the ERC-20 token standard authored by Martin Lundfall, Ansgar Dietrichs, Alex Forshtat, Ronan Sandford, and Sebastian Siemssen. The standard adds three external functions to a compliant token:
permit(owner, spender, value, deadline, v, r, s)— verifies an EIP-712 signature and setsallowance[owner][spender] = valuenonces(owner)— returns the current permit nonce for replay protectionDOMAIN_SEPARATOR()— returns the EIP-712 domain hash unique to this token contract
The signature itself is computed off-chain, usually by the user's wallet through eth_signTypedData_v4. The signed message commits the user to a specific spender, value, nonce, and deadline. Anyone can then submit the signature on-chain by calling permit(), which costs gas but does not need to come from the token owner. A relayer, a DEX router, or the spender themselves can pay that gas. The owner pays nothing and sends no transaction to grant the allowance.
This matters because legacy ERC-20 token approvals require two separate onchain transactions: approve(spender, value) from the owner, then transferFrom(owner, recipient, value) from the spender. The user funds gas twice and waits for two confirmations. ERC-2612 collapses the approval into a free signature and lets the spender batch permit() and transferFrom() into a single transaction. The article on the evolution of token approvals from ERC-20 to Permit3 traces this progression in detail.
How Does ERC-2612 Work?
ERC-2612 is a wrapper around EIP-712 typed-structured-data hashing and signing. The mechanism has four steps: the wallet builds the typed message, the user signs it, a relayer or spender submits the signature, and the token contract verifies it before mutating allowance state.
The typed message follows a fixed schema defined in the EIP:
owner: address granting the allowancespender: address receiving the allowancevalue: amount approved (in token base units)nonce: the current value ofnonces(owner), used once per signaturedeadline: a Unix timestamp after which the signature is invalid
The wallet hashes this struct together with the token's DOMAIN_SEPARATOR, which encodes the chain ID, contract address, and a version string. The domain separator scopes the signature to one specific token on one specific chain, which is what prevents cross-chain replay even when the same address holds the same token on two networks.
When permit() is called, the contract reconstructs the same hash, recovers the signer address with ecrecover, checks that the recovered address equals owner, validates that block.timestamp <= deadline, increments nonces[owner], and writes allowance[owner][spender] = value. The next instruction in the same transaction is usually a transferFrom from the spender that consumes the freshly granted allowance.
Replay protection comes from the per-owner nonce. Each successful permit() bumps the nonce by one, which invalidates every signature pinned to the prior value. The deadline supplies a second bound: even if a signature leaks before being submitted, it expires at a known timestamp. Most integrations set deadlines between 30 minutes and 24 hours.
ERC-2612 vs Permit2 vs ERC-3009
Three signature-based approval primitives circulate in production today, and they solve overlapping problems with different tradeoffs.
ERC-2612 is implemented inside the token contract itself. To use it, the token must include permit() in its bytecode. USDC, DAI, USDS, and most post-2021 ERC-20s do. Older tokens like WETH, MKR, and the original USDT do not, which means they cannot be used with ERC-2612 even when the consuming protocol supports it.
Permit2, deployed by Uniswap in November 2022, sidesteps the in-token requirement. The contract sits at 0x000000000022D473030F116dDEE9F6B43aC78BA3 on every major EVM chain. A user gives Permit2 a one-time max-approval through standard approve(), then signs Permit2-format messages for every subsequent spend. Any ERC-20 the user holds becomes permit-compatible without touching the token contract. Permit2 also adds batch transfers, multi-spender approvals, and a universal nonce scheme. The dedicated comparison at ERC-2612 Permit vs Permit2 details the design differences.
EIP-3009 is a parallel standard, finalized in 2020 alongside ERC-2612, that adds transferWithAuthorization and receiveWithAuthorization. Instead of granting an allowance, the signature authorizes a single transfer of a specific amount to a specific recipient. USDC ships with both ERC-2612 and EIP-3009, which is why USDC integrations sometimes use one and sometimes the other. The article on EIP-3009 authorization transfers covers when each fits.
Property | ERC-2612 | Permit2 | EIP-3009 |
Lives in | Token contract | Standalone contract | Token contract |
Granularity | Allowance (reusable) | Allowance or transfer | Single transfer |
Pre-approval needed | No | Yes (one-time max to Permit2) | No |
Works with legacy tokens | No | Yes | No |
Batch operations | No | Yes | No |
Replay protection | Per-owner nonce | Per-word bitmap nonce | Per-message nonce |
Adoption Across Major Tokens and Protocols
USDC began supporting ERC-2612 in August 2022 with the USDC 2.0 upgrade. The Circle-issued contract on Ethereum at 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 exposes permit(), nonces(), and DOMAIN_SEPARATOR(). Today USDC supply sits at $77.6B according to the live DeFiLlama stablecoin dashboard, and the same contract pattern ships across all 13+ chains where Circle issues native USDC. The article on Circle CCTP V2 and native USDC covers Circle's broader infrastructure.
DAI was the earliest mainstream stablecoin to ship permit. The MakerDAO team added it to the DAI contract in November 2019, predating EIP-2612 itself and informing the standard. DAI's permit interface is slightly older than the finalized EIP and uses an allowed boolean rather than a numeric value, which is why some integrations include a separate code path for DAI. DAI supply currently stands at $4.7B, with USDS (Sky's successor) at $8.4B per DeFiLlama.
Uniswap's Permit2 contract has processed signatures for USDT, WETH, WBTC, and dozens of other legacy tokens that lack native permit. The Universal Router, which is the default routing path for the Uniswap web app, uses Permit2 for every swap that involves a non-2612 token. 1inch and CoW Swap added Permit2 support in 2023, and aggregators like 0x route through Permit2 when the input token doesn't ship with native permit.
Account-abstraction wallets such as Safe and Argent use permit signatures internally to enable session keys and gasless operations. ERC-4337 bundlers can wrap a permit signature inside a UserOperation, letting the bundler pay gas while the underlying token approval still verifies cleanly onchain. The article on account abstraction for stablecoin apps walks through how this fits with smart-account wallets.
Use Cases: Where Permit Pays Off
Permit signatures unlock five concrete user experiences that legacy approvals cannot match.
Gasless swaps. A user signs a permit and a swap intent off-chain. A solver or relayer submits both in one transaction, using their own ETH for gas. The user receives the output token without ever holding ETH. CoW Swap and 1inch Fusion both work this way for any 2612-compliant input token.
Sponsored transactions. An application can pay gas on behalf of users by submitting permit() + transferFrom() from a paymaster address. Stablecoin payment apps use this pattern to onboard users who hold only USDC and no native gas token.
Single-click DEX swaps. Without permit, a first-time Uniswap user pays gas twice (approve, then swap). With permit, the swap router calls permit() and swapExactTokensForTokens() in one transaction. Uniswap's Universal Router has cut median first-swap gas by roughly 40% on tokens that support permit.
Subscription and recurring payments. Combined with ERC-7715 wallet permissions, permit signatures let users authorize recurring pulls without keeping a session-key wallet open. The signature scopes the spend to a fixed amount and deadline, then expires.
Cross-chain stablecoin movement. Stablecoin orchestration platforms like Eco use permit to skip the approve step on the source chain. The user signs once, and the routing layer handles the swap, bridge, and destination delivery in one user-visible action. Eco Routes uses permit signatures within its execution layer to let users move USDC, DAI, USDe, PYUSD, and other 2612-compliant stablecoins across 15 chains without separate approval transactions.
Security Considerations and Common Pitfalls
Permit signatures shift gas costs but introduce a phishing surface that didn't exist with onchain approvals. Several incident patterns are now well documented.
Phishing through opaque signatures. Wallets historically displayed EIP-712 signatures as raw JSON or as a generic "sign this message" prompt. Attackers built fake DEX frontends that prompted users to sign a permit granting an attacker the user's full USDC balance. The user thought they were signing a swap intent. By 2023, most wallets including MetaMask and Rabby added human-readable EIP-712 rendering that names the spender, value, and deadline in plain text. Users on outdated wallet versions remain vulnerable.
Infinite-value approvals. Many integrations request value = 2^256 - 1 ("max uint256") to avoid prompting the user again on subsequent transactions. A signed max permit, if leaked or phished, drains the entire token balance whenever the deadline allows. The conservative pattern is to scope value to the exact amount needed for the current operation, accepting the friction of a fresh signature on each spend.
Deadline misuse. Setting the deadline to type(uint256).max turns a permit into a permanent allowance redeemable at any time. The 2022 Inverse Finance incident on a permit-style flow underscored why deadlines should match the use-case window, not the maximum the type allows.
Front-running and griefing. Because anyone can submit a permit signature, an attacker who sees a pending permit transaction can front-run it with the same signature, forcing the legitimate transaction to fail with "nonce already used." This is mostly a UX bug rather than a fund-loss vector, but it can lock users out of their own approvals until they sign a new permit with the next nonce.
Domain-separator confusion. Tokens deployed with proxy patterns sometimes change implementation contracts. If the upgrade modifies the domain separator (for example, changing the contract version string), every previously-signed permit becomes invalid. USDC's 2022 upgrade managed this carefully; some smaller tokens have not.
Wallet teams have responded with three mitigations: structured signature rendering, explicit allowance limits in the prompt UI, and deadline warnings when the timestamp exceeds 24 hours. Application teams should set short deadlines, request only the value they need, and verify they handle the DAI-flavored permit interface separately from the EIP-2612 standard.
Code Walkthrough: Signing and Submitting a Permit
The end-to-end flow on USDC, using ethers.js v6 and a standard EOA wallet:
First, the spender (a DEX router or paymaster) builds the typed-data domain and message:
Domain:
{ name: "USD Coin", version: "2", chainId: 1, verifyingContract: USDC_ADDRESS }Types: the EIP-2612
Permitstruct withowner,spender,value,nonce,deadlineMessage: filled with the actual approval parameters and the current
nonces(owner)read from chain
The wallet calls signer.signTypedData(domain, types, message), which prompts the user with a structured permission display. The returned signature splits into v, r, s components.
The spender then submits a transaction that calls USDC.permit(owner, spender, value, deadline, v, r, s) followed by USDC.transferFrom(owner, recipient, value). Both calls live in a single transaction, often through a multicall or router contract. If permit() reverts (expired deadline, invalid signature, wrong nonce), the entire transaction reverts and no allowance state changes.
For Permit2 the flow is similar, but the spender's batch first reads Permit2.allowance(owner, token, spender) to confirm the user has done the one-time max-approval to Permit2 itself. The signature is then verified by Permit2 rather than the token contract, and the actual transferFrom goes through Permit2's permitTransferFrom function. The OpenZeppelin ERC20Permit reference implementation is the canonical starting point for any new token deployment that wants ERC-2612 support.
How Permit Fits Into Eco's Stablecoin Execution Layer
Cross-chain stablecoin movement compounds the gas problem. A user moving USDC from Arbitrum to Base traditionally pays for an approve on Arbitrum, a bridge transaction on Arbitrum, and sometimes a separate approve on Base before the funds become spendable. Eco's execution layer collapses this into a single signed intent. Permit handles the first leg: the user signs an ERC-2612 permit that grants Eco's solver the ability to pull the source-chain stablecoin. The solver pays gas, executes the bridge, and delivers funds on the destination chain in one user-visible action.
This pattern works across the 15 chains Eco supports because USDC, USDe, PYUSD, and other major stablecoins ship with native ERC-2612 across every Eco-supported network. For tokens that lack permit (older USDT deployments, some bridged stablecoins), Eco's routing falls back to Permit2 or to a standard approve when neither is available. The user-facing experience is the same; only the underlying signature mechanism differs.
FAQ
Is ERC-2612 the same as Permit1?
Yes. "Permit1" is informal shorthand for the original ERC-2612 standard, used to distinguish it from Uniswap's Permit2 contract. The EIP itself is titled "Permit Extension for EIP-20 Signed Approvals" and was finalized in March 2020.
Does USDT support ERC-2612?
The original Tether USDT contract on Ethereum does not implement permit(). Newer USDT deployments on Solana, Tron, and certain Layer 2s vary by issuer. Applications that need permit-style approvals on USDT typically route through Uniswap's Permit2 contract, which supplies the same UX without requiring changes to the token itself.
What's the difference between ERC-2612 and EIP-712?
EIP-712 is the underlying standard for hashing and signing typed structured data; it is the cryptographic primitive. ERC-2612 is a token-specific application of EIP-712 that defines a fixed Permit struct and a permit() function on the token contract. Every ERC-2612 signature is an EIP-712 signature, but EIP-712 has many other uses including order signatures, off-chain governance votes, and authorization tokens.
Can a permit signature be revoked once signed?
Yes, but only by spending the nonce. The owner can call permit() with a new signature on the same nonce (granting any spender any value), or any participant can submit a different valid signature pinned to that nonce. Once the nonce increments, every previously-signed permit at the prior nonce becomes unusable. There is no explicit "cancel" function in the standard.
Why do some tokens use a different permit interface than USDC?
DAI's permit interface predates EIP-2612 and uses an allowed boolean instead of a numeric value, accepting either full approval or full revocation. Wallets and integrators detect the DAI-style interface by checking the contract's PERMIT_TYPEHASH. The article on EIP vs ERC distinctions covers why standards drift across implementations.

