Guide
Overview
OpenOcean Gasless Swap lets users trade tokens without paying gas fees - OpenOcean covers the transaction costs. This function supports batch gasless transactions to minimize on-chain expenses and leverages the Permit2 authorization mechanism for enhanced security and seamless signature approvals.
Supported Chains
const gaslessChain = [
'arbitrum', 'bsc', 'sonic', 'base', 'sei',
'eth', 'hyperevm', 'avax', 'uni'
]
API Endpoints
1. Get Swap Quote
Endpoint: GET /v4/gasless/{chainId}/quote
Parameters:
inTokenAddress
: Input token addressoutTokenAddress
: Output token addressamountDecimals
: Input amount (in smallest unit)slippage
: Slippage percentage (0-100)gasPrice
: Gas priceaccount
: User wallet address
2. Execute Gasless Swap
Endpoint: POST /v4/gasless/{chainId}/swap
Request Body:
{
from: walletAccount,
to: to,
data: data,
amountDecimals: amount,
feeAmount1: fee1,
feeAmount2: fee2,
flag: flags,
gasPriceDecimals: gasPrice,
deadline: deadline,
inToken: inTokenAddress,
outToken: outTokenAddress,
nonce: nonce,
permit: permit,
hash: hash
}
3. Query Transaction Status
Endpoint:
GET /v4/gasless/{chainId}/order?orderHash={orderHash}
Permit2 Signature Process
1. Get Permit2 Contract Address
const getPermit2ContractAddress = (chainCode) => {
const contract = {
"eth": "0x000000000022D473030F116dDEE9F6B43aC78BA3",
"arbitrum": "0x000000000022D473030F116dDEE9F6B43aC78BA3",
"base": "0x000000000022D473030F116dDEE9F6B43aC78BA3",
}[chainCode] || "0x000000000022D473030F116dDEE9F6B43aC78BA3";
return contract;
};
2. Create Permit2 Signature
const setPermit2Signer = async (amount, token, permit2Address) => {
const spender = '0xB1DD8E9ebbF5F150B75642D1653dF0dacd0bfF47';
const deadline = Math.floor(Date.now() / 1000) + 60 * 30;
const nonce = await getPermit2Nonce(spender);
const permitSingle = {
permitted: { token, amount },
spender, nonce, deadline
};
const { domain, types, values } = SignatureTransfer.getPermitData(
permitSingle, permit2Address, chainId
);
const signer = await provider.getSigner();
const signature = await signer.signTypedData(domain, types, values);
// Encode Permit2 data
const permitTransferfromData = [
[[token, amount], nonce, deadline],
[spender, amount], walletAccount, signature
];
const PERMIT2_INTERFACE = new Interface(Permit2Abi);
const data = PERMIT2_INTERFACE.encodeFunctionData("0x30f28b7a", permitTransferfromData);
return { permit: data, nonce, deadline, spender };
};
Complete Transaction Flow
1. Pre-flight Checks
if (!isWalletConnected) throw new Error('Please connect wallet');
if (!getIsGasLessChain(chain.chainCode)) throw new Error('Chain not supported');
2. Get Quote
const params = {
inTokenAddress: inToken.address,
outTokenAddress: outToken.address,
amountDecimals: fromAmount * 10 ** inToken.decimals,
slippage: slippage * 100,
gasPrice: gasPrice,
account: walletAccount
};
const res = await axios.get(`/v4/gasless/${chain.chainId}/quote?${Object.entries(params).map(([k,v]) => `${k}=${v}`).join('&')}`);
const { inAmount, data, to, fees, flags, hash } = res.data.data;
3. Token Approval
const permit2Address = await getPermit2ContractAddress(chain.chainCode);
if (!isNativeToken(inToken.address, chain.chainId)) {
const approveAmount = BigNumber('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF').toFixed(0);
await checkTokenApprove(inToken.address, permit2Address, fromAmountDecimals, gasPrice, approveAmount);
}
4. Execute Swap
const permitSign = await setPermit2Signer(inAmount, inToken.address, permit2Address);
const { permit, nonce, deadline } = permitSign;
const gasLessData = {
from: walletAccount, to, data, hash,
amountDecimals: params.amountDecimals,
feeAmount1: fees[0] ? (+fees[0].inFeeAmount * (10 ** fees[0].decimals)) : 0,
feeAmount2: fees[1] ? (+fees[1].inFeeAmount * (10 ** fees[1].decimals)) : 0,
flag: flags, gasPriceDecimals: gasPrice,
deadline, inToken: inToken.address, outToken: outToken.address,
nonce: Number(nonce), permit
};
const resGasless = await axios.post(`/v4/gasless/${chain.chainId}/swap`, gasLessData);
5. Poll Status
const getGasHashTimeout = async (orderHash, i) => {
await new Promise(resolve => setTimeout(resolve, 2000));
const res = await axios.get(`/v4/gasless/${chain.chainId}/order?orderHash=${orderHash}`);
if (res.data.data.hash) return res.data.data.hash;
if (i > 30) return null; // 60 second timeout
return getGasHashTimeout(orderHash, i + 1);
};
const hash = await getGasHashTimeout(resGasless.data.orderHash, 0);
Error Handling
Wallet not connected
Chain not supported for gasless
User rejected signature
Transaction failed
Timeout handling
Dependencies
{
"axios": "^1.0.0",
"ethers": "^6.0.0",
"bignumber.js": "^9.0.0",
"@uniswap/permit2-sdk": "^1.0.0"
}
Important Notes
Only supports specific chains
Non-native tokens require approval
Signatures valid for 30 minutes
Set reasonable slippage
Handle errors properly
Poll transaction status
Last updated