Skip to main content

Documentation Index

Fetch the complete documentation index at: https://akkafinance-4d5a30d3.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

This is a complete, copy-paste-ready TypeScript script that swaps tokens on HyperEVM using the AKKA API and viem.

Prerequisites

npm install viem dotenv
Create a .env file:
WALLET_ADDRESS=0xYourWalletAddress
PRIVATE_KEY=0xYourPrivateKey
RPC_URL=https://rpc.hyperliquid.xyz/evm
API_KEY=your_akka_api_key

Full Script

swap.ts
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();

Running the script

npx tsx swap.ts

What this script does

1

Checks allowance

If the source token is native (HYPE), skips approval entirely. For ERC-20 tokens, checks if the AKKA Router already has sufficient spending approval.
2

Approves if needed

If allowance is insufficient, generates and submits an unlimited approval transaction, then waits for confirmation.
3

Executes the swap

Calls the swap endpoint with the desired parameters, then signs and broadcasts the returned transaction. Waits for on-chain confirmation.

Adapting for ERC-20 to ERC-20 swaps

To swap between two ERC-20 tokens (e.g., USDC to UBTC), change the SRC_TOKEN:
const SRC_TOKEN = '0xYourERC20TokenAddress'; // e.g., USDC on HyperEVM
const DST_TOKEN = '0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb';
const AMOUNT = parseUnits('100', 6).toString(); // 100 USDC (6 decimals)
The script will automatically detect that the source is an ERC-20 token and handle the approval flow.