Permit2

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:

  1. Check the current allowance
  2. (Optional) Approve Permit2 if needed
  3. Generate the Permit2 signature and data
  4. Build the transaction body
  5. 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.

Terminal
JS
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.

Terminal
JS
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

Terminal
JS
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.

Terminal
JS
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

Terminal
JS
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 NameChainIdPermit2 Address
Ethereum10x000000000022D473030F116dDEE9F6B43aC78BA3
BNB Chain560x000000000022D473030F116dDEE9F6B43aC78BA3
Polygon1370x000000000022D473030F116dDEE9F6B43aC78BA3
Arbitrum421610x000000000022D473030F116dDEE9F6B43aC78BA3
Base84530x000000000022D473030F116dDEE9F6B43aC78BA3
Linea591440x000000000022D473030F116dDEE9F6B43aC78BA3
Optimism100x000000000022D473030F116dDEE9F6B43aC78BA3
Avalanche431140x000000000022D473030F116dDEE9F6B43aC78BA3
Sei13290x000000000022D473030F116dDEE9F6B43aC78BA3
zkSync3240x0000000000225e31D15943971F47aD3022F714Fa
Scroll5343520x000000000022D473030F116dDEE9F6B43aC78BA3
Celo422200x000000000022D473030F116dDEE9F6B43aC78BA3
Gnosis1000x000000000022D473030F116dDEE9F6B43aC78BA3
Sonic1460x000000000022D473030F116dDEE9F6B43aC78BA3
Berachain800940x000000000022D473030F116dDEE9F6B43aC78BA3
Unichain1300x000000000022D473030F116dDEE9F6B43aC78BA3
Swell19230x000000000022D473030F116dDEE9F6B43aC78BA3
HyperEVM9990x000000000022D473030F116dDEE9F6B43aC78BA3
Plume988660x000000000022D473030F116dDEE9F6B43aC78BA3
Tac2390x000000000022D473030F116dDEE9F6B43aC78BA3
Plasma97450x000000000022D473030F116dDEE9F6B43aC78BA3

Next Steps