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.
1// EIP-2612 adds permit() to an ERC-20:2function permit(3address owner,4address spender,5uint256 value,6uint256 deadline,7uint8 v, bytes32 r, bytes32 s8) external;910// 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.
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.
1// Sign off-chain with viem, then a router does permit + action atomically2const signature = await walletClient.signTypedData({3domain: { name, version: "1", chainId, verifyingContract: token },4types: { 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]},11primaryType: "Permit",12message: { owner, spender, value, nonce, deadline },13});14// split signature -> v, r, s and pass to token.permit(...)
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.
Connect your wallet to mark this lesson as complete
Earn 25 EFFORT tokens