> ## Documentation Index
> Fetch the complete documentation index at: https://docs.delora.build/llms.txt
> Use this file to discover all available pages before exploring further.

# End-to-end Transaction Example (Ethereum)

## 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.

<CodeGroup>
  ```ts TypeScript theme={null}
  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;
    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,
      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;
  };
  ```
</CodeGroup>

## React Integration Example

For React applications, you can integrate the swap function as follows:

<CodeGroup>
  ```tsx TypeScript React theme={null}
  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>
    );
  };
  ```
</CodeGroup>

## Breaking it down

### 1. Building the quote request

The `getQuote` function constructs a query string from parameters and fetches the quote from the API:

`senderAddress` is required, while `receiverAddress` is optional for same-type routes like this one and defaults to `senderAddress`.

<CodeGroup>
  ```ts TypeScript theme={null}
  const getQuote = async (params: {
    originChainId: number;
    destinationChainId: number;
    amount: string;
    originCurrency: string;
    destinationCurrency: string;
    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();
  };
  ```
</CodeGroup>

**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)
* `senderAddress`: Required wallet address
* `receiverAddress`: Optional for same-type routes; defaults to `senderAddress`. For routes across different chain types, provide it explicitly.

### 2. Wallet provider setup

The function accepts a `Web3Provider` from `ethers.js`, which can be obtained from various sources:

**Browser extension (MetaMask, etc.):**

```ts theme={null}
const provider = new ethers.providers.Web3Provider(window.ethereum);
```

**RPC endpoint:**

```ts theme={null}
const provider = new ethers.providers.JsonRpcProvider('https://polygon-rpc.com', 137);
```

**With wallet from mnemonic (development only):**

```ts theme={null}
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:

<CodeGroup>
  ```ts TypeScript theme={null}
  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();
    }
  }
  ```
</CodeGroup>

**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:

<CodeGroup>
  ```ts TypeScript theme={null}
  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;
  ```
</CodeGroup>
