Permit2
About Permit2
OpenOcean Swap API supports Uniswap's Permit2 SignatureTransfer contract, which standardizes token approvals across applications and improves both UX and security.
Permit2 is a universal token approval contract from Uniswap. Instead of requiring users to first send an approve transaction and then a swap, Permit2 allows approvals to be handled through a single EIP-712 signature. This removes the "double transaction" flow and gives developers more control over allowance scope (amounts, deadlines, nonces).
For simpler integration, you may also consider using the AllowanceHolder contract, which requires a one-time approval without additional signatures.
Using Permit2 with OpenOcean Swap API
To integrate Permit2 (SignatureTransfer) into your swap flow:
- Check the current allowance
- (Optional) Approve Permit2 if needed
- Generate the Permit2 signature and data
- Build the transaction body
- Send the transaction with the Permit2 signature
1. Check the current allowance for Permit2
Verify whether the target token has granted Permit2 sufficient spending allowance.
1 import { ethers } from 'ethers'; 2 async function allowance() { 3 const rpcUrl = 'https://base.llamarpc.com'; 4 const token = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"; 5 const account = ""; 6 const permit2Address = "0x000000000022D473030F116dDEE9F6B43aC78BA3"; 7 const provider = new ethers.JsonRpcProvider(rpcUrl); 8 const contract = new ethers.Contract(token, ERC20_ABI, provider); 9 const allowance = await contract.allowance(account, permit2Address); 10 console.log(`allowance: ${allowance.toString()}`); 11 }
2. Approve Permit2 if needed (Optional)
If the allowance is insufficient, call the token's approve() method once to authorize Permit2.
1 import { ethers, Contract } from 'ethers'; 2 async function approve() { 3 const rpcUrl = 'https://base.llamarpc.com'; 4 const inTokenAddress = '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913'; 5 const privateKey = ''; 6 const permit2Address = '0x000000000022D473030F116dDEE9F6B43aC78BA3'; 7 let provider = new ethers.JsonRpcProvider(rpcUrl); 8 const wallet = new ethers.Wallet(privateKey, provider); 9 const contract = await new Contract(inTokenAddress, ERC20_ABI, wallet); 10 try { 11 await contract.approve(permit2Address, new BigNumber('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff').toFixed(0, 1)); 12 } catch (error) { return error; } 13 return true; 14 }
3. Generate the Permit2 signature and data
1 import { PERMIT2_ADDRESS, PermitTransferFrom, SignatureTransfer } from '@uniswap/permit2-sdk'; 2 import { ethers } from 'ethers'; 3 async function signAndBuildData() { 4 const privateKey = ""; 5 const token = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"; 6 const amount = "1000000000"; 7 const spender = "0x6352a56caadC4F1E25CD6c75970Fa768A3304e64"; // openocean contract 8 const nonce = 0; const chainId = 8453; const deadline = 1761877730; 9 const wallet = new ethers.Wallet(privateKey); 10 const permit = { permitted: { token, amount }, spender, nonce, deadline }; 11 const { domain, types, values } = SignatureTransfer.getPermitData(permit, PERMIT2_ADDRESS, chainId, undefined); 12 const signature = await wallet._signTypedData(domain, types, values); 13 const iface = new ethers.utils.Interface(PERMIT2_ABI); 14 const permitTransferFromData = [[[token, amount], nonce, deadline], [spender, amount], wallet.address, signature]; 15 const data = iface.encodeFunctionData('permitTransferFrom', permitTransferFromData); 16 return { signature, data }; 17 }
4. Build the transaction body
Use the OpenOcean Swap API response to construct the final on-chain transaction. Pass the permit data from step 3 as the permit parameter.
1 async function swap() { 2 const params = { 3 inTokenAddress: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913', 4 outTokenAddress: '0x4200000000000000000000000000000000000006', 5 slippage: 1, amount: 1000, gasPrice: 1, account: '0xB3cbe...', 6 permit: '0x30f28b7a000...' // build from step 3 data 7 }; 8 const { data } = await axios({ 9 url: `https://open-api.openocean.finance/v4/8453/swap`, 10 method: 'GET', params 11 }); 12 return data; 13 }
5. Send the transaction with the Permit2 signature
1 async function send_transaction() { 2 const rpcUrl = 'https://base.llamarpc.com'; 3 const privateKey = ''; 4 const provider = new ethers.JsonRpcProvider(rpcUrl); 5 const params = { from: '', to: '', gasPrice: '', data: '', value: '', gasLimit: '' }; // get from swap 6 const gasLimit = await provider.estimateGas(params); 7 params.gasLimit = gasLimit; 8 const wallet = new ethers.Wallet(privateKey, provider); 9 const { hash } = await wallet.sendTransaction(params); 10 return hash; 11 }
Supported Chains
| Chain Name | ChainId | Permit2 Address |
|---|---|---|
| Ethereum | 1 | 0x000000000022D473030F116dDEE9F6B43aC78BA3 |
| BNB Chain | 56 | 0x000000000022D473030F116dDEE9F6B43aC78BA3 |
| Polygon | 137 | 0x000000000022D473030F116dDEE9F6B43aC78BA3 |
| Arbitrum | 42161 | 0x000000000022D473030F116dDEE9F6B43aC78BA3 |
| Base | 8453 | 0x000000000022D473030F116dDEE9F6B43aC78BA3 |
| Linea | 59144 | 0x000000000022D473030F116dDEE9F6B43aC78BA3 |
| Optimism | 10 | 0x000000000022D473030F116dDEE9F6B43aC78BA3 |
| Avalanche | 43114 | 0x000000000022D473030F116dDEE9F6B43aC78BA3 |
| Sei | 1329 | 0x000000000022D473030F116dDEE9F6B43aC78BA3 |
| zkSync | 324 | 0x0000000000225e31D15943971F47aD3022F714Fa |
| Scroll | 534352 | 0x000000000022D473030F116dDEE9F6B43aC78BA3 |
| Celo | 42220 | 0x000000000022D473030F116dDEE9F6B43aC78BA3 |
| Gnosis | 100 | 0x000000000022D473030F116dDEE9F6B43aC78BA3 |
| Sonic | 146 | 0x000000000022D473030F116dDEE9F6B43aC78BA3 |
| Berachain | 80094 | 0x000000000022D473030F116dDEE9F6B43aC78BA3 |
| Unichain | 130 | 0x000000000022D473030F116dDEE9F6B43aC78BA3 |
| Swell | 1923 | 0x000000000022D473030F116dDEE9F6B43aC78BA3 |
| HyperEVM | 999 | 0x000000000022D473030F116dDEE9F6B43aC78BA3 |
| Plume | 98866 | 0x000000000022D473030F116dDEE9F6B43aC78BA3 |
| Tac | 239 | 0x000000000022D473030F116dDEE9F6B43aC78BA3 |
| Plasma | 9745 | 0x000000000022D473030F116dDEE9F6B43aC78BA3 |