> ## Documentation Index
> Fetch the complete documentation index at: https://docs.akka.finance/llms.txt
> Use this file to discover all available pages before exploring further.

# Slippage & Best Practices

> How to choose slippage, handle errors, and build reliable swap integrations

## Slippage

Slippage is the maximum price difference you're willing to accept between the quoted rate and the executed rate. It protects against price movement during the time between quoting and on-chain execution.

### Choosing a slippage value

| Slippage     | When to use                                                        |
| ------------ | ------------------------------------------------------------------ |
| **0.1–0.5%** | Stablecoins or high-liquidity pairs (e.g., USDC → USDT)            |
| **0.5–1%**   | Standard pairs with good liquidity (e.g., HYPE → USDC)             |
| **1–3%**     | Lower-liquidity tokens or volatile markets                         |
| **3–5%**     | Very low liquidity or fast-moving prices                           |
| **> 5%**     | Generally not recommended — consider splitting into smaller trades |

Pass slippage as a percentage (not a fraction) to the `/swap` endpoint:

```bash theme={null}
# 1% slippage
curl -H "apikey: YOUR_API_KEY" \
  "https://api.akka.finance/swap/v1/999/swap?src=0x...&dst=0x...&amount=1000000000000000000&from=0x...&slippage=1"
```

The allowed range is `0` to `50`. Up to 4 decimal places are supported (e.g., `0.5`, `1.25`).

### What happens when slippage is exceeded

The on-chain transaction **reverts**. You pay gas but no tokens are swapped and no tokens are lost. The user keeps their full balance.

***

## Quote-then-swap timing

The quote endpoint returns an estimated output amount. The swap endpoint returns the transaction to execute. Between these two calls (and before the transaction confirms), pool prices can move.

**Best practice:** Keep the gap between `/quote` and `/swap` as short as possible. In a frontend, fetch the swap data only when the user clicks "Swap" — don't pre-fetch it while they're still reviewing.

```typescript theme={null}
// Good: quote for display, swap on click
const quote = await getQuote(src, dst, amount);  // show to user
// ... user reviews and clicks "Swap" ...
const swap = await getSwap(src, dst, amount, from, slippage);  // fetch right before execution
await sendTransaction(swap.tx);
```

***

## Handling reverts

If a swap transaction reverts on-chain, it's usually one of these:

| Cause                                                   | Solution                                                                                         |
| ------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| Price moved beyond slippage tolerance                   | Increase slippage or retry immediately with a fresh quote                                        |
| Stale quote (too much time between quote and execution) | Re-fetch the swap data and retry                                                                 |
| Insufficient gas                                        | Let the API set gas automatically (don't override `gas` and `gasPrice` unless you have a reason) |
| Insufficient balance                                    | Check the user's balance before initiating the swap                                              |
| Insufficient allowance                                  | The token wasn't approved, or the approval transaction hasn't confirmed yet                      |

### Retry pattern

```typescript theme={null}
async function swapWithRetry(
  src: string, dst: string, amount: string,
  from: string, slippage: number, maxRetries = 2,
) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const { tx } = await getSwap(src, dst, amount, from, slippage);
      const hash = await sendTransaction(tx);
      return hash;
    } catch (err) {
      console.error(`Attempt ${attempt} failed:`, err);
      if (attempt === maxRetries) throw err;
      // Bump slippage slightly on retry
      slippage = Math.min(slippage * 1.5, 5);
    }
  }
}
```

***

## Gas on HyperEVM

HyperEVM has low and stable gas prices. The API returns `gasPrice` and `gas` (limit) in the swap response — use them as-is unless you have a specific reason to override.

```typescript theme={null}
const { tx } = await getSwap(src, dst, amount, from, slippage);

// Use the gas values from the API directly
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),
});
```

If you need to override gas, set a higher limit — never lower:

```typescript theme={null}
const gasWithBuffer = BigInt(tx.gas) * 130n / 100n; // 30% buffer
```

***

## Rate limiting

The API allows **20 requests per 60 seconds** per IP. Tips to stay within limits:

* **Cache quotes** — Don't re-fetch on every keystroke. Use debouncing (see [React Hooks guide](/guides/react-hooks)).
* **Poll at reasonable intervals** — For price feeds, 15–30 seconds is sufficient. Prices refresh every \~5 seconds on the backend.
* **Batch token lookups** — Use `GET /{chainId}/tokens` to fetch all tokens in one call instead of fetching individually.

If you hit the rate limit, the API returns HTTP 429. Back off and retry:

```typescript theme={null}
async function fetchWithBackoff<T>(url: string, headers: Record<string, string>): Promise<T> {
  const res = await fetch(url, { headers });
  if (res.status === 429) {
    await new Promise((r) => setTimeout(r, 5000)); // wait 5 seconds
    return fetchWithBackoff(url, headers);
  }
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  return res.json();
}
```

***

## Approval best practices

* **Approve once, swap many times** — The default approval sets an unlimited allowance. You only need to approve once per token.
* **Check before approving** — Always call `/approve/allowance` first. Sending an unnecessary approval wastes gas.
* **Wait for confirmation** — Don't call `/swap` until the approval transaction is confirmed on-chain.
* **Native tokens skip approval** — HYPE (`0xeeee...eeee`) doesn't need approval. Skip the allowance check for native tokens.

```typescript theme={null}
const NATIVE = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee';

if (src.toLowerCase() !== NATIVE) {
  const { allowance } = await checkAllowance(src, walletAddress);
  if (BigInt(allowance) < BigInt(amount)) {
    const approveTx = await getApproveTransaction(src);
    const hash = await sendTransaction(approveTx);
    await waitForReceipt(hash);  // wait before swapping
  }
}
```

***

## Amounts and decimals

All amounts in the API are in **wei** (the token's smallest unit). Each token has its own `decimals` value.

| Token | Decimals | 1.0 in wei            |
| ----- | -------- | --------------------- |
| HYPE  | 18       | `1000000000000000000` |
| USDC  | 6        | `1000000`             |
| UBTC  | 18       | `1000000000000000000` |

Use viem's `parseUnits` and `formatUnits` to convert:

```typescript theme={null}
import { parseUnits, formatUnits } from 'viem';

// Human → wei
const amountWei = parseUnits('1.5', 18).toString();  // "1500000000000000000"

// Wei → human
const amountHuman = formatUnits(BigInt('1500000000000000000'), 18);  // "1.5"
```

<Warning>
  Always use the token's actual `decimals` value from the API — don't hardcode 18. Some tokens use 6, 8, or other values.
</Warning>
