Skip to main content

Quickstart: minimal working example

The following example demonstrates a complete transaction flow:
  1. Fetches a quote from delora API for token swap
  2. Checks and approves token allowance if needed
  3. Executes the transaction using ethers.js v5
⚠️ Security Note: Never hardcode private keys or mnemonics in production code. Use environment variables or secure key management solutions.
import { ethers, BigNumber, Contract } from 'ethers';

const API_URL = 'https://api.delora.build/v1';

const ERC20_ABI = [
  'function allowance(address owner, address spender) view returns (uint256)',
  'function approve(address spender, uint256 amount) returns (bool)',
] as const;

const getQuote = async (params: {
  originChainId: number;
  destinationChainId: number;
  amount: string;
  originCurrency: string;
  destinationCurrency: string;
  tradeType: 'EXACT_INPUT' | 'EXACT_OUTPUT';
  senderAddress?: string;
  receiverAddress?: string;
}) => {
  const query = new URLSearchParams();
  
  for (const [key, value] of Object.entries(params)) {
    if (value !== undefined) {
      query.append(key, String(value));
    }
  }
  
  const url = `${API_URL}/quotes?${query.toString()}`;
  const response = await fetch(url);
  
  if (!response.ok) {
    throw new Error(`Failed to get quote: ${response.status} ${response.statusText}`);
  }
  
  return response.json();
};

export const executeSwap = async (provider: ethers.providers.Web3Provider) => {
  await provider.send('eth_requestAccounts', []);
  const wallet = provider.getSigner();
  const walletAddress = await wallet.getAddress();

  const originChainId = 137;
  const destinationChainId = 137;
  const originCurrency: string = '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359';
  const destinationCurrency = '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174';
  const amount = ethers.utils.parseUnits('100', 6).toString();

  const quote = await getQuote({
    originChainId,
    destinationChainId,
    amount,
    originCurrency,
    destinationCurrency,
    tradeType: 'EXACT_INPUT',
    senderAddress: walletAddress,
    receiverAddress: walletAddress,
  });

  if (originCurrency !== ethers.constants.AddressZero) {
    const token = new Contract(originCurrency, ERC20_ABI, wallet);
    const allowance = await token.allowance(walletAddress, quote.calldata.to);
    
    if (allowance.lt(BigNumber.from(quote.inputAmount))) {
      await (await token.approve(quote.calldata.to, ethers.constants.MaxUint256)).wait();
    }
  }

  const tx = await wallet.sendTransaction({
    to: quote.calldata.to,
    data: quote.calldata.data,
    value: quote.calldata.value,
    ...(quote.gas?.gasLimit && { gasLimit: BigNumber.from(quote.gas.gasLimit) }),
    ...(quote.gas?.maxFeePerGas && { maxFeePerGas: BigNumber.from(quote.gas.maxFeePerGas) }),
    ...(quote.gas?.maxPriorityFeePerGas && { maxPriorityFeePerGas: BigNumber.from(quote.gas.maxPriorityFeePerGas) }),
    ...(quote.gas?.gasPrice && { gasPrice: BigNumber.from(quote.gas.gasPrice) }),
  });

  await tx.wait();
  
  return tx.hash;
};

React Integration Example

For React applications, you can integrate the swap function as follows:
import { ethers } from 'ethers';
import { executeSwap } from './example';

const SwapButton = () => {
  const handleSwap = async () => {
    if (!window.ethereum) {
      alert('Please install MetaMask or another Web3 wallet');
      return;
    }
    
    const provider = new ethers.providers.Web3Provider(window.ethereum);
    
    try {
      const txHash = await executeSwap(provider);
      console.log('Transaction hash:', txHash);
    } catch (error) {
      console.error('Swap failed:', error);
    }
  };

  return (
    <button onClick={handleSwap}>
      Execute Swap
    </button>
  );
};

Breaking it down

1. Building the quote request

The getQuote function constructs a query string from parameters and fetches the quote from the API:
const getQuote = async (params: {
  originChainId: number;
  destinationChainId: number;
  amount: string;
  originCurrency: string;
  destinationCurrency: string;
  tradeType: 'EXACT_INPUT' | 'EXACT_OUTPUT';
  senderAddress?: string;
  receiverAddress?: string;
}) => {
  const query = new URLSearchParams();
  
  for (const [key, value] of Object.entries(params)) {
    if (value !== undefined) {
      query.append(key, String(value));
    }
  }
  
  const url = `${API_URL}/quotes?${query.toString()}`;
  const response = await fetch(url);
  
  if (!response.ok) {
    throw new Error(`Failed to get quote: ${response.status} ${response.statusText}`);
  }
  
  return response.json();
};
Key parameters:
  • originChainId / destinationChainId: Chain IDs (e.g., 1 for Ethereum, 137 for Polygon)
  • amount: Token amount in smallest unit (wei, satoshi, etc.)
  • originCurrency / destinationCurrency: Token contract addresses (0x0...0 for native tokens)
  • tradeType: 'EXACT_INPUT'
  • senderAddress / receiverAddress: Optional wallet addresses

2. Wallet provider setup

The function accepts a Web3Provider from ethers.js, which can be obtained from various sources: Browser extension (MetaMask, etc.):
const provider = new ethers.providers.Web3Provider(window.ethereum);
RPC endpoint:
const provider = new ethers.providers.JsonRpcProvider('https://polygon-rpc.com', 137);
With wallet from mnemonic (development only):
const provider = new ethers.providers.JsonRpcProvider('https://polygon-rpc.com', 137);
const wallet = ethers.Wallet.fromMnemonic(process.env.MNEMONIC!).connect(provider);
⚠️ Never hardcode mnemonics or private keys in production code.

3. Token allowance and approval

For ERC-20 tokens (non-native), the contract must be approved to spend tokens on behalf of the user:
if (originCurrency !== ethers.constants.AddressZero) {
  const token = new Contract(originCurrency, ERC20_ABI, wallet);
  const allowance = await token.allowance(walletAddress, quote.calldata.to);
  
  if (allowance.lt(BigNumber.from(quote.inputAmount))) {
    await (await token.approve(quote.calldata.to, ethers.constants.MaxUint256)).wait();
  }
}
Notes:
  • Native tokens (ETH, BNB, etc.) don’t require approval
  • The spender address (quote.calldata.to) is the contract that will execute the swap
  • Using MaxUint256 avoids repeated approvals, but you can use the exact amount for better security

4. Transaction execution

The quote response includes transaction parameters (to, data, value) and optional gas estimates:
const tx = await wallet.sendTransaction({
  to: quote.calldata.to,      
  data: quote.calldata.data,  
  value: quote.calldata.value,
  ...(quote.gas?.gasLimit && { gasLimit: BigNumber.from(quote.gas.gasLimit) }),
  ...(quote.gas?.maxFeePerGas && { maxFeePerGas: BigNumber.from(quote.gas.maxFeePerGas) }),
  ...(quote.gas?.maxPriorityFeePerGas && { maxPriorityFeePerGas: BigNumber.from(quote.gas.maxPriorityFeePerGas) }),
  ...(quote.gas?.gasPrice && { gasPrice: BigNumber.from(quote.gas.gasPrice) }),
});

await tx.wait();

return tx.hash;