Interacting with the Smart Contract

The route data from the V2 API can be used directly with the MultiHopRouter smart contract. The V2 API provides ready-to-use transaction calldata, making smart contract integration much simpler than before.

Contract Address

0x744489ee3d540777a66f2cf297479745e0852f7a

The V2 API returns ready-to-use transaction data in the execution field. This is the easiest way to execute swaps:

const { ethers } = require('ethers');
const axios = require('axios');

// Constants
const RPC_URL = 'https://rpc.hyperliquid.xyz/evm';
const PRIVATE_KEY = 'your-private-key';

// Function to get route from V2 API
async function getRoute(tokenIn, tokenOut, amountIn, options = {}) {
  try {
    const params = new URLSearchParams({
      tokenIn,
      tokenOut,
      amountIn,
      multiHop: options.multiHop || 'true',
      slippage: options.slippage || '1.0',
      ...options
    });
    
    const response = await axios.get(
      `https://api.liqd.ag/v2/route?${params.toString()}`
    );
    return response.data;
  } catch (error) {
    console.error('Error fetching route:', error.message);
    throw error;
  }
}

// Execute swap using ready-made calldata
async function executeSwap(tokenIn, tokenOut, amountIn, options = {}) {
  try {
    // Setup provider and wallet
    const provider = new ethers.providers.JsonRpcProvider(RPC_URL);
    const wallet = new ethers.Wallet(PRIVATE_KEY, provider);
    
    // Get route from V2 API
    const routeData = await getRoute(tokenIn, tokenOut, amountIn, options);
    
    if (!routeData || !routeData.execution) {
      throw new Error('No valid route found');
    }
    
    const { execution, tokens } = routeData;
    
    // Check if input token is WHYPE (which can be provided as native HYPE or WHYPE tokens)
    const isWHYPE = tokens.tokenIn.address.toLowerCase() === '0x5555555555555555555555555555555555555555';
    const useNativeHYPE = options.useNativeHYPE !== false; // Default to true for WHYPE
    
    // Handle token approval (only needed if using ERC-20 tokens)
    if (!isWHYPE || !useNativeHYPE) {
      const tokenContract = new ethers.Contract(
        tokens.tokenIn.address,
        ['function approve(address spender, uint256 amount) returns (bool)'],
        wallet
      );
      
      const amountToApprove = ethers.utils.parseUnits(amountIn, tokens.tokenIn.decimals);
      const approveTx = await tokenContract.approve(execution.to, amountToApprove);
      await approveTx.wait();
      console.log('Token approved');
    }
    
    // Execute the swap using the provided calldata
    const txParams = {
      to: execution.to,
      data: execution.calldata,
      gasLimit: 2000000,
      value: (isWHYPE && useNativeHYPE) 
        ? ethers.utils.parseUnits(amountIn, tokens.tokenIn.decimals)
        : 0
    };
    
    const tx = await wallet.sendTransaction(txParams);
    const receipt = await tx.wait();
    
    console.log('Swap executed:', receipt.transactionHash);
    return receipt;
  } catch (error) {
    console.error('Error executing swap:', error);
    throw error;
  }
}

// Example 1: Swapping WHYPE using native HYPE (recommended)
executeSwap(
  '0x5555555555555555555555555555555555555555', // WHYPE
  '0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb', // USD₮0  
  '100',
  { 
    multiHop: 'true',
    slippage: '1.0',
    useNativeHYPE: true // Send native HYPE (auto-wraps to WHYPE)
  }
);

// Example 2: Swapping WHYPE using ERC-20 WHYPE tokens
executeSwap(
  '0x5555555555555555555555555555555555555555', // WHYPE
  '0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb', // USD₮0  
  '100',
  { 
    multiHop: 'true',
    slippage: '1.0',
    useNativeHYPE: false // Approve and use WHYPE tokens
  }
);

// Example 3: Swapping other ERC-20 tokens (only one option)
executeSwap(
  '0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb', // USD₮0
  '0x5555555555555555555555555555555555555555', // WHYPE
  '1000',
  { 
    multiHop: 'true',
    slippage: '1.0',
    unwrapWHYPE: 'true' // Receive native HYPE instead of WHYPE
  }
);

Advanced Integration (Manual Contract Calls)

If you prefer to call the contract functions directly, you can extract the routing data from the V2 API response:

Contract Function Signature

function executeMultiHopSwap(
    address[] calldata tokens,
    uint256 amountIn,
    uint256 minAmountOut,
    Swap[][] calldata hopSwaps
) external payable returns (uint256 totalAmountOut)
Parameter
Type
Description

tokens

address[]

Array of token addresses in the swap path (including intermediates)

amountIn

uint256

Amount of input tokens (ignored when sending native HYPE as msg.value)

minAmountOut

uint256

Minimum acceptable output amount (slippage protection)

hopSwaps

Swap[][]

2D array of Swap structs, where each inner array is a hop

Note: When sending native HYPE as msg.value, set amountIn to 0. When using ERC-20 tokens, set msg.value to 0 and provide the token amount in amountIn.

Manual Integration Example

const { ethers } = require('ethers');
const axios = require('axios');

// MultiHopRouter ABI
const ROUTER_ABI = [
  "function executeMultiHopSwap(address[] calldata tokens, uint256 amountIn, uint256 minAmountOut, tuple(address tokenIn, address tokenOut, uint8 routerIndex, uint24 fee, uint256 amountIn, bool stable)[][] calldata hopSwaps) external payable returns (uint256 totalAmountOut)"
];

async function executeManualSwap(tokenIn, tokenOut, amountIn, useNativeHYPE = true) {
  try {
    const provider = new ethers.providers.JsonRpcProvider(RPC_URL);
    const wallet = new ethers.Wallet(PRIVATE_KEY, provider);
    const router = new ethers.Contract(ROUTER_ADDRESS, ROUTER_ABI, wallet);
    
    // Get route from V2 API
    const routeData = await getRoute(tokenIn, tokenOut, amountIn);
    
    if (!routeData || !routeData.execution) {
      throw new Error('No valid route found');
    }
    
    const { execution, tokens } = routeData;
    const { details } = execution;
    
    // Extract contract parameters from V2 response
    const tokensArray = details.path;
    const minAmountOut = details.minAmountOut;
    
    // Convert hopSwaps from V2 format to contract format
    const hopSwaps = details.hopSwaps.map(hop => 
      hop.map(swap => ({
        tokenIn: swap.tokenIn,
        tokenOut: swap.tokenOut,
        routerIndex: swap.routerIndex,
        fee: swap.fee,
        amountIn: swap.amountIn,
        stable: swap.stable
      }))
    );
    
    // Check if input token is WHYPE and how to provide it
    const isWHYPE = tokens.tokenIn.address.toLowerCase() === '0x5555555555555555555555555555555555555555';
    const parsedAmountIn = ethers.utils.parseUnits(amountIn, tokens.tokenIn.decimals);
    
    let tx;
    if (isWHYPE && useNativeHYPE) {
      // Option 1: Send native HYPE as msg.value (gets auto-wrapped to WHYPE)
      tx = await router.executeMultiHopSwap(
        tokensArray,
        0, // amountIn ignored when sending native HYPE
        minAmountOut,
        hopSwaps,
        {
          gasLimit: 2000000,
          value: parsedAmountIn // Send native HYPE
        }
      );
    } else {
      // Option 2: Use ERC-20 tokens (approve first)
      const tokenContract = new ethers.Contract(
        tokens.tokenIn.address,
        ['function approve(address spender, uint256 amount) returns (bool)'],
        wallet
      );
      
      const approveTx = await tokenContract.approve(router.address, parsedAmountIn);
      await approveTx.wait();
      
      // Execute swap
      tx = await router.executeMultiHopSwap(
        tokensArray,
        parsedAmountIn,
        minAmountOut,
        hopSwaps,
        {
          gasLimit: 2000000,
          value: 0 // No native value for ERC-20 tokens
        }
      );
    }
    
    const receipt = await tx.wait();
    return receipt;
  } catch (error) {
    console.error('Error executing manual swap:', error);
    throw error;
  }
}

Important Notes

  1. Slippage Protection: The V2 API pre-calculates minAmountOut based on your slippage parameter

  2. WHYPE Flexibility: When the API returns WHYPE (0x5555...) as input token, you have two options:

    • Send native HYPE as msg.value (recommended) - gets auto-wrapped to WHYPE

    • Approve WHYPE tokens and use standard ERC-20 flow

  3. Token Approval:

    • Required for all ERC-20 tokens (when not using native HYPE option)

    • Not needed when sending native HYPE for WHYPE swaps

  4. Gas Limits: Multi-hop swaps may require higher gas limits than simple swaps

  5. Error Handling: Always check that routeData.execution exists before attempting execution

  6. Contract Behavior:

    • When sending native HYPE: amountIn parameter is ignored, uses msg.value

    • When using ERC-20 tokens: msg.value should be 0, uses amountIn parameter

Last updated