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

# Swap across EVM & HyperCore

> Bridge and swap assets between EVM chains and HyperCore using Bungee

Bungee also supports swapping assets from any EVM chain to HyperCore and from HyperCore to any EVM chain.

This guide walks you through swapping assets between EVM chains and HyperCore using Bungee. It covers transaction options, and current limitations.

## Quick Start

### Key differences

Follow the "Auto routing" guides when going from any EVM to HyperCore. The main difference when integrating HyperCore Bungee API compared to standard EVM chains:

**Transaction Flow from HyperCore to EVM**

* User sends funds to a Deposit contract with the quote ID
* Bungee indexes the transaction and delivers the funds on the destination chain

**API Behavior**

* Both `userAddress` and `receiverAddress` required for quotes
* The only assets supported from/to HyperCore are [USDC (Spot)](https://hyperevmscan.io/address/0x2000000000000000000000000000000000000000) and USDC (Perps).
* There is no Fee collection currently from HyperCore to EVM
* The quote responses already take into account that HyperCore charges a 1 USDC activation fee for new account activation and withdrawals
* Only USDC (Perps) is supported when bridging out of Hypercore

## Integration Steps

### HyperCore to EVM

The ["Auto routing" guide from Bungee Docs](/integrate/integration-guides/auto-permit2-gasless-requests) outlines how to integrate the deposit flow as it is a quite similar process.

1. **Select Chains:** Users choose the source and destination chains, which determine the tokens available for bridging.
2. **Fetch Routes:** After selecting the tokens, retrieve the deposit route if available using the `/quote` endpoint. The response will contain `depositRoute.signTypedData` and `depositRoute.quoteId`.
3. **Sign and Submit:** Sign the `signTypedData` from the quote response using `primaryType: "HyperCoreTransaction:SendAsset"`, then submit the signed data to HyperLiquid's API endpoint (`https://api.hyperliquid.xyz/exchange`).
4. **Track Transaction Status:** Monitor the transaction's progress by polling the `/status` endpoint using the `quoteId` from the quote response until the bridging process is complete.

<Info>
  For HyperCore quotes, please ensure both `userAddress` and `receiverAddress` are defined.

  **`inputAmount` Format for HyperCore:**

  The `inputAmount` parameter must be provided as an **integer string in 6-decimal base units (micro-units)**. This is the same format used for USDC on most EVM chains.

  | Human-readable | `inputAmount` value |
  | -------------- | ------------------- |
  | 9.99 USDC      | `"9990000"`         |
  | 2.00 USDC      | `"2000000"`         |
  | 10.23 USDC     | `"10230000"`        |

  **Conversion formula:** `inputAmount = humanAmount × 10^6` (rounded to integer)

  For example, to send 9.99 USDC, use `inputAmount: "9990000"` (9.99 × 1,000,000 = 9,990,000).

  The HyperCore signature generation uses this micro-unit value directly. Do not pass decimal strings like `"9.99"`.

  Also, note that from HyperCore to EVM is a separate route from `autoRoute` since it is under `depositRoute`.
</Info>

## Examples

### Queries

<Accordion title="Quote from Optimism USDC to HyperCore USDC (Spot)">
  ```
  https://public-backend.bungee.exchange/api/v1/bungee/quote?userAddress=0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045&receiverAddress=0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045&originChainId=10&destinationChainId=1337&inputToken=0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85&outputToken=0x2000000000000000000000000000000000000000&inputAmount=2000000
  ```
</Accordion>

<Accordion title="Quote from HyperCore USDC (Perps) to Optimism USDC">
  ```
  https://public-backend.bungee.exchange/api/v1/bungee/quote?userAddress=0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045&receiverAddress=0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045&originChainId=1337&destinationChainId=10&inputToken=0x6d1e7cde53ba9467b783cb7c530ce054&outputToken=0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85&inputAmount=10230000
  ```

  <Info>
    For HyperCore quotes, `inputAmount` must be provided as an integer string in 6-decimal base units (micro-units). For example, to send 10.23 USDC, use `inputAmount: "10230000"` (10.23 × 10^6).
  </Info>
</Accordion>

### Scripts

<Accordion title="Quote and swap from Optimism USDC to HyperCore USDC (Spot)">
  ```tsx theme={null}
  import { privateKeyToAccount } from "viem/accounts";
  import { createPublicClient, http, createWalletClient } from "viem";
  import { optimism } from "viem/chains";

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

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

  // Create Viem clients
  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
  const quoteParamsERC20 = {
    userAddress: account.address,
    receiverAddress: account.address,
    originChainId: 10, // Optimism
    destinationChainId: 1337, // HyperCore
    inputToken: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85", // USDC on Optimism
    outputToken: "0x2000000000000000000000000000000000000000", // USDC (SPOT) on HyperCore
    //outputToken: "0x6d1e7cde53ba9467b783cb7c530ce054", // USDC (Perps) on HyperCore
    inputAmount: "2000000", // 2 USDC (6 decimals)
  };

  // Function to get a quote
  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);
      console.log(fullUrl);
      const data = await response.json();
      const serverReqId = response.headers.get("server-req-id");

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

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

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

      // Extract data based on the response structure
      let witness = null;
      let signTypedData = null;

      if (data.result.autoRoute.signTypedData) {
        signTypedData = data.result.autoRoute.signTypedData;
        // The witness is located in signTypedData.values.witness
        if (signTypedData?.values?.witness) {
          witness = signTypedData.values.witness;
        }
      }

      // Extract approval data if present
      const approvalData = data.result.autoRoute.approvalData;

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

      return {
        quoteId,
        requestType,
        witness,
        signTypedData,
        approvalData,
        fullResponse: data,
      };
    } catch (error) {
      console.error("Failed to get quote:", error);
      throw error;
    }
  }

  // Function to sign typed data using viem
  async function viemSignTypedData(signTypedData) {
    try {
      const signature = await account.signTypedData({
        types: signTypedData.types,
        primaryType: "PermitWitnessTransferFrom",
        message: signTypedData.values,
        domain: signTypedData.domain,
      });

      console.log("- Signature:", signature);
      return signature;
    } catch (error) {
      console.error("Failed to sign typed data:", error);
      throw error;
    }
  }

  // Function to submit the signed request
  async function submitSignedRequest(
    requestType,
    request,
    userSignature,
    quoteId
  ) {
    try {
      // Prepare request body
      const requestBody = {
        requestType,
        request,
        userSignature,
        quoteId,
      };

      const response = await fetch(
        `${BUNGEE_API_BASE_URL}/api/v1/bungee/submit`,
        {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(requestBody),
        }
      );

      const data = await response.json();
      const serverReqId = response.headers.get("server-req-id");
      console.log("Server request id", serverReqId);

      // Response data is saved to file but not logged to keep output clean

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

      console.log("- Request Hash:", data.result.requestHash);
      return data.result;
    } catch (error) {
      console.error("Failed to submit signed request:", error);
      throw error;
    }
  }

  // Function to check the status of a request
  async function checkStatus(requestHash) {
    try {

      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];
    } catch (error) {
      console.error("Failed to check status:", error);
      throw error;
    }
  }

  // Function to check and handle token approvals
  async function checkAndApproveToken(approvalData) {
    if (!approvalData || !approvalData.tokenAddress) {
      console.log("No approval data found or required");
      return;
    }

    console.log("\nChecking token approval...");

    // ERC20 ABI for allowance and approve functions
    const erc20Abi = [
      {
        inputs: [
          { name: "owner", type: "address" },
          { name: "spender", type: "address" },
        ],
        name: "allowance",
        outputs: [{ name: "", type: "uint256" }],
        stateMutability: "view",
        type: "function",
      },
      {
        inputs: [
          { name: "spender", type: "address" },
          { name: "amount", type: "uint256" },
        ],
        name: "approve",
        outputs: [{ name: "", type: "bool" }],
        stateMutability: "nonpayable",
        type: "function",
      },
    ];

    try {
      const currentAllowance = await publicClient.readContract({
        address: approvalData.tokenAddress,
        abi: erc20Abi,
        functionName: "allowance",
        args: [
          approvalData.userAddress,
          approvalData.spenderAddress,
        ],
      });

      console.log(`Current allowance: ${currentAllowance}`);
      console.log(`Required approval: ${approvalData.amount}`);

      // Check if approval is needed
      if (BigInt(currentAllowance) >= BigInt(approvalData.amount)) {
        console.log("Sufficient allowance already exists.");
        return;
      }

      console.log("Insufficient allowance. Approving tokens...");

      // Send approval transaction
      const hash = await walletClient.writeContract({
        address: approvalData.tokenAddress,
        abi: erc20Abi,
        functionName: "approve",
        args: [
          approvalData.spenderAddress,
          approvalData.amount,
        ],
      });

      console.log(`Approval transaction sent: ${hash}`);

      // Wait for transaction to be mined
      const receipt = await publicClient.waitForTransactionReceipt({ hash });
      console.log(`Approval confirmed in block ${receipt.blockNumber}`);

      return receipt;
    } catch (error) {
      console.error("Error in approval process:", error);
      throw error;
    }
  }

  // Main function to handle the flow
  async function main() {
    try {
      console.log("Starting Bungee ERC20 Token Test (Auto Route)...");

      // Get the quote and extract data
      console.log("\n1. Getting quote...");
      const quoteResponse = await getQuote(quoteParamsERC20);
      const { quoteId, requestType, witness, signTypedData } = quoteResponse;
      console.log(JSON.stringify(quoteResponse.fullResponse, null, 2));

      // Check if approval is needed and handle it
      //if (quoteResponse.approvalData) {
      //  await checkAndApproveToken(quoteResponse.approvalData);
      //}

      if (signTypedData && witness) {
        // Sign the typed data
        console.log("\n2. Signing typed data...");
        const signature = await viemSignTypedData(signTypedData);

        // Submit the signed request
        console.log("\n3. Submitting signed request...");
        const submitResult = await submitSignedRequest(
          requestType,
          witness,
          signature,
          quoteId
        );

        console.log(
          "\n4. Submission complete:",
          "\n- Hash:",
          submitResult.requestHash,
          "\n- Type:",
          submitResult.requestType
        );

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

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

  // Execute the main function
  main();
  ```
</Accordion>

<Accordion title="Quote and swap from HyperCore USDC (Perps) to Optimism USDC">
  ```tsx theme={null}
  import { privateKeyToAccount } from "viem/accounts";
  import { http, createWalletClient, parseSignature } from "viem";
  import { arbitrum } from "viem/chains";

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

  // Create account from private key
  const normalizedPrivateKey = process.env.PRIVATE_KEY.trim().startsWith('0x') 
    ? process.env.PRIVATE_KEY.trim() 
    : `0x${process.env.PRIVATE_KEY.trim()}`;
  const account = privateKeyToAccount(normalizedPrivateKey);

  // Create Viem clients
  const walletClient = createWalletClient({
    account,
    chain: arbitrum,
    transport: http(),
  });

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

  // Quote parameters
  const quoteParamsERC20 = {
    userAddress: account.address,
    receiverAddress: account.address,
    originChainId: 1337, // HyperCore
    destinationChainId: 10, // Optimism
    inputToken: "0x6d1e7cde53ba9467b783cb7c530ce054", // USDC (Perps) on HyperCore
    outputToken: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85", // USDC on Optimism
    inputAmount: "10230000", // USDC (Perps)
  };

  // Function to get a quote
  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);
      console.log(fullUrl);
      const data = await response.json();
      const serverReqId = response.headers.get("server-req-id");

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

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

      const quoteId = data.result.depositRoute.quoteId;
      console.log("- Quote ID:", quoteId);

      // Extract data based on the response structure
      const signTypedData = data.result.depositRoute.signTypedData;

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

  // Function to sign typed data using viem
  async function viemSignTypedData(signTypedData) {
    try {
      const signature = await walletClient.signTypedData({
        types: signTypedData.types,
        primaryType: "HyperCoreTransaction:SendAsset",
        message: signTypedData.values,
        domain: signTypedData.domain,
      });

      const signatureParsed = parseSignature(signature);
      const signatureObject = {
        r: signatureParsed.r,
        s: signatureParsed.s,
        v: Number(signatureParsed.v),
      };

      return signatureObject;
    } catch (error) {
      console.error("Failed to sign typed data:", error);
      throw error;
    }
  }

  // Function to submit the signed request
  async function submitSignedRequest(
    signTypedData,
    signature,
  ) {
    try {
      // Prepare request body
      const requestBody = {
        action: signTypedData.values,
        nonce: signTypedData.values.nonce,
        signature: signature,
      };

      console.log("- HyperCore Submit Request:", JSON.stringify(requestBody, null, 2));

      const response = await fetch(
        HYPERLIQUID_API_URL,
        {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(requestBody),
        }
      );

      const data = await response.json();
      if (data.status === 'err') {
        console.error("- HyperCore API Error Response:", data);
        throw new Error(
          `HyperCore API error: ${data.response || data.message || 'Unknown error'}`
        );
      }

      return data;
    } catch (error) {
      console.error("Failed to submit signed request:", error);
      throw error;
    }
  }

  // Function to check the status of a request
  async function checkStatus(quoteId) {
    try {

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

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

  // Main function to handle the flow
  async function main() {
    try {
      console.log("Starting Bungee ERC20 Token Test (Auto Route)...");

      // Get the quote and extract data
      console.log("\n1. Getting quote...");
      const quoteResponse = await getQuote(quoteParamsERC20);
      const { quoteId, signTypedData } = quoteResponse;
      console.log(JSON.stringify(quoteResponse.fullResponse, null, 2));

      if (signTypedData) {
        // Sign the typed data
        console.log("\n2. Signing typed data...");
        const signature = await viemSignTypedData(signTypedData);

        // Submit the signed request
        console.log("\n3. Submitting signed request...");
        const submitResult = await submitSignedRequest(
          signTypedData,
          signature,
        );

        console.log(
          "\n4. Submission complete:",
          "\n- Response:",
          submitResult,
        );

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

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

  // Execute the main function
  main();
  ```
</Accordion>
