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

# Auto routing onchain

> Integrate Bungee Auto for native tokens using the Inbox Contract

This page covers how to integrate Bungee Auto for samechain and crosschain swaps using **Native tokens** (ETH, POL, etc.) as input tokens by directly interacting with the Bungee Inbox contract.

## Overview

The Inbox Contract integration is required for native tokens and can also be used for ERC20 tokens when:

* Permit2 is not an option
* The integration is directly onchain
* You prefer a fully onchain approach

This method involves:

1. Getting a quote from the Bungee API
2. Creating and submitting a request via the inbox contract
3. Monitoring the status via the API

## Integration Steps

### Step 1: Get a Quote

For native tokens, request a quote using the address `0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE`.

```javascript theme={null}
const BUNGEE_API_BASE_URL = "https://public-backend.bungee.exchange";

const quoteParamsNative = {
  userAddress: "0x...",
  originChainId: 10, // Optimism
  destinationChainId: 42161, // Arbitrum
  inputToken: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", // ETH on Optimism
  inputAmount: "100000000000000", // 0.0001 ETH in wei
  receiverAddress: "0x...",
  outputToken: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", // ETH on Arbitrum
};
```

<Accordion title="How to enable ERC20 onchain requests">
  When integrating Bungee Auto, you can specify the `useInbox` parameter to directly create a request onchain and bypass permit2 gasless signatures:

  * **`useInbox`**: Enables `autoRoute.txData` for ERC20 tokens and disables `autoRoute.signTypedData`

  #### Example

  ```javascript theme={null}
  /**
   * Get a quote with ERC20 onchain request information
   */
  async function getQuoteWithUseInbox() {
    // Set up the parameters for the quote request
    const quoteParams = {
      userAddress: "0xYourUsersAddress",
      originChainId: "1", // Ethereum
      destinationChainId: "10", // Optimism
      inputToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC on Ethereum
      outputToken: "0x7F5c764cBc14f9669B88837ca1490cCa17c31607", // USDC on Optimism
      inputAmount: "1000000", // 1 USDC (6 decimals)
      receiverAddress: "0xYourUsersAddress",
      useInbox: true, // Enables autoRoute.txData for ERC20 tokens and disables autoRoute.signTypedData
    };

    // Build the URL with query parameters
    const url = `${BUNGEE_API_BASE_URL}/api/v1/bungee/quote`;
    const queryParams = new URLSearchParams(quoteParams);
    const fullUrl = `${url}?${queryParams}`;

    // Make the request
    const response = await fetch(fullUrl, {
      method: "GET",
      headers: {
        "Content-Type": "application/json",
      },
    });

    const data = await response.json();
    const serverReqId = response.headers.get("server-req-id");

    if (!data.success) {
      throw new Error(
        `Quote error: ${data.statusCode}: ${data.message}. server-req-id: ${serverReqId}`
      );
    }

    return data.result;
  }
  ```

  Before submitting a user request to BungeeInbox, check if the request needs approval.

  You can verify this by checking if the field `autoRoute.approvalData` contains any data.

  * If `approvalData` is present and populated, it means approval is required.
  * If `approvalData` is empty, no approval is needed, and you can submit the request directly.
</Accordion>

```javascript theme={null}
async function getQuote(params) {
  try {
    const url = `${BUNGEE_API_BASE_URL}/api/v1/bungee/quote`;
    const queryParams = new URLSearchParams(params);
    const fullUrl = `${url}?${queryParams}`;

    const response = await fetch(fullUrl);
    const data = await response.json();
    const serverReqId = response.headers.get("server-req-id");

    if (!data.success) {
      throw new Error(
        `Quote error: ${data.statusCode}: ${data.message}. server-req-id: ${serverReqId}`
      );
    }

    if (!data.result.autoRoute) {
      throw new Error(`No autoRoute available. server-req-id: ${serverReqId}`);
    }

    // Extract transaction data for native token
    const txData = data.result.autoRoute.txData;
    const requestHash = data.result.autoRoute.requestHash;

    const quoteId = data.result.autoRoute.quoteId;
    const requestType = data.result.autoRoute.requestType;
    console.log("- Quote ID:", quoteId);
    console.log("- Request Type:", requestType);

    // Log request hash
    if (data.result.autoRoute.requestHash) {
      console.log("- Request Hash:", data.result.autoRoute.requestHash);
    }

    return {
      quoteId,
      requestType,
      txData,
      requestHash,
      fullResponse: data,
    };
  } catch (error) {
    console.error("Failed to get quote:", error);
    throw error;
  }
}
```

### Step 2: Submit the Transaction to the Inbox Contract

For native tokens, submit the transaction directly to the inbox contract using the transaction data from the quote.

```javascript theme={null}
async function submitNativeTransaction(txData) {
  const hash = await walletClient.sendTransaction({
    to: txData.to,
    value: BigInt(txData.value),
    data: txData.data,
  });

  const receipt = await publicClient.waitForTransactionReceipt({ hash });

  return { hash, receipt };
}
```

### Step 3: Check Request Status

After submitting the request, check its status to track progress. You can implement a polling mechanism to continuously check until completion:

```javascript theme={null}
async function checkStatus(requestHash) {
  const response = await fetch(
    `${BUNGEE_API_BASE_URL}/api/v1/bungee/status?requestHash=${requestHash}`
  );
  const data = await response.json();
  if (!data.success) {
    throw new Error(`Status error: ${data.error?.message || "Unknown error"}`);
  }
  return data.result[0];
}

async function pollForCompletion(
  requestHash,
  interval = 5000,
  maxAttempts = 60
) {
  let attempts = 0;
  while (attempts < maxAttempts) {
    const status = await checkStatus(requestHash);
    const code = status?.bungeeStatusCode;
    if (code === 3 || code === 4) {
      console.log("Transaction complete:", status.destinationData?.txHash);
      return status;
    }
    if (code === 5) {
      throw new Error(`Request expired. Status: ${JSON.stringify(status)}`);
    }
    if (code === 6) {
      throw new Error(`Request cancelled. Status: ${JSON.stringify(status)}`);
    }
    if (code === 7) {
      throw new Error(`Request refunded. Status: ${JSON.stringify(status)}`);
    }
    attempts++;
    await new Promise((resolve) => setTimeout(resolve, interval));
  }
  throw new Error("Polling timed out. Transaction may not have completed.");
}
```

## Complete Integration Example

<Accordion title="Complete Integration Using Viem for native token requests">
  ```javascript theme={null}
  import { createPublicClient, createWalletClient, http } from "viem";
  import { privateKeyToAccount } from "viem/accounts";
  import { optimism } from "viem/chains";

  // Check if PRIVATE_KEY is set
  if (!process.env.PRIVATE_KEY) {
    console.error("Error: PRIVATE_KEY environment variable is not set");
    process.exit(1);
  }

  // Create account from private key
  // Normalize private key to ensure it has exactly one "0x" prefix (required by viem)
  const normalizedPrivateKey = `0x${process.env.PRIVATE_KEY.replace(/^0x/i, '')}`;
  const account = privateKeyToAccount(normalizedPrivateKey);

  const publicClient = createPublicClient({
    chain: optimism,
    transport: http(),
  });

  const walletClient = createWalletClient({
    account,
    chain: optimism,
    transport: http(),
  });

  // API and token parameters
  const BUNGEE_API_BASE_URL = "https://public-backend.bungee.exchange";

  // Quote parameters for native token test (ETH from Optimism to Arbitrum)
  const quoteParamsNative = {
    userAddress: account.address,
    receiverAddress: account.address,
    originChainId: 10, // Optimism
    destinationChainId: 42161, // Arbitrum
    inputToken: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", // ETH on Optimism
    outputToken: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", // ETH on Arbitrum
    inputAmount: "100000000000000", // 0.0001 ETH in wei
  };

  async function getQuote(params) {
    const url = `${BUNGEE_API_BASE_URL}/api/v1/bungee/quote`;
    const queryParams = new URLSearchParams(params);
    const fullUrl = `${url}?${queryParams}`;

    const response = await fetch(fullUrl);
    const data = await response.json();

    if (!data.success) {
      throw new Error(`Quote error: ${data.error?.message || "Unknown error"}`);
    }

    if (!data.result.autoRoute) {
      throw new Error("No autoRoute available for native token transfer");
    }

    return {
      txData: data.result.autoRoute.txData,
      requestHash: data.result.autoRoute.requestHash,
    };
  }

  async function submitNativeTransaction(txData) {
    const hash = await walletClient.sendTransaction({
      to: txData.to,
      value: BigInt(txData.value),
      data: txData.data,
    });

    const receipt = await publicClient.waitForTransactionReceipt({ hash });
    return { hash, receipt };
  }

  async function checkStatus(requestHash) {
    const response = await fetch(
      `${BUNGEE_API_BASE_URL}/api/v1/bungee/status?requestHash=${requestHash}`
    );
    const data = await response.json();

    if (!data.success) {
      throw new Error(`Status error: ${data.error?.message || "Unknown error"}`);
    }

    return data.result[0];
  }

  async function main() {
    console.log("Starting Bungee Native Token Test (Auto Route)...");

    // Get the quote
    console.log("\n1. Getting quote...");
    const { txData, requestHash } = await getQuote(quoteParamsNative);

    if (txData) {
      // Submit the transaction
      console.log("\n2. Submitting transaction...");
      const { hash, receipt } = await submitNativeTransaction(txData);
      console.log("Transaction sent:", hash);

      // Poll for completion
      console.log("\n3. Checking status...");
      let status;
      let attempts = 0;
      const maxAttempts = 60;
      const interval = 5000;
      
      while (attempts < maxAttempts) {
        await new Promise((resolve) => setTimeout(resolve, interval));
        status = await checkStatus(requestHash);
        const code = status?.bungeeStatusCode;
        console.log("Status:", code);
        
        if (code === 3 || code === 4) {
          console.log("Transaction complete:", status.destinationData?.txHash);
          break;
        }
        
        if (code === 5) {
          throw new Error(`Request expired. Status: ${JSON.stringify(status)}`);
        }
        if (code === 6) {
          throw new Error(`Request cancelled. Status: ${JSON.stringify(status)}`);
        }
        if (code === 7) {
          throw new Error(`Request refunded. Status: ${JSON.stringify(status)}`);
        }
        
        attempts++;
      }
      
      if (attempts >= maxAttempts) {
        throw new Error(`Polling timed out after ${maxAttempts} attempts. Last status: ${JSON.stringify(status)}`);
      }
    }
  }

  main();
  ```
</Accordion>
