# Swap

Allowance-holder swaps on BSC (56), Ethereum (1), Base (8453) and Polygon (137). The taker approves the settlement contract for the exact amount and sends the transaction from the quote.

| Endpoint | Use |
|---|---|
| `GET /swap/allowance-holder/price` | Indicative price, no transaction. `taker` is optional. |
| `GET /swap/allowance-holder/quote` | Firm quote with `transaction`. `taker` is required. |

## Parameters

| Parameter | Rule |
|---|---|
| `chainId` | Required. 56, 1, 8453 or 137. Other values: `400 CHAIN_NOT_SUPPORTED`. |
| `sellToken`, `buyToken` | Required. Addresses in any letter case. The native coin is `0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE`. |
| `sellAmount` | Required. Base units, decimal string, above 0. |
| `taker` | Required on quote, optional on price. With a taker, `issues` are filled and the wallet's on-chain fee rate is used. |
| `recipient` | Absent, or equal to `taker`. Another address gives `400 RECIPIENT_NOT_SUPPORTED`: the contract pays the sender. |
| `slippageBps` | Default 100, allowed 10 to 1000. |
| `slippagePpm` | 1,000 to 100,000, rounded down to whole bps (warning `slippage_ppm_rounded` when not a multiple of 100). Not together with `slippageBps`. |
| `deadlineSeconds` | Seconds until the transaction deadline (`expiresAt`). Default 120, allowed 30 to 600. |
| `excludedSources` | Comma-separated names from `GET /sources`. Unknown names are listed in `issues.invalidSourcesPassed`. |
| `gasPrice` | Gas price in wei for the estimate. Default: the chain's current price. |

Integrator fee: `swapFeeRecipient` (one wallet, or up to 3 separated by commas) and `swapFeeBps` (one value per wallet, 300 in total), optionally `swapFeeToken` (sellToken or buyToken, default buyToken); the router pays each wallet in the same transaction and the answer lists them in `fees.integratorFees`. `tradeSurplus*`, `txOrigin`, `sellEntireBalance` and `wrapUnwrapMode` are accepted and ignored; `buyAmount` (exact-out) is not supported in v1. Unknown parameters are ignored; a repeated parameter gives `400 INPUT_INVALID`.

## Pairs

Each pair needs an **anchor** on at least one side. The other side is an anchor or a token listed by `GET /tokens?chainId=`.

| Chain | Anchors |
|---|---|
| BSC (56) | BNB, WBNB, USDT, USDC |
| Ethereum (1) | ETH, WETH, USDT, USDC |
| Base (8453) | ETH, WETH, USDT, USDC |
| Polygon (137) | POL, WPOL, USDT, USDC |

| Pair | Answer |
|---|---|
| Same token on both sides | `400 TOKEN_PAIR_NOT_SUPPORTED` |
| A token not in `GET /tokens` | `400 TOKEN_NOT_SUPPORTED` with `data.token` |
| No anchor on either side | `400 TOKEN_PAIR_NOT_SUPPORTED`, `data.reason` `no_anchor` |
| Native coin and its wrapped token | `400 TOKEN_PAIR_NOT_SUPPORTED`, `data.reason` `wrap_unwrap` |
| A token with a transfer tax | `400 TOKEN_NOT_SUPPORTED`, `data.reason` `transfer_tax` (found by the quote simulation) |

## Fee

The on-chain fee is `fees.qiguardFee`: 0.50% (50 bps) of the trade, or the lower on-chain rate of the taker wallet when it has one. It is charged by the settlement contract in the same transaction.

| Sell | Buy | Fee taken from | Fee token |
|---|---|---|---|
| USDT | any token | the sell amount, before the swap | USDT |
| USDT | native coin | the sell amount | USDT |
| any token | USDT | the gross output | USDT |
| native coin | USDT | the gross output | USDT |
| token X (USDC and wrapped native included) | token Y (not USDT) | the gross output | Y |
| token (not USDT) | native coin | the gross output | native coin |
| native coin | token (not USDT) | the gross output | that token |

`buyAmount` and `minBuyAmount` are **net**: what the taker receives after the fee. The contract checks `minBuyAmount` against the net amount.

```text
minBuyAmount = floor(buyAmount * (10000 - slippageBps) / 10000)
```

A `minBuyAmount` of 0 gives `400 SELL_AMOUNT_TOO_SMALL`.

## Approvals

- `allowanceTarget` is the settlement contract of the chain (`null` when selling the native coin).
- When the taker's allowance is below `sellAmount`, the quote sets `issues.allowance = { actual, spender }`. Approve `spender` for exactly `sellAmount`, wait for the receipt, then quote again.
- `issues.allowance.resetRequired: true` (USDT on Ethereum with a non-zero allowance): approve 0 first, then the exact amount.
- When the balance is short, `issues.balance = { token, actual, expected }`.
- The SDK helper `approvalsForQuote(quote)` returns exactly these approval transactions.

## The transaction

The quote returns `transaction = { to, data, value, gas, gasPrice }`. Send it unchanged; use `gas` as the gas limit.

| Field | Meaning |
|---|---|
| `transaction.to` | The settlement contract. |
| `transaction.data` | `marketSwap`, `marketSwapNativeToToken` (selling the native coin) or `marketSwapTokenToNative` (buying the native coin), then the 18-byte Qiguard tag. |
| `transaction.value` | `sellAmount` when selling the native coin, else `0`. |
| `expiresAt` | The deadline inside `transaction.data` (Unix seconds). The contract refuses the transaction after it. |
| `qiguard` | `function`, `selector`, `adapter`, `deadline` and `tag` of the call. |
| `totalNetworkFee` | `gas` x `gasPrice`, plus the L1 data fee on Base. |

The price endpoint returns the same object without `transaction`, `qiguard` and `expiresAt`, and with top-level `gas` and `gasPrice`. When no route exists, both endpoints answer `200` with only `{ "liquidityAvailable": false, "zid": "..." }`.

## Quote checks

- **Two RPC operators.** The quote is read on one RPC operator, then the best routes are read again on a second, independent operator at the same block. They must agree within 30 bps, else `503 RPC_UNAVAILABLE`. `protection.quoteIntegrity` shows `rpcAgreement`, `maxDeviationBps` and `block`.
- **Simulation.** When the taker's balance and allowance are enough, the quote runs the exact transaction with `eth_call` from the taker and estimates its gas (`gas` = estimate x 1.2). `protection.simulation` is `passed`, `skipped_allowance` or `skipped_balance`; on the price endpoint it is `not_run`. A revert gives `400 SWAP_VALIDATION_FAILED` (`data.reason` `slippage` or `adapter_not_allowed`, `data.revert` for other reverts).
- **Paused contract.** `protection.settlementPaused` reflects the live `paused()` flag of the settlement contract. While it is true, price and quote answer `503 SETTLEMENT_PAUSED`.
- `protection.priceBand.priceImpactBps` (quote only) compares the execution price with a small reference trade on the same route.
- `protection.mev.submission` is `taker_submits`: the taker sends the transaction.

## Calldata tag

`transaction.data` ends with 18 bytes: `0x71673031` ("qg01"), the first 8 bytes of sha256 of the app id, and the first 6 bytes of the response `zid`. The contract ignores bytes after the call arguments. The tag attributes the trade to the app; an app can switch it off in the dashboard (app settings).

## Warnings

`warnings[]` lists accepted parameters that changed nothing: `trade_surplus_ignored`, `tx_origin_ignored`, `wrap_unwrap_mode_ignored`, `slippage_ppm_rounded`, and `secret_key_origin_header` (a secret key arrived with an `Origin` header; send it from a server without browser headers).
