# Permit2 — Uniswap का Token Approval Revolution
Token approvals annoying हैं — हर नए dapp के लिए एक approval tx।
Permit2 इसे solve करता है: एक approval, सभी apps के लिए।
Problem: Approval Hell
Traditional Flow
// User wants to swap USDC on Uniswap
approve(uniswap, type(uint).max) // Tx 1
swap(...) // Tx 2
// Now user wants to use Curve
approve(curve, type(uint).max) // Tx 3
swap(...) // Tx 4
// Now 1inch
approve(1inch, type(uint).max) // Tx 5
...
Problems:
- Gas waste (approval tx हर app के लिए)
- Security risk (infinite approvals scattered)
- UX nightmare (2 tx for every new app)
Permit2 Solution
// Once per token
approve(Permit2, type(uint).max) // One-time
// Then use any app (via signatures, gasless!)
Uniswap.swap(permit2Signature) // ✅ No approval tx
Curve.swap(permit2Signature) // ✅ No approval tx
1inch.swap(permit2Signature) // ✅ No approval tx
Benefits:
- One approval per token (lifetime)
- Apps don't touch token directly
- Revocable permissions
- Gasless (signature-based)
Architecture
┌─────────┐
│ User │
└────┬────┘
│ 1. Approve Permit2 (once)
▼
┌─────────────┐
│ Token │
└──────┬──────┘
│
│ 2. Permit2 can transfer
▼
┌─────────────┐
│ Permit2 │ ← Shared contract
└──────┬──────┘
│
│ 3. App requests transfer via Permit2
▼
┌─────────────┐
│ Uniswap │
│ Curve │
│ 1inch │
└─────────────┘
Two Transfer Modes
1. AllowanceTransfer
App-specific allowances through Permit2:
interface IAllowanceTransfer {
function approve(
address token,
address spender, // App (Uniswap, etc)
uint160 amount,
uint48 expiration // Time-bound!
) external;
function transferFrom(
address from,
address to,
uint160 amount,
address token
) external;
}
Example:
// User approves Uniswap via Permit2 (on-chain tx)
permit2.approve(USDC, uniswapRouter, 1000e6, block.timestamp + 1 days);
// Uniswap transfers via Permit2
permit2.transferFrom(user, pool, 1000e6, USDC);
Benefit: Expiring allowances (no infinite approvals)।
2. SignatureTransfer (Gasless!)
Transfer via signature — no approval tx:
struct PermitTransferFrom {
TokenPermissions permitted; // token + amount
address spender; // App
uint256 nonce;
uint256 deadline;
}
struct TokenPermissions {
address token;
uint256 amount;
}
User signs:
const permit = {
permitted: {
token: USDC_ADDRESS,
amount: 1000e6
},
spender: UNISWAP_ROUTER,
nonce: await permit2.nonces(userAddress),
deadline: Math.floor(Date.now() / 1000) + 3600
}
const signature = await wallet._signTypedData(domain, types, permit)
App uses:
function swap(PermitTransferFrom calldata permit, bytes calldata signature) external {
// Permit2 verifies signature + transfers token
permit2.permitTransferFrom(
permit,
SignatureTransferDetails({
to: address(this),
requestedAmount: permit.permitted.amount
}),
msg.sender,
signature
);
// Now we have tokens, execute swap
_executeSwap();
}
UX:
User clicks "Swap"
→ Wallet shows signature request (free!)
→ App submits tx with signature
→ Done (1 tx total, user pays 0 gas for approval)
Integration Example
DEX Router
import {ISignatureTransfer} from "permit2/interfaces/ISignatureTransfer.sol";
contract MyDEX {
ISignatureTransfer public immutable permit2;
constructor(address _permit2) {
permit2 = ISignatureTransfer(_permit2);
}
function swapWithPermit(
ISignatureTransfer.PermitTransferFrom calldata permit,
bytes calldata signature,
address tokenOut,
uint amountOutMin
) external returns (uint amountOut) {
// Transfer tokenIn from user via Permit2
permit2.permitTransferFrom(
permit,
ISignatureTransfer.SignatureTransferDetails({
to: address(this),
requestedAmount: permit.permitted.amount
}),
msg.sender,
signature
);
// Execute swap
amountOut = _swap(
permit.permitted.token,
tokenOut,
permit.permitted.amount,
amountOutMin
);
// Send output to user
IERC20(tokenOut).transfer(msg.sender, amountOut);
}
}
Frontend
import { SignatureTransfer } from '@uniswap/permit2-sdk'
const permit: PermitTransferFrom = {
permitted: {
token: USDC.address,
amount: parseUnits('1000', 6)
},
spender: DEX_ROUTER,
nonce: await permit2.nonces(account),
deadline: Math.floor(Date.now() / 1000) + 3600
}
const { domain, types, values } = SignatureTransfer.getPermitData(
permit,
PERMIT2_ADDRESS,
chainId
)
const signature = await signer._signTypedData(domain, types, values)
// Submit tx
await dex.swapWithPermit(permit, signature, DAI.address, minAmountOut)
Security Properties
1. Expiration
// Allowance expires after 1 day
permit2.approve(token, spender, amount, block.timestamp + 1 days);
Traditional approvals = forever। Permit2 = time-bound।
2. Nonces
// Each signature uses unique nonce
mapping(address => uint256) public nonces;
Prevents replay attacks।
3. Witness Data
struct PermitWitnessTransferFrom {
PermitTransferFrom permit;
bytes32 witness; // Additional data user signs
}
Example: User signs swap params too (slippage, deadline)।
bytes32 witness = keccak256(abi.encode(
tokenOut,
amountOutMin,
deadline
));
Prevents parameter manipulation।
4. Permit2 is Immutable
// Deployed by Uniswap, audited, immutable
// Address: 0x000000000022D473030F116dDEE9F6B43aC78BA3
No upgrade risk — battle-tested code।
Gotchas
User Still Needs Initial Approval
// User must approve Permit2 once per token
IERC20(token).approve(permit2Address, type(uint256).max);
UX: First tx per token still needed।
Mitigation: Batch approvals (approve multiple tokens in one tx)।
Not All Apps Support
2026 में adoption growing लेकिन universal नहीं:
- ✅ Uniswap, 1inch, Cowswap
- ✅ Most new DEXs
- ❌ Many old contracts (pre-2023)
Gas Overhead
Signature verification ~3-5k gas extra:
Traditional: approve() + transferFrom() = 50k gas
Permit2: permitTransferFrom() = 55k gas (first time)
permitTransferFrom() = 35k gas (subsequent, cached)
Net: Saves gas after first use।
Adoption (2026)
Protocols Using Permit2
- Uniswap V3/V4: Default
- 1inch: Full support
- Cowswap: Intent-based (Permit2 perfect fit)
- Across: Cross-chain bridging
- Most ERC-7683 implementations
How to Check
// Check if contract supports Permit2
try IPermit2User(contract).permit2() returns (address p2) {
// Supports Permit2 ✅
} catch {
// Fallback to traditional approve
}
Comparison
| Method | Approval Cost | Transfer Cost | Expiry | Revocable |
|--------|---------------|---------------|--------|-----------|
| Traditional | 45k gas | 30k gas | Never | Manual |
| Permit2 Allowance | 0 (signature) | 35k gas | Yes | Yes |
| Permit2 Signature | 0 (signature) | 40k gas | Per-tx | N/A |
Conclusion
Permit2 new standard बन रहा है — खासकर intents और gasless UX के लिए।
When to use:
- New dapp building? Integrate Permit2 from day 1
- Existing app? Add Permit2 as optional flow
- User-facing product? Gasless approvals = better UX
Adoption tips:
- Use
@uniswap/permit2-sdk(official SDK)
- Support fallback to traditional approvals
- Educate users (one-time approval per token)
2026 में Permit2 = best practice for token transfers। Ignore at your own UX peril। ✅