Back to Lessons
Lesson 11

EIP-2612 Permit: Gasless Approvals

+25EFFORT
5 sections
approve()
01
text

The classic approve() + transferFrom() flow needs TWO transactions: first you approve, then the contract pulls your tokens. That means two gas fees, and — critically — you must already hold ETH to pay for the approve. EIP-2612 fixes this with permit(): you sign an allowance off-chain (free, no gas), and anyone can submit that signature on-chain, often bundled into the very same transaction as the action.

02
code
solidity
1// EIP-2612 adds permit() to an ERC-20:
2function permit(
3 address owner,
4 address spender,
5 uint256 value,
6 uint256 deadline,
7 uint8 v, bytes32 r, bytes32 s
8) external;
9
10// The owner signs this EIP-712 typed struct off-chain:
11// Permit(address owner,address spender,uint256 value,
12// uint256 nonce,uint256 deadline)
13// nonces[owner] increments on each use -> no replay.
03
text

Here is the flow: the owner signs the EIP-712 typed data (owner, spender, value, the current nonce, and a deadline). The token's permit() recovers the signer with ecrecover, checks that block.timestamp <= deadline and that the nonce matches, then sets the allowance exactly as approve() would — but the gas is paid by whoever submits the signature, not necessarily the owner.

04
code
javascript
1// Sign off-chain with viem, then a router does permit + action atomically
2const signature = await walletClient.signTypedData({
3 domain: { name, version: "1", chainId, verifyingContract: token },
4 types: { Permit: [
5 { name: "owner", type: "address" },
6 { name: "spender", type: "address" },
7 { name: "value", type: "uint256" },
8 { name: "nonce", type: "uint256" },
9 { name: "deadline", type: "uint256" },
10 ]},
11 primaryType: "Permit",
12 message: { owner, spender, value, nonce, deadline },
13});
14// split signature -> v, r, s and pass to token.permit(...)
05
note
Key Insight

Security: a permit signature is a bearer authorization. If a phishing site tricks you into signing one, the attacker can submit it to grant themselves an allowance — just as dangerous as a malicious approve(). The deadline bounds how long it is valid, and the per-owner nonce makes each signature single-use, but always check what you are signing.

Complete

Connect your wallet to mark this lesson as complete

Earn 25 EFFORT tokens