Skip to main content

Bungee Auto (Native Token)

This page covers how to integrate Bungee Auto for same-chain and cross-chain swaps using Native tokens (ETH, MATIC, 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.

API Reference: Quote Endpoint

const BUNGEE_API_BASE_URL = "https://public-backend.bungee.exchange";

const quoteParamsNative = {
userAddress: "0x...",
originChainId: 10, // Optimism
destinationChainId: 42161, // Arbitrum
inputToken: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", // ETH on Arbitrum
inputAmount: "100000000000000", // 0.0001 ETH in wei
receiverAddress: "0x...",
outputToken: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", // ETH on Optimism};
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.

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

const receipt = await walletClient.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:

API Reference: Status Endpoint

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) {
console.log("Transaction complete:", status.destinationData?.txHash);
return status;
}
attempts++;
await new Promise((resolve) => setTimeout(resolve, interval));
}
throw new Error("Polling timed out. Transaction may not have completed.");
}

Complete Integration Example

Complete Integration Using Viem
import { createPublicClient, createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { optimism } from "viem/chains";
import { logToJson } from "../utils/logger.js";
import path from "node:path";

// Define script-specific output path
const OUTPUT_DIR = path.resolve(
process.cwd(),
"scripts/examples/output/auto-native"
);

// Check if PRIVATE_KEY is set, set in console, i.e.: export PRIVATE_KEY=abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890
if (!process.env.PRIVATE_KEY) {
console.error("Error: PRIVATE_KEY environment variable is not set");
console.error(
"Example: PRIVATE_KEY=abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"
);
process.exit(1);
}

// Create account from private key
const account = privateKeyToAccount(`0x${process.env.PRIVATE_KEY}`);

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) {
try {
const url = `${BUNGEE_API_BASE_URL}/api/v1/bungee/quote`;
const queryParams = new URLSearchParams(params);
const fullUrl = `${url}?${queryParams}`;

// Log the request parameters
logToJson(params, "quote_request_params", OUTPUT_DIR);

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

// Log the response
logToJson(data, "quote_response", OUTPUT_DIR);

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");
}

// 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;
}
}

// Function to submit the transaction to the inbox contract
async function submitNativeTransaction(txData) {
try {
logToJson(txData, "tx_data", OUTPUT_DIR);

console.log("- Submitting transaction to inbox contract...");
console.log(" To:", txData.to);
console.log(" Value:", txData.value);
console.log(" Data:", txData.data);

// Send the transaction
const hash = await walletClient.sendTransaction({
to: txData.to,
value: BigInt(txData.value),
data: txData.data,
});

console.log("- Transaction sent:", hash);

// Wait for transaction to be mined
const receipt = await publicClient.waitForTransactionReceipt({ hash });
console.log("- Transaction mined in block:", receipt.blockNumber);

return {
hash,
receipt,
};
} catch (error) {
console.error("Failed to submit transaction:", error);
throw error;
}
}

// Function to check the status of a request
async function checkStatus(requestHash) {
try {
logToJson({ requestHash }, "status_request_params", OUTPUT_DIR);

const response = await fetch(
`${BUNGEE_API_BASE_URL}/api/v1/bungee/status?requestHash=${requestHash}`
);
const data = await response.json();
logToJson(data, "status_response", OUTPUT_DIR);

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

return data.result[0];
} catch (error) {
console.error("Failed to check status:", error);
throw error;
}
}

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

// Get the quote and extract data
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(
"\n3. Transaction submitted:",
"\n- Hash:",
hash,
"\n- Status:",
receipt.status
);

console.log("- Request Hash:", requestHash);

// Wait for 5 seconds before checking status
console.log("\n4. Waiting for 5 seconds...");
let status;
do {
await new Promise((resolve) => setTimeout(resolve, 5000));
console.log("\n5. Checking status...");
try {
status = await checkStatus(requestHash);
console.log("- Status details:", status.bungeeStatusCode);
} catch (error) {
console.error(
"Failed to check status:",
error?.message || "Unknown error"
);
}
} while (status?.bungeeStatusCode !== 3);

console.log(
"\n6. Transaction complete:",
"\n- Hash:",
status.destinationData?.txHash || "Transaction hash not available"
);
} else {
console.log("No transaction data available in the quote response");
}
} catch (error) {
console.error("Error in processing:", error?.shortMessage || error.message);
throw error;
}
}

// Execute the main function
main();