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

# Quickstart

> Sell 1,000 USDC for IDRX on Base with JavaScript and viem: read the deployment, get a quote, approve, fill, verify.

This guide sells **1,000 USDC for IDRX on Base**. The same flow works for every pair the API quotes.

## Before you start

* API root: `https://staging.app.aile.tech/api/base/v1`
* Network: Base mainnet, chain ID `8453`. Staging trades real assets under small limits.
* Authentication: `Authorization: Bearer <AILE_API_KEY>`. Aile issues the key; keep it on your backend.
  `GET /quote` needs it, the other routes do not. [Get an API key](https://staging.docs.aile.tech/legal/get-an-api-key.md).
* Amounts are strings in base units. USDC has 6 decimals, IDRX 2.
* A quote is valid for about **30 seconds** and pinned to **one taker address**: the address that will send
  the fill and receive the bought token. Decide that address before you ask.

```javascript theme={null}
const API = 'https://staging.app.aile.tech/api/base/v1';
const headers = { authorization: `Bearer ${process.env.AILE_API_KEY}` };

async function get(path, params) {
  const url = `${API}${path}${params ? `?${new URLSearchParams(params)}` : ''}`;
  const res = await fetch(url, { headers });
  const body = await res.json();
  if (!res.ok) throw new Error(`${res.status} ${body.reason ?? ''} ${body.detail ?? body.title ?? ''}`);
  return body;
}
```

**1. Read the deployment**

```javascript theme={null}
const config = await get('/config');
const tokens = [...config.tokens, ...(config.bookTokens ?? [])];
const lop = config.contracts.lop; // the order protocol: approve it, send fills to it
const USDC = tokens.find((t) => t.symbol === 'USDC');
const IDRX = tokens.find((t) => t.symbol === 'IDRX');
```

`config` is public and cacheable for 60 s. `tokens` are the deployment's tokens, `bookTokens` the ones
added since; both carry `address` and `decimals`. Which pairs are actually quoted is a question for
`/levels`, or simply ask for a quote.

**2. Read indicative prices (optional)**

`GET /levels` answers every quoted direction in one call, with no key, cacheable for a few seconds. Use
it to decide whether to route through Aile and at what size; it never signs or reserves anything.

```javascript theme={null}
const { levels } = await get('/levels', { pair: 'IDRX/USDC' });
const usdcToIdrx = levels.find((l) => l.sellToken === 'USDC' && l.buyToken === 'IDRX');
// usdcToIdrx: { firmQuoted, makers, maxFirmSize: { sellAmount, buyAmount }, ladder, reference: { rate, source, at }, reason }
if (!usdcToIdrx?.firmQuoted) throw new Error(`not quoted now: ${usdcToIdrx?.reason}`);
const price = Number(usdcToIdrx.maxFirmSize.buyAmount) / 100 / (Number(usdcToIdrx.maxFirmSize.sellAmount) / 1e6);
```

A direction with `firmQuoted: false` will not give a quote right now; treat it as absent.

**3. Get a quote**

```javascript theme={null}
const quote = await get('/quote', {
  sellToken: 'USDC',
  buyToken: 'IDRX',
  sellAmount: '1000000000', // 1,000 USDC, exact-in; or buyAmount for exact-out, never both
  taker,                    // the address that will send the fill
});
console.log(quote.buyAmount, quote.expiresAt, quote.maxFirmSize, quote.referenceRate);
```

What comes back, in short (every amount is a string in base units):

| Field | Meaning |
| - | - |
| `quoteId` | Stable id of this quote. |
| `sellAmount`, `buyAmount` | The exact amounts of the fill. |
| `expiresAt` | Unix seconds; the order is dead after it (about 30 s from signing). |
| `maxFirmSize` | `{ sellAmount, buyAmount }`: the largest quote the maker can pay now, at this rate. |
| `fill` | `{ method, to, data, value, allowanceTarget, amount, takerTraits }`: send `to` + `data` + `value` as returned; approve `allowanceTarget`. |
| `referenceRate` | The mid the quote was priced from, in human units. |
| `order`, `signature`, `maker`, `fees`, `extension`, `alternatives` | Informational. Do not pin the maker address; it changes. |

The full schema is in the [reference](https://staging.docs.aile.tech/api-reference/get-quote.md). Problems come back as RFC 7807 JSON
with a machine-readable `reason`; the catalogue is in the [API overview](https://staging.docs.aile.tech/api-reference/overview.md#problems).

**4. Approve and fill**

```javascript theme={null}
import { createPublicClient, createWalletClient, erc20Abi, http, parseGwei } from 'viem';
import { base } from 'viem/chains';

const pub = createPublicClient({ chain: base, transport: http(RPC) });
const wallet = createWalletClient({ account, chain: base, transport: http(RPC) }); // account.address === taker

if (quote.fill.to.toLowerCase() !== lop.toLowerCase()) throw new Error('unexpected fill target');
if (quote.expiresAt - Date.now() / 1000 < 10) throw new Error('quote too old, request a new one');

const allowance = await pub.readContract({
  address: USDC.address, abi: erc20Abi, functionName: 'allowance', args: [taker, quote.fill.allowanceTarget],
});
if (allowance < BigInt(quote.sellAmount)) {
  const h = await wallet.writeContract({
    address: USDC.address, abi: erc20Abi, functionName: 'approve', args: [quote.fill.allowanceTarget, BigInt(quote.sellAmount)],
  });
  await pub.waitForTransactionReceipt({ hash: h });
}

const hash = await wallet.sendTransaction({
  to: quote.fill.to,
  data: quote.fill.data,
  value: BigInt(quote.fill.value),
  gas: 300000n,
  maxPriorityFeePerGas: parseGwei('0.03'), // Base's real tip floor is above what some RPCs suggest
});
const receipt = await pub.waitForTransactionReceipt({ hash });
```

A fill is exact: the transaction either delivers `buyAmount` or reverts. It uses about 120k gas. If it
reverts with `OrderExpired`, the 30 s passed: request a new quote. `BitInvalidatedOrder` means this quote
was already filled; a quote fills once.

**5. Verify what arrived**

Read the receipt's `Transfer` logs of the bought token to the taker and compare with `buyAmount`:

```javascript theme={null}
import { parseEventLogs } from 'viem';

const got = parseEventLogs({ abi: erc20Abi, eventName: 'Transfer', logs: receipt.logs })
  .filter((l) => l.address.toLowerCase() === IDRX.address.toLowerCase() && l.args.to.toLowerCase() === taker.toLowerCase())
  .reduce((s, l) => s + l.args.value, 0n);
console.log(got === BigInt(quote.buyAmount)); // true
```

Optionally, `GET /orders/{orderHash}` reports the gateway's view of the same fill once indexed
(`orderHash` is the EIP-712 hash of `quote.order`).

## Routes used

| Method | Path | Key | Purpose |
| - | - | - | - |
| GET | `/config` | no | chain, order protocol address, tokens |
| GET | `/levels` | no | indicative sizes and prices per direction, `maxFirmSize` |
| GET | `/quote` | yes | the quote: a signed order and ready calldata |
| GET | `/orders/{hash}` | no | fill status, once indexed (optional) |

> [!TIP]
>
> Filling from a contract rather than a wallet? Read [Aggregators and routers](https://staging.docs.aile.tech/integrate/aggregators-and-routers.md):
> the contract is the `taker`, and the bought token is paid to it.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.