Loading...
Loading...
© 2026 Hydrex. All rights reserved.
Complete swap execution example using viem.
npm install viemimport {
createPublicClient,
createWalletClient,
http,
maxUint256,
} from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { base } from "viem/chains";
const BASE_URL = "https://router.api.hydrex.fi";
const account = privateKeyToAccount(process.env.PRIVATE_KEY);
const publicClient = createPublicClient({
chain: base,
transport: http(),
});
const walletClient = createWalletClient({
account,
chain: base,
transport: http(),
});
const ERC20_ABI = [
{
name: "allowance",
type: "function",
stateMutability: "view",
inputs: [
{ name: "owner", type: "address" },
{ name: "spender", type: "address" },
],
outputs: [{ type: "uint256" }],
},
{
name: "approve",
type: "function",
stateMutability: "nonpayable",
inputs: [
{ name: "spender", type: "address" },
{ name: "amount", type: "uint256" },
],
outputs: [{ type: "bool" }],
},
];
async function getQuote({
fromTokenAddress,
toTokenAddress,
amount,
taker,
slippage = 50,
referral,
referralFeeBps = 0,
origin = "my-app",
}) {
const p = new URLSearchParams({
fromTokenAddress,
toTokenAddress,
amount: amount.toString(),
chainId: "8453",
taker,
slippage: slippage.toString(),
origin,
...(referral && { referral, referralFeeBps: referralFeeBps.toString() }),
});
const res = await fetch(`${BASE_URL}/quote?${p}`);
if (!res.ok) {
const error = await res.json().catch(() => ({}));
throw new Error(`Router ${res.status}: ${error.message}`);
}
return res.json();
}
async function ensureAllowance(tokenAddress, spender, amount) {
const currentAllowance = await publicClient.readContract({
address: tokenAddress,
abi: ERC20_ABI,
functionName: "allowance",
args: [account.address, spender],
});
if (currentAllowance < BigInt(amount)) {
const { request } = await publicClient.simulateContract({
address: tokenAddress,
abi: ERC20_ABI,
functionName: "approve",
args: [spender, maxUint256],
account,
});
const hash = await walletClient.writeContract(request);
await publicClient.waitForTransactionReceipt({ hash });
}
}
async function executeSwap(fromTokenAddress, quote) {
const ETH = "0x0000000000000000000000000000000000000000";
if (fromTokenAddress.toLowerCase() !== ETH) {
await ensureAllowance(
fromTokenAddress,
quote.transaction.to,
quote.amountIn
);
}
const hash = await walletClient.sendTransaction({
to: quote.transaction.to,
data: quote.transaction.data,
value: BigInt(quote.transaction.value ?? "0"),
});
return await publicClient.waitForTransactionReceipt({ hash });
}
// Usage
const FROM_TOKEN = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"; // USDC
const quote = await getQuote({
fromTokenAddress: FROM_TOKEN,
toTokenAddress: "0x4200000000000000000000000000000000000006", // WETH
amount: "100000000", // 100 USDC (6 decimals)
taker: account.address,
referral: "0xYourReferralWallet",
referralFeeBps: 10,
});
const receipt = await executeSwap(FROM_TOKEN, quote);
console.log("Swap confirmed:", receipt.transactionHash);Fetches the best swap route from the Router API with all necessary transaction data.
Reads the current ERC-20 allowance via publicClient.readContract, then approves maxUint256 if the allowance is insufficient, avoiding repeated approvals.
Sends the router calldata to the blockchain via walletClient.sendTransaction and waits for confirmation. Handles ERC-20 approval before submitting.