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 address

  • outTokenAddress: Output token address

  • amountDecimals: Input amount (in smallest unit)

  • slippage: Slippage percentage (0-100)

  • gasPrice: Gas price

  • account: 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

  1. Only supports specific chains

  2. Non-native tokens require approval

  3. Signatures valid for 30 minutes

  4. Set reasonable slippage

  5. Handle errors properly

  6. Poll transaction status

Last updated