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

# How to add swaps to your app using Kuru Flow

Your application may need to allow users to swap between two assets. You want your users
to get the best price available across the ecosystem.

Aggregators check many liquidity sources and find the best price. Integrating an aggregator API
can thus be a convenient and efficient means of enabling swapping in your app.

In this guide, we will examine integrating one such aggregator,
[Kuru Flow](https://docs.kuru.io/kuru-flow/flow-overview). You'll learn how to integrate Kuru
Flow into a frontend application—from authentication to fetching quotes and executing swaps.

## Requirements

Before you begin, ensure you have the following:

* A frontend framework with wallet connection (React, Next.js, etc.)
* [wagmi](https://wagmi.sh) and [viem](https://viem.sh) for wallet interactions
* User wallet connected to Monad mainnet (chain ID `143`)

## 1. Configure Authentication

We first need to get credentials to access the Kuru Flow API.
Kuru Flow uses JWT tokens for API authentication.

```typescript lines title="lib/kuru-flow.ts" theme={null}
const KURU_FLOW_API = "https://ws.kuru.io";

async function generateToken(userAddress: string) {
  const response = await fetch(`${KURU_FLOW_API}/api/generate-token`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ user_address: userAddress }),
  });

  return response.json();
  // Returns: { token: string, expires_at: number, rate_limit: { rps: number, burst: number } }
}
```

## 2. Get a Swap Quote

The `/api/quote` endpoint calculates the best swap path and returns transaction data.

The following parameters are supported:

| Parameter           | Required | Description                                      |
| ------------------- | -------- | ------------------------------------------------ |
| `userAddress`       | Yes      | User's wallet address                            |
| `tokenIn`           | Yes      | Token address (`0x0` for native MON)             |
| `tokenOut`          | Yes      | Token address (`0x0` for native MON)             |
| `amount`            | Yes      | Amount in smallest token units                   |
| `autoSlippage`      | No\*     | Set to `true` for automatic slippage calculation |
| `slippageTolerance` | No\*     | Manual slippage in basis points (1-10000)        |
| `referrerAddress`   | No       | Your address to receive referral fees            |
| `referrerFeeBps`    | No       | Referral fee in basis points (0-10000)           |

\*Either `autoSlippage: true` OR `slippageTolerance` must be provided.

We can write a small function to interact with the `quote` API:

```typescript lines title="lib/kuru-flow.ts" theme={null}
const KURU_FLOW_API = "https://ws.kuru.io";

// Your wallet address to receive referral fees
const REFERRER_ADDRESS = "0xYOUR_WALLET_ADDRESS_HERE";
const REFERRER_FEE_BPS = 50; // 0.5%

async function getQuote(
  userAddress: string,
  tokenIn: string,
  tokenOut: string,
  amount: string,
  token: string
) {
  const response = await fetch(`${KURU_FLOW_API}/api/quote`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${token}`,
    },
    body: JSON.stringify({
      userAddress,
      tokenIn,
      tokenOut,
      amount,
      autoSlippage: true,
      referrerAddress: REFERRER_ADDRESS,
      referrerFeeBps: REFERRER_FEE_BPS,
    }),
  });

  return response.json();
}
```

### Response Structure

```typescript lines title="lib/kuru-flow.ts" theme={null}
export interface QuoteResponse {
  type: string;
  status: "success" | "error";
  output: string;           // Expected output amount (in wei)
  minOut: string;           // Minimum output with slippage
  transaction: {
    to: string;             // Router contract address
    calldata: string;       // Transaction calldata (WITHOUT 0x prefix!)
    value: string;          // Native MON amount (non-zero when swapping native MON)
  };
  gasPrices: {
    slow: string;
    standard: string;
    fast: string;
    rapid: string;
    extreme: string;
  };
  message?: string;         // Error message if status is "error"
}
```

<Note>
  The `calldata` field does NOT include the `0x` prefix. You must add it before sending the transaction.
</Note>

## 3. Check Token Allowance (ERC20 Only)

Before executing a swap with ERC20 tokens, we should verify that the router has permission to
spend the user's tokens.

<Tip>
  **Native MON does not require approval.** Skip this step when swapping native MON.
</Tip>

```typescript lines title="components/SwapCard.tsx" theme={null}
import { useState } from "react";
import { useAccount, useReadContract } from "wagmi";
import { parseUnits } from "viem";
import type { QuoteResponse } from "@/lib/kuru-flow";

// Token addresses
const NATIVE_MON = "0x0000000000000000000000000000000000000000" as const;
const USDC = "0x754704Bc059F8C67012fEd69BC8A327a5aafb603" as const;

const ERC20_ABI = [
  {
    name: "allowance",
    type: "function",
    stateMutability: "view",
    inputs: [
      { name: "owner", type: "address" },
      { name: "spender", type: "address" },
    ],
    outputs: [{ name: "", type: "uint256" }],
  },
] as const;

// Inside your component:
const { address } = useAccount();
const [amount, setAmount] = useState("");
const [quote, setQuote] = useState<QuoteResponse | null>(null);

const tokenIn = USDC; // Example: swapping USDC
const isNativeToken = tokenIn === NATIVE_MON;
const amountWei = amount ? parseUnits(amount, 6) : BigInt(0);

// Check allowance against router address from quote (skip for native MON)
const { data: allowance } = useReadContract({
  address: tokenIn,
  abi: ERC20_ABI,
  functionName: "allowance",
  args: address && quote?.transaction?.to
    ? [address, quote.transaction.to as `0x${string}`]
    : undefined,
  query: {
    enabled: !isNativeToken, // Don't check allowance for native token
  },
});

// Native tokens don't need approval
const needsApproval = !isNativeToken && allowance !== undefined && amountWei > allowance;
```

## 4. Approve Token Spending (ERC20 Only)

If allowance is insufficient for an ERC20 token, we should request approval before swapping.

```typescript lines title="components/SwapCard.tsx" theme={null}
import { encodeFunctionData } from "viem";
import { useSendTransaction } from "wagmi";

const APPROVE_ABI = [
  {
    name: "approve",
    type: "function",
    stateMutability: "nonpayable",
    inputs: [
      { name: "spender", type: "address" },
      { name: "amount", type: "uint256" },
    ],
    outputs: [{ name: "", type: "bool" }],
  },
] as const;

// Inside your component:
const { sendTransaction: sendApproveTx } = useSendTransaction();

const handleApprove = () => {
  if (!quote?.transaction?.to) return;

  const data = encodeFunctionData({
    abi: APPROVE_ABI,
    functionName: "approve",
    args: [quote.transaction.to as `0x${string}`, amountWei],
  });

  sendApproveTx({
    to: tokenIn,
    data,
  });
};
```

<Tip>
  Use viem's `encodeFunctionData` instead of manual hex string manipulation for reliable ABI encoding.
</Tip>

## 5. Execute the Swap

To submit the swap, we should use the transaction data from the quote response:

```typescript lines title="components/SwapCard.tsx" theme={null}
// Inside your component:
const { sendTransaction: sendSwapTx } = useSendTransaction();

const handleSwap = () => {
  if (!quote || quote.status !== "success") return;

  // IMPORTANT: Add 0x prefix to calldata
  const calldata = quote.transaction.calldata.startsWith("0x")
    ? quote.transaction.calldata
    : `0x${quote.transaction.calldata}`;

  sendSwapTx({
    to: quote.transaction.to as `0x${string}`,
    data: calldata as `0x${string}`,
    value: BigInt(quote.transaction.value || "0"),
  });
};
```

## Token Addresses and Decimals (Mainnet)

| Token        | Address                                      | Decimals | Notes              |
| ------------ | -------------------------------------------- | -------- | ------------------ |
| MON (native) | `0x0000000000000000000000000000000000000000` | 18       | No approval needed |
| USDC         | `0x754704Bc059F8C67012fEd69BC8A327a5aafb603` | 6        | Requires approval  |

<Warning>
  Be careful with token decimals when formatting output. USDC uses 6 decimals, not 18.

  ```typescript theme={null}
  import { formatEther, formatUnits } from "viem";

  // WRONG - assumes 18 decimals
  formatEther(BigInt(quote.output))  // Shows 0.000000002301622011

  // CORRECT - use actual token decimals
  formatUnits(BigInt(quote.output), 6)  // Shows 2301.62
  ```
</Warning>

## Referral Fee System

Kuru features a referral system. As the referrer, we can earn fees on every swap by including your referrer details in quote requests.

```typescript lines title="lib/kuru-flow.ts" theme={null}
// Replace with YOUR wallet address to receive referral fees
export const REFERRER_ADDRESS = "0xYOUR_WALLET_ADDRESS_HERE";

// Fee in basis points (50 = 0.5%, 100 = 1%)
export const REFERRER_FEE_BPS = 50;
```

**Example fee calculations at 50 bps (0.5%):**

| Swap Output | Fee Amount |
| ----------- | ---------- |
| 100 USDC    | 0.5 USDC   |
| 500 USDC    | 2.5 USDC   |
| 1000 USDC   | 5 USDC     |

<Warning>
  Replace `0xYOUR_WALLET_ADDRESS_HERE` with your actual wallet address before deploying. The `referrerAddress` must be a valid Ethereum address or transactions will fail.
</Warning>

## Error Handling

Along the way, we might run into errors. Here is how to interpret them:

| Status Code | Meaning                                 |
| ----------- | --------------------------------------- |
| 400         | Bad request - check parameters          |
| 401         | Unauthorized - invalid or expired token |
| 429         | Rate limited - wait before retrying     |
| 500         | Server error - retry later              |

## Common Mistakes

1. **Missing 0x prefix** - Add `0x` prefix to calldata before sending
2. **Invalid referrer address** - Must be a valid Ethereum address
3. **Skipping approval for ERC20** - ERC20 tokens require approval before swap (native MON does not)
4. **Wrong decimals** - USDC has 6 decimals, MON has 18
5. **Zero value for native MON swaps** - When swapping native MON, the transaction `value` must be set to the swap amount

## Next Steps

* Try the [Live Demo](https://kuru-flow-api-example-one.vercel.app/) to see Kuru Flow in action
* Clone the [Example Repository](https://github.com/monad-developers/kuru-flow-api-example) for a complete working implementation
* Read the [Kuru Flow Overview](https://docs.kuru.io/kuru-flow/flow-overview) for more details
* See [Monad Network Information](/developer-essentials/network-information) for RPC endpoints and chain configuration
