# Get Orderbook
Source: https://docs.kipseli.capital/api-reference/get-price
get /v2/price
Returns the latest cached on-chain orderbook for all configured token pairs.
The cache refreshes on every new block via `eth_subscribe`, so the data always
reflects the most recent chain state.
Each pair has a `baseToken` (the traded token, e.g. WETH) and a `quoteToken` — the
chain's quote stablecoin: USDC on Base (`8453`), USDT on BNB Chain (`56`), USDG on
Robinhood Chain (`4663`).
Prices are expressed as **quote token per base token** (decimal strings).
Amounts are **human-readable base token quantities** (decimal strings).
Levels are **marginal** — each level is an independent price point, not cumulative depth.
`bids` are sorted descending (best bid first); `asks` are sorted ascending (best ask first).
Use the `chainId` query parameter to select the chain. Omitting it (or passing `0`)
targets the default chain, Base (`8453`). `chainId`, `blockNumber`, and `blockTime`
in the response all belong to the selected chain.
Returns the live on-chain orderbook for all configured token pairs on a given chain. The cache is refreshed on every new block via `eth_subscribe`, so the response always reflects the most recent chain state.
Use this endpoint to get current market prices before executing a swap, or to display market depth to users.
## Selecting a Chain
Pass the target chain with the `chainId` query parameter:
```bash theme={null}
# Base (8453)
curl "https://propamm.kipseli.win/v2/price?chainId=8453" \
-H "X-API-KEY: $KIPSELI_API_KEY"
# BNB Chain (56)
curl "https://propamm.kipseli.win/v2/price?chainId=56" \
-H "X-API-KEY: $KIPSELI_API_KEY"
# chainId omitted — falls back to Base (8453)
curl "https://propamm.kipseli.win/v2/price" \
-H "X-API-KEY: $KIPSELI_API_KEY"
```
Each chain quotes against its own stablecoin — the `quoteToken`:
| Chain | `chainId` | Quote token |
| --------------- | --------- | --------------------------------------------------- |
| BNB Chain | `56` | USDT — `0x55d398326f99059fF775485246999027B3197955` |
| Base | `8453` | USDC — `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` |
| Robinhood Chain | `4663` | USDG — `0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168` |
Every pair on a chain shares that chain's quote token, and each pair echoes it in `quoteToken`. Read it on-chain with `getQuoteToken()` on the chain's [QuoteLens helper](/index#contract-addresses) if you need to confirm it at runtime.
If `chainId` is omitted (or `0`), the request targets Base (`8453`). The response echoes the resolved chain in `chainId`, and `blockNumber` / `blockTime` refer to that chain.
Chain-related failures:
| Status | Code | When |
| ------ | --------------------- | -------------------------------------------------------------------------- |
| 400 | `UNSUPPORTED_CHAIN` | Well-formed `chainId` the service does not serve |
| 422 | `VALIDATION_ERROR` | `chainId` is not a base-10 unsigned integer |
| 503 | `SERVICE_UNAVAILABLE` | Supported chain with no live orderbook yet (bootstrapping, or RPC is down) |
## Understanding the Orderbook
* **bids** — prices at which PropAMM will **buy** the base token (you sell base, receive the quote token). Sorted **descending**.
* **asks** — prices at which PropAMM will **sell** the base token (you buy base, pay the quote token). Sorted **ascending**.
* **price** is always **quote token per base token** — USDC on Base, USDT on BNB Chain, USDG on Robinhood Chain.
* Each level is **marginal** — independent price points, not cumulative depth.
* Prices and amounts are **decimal strings** to preserve EVM-scale precision.
Quote-token decimals differ per chain — USDC and USDG use **6**, BNB Chain's USDT uses **18**. `price` and `amount` are human-readable decimal strings and unaffected, but any wei-denominated value you compute from them (e.g. `minAmountOut` in [Request Swap Verification](/api-reference/sign-quote)) must be scaled with the right decimals for that chain.
A chain whose orderbook has not bootstrapped yet does not return an empty book — it returns `503 SERVICE_UNAVAILABLE`. A `200` with an empty `pairs` array means the chain is live but currently tracks no pairs.
## Alternative: On-chain Quote
For a precise quote for a specific input amount, use the on-chain `quote()` function with an EIP-712 signature. See the [Integration Guide](/index#getting-a-quote).
***
## Query Parameters
| Parameter | Type | Required | Description |
| --------- | ------- | -------- | ------------------------------------------------------------------------ |
| `chainId` | integer | No | Chain to return the orderbook for. Omitted or `0` targets `8453` (Base). |
***
## Response Fields
### Top-level
| Field | Type | Description |
| ------------- | ------- | -------------------------------------------------------- |
| `chainId` | integer | Chain this orderbook belongs to (the resolved `chainId`) |
| `blockNumber` | integer | Block at which the orderbook was last refreshed |
| `blockTime` | integer | Unix timestamp (seconds) of that block |
| `pairs` | array | One entry per pair configured on that chain |
### `pairs[].PairOrderbook`
| Field | Type | Description |
| ------------ | ------ | ----------------------------------------------------------- |
| `baseToken` | string | EVM address of the traded token (e.g. WETH) |
| `quoteToken` | string | EVM address of the chain's quote token (USDC / USDT / USDG) |
| `bids` | array | Buy-side levels, sorted by descending price |
| `asks` | array | Sell-side levels, sorted by ascending price |
### `bids[].OrderbookLevel` / `asks[].OrderbookLevel`
| Field | Type | Description |
| -------- | ------ | ------------------------------------------------------------- |
| `price` | string | Quote token per base token, decimal string (e.g. `"3200.50"`) |
| `amount` | string | Base token quantity, decimal string (e.g. `"1.5"`) |
# Request Swap Verification
Source: https://docs.kipseli.capital/api-reference/sign-quote
post /v2/swap/sign
ABI-encodes `(tokenIn, tokenOut, timestamp, fee, originRate, userAddress)`,
Keccak256-hashes the payload, and signs it with secp256k1.
The recovery ID is adjusted from `[0,1]` to `[27,28]` per the Ethereum Yellow Paper.
The response contains ABI-encoded verification data `(fee, originRate, userAddress, signature)`
ready for on-chain submission.
Use the `chainId` body field to select the chain the swap will be executed on;
the token pair must be supported there. Omitting it (or passing `0`) targets the
default chain, Base (`8453`).
Note that the signed payload itself does **not** include the chain id — `chainId`
selects which chain's pair list and signing key are used.
Before executing a swap on-chain, call this endpoint to obtain `verificationData` and `timestamp`. Both values must be passed directly into the `swap()` function — if either is modified or expired, the transaction will revert.
Optionally, pass `amountIn` (and `minAmountOut`) to receive pre-built `calldata` ready for direct submission.
## Selecting a Chain
Pass the target chain in the request body with `chainId`:
| Chain | `chainId` | Router (PropAmm) | Quote token |
| --------------- | --------- | -------------------------------------------- | ----------- |
| BNB Chain | `56` | `0x4cb2140C29518db6203f661B9F6dD60B68BB7f0d` | USDT |
| Base | `8453` | `0x71C2Ed90CC288229Be59F26b8B3EEF3C07d7ab99` | USDC |
| Robinhood Chain | `4663` | `0x4f1ce663bF2E5e3b4A4ba88F6D1BF227e5597402` | USDG |
Every pair is base token / quote token, so the stablecoin leg of your `tokenIn`/`tokenOut` must be that chain's quote token — USDT on BNB Chain, not USDC. See [Get Orderbook](/api-reference/get-price#selecting-a-chain) for addresses.
If `chainId` is omitted (or `0`), the request targets Base (`8453`). `chainId` selects which chain's pair list is validated against and which signing key is used — the token pair must be supported on that chain, or the request fails with `400 PAIR_NOT_FOUND`. An unsupported chain returns `400 UNSUPPORTED_CHAIN`.
## Swap Execution Flow
### Step 1 — Call this endpoint
**Minimal (verification data only):**
```bash theme={null}
curl -X POST https://propamm.kipseli.win/v2/swap/sign \
-H "Content-Type: application/json" \
-H "X-API-KEY: $KIPSELI_API_KEY" \
-d '{
"chainId": 8453,
"tokenIn": "0x4200000000000000000000000000000000000006",
"tokenOut": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"fee": "30",
"userAddress": "0x1111111111111111111111111111111111111111"
}'
```
**Response:**
```json theme={null}
{
"verificationData": "0x...",
"timestamp": 1741737600
}
```
**With calldata (pass `amountIn` and optionally `minAmountOut`):**
```bash theme={null}
curl -X POST https://propamm.kipseli.win/v2/swap/sign \
-H "Content-Type: application/json" \
-H "X-API-KEY: $KIPSELI_API_KEY" \
-d '{
"chainId": 8453,
"tokenIn": "0x4200000000000000000000000000000000000006",
"tokenOut": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"fee": "30",
"userAddress": "0x1111111111111111111111111111111111111111",
"amountIn": "1000000000000000000",
"minAmountOut": "3150000000"
}'
```
**Response:**
```json theme={null}
{
"verificationData": "0x...",
"timestamp": 1741737600,
"calldata": "0x..."
}
```
The `calldata` field is ABI-encoded for `swap(address,uint256,address,uint256,uint256,bytes)` and can be submitted directly to the router without further encoding.
### Step 2 — Approve the Router
Use the Router for the chain you passed as `chainId` (see [Selecting a Chain](#selecting-a-chain)). The example below is Base.
```solidity theme={null}
IERC20(tokenIn).approve(
0x71C2Ed90CC288229Be59F26b8B3EEF3C07d7ab99, // Router on Base (8453)
amountIn
);
```
### Step 3 — Execute the swap on-chain
**Option A — build the call yourself:**
```solidity theme={null}
IPropAmm(0x71C2Ed90CC288229Be59F26b8B3EEF3C07d7ab99).swap( // Router on Base (8453)
tokenIn,
amountIn,
tokenOut,
minOutAmount, // slippage protection — use quote() to calculate
timestamp, // from API response
verificationData // from API response
);
```
| Parameter | Description |
| ------------------ | ---------------------------------------------------------------- |
| `tokenIn` | Input token address |
| `amountIn` | Amount of input token (in token decimals) |
| `tokenOut` | Output token address |
| `minOutAmount` | Minimum acceptable output — set this to protect against slippage |
| `timestamp` | Returned by this API — pass through unchanged |
| `verificationData` | Returned by this API — pass through unchanged |
**Option B — use the `calldata` from the API (requires `amountIn` in Step 1):**
```solidity theme={null}
// Router on Base (8453)
(bool success, ) = address(0x71C2Ed90CC288229Be59F26b8B3EEF3C07d7ab99).call(calldata);
require(success, "swap failed");
```
The `calldata` is already ABI-encoded for `swap(address,uint256,address,uint256,uint256,bytes)` — no further encoding needed.
***
## Request Fields
| Field | Type | Required | Description |
| -------------- | ------- | -------- | -------------------------------------------------------------------------- |
| `chainId` | integer | No | Chain the swap will be executed on. Omitted or `0` targets `8453` (Base). |
| `tokenIn` | string | Yes | Input token address on `chainId` |
| `tokenOut` | string | Yes | Output token address on `chainId` |
| `fee` | string | Yes | Fee tier in 0.1 bps resolution — see [Fee Parameter](#fee-parameter) |
| `userAddress` | string | Yes | Address that will submit the swap |
| `amountIn` | string | No | Input amount in wei. When provided, the response includes `calldata` |
| `minAmountOut` | string | No | Minimum accepted output in wei. Only used with `amountIn`; defaults to `0` |
***
## Fee Parameter
The `fee` field uses **0.1 bps resolution**:
| Value | Rate |
| ----- | ---------------- |
| `1` | 0.1 bps (0.001%) |
| `10` | 1 bps (0.01%) |
| `30` | 3 bps (0.03%) |
| `100` | 10 bps (0.10%) |
The fee is deducted from the output amount (`amountOut`) and settled monthly in a mutually agreed currency.
# Kipseli PropAMM
Source: https://docs.kipseli.capital/index
Integrate with Kipseli PropAMM — a professional AMM for token swaps on Base, BNB Chain and Robinhood Chain
## What is Kipseli PropAMM?
Kipseli PropAMM is a professional Automated Market Maker deployed on Base, BNB Chain and Robinhood Chain that enables token swaps with competitive, on-chain pricing. Integrators can get quotes and execute swaps by combining on-chain contract calls with a lightweight signing API.
## Contract Addresses
The **Router (PropAmm)** is the contract you approve `tokenIn` on and call `swap()` / `quote()` against. The **Helper (QuoteLens)** is a read-only view contract for listed tokens and reserves.
| Chain | Router (PropAmm) | Helper (QuoteLens) | QuoteToken |
| --------------- | -------------------------------------------- | -------------------------------------------- | ---------- |
| BNB Chain | `0x4cb2140C29518db6203f661B9F6dD60B68BB7f0d` | `0x6E56480F8D8e17A1c7148f43BC3762E59C3Abe90` | USDT |
| Base | `0x71C2Ed90CC288229Be59F26b8B3EEF3C07d7ab99` | `0x62aFF80b3d2afE0E497f1ef735a6FDc9C3ef1acF` | USDC |
| Robinhood Chain | `0x4f1ce663bF2E5e3b4A4ba88F6D1BF227e5597402` | `0xABa7C80918d8127C23BE2bef649832050a0Cf08a` | USDG |
The **EIP-712 Verifier** is deployed at the same address on every chain — `0xCa369e97cc161c3c3a7368f9bC55A47F36a0A91E`. Only the `chainId` in the EIP-712 domain changes per chain. See [EIP-712 Signature](#eip-712-signature).
***
## Contract Interfaces
### Router (IPropAmm)
```solidity theme={null}
interface IPropAmm {
function swap(
address tokenIn,
uint amountIn,
address tokenOut,
uint minOutAmount,
uint quoteTimestamp,
bytes calldata verificationData
) external returns (uint amountOut);
function quote(
address tokenIn,
uint amountIn,
address tokenOut,
uint timestampInMilisec,
bytes memory sig
) external view returns (uint amountOut);
}
```
### Helper (IQuoteLens)
```solidity theme={null}
interface IQuoteLens {
function getListedTokens() external view returns (address[] memory);
function getQuoteToken() external view returns (address);
function getReserveBalances(address[] memory tokens) external view returns (uint[] memory);
}
```
***
## Whitelist Requirement
Both on-chain quoting (EIP-712 signature) and the swap signing API require your address to be **whitelisted**. Contact the Kipseli team and provide your signing address to get access.
## API Authentication
All HTTP API endpoints (`/v2/*`) require an API key. Pass it on every request via the `X-API-KEY` header:
```bash theme={null}
-H "X-API-KEY: $KIPSELI_API_KEY"
```
Contact the Kipseli team to obtain your API key. Requests with a missing or invalid key return `401 UNAUTHORIZED`.
***
## Integration Overview
### Getting a Quote
Choose one of two methods:
**Method 1 — On-chain via EIP-712 signature**
1. Generate `timestampInMilisec` (current time in ms). Must be within **10 seconds** of the current block.
2. Sign the EIP-712 typed data with your whitelisted key (see [EIP-712 Signature](#eip-712-signature) below).
3. Call `quote(tokenIn, amountIn, tokenOut, timestampInMilisec, signature)` on the Router.
4. Receive `amountOut` — your expected output amount.
**Method 2 — Off-chain via API**
Call `GET /v2/price?chainId=` to get the live on-chain orderbook for all configured pairs on that chain. If `chainId` is omitted, it falls back to Base (`8453`). See [Get Orderbook](/api-reference/get-price).
***
### Executing a Swap
**Step 1 — Get verification data**
Call `POST /v2/swap/sign` with your swap parameters, including `chainId` for the chain you are trading on (omitted falls back to Base `8453`). The API returns `verificationData` and `timestamp`. Optionally pass `amountIn` (and `minAmountOut`) to also receive pre-built `calldata`. See [Request Swap Verification](/api-reference/sign-quote).
**Step 2 — Approve the Router**
```solidity theme={null}
IERC20(tokenIn).approve(
0x71C2Ed90CC288229Be59F26b8B3EEF3C07d7ab99,
amountIn
);
```
**Step 3 — Call `swap()`**
*Option A — build the call yourself:*
```solidity theme={null}
IPropAmm(0x71C2Ed90CC288229Be59F26b8B3EEF3C07d7ab99).swap(
tokenIn,
amountIn,
tokenOut,
minOutAmount, // slippage protection — derive from quote()
timestamp, // from API response
verificationData // from API response
);
Both `timestamp` and `verificationData` must be passed directly from the API response without modification. If either is changed or expired, the transaction will revert.
```
*Option B — use the `calldata` returned by the API (requires `amountIn` in Step 1):*
```solidity theme={null}
(bool success, ) = address(0x71C2Ed90CC288229Be59F26b8B3EEF3C07d7ab99).call(calldata);
require(success, "swap failed");
```
***
## EIP-712 Signature
To call `quote()` on-chain, you must sign the following struct with a whitelisted key:
```solidity theme={null}
PropAmmVerification(
address tokenIn,
address tokenOut,
uint256 timestampInMilisec
)
```
**Domain:**
```json theme={null}
{
"name": "VerificationImpl",
"version": "1",
"chainId": 8453,
"verifyingContract": "0xCa369e97cc161c3c3a7368f9bC55A47F36a0A91E"
}
```
`verifyingContract` is the same on every chain — set `chainId` to the chain you are trading on (`56` BNB Chain, `8453` Base, `4663` Robinhood Chain).
**Example (ethers.js):**
```javascript theme={null}
const domain = {
name: "VerificationImpl",
version: "1",
chainId: 8453,
verifyingContract: "0xCa369e97cc161c3c3a7368f9bC55A47F36a0A91E",
};
const types = {
PropAmmVerification: [
{ name: "tokenIn", type: "address" },
{ name: "tokenOut", type: "address" },
{ name: "timestampInMilisec", type: "uint256" },
],
};
const value = { tokenIn, tokenOut, timestampInMilisec };
const signature = await signer.signTypedData(domain, types, value);
// Call on-chain:
// quote(tokenIn, amountIn, tokenOut, timestampInMilisec, signature)
```
***
## Fee Structure
The `fee` field in swap requests uses **0.1 bps resolution**:
| Value | Rate |
| ----- | ---------------- |
| `1` | 0.1 bps (0.001%) |
| `10` | 1 bps (0.01%) |
| `30` | 3 bps (0.03%) |
| `100` | 10 bps (0.10%) |
* Fee is **deducted from the output amount** (`amountOut`).
* Fees are **settled monthly** in a mutually agreed currency (e.g. USDC, ETH).
## Error Format
All API errors follow a consistent envelope:
```json theme={null}
{
"error": {
"code": "VALIDATION_ERROR",
"message": "tokenIn: invalid EVM address",
"requestId": "abc-123"
}
}
```
| Code | HTTP Status | Description |
| --------------------- | ----------- | ----------------------------------------------------------- |
| `INVALID_JSON` | 400 | Malformed or missing JSON body |
| `UNSUPPORTED_FIELD` | 400 | Unknown field in request body |
| `UNSUPPORTED_CHAIN` | 400 | `chainId` is not a chain the service serves |
| `PAIR_NOT_FOUND` | 400 | Token pair is not tracked on the selected chain |
| `UNAUTHORIZED` | 401 | Missing or invalid API key |
| `VALIDATION_ERROR` | 422 | Field-level validation failure |
| `SIGNING_FAILED` | 500 | secp256k1 signing error |
| `ENCODING_FAILED` | 500 | ABI encoding error |
| `INTERNAL_ERROR` | 500 | Unexpected server error |
| `SERVICE_UNAVAILABLE` | 503 | Chain has no live orderbook yet (bootstrapping or RPC down) |