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
Simple Integration (Recommended)
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)
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
, setamountIn
to 0. When using ERC-20 tokens, setmsg.value
to 0 and provide the token amount inamountIn
.
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
Slippage Protection: The V2 API pre-calculates
minAmountOut
based on your slippage parameterWHYPE 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 WHYPEApprove WHYPE tokens and use standard ERC-20 flow
Token Approval:
Required for all ERC-20 tokens (when not using native HYPE option)
Not needed when sending native HYPE for WHYPE swaps
Gas Limits: Multi-hop swaps may require higher gas limits than simple swaps
Error Handling: Always check that
routeData.execution
exists before attempting executionContract Behavior:
When sending native HYPE:
amountIn
parameter is ignored, usesmsg.value
When using ERC-20 tokens:
msg.value
should be 0, usesamountIn
parameter
Last updated