import 'dotenv/config';
import {
createPublicClient,
createWalletClient,
http,
parseUnits,
defineChain,
} from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
// ─── Configuration ──────────────────────────────────────────
const API_BASE = 'https://api.akka.finance';
const CHAIN_ID = 999;
const API_KEY = process.env.API_KEY!;
const SRC_TOKEN = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee'; // HYPE (native)
const DST_TOKEN = '0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb'; // UBTC
const AMOUNT = parseUnits('1', 18).toString(); // 1 HYPE
const SLIPPAGE = 1; // 1%
// ─── Chain Definition ───────────────────────────────────────
const hyperEVM = defineChain({
id: CHAIN_ID,
name: 'HyperEVM',
nativeCurrency: { name: 'HYPE', symbol: 'HYPE', decimals: 18 },
rpcUrls: {
default: { http: [process.env.RPC_URL!] },
},
});
// ─── Clients ────────────────────────────────────────────────
const account = privateKeyToAccount(process.env.PRIVATE_KEY! as `0x${string}`);
const publicClient = createPublicClient({
chain: hyperEVM,
transport: http(),
});
const walletClient = createWalletClient({
account,
chain: hyperEVM,
transport: http(),
});
// ─── Helpers ────────────────────────────────────────────────
async function akkaFetch(path: string, params: Record<string, string>) {
const url = `${API_BASE}${path}?${new URLSearchParams(params)}`;
const res = await fetch(url, { headers: { apikey: API_KEY } });
if (!res.ok) {
const error = await res.json();
throw new Error(`AKKA API error: ${error.message}`);
}
return res.json();
}
// ─── Step 1: Check Allowance ────────────────────────────────
async function checkAllowance(): Promise<bigint> {
// Native tokens don't need approval
if (SRC_TOKEN.toLowerCase() === '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee') {
console.log('Native token — no approval needed');
return BigInt(2) ** BigInt(256) - BigInt(1); // max uint256
}
const data = await akkaFetch(`/swap/v1/${CHAIN_ID}/approve/allowance`, {
tokenAddress: SRC_TOKEN,
walletAddress: process.env.WALLET_ADDRESS!,
});
console.log('Current allowance:', data.allowance);
return BigInt(data.allowance);
}
// ─── Step 2: Approve If Needed ──────────────────────────────
async function approveIfNeeded(currentAllowance: bigint) {
if (currentAllowance >= BigInt(AMOUNT)) {
console.log('Allowance sufficient — skipping approval');
return;
}
console.log('Approving AKKA Router...');
const approveTx = await akkaFetch(
`/swap/v1/${CHAIN_ID}/approve/transaction`,
{ tokenAddress: SRC_TOKEN },
);
const hash = await walletClient.sendTransaction({
to: approveTx.to as `0x${string}`,
data: approveTx.data as `0x${string}`,
value: BigInt(approveTx.value),
});
console.log('Approval tx sent:', hash);
const receipt = await publicClient.waitForTransactionReceipt({ hash });
console.log('Approval confirmed in block:', receipt.blockNumber);
}
// ─── Step 3: Execute Swap ───────────────────────────────────
async function executeSwap() {
console.log('Getting swap transaction...');
const swap = await akkaFetch(`/swap/v1/${CHAIN_ID}/swap`, {
src: SRC_TOKEN,
dst: DST_TOKEN,
amount: AMOUNT,
from: process.env.WALLET_ADDRESS!,
slippage: SLIPPAGE.toString(),
});
console.log('Expected output:', swap.dstAmount);
const { tx } = swap;
const hash = await walletClient.sendTransaction({
to: tx.to as `0x${string}`,
data: tx.data as `0x${string}`,
value: BigInt(tx.value),
gasPrice: BigInt(tx.gasPrice),
gas: BigInt(tx.gas),
});
console.log('Swap tx sent:', hash);
const receipt = await publicClient.waitForTransactionReceipt({ hash });
console.log('Swap confirmed in block:', receipt.blockNumber);
}
// ─── Main ───────────────────────────────────────────────────
async function main() {
try {
const allowance = await checkAllowance();
await approveIfNeeded(allowance);
await executeSwap();
console.log('Done!');
} catch (error) {
console.error('Error:', error);
process.exit(1);
}
}
main();