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.
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:
Getting a quote from the Bungee API
Creating and submitting a request via the inbox contract
Monitoring the status via the API
Integration Steps
Step 1: Get a Quote
For native tokens, request a quote using the address 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE.
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
};
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 /**
* 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.
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 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:
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
Complete Integration Using Viem for native token requests
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 ( " \n 1. Getting quote..." );
const { txData , requestHash } = await getQuote ( quoteParamsNative );
if ( txData ) {
// Submit the transaction
console . log ( " \n 2. Submitting transaction..." );
const { hash , receipt } = await submitNativeTransaction ( txData );
console . log ( "Transaction sent:" , hash );
// Poll for completion
console . log ( " \n 3. 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 ();