# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
This site has an agentic ask interface you may use to query the documentation dynamically by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.turbine.exchange/index.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

---

# About

Turbine. Trade slow and pay less.

Turbine is a private orderbook built for large and long-running orders to settle peer-to-peer.

Orders and LP on Turbine [follow the market](/features/market-tracking-order) and settle only when the spread is low. Turbine can settle small and large trades at low, zero, and even negative spreads.

{% hint style="success" icon="globe-pointer" %}
Go to [app.turbine.exchange](https://app.turbine.exchange/) to trade on Turbine!
{% endhint %}

### How Turbine Works

Turbine:

* Keeps orders [evergreen](/features/market-tracking-order) by making them follow the market price.
* Keeps orders [private](/features/private-orderbook), hosted trustlessly on a TEE[^1].
* [Delays trades](/features/the-speedbump) to protect traders and liquidity from stale price arbitrage.
* Updates [pooled liquidity](/features/market-tracking-lp) to follow the [market price](/features/market-price-oracle).
* Settles in [batches](/features/batching-solver) with uniform clearing prices across all Turbine orders, Turbine LP, and all on-chain liquidity (including [PMM RFQ](#user-content-fn-2)[^2]).

No one can see your orders before settlement, including the Turbine team. You don't have to trust us; you can verify it. For step-by-step verification (Trust Center walkthrough and manual quote verification), see [Trusted Execution Environment](/resources/security/trusted-execution-environment).

### Why Trade or LP on Turbine?

Turbine gets you the best trade when price matters more than speed. LPs can provide market liquidity without the risk of LVR.

You only answer one question: **until when** do you want to settle the trade? Pick a deadline and Turbine [handles](/trading/readme/auto-mode) the spread to maximize surplus.

### A Different Approach to DeFi Trading

Turbine drops the focus on low latency and time-price priority, and removes reliance on professional intermediaries. The result is the lowest-cost exchange possible.

Turbine maximizes peer-to-peer trades (CoWs), protects you against MEV, and relies exclusively on trustless exchange logic. Every deployment runs on an attested TEE, so you can verify exactly which code settles your trade.

Turbine is built for traders who prioritize fair execution over speed.

Read more about Turbine's [core concepts](/core-concepts).

### Core Contributors

[PropellerHeads](https://www.propellerheads.xyz/), led by Markus and Alan, built solvers and found that the DeFi trade supply chain today (auctions and routers) is centralized, trusted, and not competitive on large trades.

We believe in:

* **Self-sovereign trading**: Markets that anyone can use without technical or other barriers, and that protect traders and LPs from adversarial fills.
* **Peer markets**: Markets that operate solely through retail traders and LPs, eliminating expensive intermediaries.
* **DeFi-first design**: Design for the dynamic, heterogeneous, open-access, and transparent landscape of DEX liquidity.

Turbine integrates Propeller's DEX Indexer [Tycho](https://www.propellerheads.xyz/tycho), and Open Source Router [Fynd](https://docs.fynd.xyz/get-started/quickstart) to trade on DEXs.

### Audit

The backend, frontend, and SDK have been audited by Trail of Bits: see their [Audit](/resources).

[^1]: Trusted Execution Environment

[^2]: Private Market Makers Request For Quote


# Getting Started

{% hint style="success" icon="globe-pointer" %}
Go to [app.turbine.exchange](https://app.turbine.exchange/) to start trading!
{% endhint %}

Here's what you can do with Turbine.

### Place orders

Place orders that follow the market and settle only at your accepted spread or better.

With [**Auto**](/trading/readme/auto-mode)**,** you set a deadline and Turbine manages the spread for you. The more time you give it, the better the price it can find.

Orders are private and settle in frequent batches against other Turbine orders, Turbine LP, and all on-chain liquidity.

{% content-ref url="/pages/6PYguhRcY2gbN27PXrMd" %}
[Trade via Web UI](/trading/readme)
{% endcontent-ref %}

### Add liquidity

Add liquidity to Turbine Uniswap v4 hook pools. Turbine batches use these pools exclusively. The Turbine price oracle sets their prices, and the time delay protects them from stale-price arbitrage and just-in-time liquidity.

{% content-ref url="/pages/mtg4Io3Zd4HJE7dYpSzV" %}
[Manage Liquidity via Web UI](/liquidity/manage-liquidity-via-web-ui)
{% endcontent-ref %}

### Place orders programmatically

Turbine is built for programmatic trading: TWAP execution, large position buys and sells, and maker inventory rebalancing.

{% content-ref url="/pages/N3IsWEebAJLvHoLTBc46" %}
[Trade Programmatically](/trading/trade-programmatically)
{% endcontent-ref %}

### Market make

As a professional maker, provide price levels and settle against Turbine orders. The Speedbump also delays your orders.

{% content-ref url="/pages/0B75XQ1gqXdNk3lQviYI" %}
[Market Make](/advanced/market-make)
{% endcontent-ref %}

### Offer low-spread, private trades in your app or wallet

Turbine has the lowest spreads in DeFi. Integrate it into your app or wallet to offer tighter spreads, private orders, and long-running trades.

{% content-ref url="/pages/hz7MRoT4IYBbgZOyV5Ox" %}
[Integrate](/advanced/integrate)
{% endcontent-ref %}


# Core Concepts

## Slow Trading

*Good prices come to those who wait.*

Fast markets are good for click-and-buy UX and price discovery, and are friendlier to market makers. But they are also centralizing, pay more for the same depth, and have larger spreads.

Slow trading protects you and Turbine LPs from snipers. This lowers spread and slippage, and makes batching possible, which increases peer-to-peer trades. Both peer-to-peer trades and tighter LP spreads lower your cost.

*Read more about how the* [*Turbine Speedbump*](/features/the-speedbump) *works, and about the myth of fast trading.*

## Batching Market

*No spread is the best spread.*

Settling directly with another trader is best: you don't pay for third-party middlemen liquidity (LP, market maker). But to find matches, you need to [trade slow](/features/the-speedbump) and settle in [batches](/features/batching-solver).

## Trustless Orderbook

*Trust introduces risk, centralization, and incentives for collusion.*

Turbine is a trustless market from day 0: runs in a Trusted Execution Environment, hosting a private orderbook that no one can inspect.

{% hint style="info" %}
Verify Turbine's Deployment: [Trusted Execution Environment](/resources/security/trusted-execution-environment#verify-turbine).
{% endhint %}

## Oracle Guided Market

*If snipers are slowed down, markets can have an opinion on price.*

Slow trading opens a new paradigm: an informed market. Turbine reads prices from fast markets with its [Market Price Oracle](/features/market-price-oracle) and moves LP and order limit prices to the current market price before settlement.

This makes long-running orders possible, which match peer-to-peer and can't be sniped. Long-running orders save cost, improve LP revenue, and lower trader spread.

## Spread Orders

*A trade is good if the spread is low.*

To many traders, what matters is not how fast a trade executes or how close to a limit price, but whether it has a good markout. Outside of insider trading, expected markout equals spread versus market price at time of settlement. A good trade is a trade with low spread versus market.

In Turbine, every order is a [spread order](/features/market-tracking-order#how-a-spread-order-works); it accepts settlement at a target spread or better, and never goes stale. The order tracks the market and waits patiently for liquidity at the spread it wants. You name how long you're willing to wait, and Turbine works the spread down for you over that time. The more patient you are, the lower the expected spread you pay.

## Dynamic LP

*Slow markets protect LPs from LVR.*

Turbine pools are custom Uniswap v4 hooks that offer a price at constant spread above the current market price. Turbine reads the price after traders commit to their trade to protect LPs from arbitrageurs (and thereby LVR).


# Trade via Web UI

This guide explains how to trade using the web interface at [app.turbine.exchange](https://app.turbine.exchange).

{% stepper %}
{% step %}
**Connect wallet**

Click the Connect Wallet button and select your preferred wallet.

On mobile, it's best to use a web browser built into the wallet app.

<figure><img src="https://1965250678-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqEbGnoRTf6xKvVIS3IIz%2Fuploads%2FaU15fLQWjK1h1NY092R4%2FTrade%20via%20Web%20UI%20-%201.gif?alt=media&amp;token=c8ffc908-7688-415e-818c-6db98997dea1" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}
**Select sell and buy token**

Search by name or paste a token address. Common tokens are pinned at the top.

<figure><img src="https://1965250678-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqEbGnoRTf6xKvVIS3IIz%2Fuploads%2F8904PUpNV6dyYGuIkGou%2FTrade%20via%20Web%20UI%20-%202.gif?alt=media&amp;token=549eabf2-d7e6-4169-be48-88572c0e3c8c" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}
**Set sell amount**

Enter how much you want to sell.

The buy amount isn't shown as a fixed number. Turbine orders settle at a spread from market price, so the actual amount you receive depends on when and how the order settles. The maximum you pay is the current DEX aggregator rate; Turbine targets a better price than that.

The minimum trade size is **$10 USDC equivalent**.

See [Auto](/trading/readme/auto-mode) for how Turbine prices your order.

<figure><img src="https://1965250678-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqEbGnoRTf6xKvVIS3IIz%2Fuploads%2FvQqbpUkMVn2jWaK4Dcd2%2FTrade%20via%20Web%20UI%20-%203.gif?alt=media&amp;token=bae1145b-2d94-43c2-a949-12c38f345dc2" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}
**Pick your deadline**

Use the Fill within slider to choose how long Turbine has to fill your order. The longer the deadline, the better the price Turbine can find. Expiry and price protection are set for you automatically.

<figure><img src="https://1965250678-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqEbGnoRTf6xKvVIS3IIz%2Fuploads%2FAzRfqsHzYcXpoRU13wCu%2FTrade%20via%20Web%20UI%20-%204.gif?alt=media&amp;token=d78fd673-5db0-4dbc-93b3-6c7b8a7c3a5d" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}
**Place the order**

When you click *Place order*, Turbine walks you through up to three signing steps:

1. **Sign in** (first time only). Turbine asks you to [sign in with Ethereum](/design-decisions/why-sign-in-with-ethereum) so the app can fetch the status of your open orders without re-prompting on every refresh.
2. **Approve Permit2 (on-chain)**, first time per sell token only. Sign an on-chain transaction granting the Permit2 contract permission to spend that token. Costs a small amount of gas; never repeated for the same token.
3. **Sign Permit2 approval (off-chain).** Sign an off-chain Permit2 message that authorizes Turbine to spend your sell token for this specific order. No gas. See [Why Unlimited Allowance](/design-decisions/why-unlimited-allowance) for why this is set to an unlimited amount.

After the third step, your order is in the Turbine orderbook. You don't pay any gas for the settlement itself; that's covered by Turbine's platform fee.

<figure><img src="https://1965250678-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqEbGnoRTf6xKvVIS3IIz%2Fuploads%2FjqU4xoVqYVrbDk72QIIl%2FTrade%20via%20Web%20UI%20-%205.gif?alt=media&amp;token=150849f0-084e-42dc-8da5-dd9a3e05a0ed" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}
**Wait until the order settles**

Your order is now live. Cancel any time, or close the tab. Turbine keeps tracking the order on your behalf.

<figure><img src="https://1965250678-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqEbGnoRTf6xKvVIS3IIz%2Fuploads%2FjPlQcXnF9fRHDZT0zBOM%2FTrade%20via%20Web%20UI%20-%206.gif?alt=media&amp;token=da36da30-8bd4-4400-9bb3-61ae076d5c1b" alt=""><figcaption></figcaption></figure>
{% endstep %}
{% endstepper %}


# Auto Mode

Time-Bound Orders

**Auto Spread** settles your trade within your deadline at the lowest possible spread.

You pick the deadline; Turbine dynamically manages the spread to optimize your surplus.

### How Auto works

When you place an Auto order, Turbine builds a target-spread schedule that spans the order's lifetime.

The maximum spread the order accepts changes over time: it starts deeply below market (*negative spread*) and relaxes toward the current DEX aggregator rate (+ buffer) by your deadline.

There are two types of spread schedules: one type for the shortest orders (10 minutes), and another for the rest.

#### Long order spread schedule

This schedule applies to all orders whose lifetime is longer than 10 minutes.

<figure><img src="https://1965250678-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqEbGnoRTf6xKvVIS3IIz%2Fuploads%2FfN9mMnPNcKjaYR9y53PJ%2FLong%20order%20spread%20schedule%20(1).gif?alt=media&amp;token=6a98f553-4cb7-46dd-93e7-e11741ab4038" alt=""><figcaption></figcaption></figure>

The schedule walks through four phases:

<table><thead><tr><th width="124">Phase</th><th width="136">Time fraction</th><th width="485">Mechanism</th></tr></thead><tbody><tr><td>1 · yolo</td><td>0–10%</td><td>Target spread is deeply negative. The order fills only against an exceptional match, like a whale order with significant price impact.</td></tr><tr><td>2 · earn</td><td>10–50%</td><td>Target spread crosses through the negative-spread band. Most peer matching happens here; this is the widest phase.</td></tr><tr><td>3 · ultra</td><td>50–75%</td><td>Target spread approaches the aggregator rate. Turbine LP sits just above the aggregator rate to wait for swings in AMM spread.</td></tr><tr><td>4 · sure thing</td><td>75–100%</td><td>Target spread crosses the aggregator rate slightly to guarantee settlement.</td></tr></tbody></table>

The schedule starts at **−10%** (the "yolo" anchor) and ends at **the aggregator rate plus platform fees plus a small buffer** (the "sure thing" floor). These are the two fixed anchors; Turbine manages the shape between them.

#### Short order spread schedule

This schedule applies to 10-minute orders. It is better suited for short orders which don't have so much time to spare to hope for negative spread.

<figure><img src="https://1965250678-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqEbGnoRTf6xKvVIS3IIz%2Fuploads%2FWFnsLOpKYAe5N7xJRHLE%2FShort%20order%20spread%20schedule%20(2).gif?alt=media&amp;token=dc22bf4e-7966-4cf9-b01a-67ca2427269b" alt=""><figcaption></figcaption></figure>

The schedule walks through three phases:

<table><thead><tr><th width="124">Phase</th><th width="136">Time fraction</th><th width="485">Mechanism</th></tr></thead><tbody><tr><td>2 · earn</td><td>0-10%</td><td>Target spread starts at negative values and quickly goes to slightly positive.</td></tr><tr><td>3 · normal</td><td>10–80%</td><td>Target spread approaches and slightly crosses the aggregator rate.</td></tr><tr><td>4 · sure thing</td><td>80–100%</td><td>Target spread aggressively goes to twice the market spread, to ensure settlement.</td></tr></tbody></table>

### What happens in each batch

Every batch (about 12 seconds):

1. Turbine fetches the current market mid-price.
2. Your order's target spread is read from its schedule.
3. Your order enters the book for this batch at `limit price = market price - target spread`.
4. The batch clears and sets a uniform price for the pair. If the clearing price is at or below your target spread, your order settles (fully or partially) at that clearing price against a peer, [Turbine LP](/features/market-tracking-lp), or on-chain liquidity (Tycho-routed DEXs and PMM RFQ).
5. If no match clears at the target spread, your order remains in the book. The next batch reads the next point on the schedule.

The schedule keeps moving regardless of fills: every batch, the target spread decays slightly, until by the deadline it reaches the "sure thing" floor, where on-chain liquidity fills it.

### Worst case, best case

* **Worst case:** the spread you would get on any DEX aggregator right now, or larger if the order is in danger of not getting filled.
* **Best case:** much lower spread. Can include negative spread, meaning you receive more than mid-price.

The longer your deadline, the more time your trade spends in the negative-spread and near-mid bands where peer matches and AMM dislocations concentrate, and you settle at a significant surplus.

### Picking a deadline

The *Fill within* slider is the only setting you need. Pick the longest deadline you're comfortable with. The order is private, MEV-protected, and tracks the market the whole time. You can cancel at any point.

#### See also

{% content-ref url="/pages/oSTrZMbruCmH5RYx8BlN" %}
[Market Tracking Order](/features/market-tracking-order)
{% endcontent-ref %}

{% content-ref url="/pages/xi5YyZiGwS95iyNddZfr" %}
[The Speedbump](/features/the-speedbump)
{% endcontent-ref %}

{% content-ref url="/pages/EgLMwAmgTx9vjQVwJixV" %}
[Batching Solver](/features/batching-solver)
{% endcontent-ref %}


# Advanced Mode

Market Tracking Orders

Define the spread you're willing to pay. Your order tracks the market at that spread and never goes stale.

In every batch, after fetching the newest market price, Turbine resets the limit price of your order to the market price minus your set spread.

### What are the benefits?

* **Orders never go stale:** They run as long as you want, tracking the market every batch.
* **You set the spread:** Instead of accepting the current market spread, wait until you settle for the spread you want (or better), for example by settling in a [coincidence of wants](/features/batching-solver).
* **Your order stays protected:** The [Speedbump](/features/the-speedbump) safeguards your order against bots that target stale orders. Open orders are [private](/features/private-orderbook), protecting you from frontrunning and sandwich attacks.

Arbitrageurs cannot snipe your order because the Speedbump delays them, and your trade is private.

Because the limit price never goes stale, your order runs safely for as long as you need. The longer it runs, the higher the chance it settles in a coincidence of wants (CoW) with other orders, which improves your price by skipping price impact and DEX fees.

### Spread Setting

You define the maximum percentage spread you are willing to pay above the current market price.

Spread can also be zero (fills only at mid-price) or negative (fills only above mid-price; you receive the spread, acting as resting maker liquidity).

The higher you set the maximum spread, the faster your order settles.

No matter how high you set your spread, you never pay more than the batch's uniform clearing price. You pay your spread or less.

The lower the spread you set, the longer your order takes to clear, but the better your price when it does.

Spread precision is in basis points (0.01%). Anything finer rounds down at submission.

To let Turbine automatically manage your spread until your order deadline (instead of a constant spread), see [Auto](/trading/readme/auto-mode).

### How the Fast / Cheap / Earn presets are computed

All three chips are derived from a single base: the current AMM spread for your pair plus the platform fee. Call that `D`. The Fast chip uses `D` as-is; Cheap and Earn scale it.

<table><thead><tr><th width="91">Preset</th><th width="267">Formula</th><th width="385">Behavior</th></tr></thead><tbody><tr><td><strong>Fast</strong></td><td><code>D</code> = AMM spread + platform fee</td><td>Same execution profile as a normal DEX aggregator.</td></tr><tr><td><strong>Cheap</strong></td><td><code>D × 0.6</code></td><td>Tighter target. The fee is built into the spread, not added on top, so the number you see is what you pay versus mid.</td></tr><tr><td><strong>Earn</strong></td><td><code>D × −0.1</code></td><td>Negative. Your order fills only if Turbine can settle it at a price better than mid-price.</td></tr></tbody></table>

You can ignore the presets and type any custom spread in basis points.

#### See also

{% content-ref url="/pages/oSTrZMbruCmH5RYx8BlN" %}
[Market Tracking Order](/features/market-tracking-order)
{% endcontent-ref %}


# Trade Programmatically

Auto rebalance at low spread and increase your profits.

To trade on Turbine you:

1. Authenticate
2. Submit an order intent
3. Wait for Turbine to execute a trade

{% hint style="success" %}
The easiest way to interact with Turbine programmatically is the [Typescript SDK](https://github.com/propeller-heads/turbine-sdk).

We will use it throughout this guide.
{% endhint %}

{% hint style="info" %}
See the [`submit-orders.ts`](https://github.com/propeller-heads/turbine-sdk/blob/main/scripts/submit-orders.ts) script in the SDK repo for a complete example of submitting orders.
{% endhint %}

{% stepper %}
{% step %}
**Prepare your wallet**

To submit an order intent to Turbine and have it executed, the following conditions must be met:

* You need to have enough balance of sell token.
* You need to approve [Permit2 contract](https://etherscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3) to spend at least the amount you're selling (i.e. call `approve(permit2address, amount)` on the sell token contract).
  {% endstep %}

{% step %}
**Instantiate TurbineClient**

Fill the below example with the following values:

* `PRIVATE_KEY` - private key of the wallet
* `RPC_URL` - URL to access Ethereum blockchain; you can use your private RPC or pick one from <https://ethereumnodes.com/>
* `TURBINE_API_URL` - URL of Turbine API instance, with `/api` suffix. E.g. `https://api.turbine.exchange/api`.

```typescript
import { createPublicClient, createWalletClient, http, Hex } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { TurbineClient, OrderIntent, getRandomSalt, spreads } from "turbine-sdk";

const account = privateKeyToAccount(PRIVATE_KEY);
const walletClient = createWalletClient({
    account: account,
    chain: mainnet,
    transport: http(RPC_URL),
});
const publicClient = createPublicClient({
    chain: mainnet,
    transport: http(RPC_URL),
});

const turbineClient = await TurbineClient.create(
    walletClient,
    publicClient,
    TURBINE_API_URL
);
```

{% endstep %}

{% step %}
**Authenticate**

To submit orders you must be authenticated. See [Authentication](/reference/api/authentication) for details.

The SDK handles authentication automatically.
{% endstep %}

{% step %}
**Create an order intent**

Create an order to sell 30 USDC for WETH.

Sell at most 1% below the market price, with a floor of 0.007 WETH for those 30 USDC (\~0.00023 WETH per USDC).

{% hint style="info" %}
In Turbine, all orders are sell orders, so the higher the price, the better.
{% endhint %}

```typescript
const now = BigInt(Math.floor(Date.now() / 1000));
const orderIntent: OrderIntent = {
    owner: account.address,
    sellToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
    buyToken: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // WETH
    sellAmount: 30n * 10n ** 6n, // 30 USDC in atomic units
    minBuyAmount: 7n * 10n ** 15n, // 0.007 WETH in atomic units
    spreadCurve: spreads.constant(100), // 1%
    startTime: now,
    endTime: now + 300n, // 300 seconds = 5 minutes
    partialFill: true, // only partially fillable orders are supported
    callData: "0x",
    callDataTarget: "0x0000000000000000000000000000000000000000",
    salt: getRandomSalt(),
};
```

{% endstep %}

{% step %}
**Submit order intent**

Submit the intent to Turbine:

```typescript
const orderHash = await turbineClient.addOrder(orderIntent);
```

This call errors if Turbine rejects your order during initial validation.

Submitting the intent does not execute it. Your order must still pass [the speedbump](/features/the-speedbump) and then get matched.

{% hint style="info" %}
This method calls: [Turbine](/reference/api/readme/turbine#post-api-add_order)
{% endhint %}
{% endstep %}

{% step %}
**Check order state**

To check order state, call `getOrderStates`:

```typescript
const orderState = (await turbineClient.getOrderStates([orderHash]))[0];
console.log(orderState.status);
```

The order can be in the following states:

* `Active` - The order is in the orderbook.
* `Invalid` - The order was removed due to validation errors during a settlement.
* `Expired` - The order was removed because it expired.
* `Filled` - The order was fully filled and removed from the orderbook.
* `PendingCancellation` - The owner has requested cancellation.
* `Canceled` - The owner canceled the order.

{% hint style="info" %}
This method calls: [Turbine](/reference/api/readme/turbine#post-api-order_states)
{% endhint %}
{% endstep %}
{% endstepper %}


# Supported Tokens

Turbine supports tokens whose mid-price can be sourced reliably from major CEX orderbooks. To be listed, a token needs:

* **At least three independent CEX venues** with live order-book WebSocket feeds quoting the token against an on-chain hub (USDC/USDT) or fiat USD. This powers Turbine's [Market Price Oracle](/features/market-price-oracle).
* **Sufficient book depth.** Turbine samples mid-prices at multiple trade sizes between $1,000 and the smaller of total bid/ask depth. Tokens with very thin books are skipped.
* **No unusual on-chain behavior.** Rebasing, fee-on-transfer, and other non-standard ERC-20 patterns are not supported.

### Live source of truth

The authoritative supported-token list, with addresses, decimals, class (Stable / Regular / Meme; see [Fees](/trading/fees)), and per-venue oracle feeds, is at:

```
https://api.turbine.exchange/api/config
```

Each entry includes the venues that feed its price.

### Listed tokens

Major liquid tokens are pinned in the token picker on the trade and pools pages. You can also search by symbol or paste an ERC-20 contract address. If a token does not appear in the picker after pasting its address, it does not currently meet the requirements above.

### Tokens not currently supported

Some stablecoins don't meet the three-oracle threshold yet and are not tradeable. Check [the config endpoint](https://api.turbine.exchange/api/config) for the live allowlist on the network you are using; if a token isn't there, it's not supported.

### Minimum trade size

The minimum trade size for any supported pair is **$10 USDC equivalent**.

{% hint style="info" %}
Want a specific token added? Contact `@tanay_j` on Telegram.
{% endhint %}


# Fees

Turbine Fee Schedule

Turbine charges a single **platform fee** per trade, taken from the proceeds at settlement. The fee absorbs the gas cost of settling your order on-chain, which is why **you do not pay gas separately to place or settle an order**; only the one-time on-chain Permit2 approval (the first time you connect) costs gas.

**The platform fee is included in the spread you see in the UI.** Turbine will try to fill your order at tighter spread, so that you still get what you requested even after taking fees.

### Current fees

Platform fee is a sum of 4 components: a flat **baseline**, plus **token class**, **volume**, and **gas** surcharges.

#### Baseline

| Component | Fee   |
| --------- | ----- |
| Baseline  | 0.02% |

#### Token class fee

| Pair type     | Fee    |
| ------------- | ------ |
| Stable–Stable | 0%     |
| Stable–Major  | 0.01%  |
| Major–Major   | 0.025% |
| Other Swaps   | 0.05%  |

#### Volume fee

| Order size   | Fee   |
| ------------ | ----- |
| < $10k       | 0.05% |
| $10k – $100k | 0.01% |
| ≥ $100k      | 0%    |

**For example**, swapping 5,000 USDC to WETH:

0.02% baseline fee

\+ 0.01% token class fee (USDC is Stable, WETH is a Major)

\+ 0.05% volume fee (volume is less than $10k)

gives 0.08% fee, which is $4.


# Manage Liquidity via Web UI

This guide describes how to provide and withdraw liquidity via the web UI at [app.turbine.exchange/liquidity](https://app.turbine.exchange/liquidity) ("Pools" tab at the top of the page).

Go to:

* :arrow\_right: [#what-to-expect-from-your-position](#what-to-expect-from-your-position "mention")
* :arrow\_right: [#exploring-pools-and-your-positions](#exploring-pools-and-your-positions "mention")
* :arrow\_right: [#adding-liquidity](#adding-liquidity "mention")
* :arrow\_right: [#withdrawing-liquidity](#withdrawing-liquidity "mention")

## :bar\_chart: What to expect from your position <a href="#what-to-expect-from-your-position" id="what-to-expect-from-your-position"></a>

Turbine pools are not like Uniswap v2 or v3 pools. They are **fully concentrated**: all your deposited capital sits at exactly the current market mid-price, plus the spread you chose (your "fee tier"). That is what makes them more capital-efficient than concentrated-liquidity AMMs. See [Market Tracking LP](/features/market-tracking-lp).

Day-to-day, this means:

* **Your token mix shifts over time.** As Turbine routes trades through your pool, you accumulate the token traders are selling and give up the token they are buying. After a one-sided trading session you can hold entirely one of the two tokens, valued at the current market price.
* **This is intentional.** Concentrating all capital at the market price means every batch that uses the pool trades against your full liquidity, not a thin band. The trade-off is that the composition swings.
* **You earn the spread on every trade routed through your pool.** This is your fee income.
* **When ETH rises, you end up holding USDC; when ETH falls, you hold ETH.** Your pool is the counterparty to every trade routed through it. When ETH is in demand, traders take ETH from your pool and leave USDC. When traders sell ETH, they push it into your pool and take USDC. **Fees compensate for this drift. They cover it fully when the market oscillates, and only partly when it trends in one direction.**

#### For example

You own 1% of a pool with 5 WETH + 10,000 USDC at ETH = $2,000. Your share is 0.05 WETH + 100 USDC, worth $200. The pool fee tier is 0.1%.

If the price moves and the pool flips fully to one side:

|                          | ETH rises to $2,200  | ETH falls to $1,800  |
| ------------------------ | -------------------- | -------------------- |
| **Pool now holds**       | 0 WETH + 21,011 USDC | 10.561 WETH + 0 USDC |
| **Your 1% is worth**     | $210.11              | $190.10              |
| **If you had just held** | $210                 | $190                 |
| **You vs. holding**      | +$0.11 (+0.05%)      | +$0.10 (+0.05%)      |

In both cases, your share overperforms a passive hold by 0.05%.

A pool flips fully one-sided when a single swap matches the dollar value of its smaller side. In the example, the WETH side was worth $10,000, so a single $10,000 swap drains it. Check a pool's reserves before adding liquidity.

#### **But there are also scenarios where you can lose**

You own 1% of a pool with 5 WETH + 10,000 USDC at ETH = $2,000. Your share is 0.05 WETH + 100 USDC, worth $200. The pool fee tier is 0.1%.

**First, ETH falls to $1,800**

| **Pool now holds**       | 10.561 WETH + 0 USDC |
| ------------------------ | -------------------- |
| **Your 1% is worth**     | $190.10              |
| **If you had just held** | $190                 |
| **You vs. holding**      | +$0.10 (+0.05%)      |

**Then ETH falls further to $1,600 with no trades on the pool**

| **Pool still holds**     | 10.561 WETH + 0 USDC                              |
| ------------------------ | ------------------------------------------------- |
| **Your 1% is worth**     | $168.98                                           |
| **If you had just held** | $180                                              |
| **You vs. holding**      | <mark style="color:$danger;">-$11.02 (-6%)</mark> |

**Then ETH rises back to $2,000. Takers buy all ETH.**

| **Pool holds**           | 0 WETH + 21,143.12 USDC |
| ------------------------ | ----------------------- |
| **Your 1% is worth**     | $211.43                 |
| **If you had just held** | $200                    |
| **You vs. holding**      | +$11.43 (+5.72%)        |

#### In practice:

* **Stablecoin pairs** (USDC/USDT and similar) have small composition shifts; your USD-marked position tracks fees earned closely.
* **Volatile pairs** (e.g. ETH/USDC) shift noticeably with the market. Provide liquidity only in pairs you would be comfortable holding either side of.
* **The Speedbump protects you from LVR.** No one can race the oracle and trade against a stale price. See [The Speedbump](/features/the-speedbump).

## :eyes: Exploring pools and your positions

The main Pools page lists all pools.

The "My positions" tab filters it to just the pools you have liquidity in.

{% hint style="info" %}
WORK IN PROGRESS: Columns "APY", "Volume 24 H" and "Fees 24 H" don't show anything yet. We're working on a tool that computes these values.
{% endhint %}

Clicking a row takes you to the pool details page.

<figure><img src="https://1965250678-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqEbGnoRTf6xKvVIS3IIz%2Fuploads%2F5AEIAf5yoCwwP4zlFZx2%2FScreenshot%202026-04-13%2011%3A44%3A47.png?alt=media&amp;token=c1117030-b89b-4af5-9326-2242d3b872c7" alt="" width="563"><figcaption></figcaption></figure>

On the pool details page, you can:

### Add or remove liquidity

<figure><img src="https://1965250678-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqEbGnoRTf6xKvVIS3IIz%2Fuploads%2FqRzDulfwznoywq6v9CDn%2Fimage.png?alt=media&amp;token=a7e7aeee-5301-4258-9ce1-9bbaaf08bd46" alt=""><figcaption></figcaption></figure>

### See your position in the pool

The amount of both tokens you own is a fraction of current pool reserves, so it changes when someone swaps on the pool.

The values shown here include the fees you earned.

<figure><img src="https://1965250678-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqEbGnoRTf6xKvVIS3IIz%2Fuploads%2F9gwjgMJbw77trnuDkWC8%2Fimage.png?alt=media&amp;token=61d33489-241a-4eb0-8f7a-600a546e58a5" alt=""><figcaption></figcaption></figure>

### Links

* Etherscan page of the smart contract representing the LP token for this pool (the token that represents your share in the pool)
* Etherscan pages of the smart contracts of both pool tokens

<figure><img src="https://1965250678-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqEbGnoRTf6xKvVIS3IIz%2Fuploads%2Fzjl0GU35ChYWRx7AGI8b%2Fimage.png?alt=media&amp;token=980254c3-ad7b-4737-a62e-d175777f4f71" alt=""><figcaption></figcaption></figure>

## :potable\_water: Adding liquidity

You can add liquidity using the dedicated interface or directly from the pool details page.

This guide describes adding liquidity using the dedicated interface.

{% stepper %}
{% step %}

#### Click on New Position

<figure><img src="https://1965250678-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqEbGnoRTf6xKvVIS3IIz%2Fuploads%2FcAo1dmUZIb1pCBU4uWDr%2FScreenshot%202026-04-13%2009%3A51%3A25.png?alt=media&amp;token=116d4063-42e9-4ce3-b592-988edc464766" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}

#### Select token pair

<figure><img src="https://1965250678-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqEbGnoRTf6xKvVIS3IIz%2Fuploads%2F1wgz2wGwMnZsa2SMoWTS%2FScreenshot%202026-04-13%2009%3A52%3A49.png?alt=media&amp;token=a57ce2dc-8228-4e68-bade-ce7b922613a0" alt="" width="423"><figcaption></figcaption></figure>
{% endstep %}

{% step %}

#### Select fee tier

Fee tier is **how much above the market mid-price the pool trades**. Read more in [Fees](/trading/fees).

You can select one of the suggested tiers or pick a custom one.

{% tabs %}
{% tab title="Suggested fee tier" %}

<figure><img src="https://1965250678-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqEbGnoRTf6xKvVIS3IIz%2Fuploads%2FE6WpUzlPqAZa5GF9H37G%2FScreenshot%202026-04-13%2009%3A53%3A19.png?alt=media&amp;token=d771f489-1c18-4870-9812-30bbec57aada" alt="" width="423"><figcaption></figcaption></figure>
{% endtab %}

{% tab title="Custom fee tier" %}

<div><figure><img src="https://1965250678-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqEbGnoRTf6xKvVIS3IIz%2Fuploads%2FopxgnJ0zoi9MJgC7XHFx%2FScreenshot%202026-04-13%2009%3A58%3A13.png?alt=media&amp;token=de6aa5a0-6984-491a-a876-5443772f8931" alt=""><figcaption><p>Click the text below suggested fee tiers</p></figcaption></figure> <figure><img src="https://1965250678-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqEbGnoRTf6xKvVIS3IIz%2Fuploads%2FXyyNSSnZR0nbbPo1L1Mw%2FScreenshot%202026-04-13%2010%3A00%3A32.png?alt=media&amp;token=35523bc8-0ce1-4602-b75a-c4b11104a308" alt=""><figcaption><p>Search for existing tiers and click "Select this fee tier", or click "Create new fee tier"</p></figcaption></figure> <figure><img src="https://1965250678-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqEbGnoRTf6xKvVIS3IIz%2Fuploads%2FDOa3xLf5l8Bbi8JlZ1i5%2FScreenshot%202026-04-13%2010%3A00%3A57.png?alt=media&amp;token=9e5109b1-1b17-459f-a53f-acedb290d1ee" alt=""><figcaption><p>Specify new fee tier and confirm</p></figcaption></figure></div>
{% endtab %}
{% endtabs %}

If the pool with the selected fee tier doesn't exist yet, you will see the following:

<figure><img src="https://1965250678-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqEbGnoRTf6xKvVIS3IIz%2Fuploads%2F44FOb8Iw3hQgOyOc4DXP%2FScreenshot%202026-04-13%2010%3A05%3A17.png?alt=media&amp;token=3396e8b8-7cce-47be-ae5a-94be5265bbfc" alt=""><figcaption></figcaption></figure>

This means you are creating a new pool, which costs some gas.
{% endstep %}

{% step %}

#### Specify provided token amounts

The amounts are independent and do not affect the pool's price. You can provide both tokens or just one.

<figure><img src="https://1965250678-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqEbGnoRTf6xKvVIS3IIz%2Fuploads%2F6kCgHJ2zOORIbMTqOo6t%2FScreenshot%202026-04-13%2010%3A09%3A15.png?alt=media&amp;token=38819a07-a387-48af-b1f0-e15eb28a26a2" alt="" width="392"><figcaption></figcaption></figure>
{% endstep %}

{% step %}

#### Create position

If you selected an existing fee tier, you will see an "Add liquidity" button. If the selected fee tier doesn't exist yet, you will see a "Create" button. Click it.

Here's what happens after you click the button:

1. **Log in:** Sign the authentication message to log in to Turbine.
2. **\[Optional] Approve Permit2 contract:** If you haven't done this before, you will be prompted to sign a transaction allowing the Permit2 contract to spend your token. This happens for both pool tokens, regardless of the provided amount, and costs some gas.
3. **Sign Permit2 approval:** Sign an offchain approval message allowing Turbine to take the token amounts you are providing to the pool.
4. **\[Optional] Sign pool creation transaction:** If you selected a fee tier that doesn't exist yet, you will be prompted to sign a transaction that creates a new pool. It costs some gas.
5. **Wait for execution:** Turbine receives your liquidity intent. After it passes the Speedbump, it executes in the nearest batch.
   {% endstep %}
   {% endstepper %}

## :pouring\_liquid: Withdrawing liquidity

{% stepper %}
{% step %}

#### Open pool details

Select the pool you want to withdraw from. Click "My positions" to view just the pools you have a position in.

<figure><img src="https://1965250678-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqEbGnoRTf6xKvVIS3IIz%2Fuploads%2FtuANGypzasUmlWiMrPLh%2FScreenshot%202026-04-13%2012%3A27%3A37.png?alt=media&amp;token=a5a2dea0-5619-4459-8e0b-7871ef0329d7" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}

#### Click "Remove liquidity"

<figure><img src="https://1965250678-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqEbGnoRTf6xKvVIS3IIz%2Fuploads%2FPVZGjRZAgEj6UuDYAkFE%2FScreenshot%202026-04-13%2012%3A29%3A04.png?alt=media&amp;token=d2cc6319-4a68-4d69-8d65-c22e153f5d44" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}

#### Specify the amount

Specify how much of your liquidity you want to withdraw. 100% withdraws all your liquidity.

The app shows estimated amounts that will be withdrawn.

Withdrawals always use the ratio of pool reserves. You cannot withdraw funds in a different ratio.

The withdrawn token amounts can differ from the estimate when Turbine swaps the pool in the meantime; the total *value* stays close to the estimate.

<figure><img src="https://1965250678-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqEbGnoRTf6xKvVIS3IIz%2Fuploads%2FXxhAuwwfGnOwUPp75hMB%2FScreenshot%202026-04-13%2012%3A34%3A32.png?alt=media&amp;token=4b0560d1-7bb7-4769-ac17-f73dcae22920" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}

#### \[Optional] Approve Permit2 contract

If this is your first withdrawal from this pool, allow the Permit2 contract to spend your LP token. This costs some gas.

<figure><img src="https://1965250678-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqEbGnoRTf6xKvVIS3IIz%2Fuploads%2F7uryEo9uweEZxwgGf7iV%2FScreenshot%202026-04-13%2012%3A41%3A43.png?alt=media&amp;token=690eacbb-5e98-4a5a-8640-20308ef6038b" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}

#### Confirm

Click "Remove Liquidity" and sign the Permit2 allowance for Turbine to spend your LP token.

Turbine executes your intent after it passes the speedbump. After about 30 seconds you will see updated details of your position in the pool.

<figure><img src="https://1965250678-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqEbGnoRTf6xKvVIS3IIz%2Fuploads%2FiH6m6yny3uL0ncPidAnE%2FScreenshot%202026-04-13%2012%3A44%3A26.png?alt=media&amp;token=0f7ce925-7f16-496c-8519-8d13999ecd67" alt=""><figcaption></figcaption></figure>
{% endstep %}
{% endstepper %}


# Manage Liquidity Programmatically

Auto LP

Managing your liquidity on Turbine means:

1. Authenticating
2. Submitting a liquidity intent
3. Waiting for Turbine to execute the intent, or executing it directly.

We'll explore these steps below.

{% hint style="success" %}
The easiest way to interact with Turbine programmatically is the [Typescript SDK](https://github.com/propeller-heads/turbine-sdk).

We will use it throughout this guide.
{% endhint %}

{% hint style="info" %}
Learn more about liquidity provision: [Market Tracking LP](/features/market-tracking-lp) and [Liquidity Pools in Depth](/reference/technical/liquidity-pools-in-depth).
{% endhint %}

## Providing and Withdrawing Liquidity via API

{% hint style="info" %}
See [`add-liquidity.ts`](https://github.com/propeller-heads/turbine-sdk/blob/main/scripts/add-liquidity.ts) in the SDK repo for a complete example of providing liquidity.

See [`remove-liquidity.ts`](https://github.com/propeller-heads/turbine-sdk/blob/main/scripts/remove-liquidity.ts) in the SDK repo for a complete example of withdrawing liquidity.
{% endhint %}

{% stepper %}
{% step %}
**Prepare your wallet**

To submit a liquidity intent to Turbine and have it executed, the following conditions must be met:

{% tabs %}
{% tab title="Provide liquidity" %}

* You need to have enough balance of provided tokens.
* You need to approve [Permit2 contract](https://etherscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3) to spend at least the amounts you're providing (i.e. call `approve(permit2address, amount)` on the provided token contracts; you can use [`approve-token.ts`](https://github.com/propeller-heads/turbine-sdk/blob/main/scripts/approve-token.ts) script).
  {% endtab %}

{% tab title="Withdraw liquidity" %}

* You need to have enough balance of pool's LP token.
* You need to approve [Permit2 contract](https://etherscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3) to spend at least the amount of LP token you want to burn (i.e. call `approve(permit2address, amount)` on the LP token contract; you can use the [`approve-lp-token.ts`](https://github.com/propeller-heads/turbine-sdk/blob/main/scripts/approve-lp-token.ts) script).
  {% endtab %}
  {% endtabs %}
  {% endstep %}

{% step %}
**Instantiate TurbineClient**

Set environment variables for the below example to work:

* `PRIVATE_KEY` - private key of the wallet
* `RPC_URL` - URL to access Ethereum blockchain; you can use your private RPC or pick one from <https://ethereumnodes.com/>
* `TURBINE_API_URL` - URL of Turbine API instance, with `/api` suffix. E.g. `https://api.turbine.exchange/api`.

```typescript
import { createPublicClient, createWalletClient, http, Hex } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { TurbineClient, getRandomSalt } from "turbine-sdk";

const account = privateKeyToAccount(PRIVATE_KEY);
const walletClient = createWalletClient({
    account: account,
    chain: mainnet,
    transport: http(RPC_URL),
});
const publicClient = createPublicClient({
    chain: mainnet,
    transport: http(RPC_URL),
});

const turbineClient = await TurbineClient.create(
    walletClient,
    publicClient,
    TURBINE_API_URL
);
```

{% endstep %}

{% step %}
**Authenticate**

To submit liquidity intents you must be authenticated. See [Authentication](/reference/api/authentication) for details.

The SDK handles authentication automatically.
{% endstep %}

{% step %}
**Get available pools**

Call `getPools()` to get a list of available pools:

```typescript
const pools = await turbineClient.getPools();
```

The result looks like this:

```typescript
[
  {
    metadata: {
      token0: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
      token1: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
      fee: 3000,
      lpToken: '0xB03aC5cabFc98DB26C0D9eA232E1e900724d91F3'
    },
    state: { reserve0: 0n, reserve1: 0n, liquidity: 0n },
    stats: { weeklySellVolumeToken0: 0n, weeklySellVolumeToken1: 0n }
  },
  {
    metadata: {
      token0: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
      token1: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
      fee: 10000,
      lpToken: '0x2bcF0170103930dc12d748c8b54205a36B7351dA'
    },
    state: { reserve0: 10000000000n, reserve1: 500000000000000000n, liquidity: 250000005000000000n },
    stats: { weeklySellVolumeToken0: 0n, weeklySellVolumeToken1: 0n }
  }
]
```

The snippet above shows two USDC/WETH pools: one at 0.3% fee tier and one at 1% fee tier. The first is empty; the second holds 10,000 USDC and 0.5 WETH.

{% hint style="info" %}
Pool statistics aren't implemented yet — the `stats` fields above return placeholders.
{% endhint %}
{% endstep %}

{% step %}
**Create a liquidity intent**

{% tabs %}
{% tab title="Provide liquidity" %}
Create an intent to provide exactly 1000 USDC and 0.1 WETH to the pool at 1% fee tier.

```typescript
const liquidityIntent: AddLiquidityIntent = {
    owner: account.address,
    token0: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" as Hex,
    token1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" as Hex,
    fee: 10000,
    token0Amount: 1000000000n,
    token1Amount: 100000000000000000n,
    exact: true,
    salt: getRandomSalt(),
};
```

{% endtab %}

{% tab title="Withdraw liquidity" %}
Create an intent to withdraw 40% of liquidity from the USDC/WETH pool at 1% fee tier. 40% of total pool liquidity equals 100,000,002 × 10<sup>9</sup> units of LP token.

```typescript
const liquidityIntent: RemoveLiquidityIntent = {
    owner: account.address,
    token0: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" as Hex,
    token1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" as Hex,
    fee: 10000,
    lpToken: "0x2bcF0170103930dc12d748c8b54205a36B7351dA" as Hex,
    lpTokenAmount: 100000002000000000n,
    salt: getRandomSalt(),
};
```

{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}
**Submit liquidity intent**

Submit the intent to Turbine:

{% tabs %}
{% tab title="Provide liquidity" %}

```typescript
const intentHash = await turbineClient.addLiquidity(liquidityIntent);
```

{% hint style="info" %}
This method calls: [Turbine](/reference/api/readme/turbine#post-api-add_liquidity)
{% endhint %}
{% endtab %}

{% tab title="Withdraw liquidity" %}

```typescript
const intentHash = await turbineClient.removeLiquidity(liquidityIntent);
```

{% hint style="info" %}
This method calls: [Turbine](/reference/api/readme/turbine#post-api-remove_liquidity)
{% endhint %}
{% endtab %}
{% endtabs %}

This call errors if Turbine rejects your intent during initial validation.

Submitting the intent does not execute it immediately. Your intent must still pass [The Speedbump](/features/the-speedbump).
{% endstep %}

{% step %}
**Check intent state**

To check intent state, call `getLiquidityIntents`:

```typescript
const intentState = (await turbineClient.getLiquidityIntents([intentHash]))[0];
console.log(intentState.status);
```

The intent can be in the following states:

* `Pending` - The intent is waiting to pass the speedbump.
* `Invalid` - The intent was removed due to validation errors during a settlement.
* `Expired` - The intent was removed because it expired before execution.
* `Executed` - The intent was executed.
* `PendingCancellation` - The owner has requested cancellation.
* `Canceled` - The owner canceled the intent.

{% hint style="info" %}
This method calls: [Turbine](/reference/api/readme/turbine#post-api-liquidity_intent_states)
{% endhint %}
{% endstep %}
{% endstepper %}

## Directly Withdrawing Liquidity Onchain

You can withdraw liquidity by submitting onchain transactions, without the Turbine backend acting as intermediary. The speedbump still applies.

Direct onchain withdrawal has two steps:

1. Submit a liquidity withdrawal intent to LiquidityRouter
2. Execute the submitted intent (after the speedbump)

Instead of authenticating to the Turbine backend, you confirm your identity by signing a transaction.

{% stepper %}
{% step %}
**Create liquidity intent**

Follow the steps in [#providing-and-withdrawing-liquidity-via-api](#providing-and-withdrawing-liquidity-via-api "mention") up through creating the liquidity intent. You can skip authentication.
{% endstep %}

{% step %}
**Submit liquidity intent onchain**

```typescript
const { txHash, intentHash } =
    await turbineClient.submitRemoveLiquidityIntentOnchain(liquidityIntent);
```

{% hint style="info" %}
This method calls [TurbineLiquidityRouter](/reference/contracts/solidity-documentation/univ4-hook/contract.turbineliquidityrouter#submitremoveliquidityintent)
{% endhint %}
{% endstep %}

{% step %}
**Execute the liquidity intent**

After submitting, wait at least the speedbump duration (12 seconds, or 1 Ethereum block).

Then execute the intent:

```typescript
await turbineClient.executePendingRemoveLiquidityIntentsOnchain([intentHash]);
```

{% endstep %}
{% endstepper %}

Turbine also executes liquidity removal intents submitted directly to LiquidityRouter (not only those submitted via the Turbine API).

#### See also

{% columns %}
{% column %}
{% content-ref url="/pages/IKe5swJDlfB3HKjSeAqZ" %}
[Market Tracking LP](/features/market-tracking-lp)
{% endcontent-ref %}
{% endcolumn %}

{% column %}
{% content-ref url="/spaces/QHptutFygga9WKVU1emZ/pages/w93B4h4yx9hA0notuTxS" %}
[Liquidity Pools in Depth](/reference/technical/liquidity-pools-in-depth)
{% endcontent-ref %}
{% endcolumn %}
{% endcolumns %}


# Market Make

You can market make in Turbine in two ways:

* using orders with negative spread
* using orders with explicit limit price

{% hint style="info" %}
See [Trade Programmatically](/trading/trade-programmatically) for a guide on how to place orders programmatically at scale.
{% endhint %}

## Negative spread orders

Turbine accepts orders with negative spread.

Negative spread means you are willing to trade only at a **better** price than current market mid-price.

Use this option to be the counterparty to takers that want to trade faster. If your spread is better than what DEXes and Turbine liquidity offer, taker orders match against yours.

A large order with negative spread can settle partially against smaller orders with positive spread.

Note that Turbine also tries to settle your order against DEX liquidity. The probability of finding such a settlement is low, but not zero.

## Explicit limit price

Every order in Turbine has a limit price, but usually the spread setting is stricter.

If you set a very wide spread but a strict limit price, you effectively turn the spread order into a classic limit order.

The web UI does not support setting the limit price precisely, but you can do it programmatically: [Trade Programmatically](/trading/trade-programmatically).

Set `spreadCurve` to `spreads.constant(9999)` (99.99%, maximum possible value) and `minBuyAmount` to a value such that `minBuyAmount = sellAmount * yourLimitPrice`.

You can submit multiple orders for the same pair at different limit prices, representing different price levels.

We recommend an order lifetime of **at least 24 seconds** (minimum is 12 seconds, one Ethereum block). Your order must first pass the Speedbump, then get included in a batch in the next block.

## Rate limiting

Current rate limits are described in [Rate Limiting](/reference/api/rate-limiting).

If you need higher limits, reach out to `tanay_j` on Telegram.


# Integrate

Help your users save on swaps.

{% hint style="info" %}
Please reach out to `tanay_j` on Telegram if you are interested in integrating Turbine into your app.
{% endhint %}

You can interact with the Turbine API programmatically. See [Reference](https://docs.turbine.exchange/reference/) and [Typescript SDK](https://github.com/propeller-heads/turbine-sdk).

Turbine's backend enforces a strict CORS and cookies policy: it accepts requests only from the official frontend at [https://app.turbine.exchange](https://app.turbine.exchange/). Requests from web apps on other domains are not served.

You can interact with the Turbine API from your backend, but not from a third-party frontend.

Frontend integration will be enabled in August/September 2026.


# Playground

Try Turbine without real funds

At [playground.turbine.exchange](https://playground.turbine.exchange/) you can try all functionalities of Turbine without touching real funds.

Settlements are simulated (they are not visible onchain), but you can see how an order would be executed.

There is simulated traffic, so you can experience the full potential of peer-to-peer trading.

API base URL is `https://playground-api.turbine.exchange/api`&#x20;

<figure><img src="https://1965250678-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqEbGnoRTf6xKvVIS3IIz%2Fuploads%2FVID5R9FNn3IM7y2zKK9A%2Fimage.png?alt=media&amp;token=835a72f0-5704-4415-b4ac-a1621854fa95" alt=""><figcaption></figcaption></figure>


# Market Tracking Order

Turbine orders move with the market to never go stale.

Every order you place on Turbine is a **Spread Order**. Instead of locking a fixed limit price, your order settles at a spread from the current market mid-price, and that price updates each batch to track the market. You choose how long to give it, and Turbine manages the spread.

<figure><img src="https://1965250678-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqEbGnoRTf6xKvVIS3IIz%2Fuploads%2F2Wy8Tr0U0SDV99SUf59G%2FORDERS%20THAT%20NEVER%20GO%20STALE%20(1).gif?alt=media&amp;token=05d1aa14-21d1-4899-bb77-652fdfa2df4f" alt=""><figcaption></figcaption></figure>

### How a Spread Order works

* **Tracks the market.** Each batch, Turbine re-fetches the market mid-price from the price oracle and resets your order's effective price to mid ± its current spread. Your order never goes stale.
* **Settles in batches.** All open orders for the same pair clear at one uniform price each batch. You either get your price or a better one, never worse. See [Batching Solver.](/features/batching-solver)
* **Lives until it fills.** Your order stays open until it settles, until you cancel it, or until its expiry. Because the price is never stale, your order can run for hours or days without sniping risk.
* **Pays the spread.** The spread is what you give up vs. the market mid-price. The more time you give Turbine, the tighter the spread it can settle at, and the better your price. With less time it settles closer to the baseline aggregator rate to make sure it fills. Patient orders can settle at zero or even negative spread, where you receive better than mid-price.

### Why it exists

Classical limit orders go stale as the market moves, and snipers pick them off. Market orders pay slippage and get front-run. A Spread Order eliminates both:

* Your price tracks the market, so it cannot go stale.
* Your order settles in batches behind the [Speedbump](/features/the-speedbump), so no one can race the oracle.
* Your open order stays inside Turbine's [Private Orderbook](/features/private-orderbook), so it cannot be front-run.

### How Turbine sets your spread

With Auto, you pick how long to wait and Turbine manages the spread for you over the order's lifetime. The spread starts deeply negative, so the order first tries to fill at prices well better than mid (chasing CoWs and Turbine LP), then widens through the order's lifetime to make sure it fills by your deadline. The more time you give it, the better the price it can find. See [Auto Mode](/trading/readme/auto-mode).

### Settlement outcomes

In every batch, your Spread Order can settle in three ways, ranked best to worst:

* **Coincidence of wants (CoW):** Your order matches an opposite-side order at zero spread. Both sides skip AMM fees and price impact. The longer your order stays open, the higher your chance of a CoW.
* **Turbine Pool:** A Market Tracking LP pool fills your order at its current mid-price plus its fee tier.
* **External on-chain liquidity:** Turbine routes your order over indexed AMMs. This is the fallback when no internal liquidity matches at your price.

In all three cases, the price you pay is bounded by your order's current spread.

#### See also

{% content-ref url="/pages/xi5YyZiGwS95iyNddZfr" %}
[The Speedbump](/features/the-speedbump)
{% endcontent-ref %}

{% content-ref url="/pages/EgLMwAmgTx9vjQVwJixV" %}
[Batching Solver](/features/batching-solver)
{% endcontent-ref %}

{% content-ref url="/pages/qx5qJXBUorCDUf5pIN8O" %}
[Private Orderbook](/features/private-orderbook)
{% endcontent-ref %}

{% content-ref url="/pages/qkFCCTf8zHaFoHbULz51" %}
[Auto Mode](/trading/readme/auto-mode)
{% endcontent-ref %}


# The Speedbump

Turbine delays order placement and cancellations, LP deposits and withdrawals, by <code class="expression">space.vars.speedbumpDuration</code>, blocking arbitrage trades and just-in-time (JIT) liquidity.

This delay (what we call the "Speedbump") gives Turbine time to read the market *after* you commit to your action, and update order and LP prices accordingly. Useful to uninformed traders; a non-starter for informed ones.

Your order can actively [follow the market](/features/market-tracking-order) because the Speedbump blocks bots that read the market faster than Turbine's oracle.

### How the speedbump works

<figure><img src="https://1965250678-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqEbGnoRTf6xKvVIS3IIz%2Fuploads%2FAJZGZI9zpNTLlRGWXlwD%2FSpeedbump1.gif?alt=media&amp;token=e5f6ab8f-629d-4812-bab4-d36f706ae662" alt=""><figcaption></figcaption></figure>

* **Slow Zone:** All trades and LP actions are speedbumped, and act in the slow paradigm.
* **Fast Zone:** Turbine itself reads market information (price oracles, chain state, orderbook prices) instantly. Only user actions are speedbumped, never Turbine's own oracle.

### For example

Imagine a large ETF inflow for ETH on a major CEX increases the ETH price by 1%.

In an unprotected orderbook, arbitrage bots buy ETH below market from sitting orders (and from passive AMM LPs) and sell it at the new, higher market price.

In Turbine, the bot's buy order is delayed by <code class="expression">space.vars.speedbumpDuration</code>. This gives Turbine time to update the limit price of all user orders (and LP liquidity) in the orderbook to the new mid-price, before the bot's order hits. This makes the bot's orders unprofitable.

Even though Turbine might read prices slower than the bot, Turbine's price update still arrives in the market before the bot's order.


# Batching Solver

Turbine has a built-in solver that settles orders in batches.

### Batches over Turbine + Ethereum Liquidity

Every batch includes:

* [**Turbine Orders**](/trading/readme/auto-mode): Turbine orders that track the market.
* [**Turbine Pools**](/features/market-tracking-lp): Turbine pools that track the market.
* **On-chain liquidity:** All AMM pools and PMM quotes from Tycho.

### Limit Order vs. Spread Order Batching

**Limit Order Batching**: In limit order markets, you only match if an opposite trade arrives before the market moves, which rarely happens. So you usually get stuck or overpay; only when the market is less volatile do you settle at market rate.

<figure><img src="https://1965250678-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqEbGnoRTf6xKvVIS3IIz%2Fuploads%2FhGphipKeGJzWIf7dOIEc%2FLimit%20Order%20(1).gif?alt=media&amp;token=f39be8c6-72c6-4646-872d-3c878dbd61cc" alt=""><figcaption><p>Limit Order Batching -> Few P2P Trades</p></figcaption></figure>

**Spread Order Batching:** In Turbine, orders and pools follow the market price. Orders match peer-to-peer, *no matter where the market moves***.** The longer an order stays in Turbine, the higher the chance of settling peer-to-peer. As a result, CoWs are the default, and orders rarely get stuck. Worst case, you pay market rate (AMM router price).

<figure><img src="https://1965250678-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqEbGnoRTf6xKvVIS3IIz%2Fuploads%2FeOaFEAP3foQ46jl6zWBc%2FSpread%20Order%20Batching%20(2).gif?alt=media&amp;token=629445f4-b5fd-4a90-a89b-90fff7f7ecc9" alt=""><figcaption><p>Spread Order Batching -> Many P2P Trades</p></figcaption></figure>

<table><thead><tr><th width="219.078125"></th><th width="287.9921875">Limit Order Markets</th><th>Spread Order Market</th></tr></thead><tbody><tr><td>⛔️ <strong>Stuck Orders</strong></td><td>A lot 😔</td><td>Rarely ☺️</td></tr><tr><td>🤕 <strong>Paid Too Much</strong></td><td>A lot 😔</td><td>Rarely ☺️</td></tr><tr><td>🤝 <strong>P2P Trade</strong></td><td>Rarely 😔</td><td><strong>A lot</strong> ☺️</td></tr><tr><td>✅ <strong>Market Rate</strong></td><td>Rarely 😔</td><td>Sometimes</td></tr></tbody></table>

### Fair Settlement

In Turbine, all orders for the same pair in the same batch clear at the same **uniform clearing price**.

### Default = Market Rate

Because Turbine includes all on-chain liquidity (via [Fynd](https://docs.fynd.xyz/get-started/quickstart)), the worst price you settle at is what you'd get from optimally routing across all on-chain liquidity.

Peer-to-peer trades and Turbine LP pools give you the surplus above the AMM routes.


# Private Orderbook

*Pre-settlement privacy*: Your order on Turbine is invisible until it settles.

**Hosted on a TEE**: Turbine runs on a [Trusted Execution Environment](/resources/security/trusted-execution-environment) (TEE). No data, including orders, leaves the TEE. The data is completely private. The solver and the price oracle also run on the same TEE.

**Verifiability:** With our attestation service, you can verify that this code runs on the TEE.

{% hint style="info" %}
Turbine's source code is closed during Beta. We will open-source it soon.
{% endhint %}

**Encrypted Transfer**: Your communication with Turbine is encrypted using SSL that terminates only inside the TEE.

<figure><img src="https://1965250678-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqEbGnoRTf6xKvVIS3IIz%2Fuploads%2F7mSYs3cIT7aaqipFsiP4%2FPrivate%20Orderbook%20(1).gif?alt=media&amp;token=52c94e6f-df7e-4bf4-af1d-ffa412886f60" alt=""><figcaption></figcaption></figure>

No solver, core engineer, market maker, or block builder can see your open order.

#### Further Reading

* [EVIntent - Dark Matter in MEV](https://www.propellerheads.xyz/blog/ev-intent): If open orders are public, you can be frontrun in multi-block frontruns and sandwiches.


# Market Tracking LP

Turbine pools are constant-sum pools implemented as custom Uniswap v4 hooks.

### Features

* **Protected against LVR.** Turbine pools update their price before every batch settlement using Turbine's market-price oracle. Only trades that pass the Speedbump settle against Turbine pools. This eliminates stale-price sniping, which causes LVR.
* **Maximum capital efficiency.** Turbine pools concentrate all liquidity at the current market price as determined by the Turbine price oracle, more capital-efficient than concentrated-liquidity AMMs.
* **LP Speedbump.** Deposits and withdrawals are delayed by the Speedbump (<code class="expression">space.vars.speedbumpDuration</code>), just like trades. This protects pools from just-in-time liquidity attacks that aim to snipe rewards or trade on stale prices. Deposits and withdrawals execute in the next batch.
* **Exclusive to Turbine.** Turbine pools are custom Uniswap v4 pools that only Turbine can trade against.

<figure><img src="https://1965250678-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqEbGnoRTf6xKvVIS3IIz%2Fuploads%2Ft7TkR2Mzj24OnJ5lODJ0%2Fimage.png?alt=media&amp;token=9208b72d-b85f-4391-88d3-ff12d4c36d96" alt=""><figcaption></figcaption></figure>

Before each settlement, Turbine determines the market price and updates the mid-price of all pools, moving the constant-sum curve.

<figure><img src="broken://files/xUSPJUYUlYp9LljcHAbG" alt=""><figcaption></figcaption></figure>

#### What "constant-sum" means for your position

Constant-sum pools price every unit of liquidity at the same rate: the current market mid-price plus your fee tier. There is no `x·y=k` curve; the pool quotes a single price across its full depth. As trades move through the pool, your two tokens are not held in a fixed ratio.

For what this means day-to-day, including how your token mix shifts and your exposure to price moves, see [Manage Liquidity via Web UI](/liquidity/manage-liquidity-via-web-ui).

#### See also

{% content-ref url="/pages/mHCmUHVHhOZAM8u492kA" %}
[Manage Liquidity Programmatically](/liquidity/liquidity-provision-for-engineers)
{% endcontent-ref %}


# Market Price Oracle

Before every batch, Turbine determines a reference mid-price for each token pair. All orders and LP are offset from this price by the user-defined spread.

**Cross-venue mid-price**

The pair mid-price is the median of all valid per-venue prices. Turbine prunes outliers iteratively: at each step it removes the price furthest from the median while either condition below holds. It stops once neither holds, or once two prices remain.

* **Mean–median divergence**: mean and median differ by more than `mean_median_threshold` (1%).
* **Individual deviation**: some price is further from the median than `max_price_deviation × median` (0.1%).

A single batch snapshot makes the conversion easier to see: venue prices are placed around the final mid, off-market venues are pruned, and the median of the accepted prices becomes Turbine's batch mid-price.

<figure><img src="https://1965250678-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqEbGnoRTf6xKvVIS3IIz%2Fuploads%2Fgit-blob-a5e50fa29072876a3574517157881f53147dd19d%2Fimage%20(18).png?alt=media" alt="AAVE-USDC oracle venue prices converted into Turbine&#x27;s median mid-price"><figcaption><p>Real AAVE/USDC oracle snapshot from Grafana. Mint dots are accepted venue prices inside the +/-10 bps guardrail, red crosses are pruned venues, and the yellow line is the median mid-price used for the batch.</p></figcaption></figure>

**Pause on invalid price**: if fewer than `min_valid` (3) venues produce a fresh, non-outlier price for a pair, Turbine excludes all orders and LP on that pair from the current batch.

<figure><img src="https://1965250678-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqEbGnoRTf6xKvVIS3IIz%2Fuploads%2Fgit-blob-6c4fffc4953678d05bc2c7fa5b7643ccb107eeac%2Fimage.png?alt=media" alt="AAVE-USDC oracle guardrails with per-venue prices, pruned samples, and accepted venue count"><figcaption><p>Real AAVE/USDC oracle sample from Grafana. Venue prices are shown as bps from Turbine's median oracle price; dashed lines show the +/-10 bps threshold, red markers are in-range pruned samples, and the lower panel shows how many venues remain after pruning.</p></figcaption></figure>

**Per-venue prices**

Turbine sources prices from live WebSocket order-book feeds: Binance, Bingx, Bitget, Coinbase, Hyperliquid, Kraken, Kucoin, Lighter, and Okx.

<figure><img src="https://1965250678-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqEbGnoRTf6xKvVIS3IIz%2Fuploads%2Fgit-blob-28dec160fb42410392cb1de3e15cb9cfce59d686%2Fimage%20(19).png?alt=media" alt="How Turbine calculates a venue mid-price from order-book depth samples"><figcaption><p>Illustrative AAVE/USDC venue calculation. Turbine walks both sides of the order book to several quote sizes, computes a depth-anchored midpoint for each size, takes the median across size samples, then reports the median across recent snapshots.</p></figcaption></figure>

The per-token mid on a venue is built in two steps:

1. **Depth-anchored mid per snapshot.** For each book update, Turbine walks both sides until a target trade size (in the venue's quote currency) is consumed, then takes the midpoint of the worst bid and ask reached. This reflects the cost of trading at that size, not just the top-of-book spread. To remove sensitivity to a single size, Turbine samples 5 log-spaced sizes between $1,000 and the smaller of total bid/ask depth, and takes the median across them. Books with under $1,000 on either side are skipped.
2. **Temporal median across recent snapshots.** The depth-anchored mid is appended to a per-token buffer. The venue's reported price is the median of samples within the last 500 ms; if fewer than two samples are that recent, it falls back to the most recent sample. This smooths sub-second spoofing attempts on fast feeds while remaining usable for slower or stable ones.

A venue's pair price is rejected only if either leg lacks a fresh sample.


# Why Unlimited Allowance

Turbine asks you for unlimited allowance to hide your sell amount.

**TL;DR:** The allowance is unlimited to hide the full sell amount of your order.

When placing an order, you include a signed Permit2 allowance. It allows the TurbineSettler contract to spend an unlimited amount of your sell token for the duration of your order.

When Turbine settles your order on-chain, it uses the allowance you signed to take your sell token from your wallet. All on-chain data is public, so the allowance also becomes public.

Turbine orders are partially fillable, so settlement can happen across multiple blocks. This is common with low-spread orders.

Your allowance becomes public when Turbine settles the **first** part of your order. **If the allowance matched the exact sell amount, the remaining part of your order would become public.**

With unlimited approval, nobody knows whether the settlement they saw on-chain filled the order fully or left an unfilled portion in the orderbook.

Turbine keeps your order private until it is fully settled, which prevents anyone from frontrunning you and gives you better prices.


# Why Sign in with Ethereum

**TL;DR: To avoid asking you for a signature every 5 seconds.**

Sign in with Ethereum (SIWE) lets you log in to a web app using your Ethereum wallet, the same way you log in to web2 apps like your bank.

Turbine asks you to sign a SIWE authentication message once to log in. After that, the web interface fetches the status of your open orders periodically, without prompting you for a signature each time.


# API Specification

Explore the API specification here in the docs, or in Swagger UI at <https://api.turbine.exchange/api/swagger-ui/>.

The OpenAPI specification is hosted at <https://api.turbine.exchange/api/api-docs/openapi.json>.


# Turbine

Turbine API endpoints

## POST /api/add\_liquidity

> Submit a new liquidity request to the system

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"tags":[{"name":"turbine","description":"Turbine API endpoints"}],"paths":{"/api/add_liquidity":{"post":{"tags":["turbine"],"summary":"Submit a new liquidity request to the system","operationId":"add_liquidity","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddLiquidity"}}},"required":true},"responses":{"200":{"description":"Liquidity intent successfully submitted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddLiquidityResponse"}}}},"400":{"description":"Invalid intent, signatures, or session state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}},"401":{"description":"User not authorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}}}}}},"components":{"schemas":{"AddLiquidity":{"type":"object","description":"Represents a liquidity addition with SignatureTransfer permit functionality.\n\nThis struct encapsulates all necessary information needed to submit a new intent to add\nliquidity to the system, including the intent and SignatureTransfer permit data for approvals.\nUser authentication is handled via session-based SIWE verification.\n\n# Fields\n* `add_liquidity` The intent to add liquidity\n* `permit_tokens` The batch SignatureTransfer permit and signature for token0 and token1","required":["addLiquidity","permitTokens"],"properties":{"addLiquidity":{"$ref":"#/components/schemas/AddLiquidityIntent"},"permitTokens":{"$ref":"#/components/schemas/SignedBatchSignatureTransferDTO"}}},"AddLiquidityIntent":{"type":"object","required":["owner","token0","token1","fee","token0Amount","token1Amount","exact","salt"],"properties":{"exact":{"type":"boolean","description":"Whether the user wants to provide exactly the specified amounts (and is willing to\npay a swap fee first) or would rather like to provide liquidity in current ratio of pool\nreserves (in such case the specified token amounts are treated as maximum amounts)."},"fee":{"type":"integer","format":"uint32","description":"The fee for the pool","minimum":0},"owner":{"type":"string","format":"address","description":"The address of the user submitting the intent"},"salt":{"type":"string","format":"bytes32","description":"Used to differentiate between intents with the same parameters"},"token0":{"type":"string","format":"address","description":"The token0 of the pool. Tokens must be sorted ascending by address so that\n`token0 < token1`; unordered intents are rejected."},"token0Amount":{"type":"string","format":"uint256","description":"The amount of token0 to provide. If `exact` is true, this is the exact amount of token0 to\nprovide. If `exact` is false, this is the maximum amount of token0 to provide."},"token1":{"type":"string","format":"address","description":"The token1 of the pool. Must be greater than `token0` (see `token0`)."},"token1Amount":{"type":"string","format":"uint256","description":"The amount of token1 to provide. If `exact` is true, this is the exact amount of token1 to\nprovide. If `exact` is false, this is the maximum amount of token1 to provide."}}},"SignedBatchSignatureTransferDTO":{"type":"object","description":"Signed batch SignatureTransfer permit (for add liquidity token0 and token1 transfer)","required":["permit","signature"],"properties":{"permit":{"$ref":"#/components/schemas/PermitBatchTransferFrom"},"signature":{"$ref":"#/components/schemas/PermitSignature"}}},"PermitBatchTransferFrom":{"type":"object","description":"DTO for ISignatureTransfer.PermitBatchTransferFrom - batch token transfer authorization","required":["permitted","nonce","deadline"],"properties":{"deadline":{"type":"string","format":"uint256"},"nonce":{"type":"string","format":"uint256"},"permitted":{"type":"array","items":{"$ref":"#/components/schemas/TokenPermissions"}}}},"TokenPermissions":{"type":"object","description":"Token permissions for ISignatureTransfer permits.\nUsed in PermitTransferFrom and PermitBatchTransferFrom.","required":["token","amount"],"properties":{"amount":{"type":"string","format":"uint256","description":"The maximum amount that can be transferred"},"token":{"type":"string","format":"address","description":"The token address"}}},"PermitSignature":{"type":"object","required":["r","s","yParity"],"properties":{"r":{"type":"string","format":"uint256"},"s":{"type":"string","format":"uint256"},"yParity":{"type":"boolean","format":"boolean"}}},"AddLiquidityResponse":{"type":"object","description":"Response returned after successfully submitting a liquidity intent to the system.\nContains the unique identifier assigned to the submitted intent.\n\n# Fields\n* `hash` - Unique identifier assigned to the liquidity intent","required":["intentHash"],"properties":{"intentHash":{"type":"string","format":"bytes32"}}},"ErrorResponsePayload":{"type":"object","description":"Structured error response returned by the API","required":["code","message"],"properties":{"code":{"type":"string","description":"Error code in SNAKE_CASE format"},"inner":{"type":["array","null"],"items":{"$ref":"#/components/schemas/InnerErrorResponsePayload"},"description":"Optional array of nested errors (only present for validation errors)"},"message":{"type":"string","description":"Human-readable error message"}}},"InnerErrorResponsePayload":{"type":"object","description":"Individual error within a validation error response","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}
```

## POST /api/add\_order

> Submit a new order to the system

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"tags":[{"name":"turbine","description":"Turbine API endpoints"}],"paths":{"/api/add_order":{"post":{"tags":["turbine"],"summary":"Submit a new order to the system","operationId":"add_order","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddOrder"}}},"required":true},"responses":{"200":{"description":"Order successfully submitted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddOrderResponse"}}}},"400":{"description":"Invalid order, signature, or session state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}},"401":{"description":"User not authorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}},"503":{"description":"Orderbook is full","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}}}}}},"components":{"schemas":{"AddOrder":{"type":"object","description":"Represents an order submission with optional permit functionality.\n\nThis struct encapsulates all necessary information needed to submit a new order to the system.\n`signed_permit` is required only for non-smart orders.","required":["order"],"properties":{"annotations":{"$ref":"#/components/schemas/OrderAnnotations","description":"Optional informational annotations stored alongside the order in order history\nand echoed back from the order-state endpoints. Not part of the signed order."},"order":{"$ref":"#/components/schemas/OrderIntent"},"signedPermit":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/SignedPermitDTO","description":"The optional signed permit. Only required for non-smart orders.\nDefaults to None in the JSON input.\nBut defaults to a permit in the API schema, as an example."}],"default":{"permit":{"details":{"token":"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48","amount":"1461501637330902918203684832716283019655932542975","expiration":1630003600,"nonce":0},"spender":"0x346a33009161ed9085b75c9194a523322236f74e","sigDeadline":"1630003600"},"signature":{"r":"93806474433608546370310852361160557641089548055391845313596357890102999855022","s":"44967894251262473816400307202805291026637879676677543909166458416059363900630","yParity":true}}}}},"OrderAnnotations":{"type":"object","description":"Additional data attached to an order, provided by the user. Purely informational —\nnot part of the signed order and not used by the protocol.","properties":{"spreadAtSubmissionHbp":{"type":["integer","null"],"format":"int64","description":"The market spread (in hundredths of a basis point, 10_000 hbp = 1%) quoted to the\nuser at submission time, as returned by `/api/quote` (`amm_spread_hbp`).","minimum":0}}},"OrderIntent":{"type":"object","description":"The swap intent for a user","required":["owner","sellToken","buyToken","sellAmount","minBuyAmount","spreadCurve","startTime","endTime","partialFill","callData","callDataTarget","calldataSellAmountOffset","calldataBuyAmountOffset","salt"],"properties":{"buyToken":{"type":"string","format":"address","description":"The token being bought"},"callData":{"type":"string","format":"bytes","description":"Optional call data for smart orders, allowing custom routing"},"callDataTarget":{"type":"string","format":"address","description":"The address of the target contract for the calldata"},"calldataBuyAmountOffset":{"type":"integer","format":"uint64","description":"The offset of the buy amount in the calldata\nIf Some, used to update the calldata with the actual buy amount","minimum":0},"calldataSellAmountOffset":{"type":"integer","format":"uint64","description":"The offset of the sell amount in the calldata\nIf Some, used to update the calldata with the actual sell amount","minimum":0},"endTime":{"type":"integer","format":"uint256","description":"Block timestamp until when the order is valid.","minimum":0},"minBuyAmount":{"type":"string","format":"uint256","description":"The minimum amount of tokens being bought, effectively defining limit price."},"owner":{"type":"string","format":"address","description":"The address of the user submitting the order"},"partialFill":{"type":"boolean","format":"boolean","description":"Flag allowing partial fills"},"salt":{"type":"string","format":"bytes32","description":"The offset of the buy amount in the calldata\nUsed to differentiate between orders with the same parameters"},"sellAmount":{"type":"string","format":"uint196","description":"The amount of tokens being sold"},"sellToken":{"type":"string","format":"address","description":"The token being sold"},"spreadCurve":{"$ref":"#/components/schemas/SpreadCurveDto","description":"A piecewise-linear spread curve mapping a fraction of the order window to a mid-price\ndelta in basis points. Each interior point's `windowBps` is a fraction of the order\nduration in basis points (5_000 = halfway)."},"startTime":{"type":"integer","format":"uint256","description":"Block timestamp since when the order is valid","minimum":0}}},"SpreadCurveDto":{"type":"object","description":"Piecewise-linear spread curve. Interior `points` express knot positions as a fraction\nof the order's `[start_time, end_time]` window in basis points (`windowBps`), so the\ncurve is portable across orders of different durations.","required":["startDeltaBps","endDeltaBps"],"properties":{"endDeltaBps":{"type":"integer","format":"int32","description":"Mid-price delta at the order's `end_time`, in basis points."},"points":{"type":"array","items":{"$ref":"#/components/schemas/CurvePointDto"},"description":"Interior knots. Each `windowBps` must lie in `[1, 9_999]` and values must be\nstrictly increasing across `points`. Defaults to `[]` if absent, yielding a single\nlinear segment from `startDeltaBps` to `endDeltaBps`."},"startDeltaBps":{"type":"integer","format":"int32","description":"Mid-price delta at the order's `start_time`, in basis points."}}},"CurvePointDto":{"type":"object","description":"A single interior knot in a [`SpreadCurveDto`].","required":["windowBps","deltaBps"],"properties":{"deltaBps":{"type":"integer","format":"int32","description":"Mid-price delta in basis points at this knot."},"windowBps":{"type":"integer","format":"int32","description":"Knot position as a fraction of the order window in basis points. Must lie in\n`(0, 10_000)` exclusive — `5_000` is the order midpoint.","maximum":9999,"minimum":1}}},"SignedPermitDTO":{"type":"object","required":["permit","signature"],"properties":{"permit":{"$ref":"#/components/schemas/PermitSingle"},"signature":{"$ref":"#/components/schemas/PermitSignature"}}},"PermitSingle":{"type":"object","required":["details","spender","sigDeadline"],"properties":{"details":{"$ref":"#/components/schemas/PermitDetails"},"sigDeadline":{"type":"string","format":"uint256"},"spender":{"type":"string","format":"address"}}},"PermitDetails":{"type":"object","required":["token","amount","expiration","nonce"],"properties":{"amount":{"type":"string","format":"uint160"},"expiration":{"type":"integer","format":"uint64","minimum":0},"nonce":{"type":"integer","format":"uint64","minimum":0},"token":{"type":"string","format":"address"}}},"PermitSignature":{"type":"object","required":["r","s","yParity"],"properties":{"r":{"type":"string","format":"uint256"},"s":{"type":"string","format":"uint256"},"yParity":{"type":"boolean","format":"boolean"}}},"AddOrderResponse":{"type":"object","description":"Response returned after successfully submitting an order to the system.\nContains the unique identifier assigned to the submitted order.\n\n# Fields\n* `order_hash` - Unique identifier assigned to the submitted order","required":["orderHash"],"properties":{"orderHash":{"type":"string","format":"bytes32"}}},"ErrorResponsePayload":{"type":"object","description":"Structured error response returned by the API","required":["code","message"],"properties":{"code":{"type":"string","description":"Error code in SNAKE_CASE format"},"inner":{"type":["array","null"],"items":{"$ref":"#/components/schemas/InnerErrorResponsePayload"},"description":"Optional array of nested errors (only present for validation errors)"},"message":{"type":"string","description":"Human-readable error message"}}},"InnerErrorResponsePayload":{"type":"object","description":"Individual error within a validation error response","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}
```

## POST /api/add\_orders

> Submit multiple orders to the system

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"tags":[{"name":"turbine","description":"Turbine API endpoints"}],"paths":{"/api/add_orders":{"post":{"tags":["turbine"],"summary":"Submit multiple orders to the system","operationId":"add_orders","requestBody":{"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AddOrder"}}}},"required":true},"responses":{"200":{"description":"Orders successfully submitted","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AddOrderResponse"}}}}},"400":{"description":"Invalid orders, signatures, or session state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}},"401":{"description":"User not authorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}},"503":{"description":"Orderbook is full","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}}}}}},"components":{"schemas":{"AddOrder":{"type":"object","description":"Represents an order submission with optional permit functionality.\n\nThis struct encapsulates all necessary information needed to submit a new order to the system.\n`signed_permit` is required only for non-smart orders.","required":["order"],"properties":{"annotations":{"$ref":"#/components/schemas/OrderAnnotations","description":"Optional informational annotations stored alongside the order in order history\nand echoed back from the order-state endpoints. Not part of the signed order."},"order":{"$ref":"#/components/schemas/OrderIntent"},"signedPermit":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/SignedPermitDTO","description":"The optional signed permit. Only required for non-smart orders.\nDefaults to None in the JSON input.\nBut defaults to a permit in the API schema, as an example."}],"default":{"permit":{"details":{"token":"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48","amount":"1461501637330902918203684832716283019655932542975","expiration":1630003600,"nonce":0},"spender":"0x346a33009161ed9085b75c9194a523322236f74e","sigDeadline":"1630003600"},"signature":{"r":"93806474433608546370310852361160557641089548055391845313596357890102999855022","s":"44967894251262473816400307202805291026637879676677543909166458416059363900630","yParity":true}}}}},"OrderAnnotations":{"type":"object","description":"Additional data attached to an order, provided by the user. Purely informational —\nnot part of the signed order and not used by the protocol.","properties":{"spreadAtSubmissionHbp":{"type":["integer","null"],"format":"int64","description":"The market spread (in hundredths of a basis point, 10_000 hbp = 1%) quoted to the\nuser at submission time, as returned by `/api/quote` (`amm_spread_hbp`).","minimum":0}}},"OrderIntent":{"type":"object","description":"The swap intent for a user","required":["owner","sellToken","buyToken","sellAmount","minBuyAmount","spreadCurve","startTime","endTime","partialFill","callData","callDataTarget","calldataSellAmountOffset","calldataBuyAmountOffset","salt"],"properties":{"buyToken":{"type":"string","format":"address","description":"The token being bought"},"callData":{"type":"string","format":"bytes","description":"Optional call data for smart orders, allowing custom routing"},"callDataTarget":{"type":"string","format":"address","description":"The address of the target contract for the calldata"},"calldataBuyAmountOffset":{"type":"integer","format":"uint64","description":"The offset of the buy amount in the calldata\nIf Some, used to update the calldata with the actual buy amount","minimum":0},"calldataSellAmountOffset":{"type":"integer","format":"uint64","description":"The offset of the sell amount in the calldata\nIf Some, used to update the calldata with the actual sell amount","minimum":0},"endTime":{"type":"integer","format":"uint256","description":"Block timestamp until when the order is valid.","minimum":0},"minBuyAmount":{"type":"string","format":"uint256","description":"The minimum amount of tokens being bought, effectively defining limit price."},"owner":{"type":"string","format":"address","description":"The address of the user submitting the order"},"partialFill":{"type":"boolean","format":"boolean","description":"Flag allowing partial fills"},"salt":{"type":"string","format":"bytes32","description":"The offset of the buy amount in the calldata\nUsed to differentiate between orders with the same parameters"},"sellAmount":{"type":"string","format":"uint196","description":"The amount of tokens being sold"},"sellToken":{"type":"string","format":"address","description":"The token being sold"},"spreadCurve":{"$ref":"#/components/schemas/SpreadCurveDto","description":"A piecewise-linear spread curve mapping a fraction of the order window to a mid-price\ndelta in basis points. Each interior point's `windowBps` is a fraction of the order\nduration in basis points (5_000 = halfway)."},"startTime":{"type":"integer","format":"uint256","description":"Block timestamp since when the order is valid","minimum":0}}},"SpreadCurveDto":{"type":"object","description":"Piecewise-linear spread curve. Interior `points` express knot positions as a fraction\nof the order's `[start_time, end_time]` window in basis points (`windowBps`), so the\ncurve is portable across orders of different durations.","required":["startDeltaBps","endDeltaBps"],"properties":{"endDeltaBps":{"type":"integer","format":"int32","description":"Mid-price delta at the order's `end_time`, in basis points."},"points":{"type":"array","items":{"$ref":"#/components/schemas/CurvePointDto"},"description":"Interior knots. Each `windowBps` must lie in `[1, 9_999]` and values must be\nstrictly increasing across `points`. Defaults to `[]` if absent, yielding a single\nlinear segment from `startDeltaBps` to `endDeltaBps`."},"startDeltaBps":{"type":"integer","format":"int32","description":"Mid-price delta at the order's `start_time`, in basis points."}}},"CurvePointDto":{"type":"object","description":"A single interior knot in a [`SpreadCurveDto`].","required":["windowBps","deltaBps"],"properties":{"deltaBps":{"type":"integer","format":"int32","description":"Mid-price delta in basis points at this knot."},"windowBps":{"type":"integer","format":"int32","description":"Knot position as a fraction of the order window in basis points. Must lie in\n`(0, 10_000)` exclusive — `5_000` is the order midpoint.","maximum":9999,"minimum":1}}},"SignedPermitDTO":{"type":"object","required":["permit","signature"],"properties":{"permit":{"$ref":"#/components/schemas/PermitSingle"},"signature":{"$ref":"#/components/schemas/PermitSignature"}}},"PermitSingle":{"type":"object","required":["details","spender","sigDeadline"],"properties":{"details":{"$ref":"#/components/schemas/PermitDetails"},"sigDeadline":{"type":"string","format":"uint256"},"spender":{"type":"string","format":"address"}}},"PermitDetails":{"type":"object","required":["token","amount","expiration","nonce"],"properties":{"amount":{"type":"string","format":"uint160"},"expiration":{"type":"integer","format":"uint64","minimum":0},"nonce":{"type":"integer","format":"uint64","minimum":0},"token":{"type":"string","format":"address"}}},"PermitSignature":{"type":"object","required":["r","s","yParity"],"properties":{"r":{"type":"string","format":"uint256"},"s":{"type":"string","format":"uint256"},"yParity":{"type":"boolean","format":"boolean"}}},"AddOrderResponse":{"type":"object","description":"Response returned after successfully submitting an order to the system.\nContains the unique identifier assigned to the submitted order.\n\n# Fields\n* `order_hash` - Unique identifier assigned to the submitted order","required":["orderHash"],"properties":{"orderHash":{"type":"string","format":"bytes32"}}},"ErrorResponsePayload":{"type":"object","description":"Structured error response returned by the API","required":["code","message"],"properties":{"code":{"type":"string","description":"Error code in SNAKE_CASE format"},"inner":{"type":["array","null"],"items":{"$ref":"#/components/schemas/InnerErrorResponsePayload"},"description":"Optional array of nested errors (only present for validation errors)"},"message":{"type":"string","description":"Human-readable error message"}}},"InnerErrorResponsePayload":{"type":"object","description":"Individual error within a validation error response","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}
```

## POST /api/cancel\_order

> Remove an order from the system

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"tags":[{"name":"turbine","description":"Turbine API endpoints"}],"paths":{"/api/cancel_order":{"post":{"tags":["turbine"],"summary":"Remove an order from the system","operationId":"cancel_order","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelOrder"}}},"required":true},"responses":{"200":{"description":"Order successfully queued for cancellation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelOrderResponse"}}}},"401":{"description":"User not authorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}},"404":{"description":"Order not found or not owned by caller","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}}}}}},"components":{"schemas":{"CancelOrder":{"type":"object","required":["orderHash"],"properties":{"orderHash":{"type":"string","format":"bytes32"}}},"CancelOrderResponse":{"type":"object","required":["orderHash"],"properties":{"orderHash":{"type":"string","format":"bytes32"}}},"ErrorResponsePayload":{"type":"object","description":"Structured error response returned by the API","required":["code","message"],"properties":{"code":{"type":"string","description":"Error code in SNAKE_CASE format"},"inner":{"type":["array","null"],"items":{"$ref":"#/components/schemas/InnerErrorResponsePayload"},"description":"Optional array of nested errors (only present for validation errors)"},"message":{"type":"string","description":"Human-readable error message"}}},"InnerErrorResponsePayload":{"type":"object","description":"Individual error within a validation error response","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}
```

## GET /api/config

> Get the current config

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"tags":[{"name":"turbine","description":"Turbine API endpoints"}],"paths":{"/api/config":{"get":{"tags":["turbine"],"summary":"Get the current config","operationId":"config","responses":{"200":{"description":"Config retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfigResponse"}}}}}}}},"components":{"schemas":{"ConfigResponse":{"type":"object","required":["version","turbineSettlerAddress","turbineSignerAddress","lpHookAddress","lpRouterAddress","poolManagerAddress","submitSettlements","siweDomain","siweUri","minTradeSizeUsdc","minLiquidityVolumeUsdc","eip712Domain","maxSignatureLifetimeS","tokens"],"properties":{"eip712Domain":{"$ref":"#/components/schemas/Eip712DomainInfo","description":"The EIP-712 domain that `/api/eip712/` request signatures are verified against."},"lpHookAddress":{"type":"string","format":"address"},"lpRouterAddress":{"type":"string","format":"address"},"maxSignatureLifetimeS":{"type":"integer","format":"int64","description":"Maximum allowed remaining validity of an EIP-712 auth signature, in seconds.","minimum":0},"minLiquidityVolumeUsdc":{"type":"string","format":"uint256","description":"Minimum total USDC volume for a liquidity provision intent, in native USDC units\n(6 decimals). A value of `10000000` is 10 USDC."},"minTradeSizeUsdc":{"type":"string","format":"uint256","description":"Minimum trade size in native USDC units (6 decimals). A value of `10000000`\nis 10 USDC."},"poolManagerAddress":{"type":"string","format":"address"},"siweDomain":{"type":"string"},"siweUri":{"type":"string"},"submitSettlements":{"type":"boolean"},"tokens":{"type":"array","items":{"$ref":"#/components/schemas/TokenInfo"}},"turbineSettlerAddress":{"type":"string","format":"address"},"turbineSignerAddress":{"type":"string","format":"address"},"version":{"type":"string"}}},"Eip712DomainInfo":{"type":"object","description":"Wire form of the EIP-712 signing domain served by `/api/config`. This is the single\nsource of truth for the domain: both `/api/config` and signature verification read\ntheir alloy [`Eip712Domain`] from it, so what clients sign against is exactly what\nthe server verifies.","required":["name","version","chainId","verifyingContract","salt"],"properties":{"chainId":{"type":"integer","format":"int64","minimum":0},"name":{"type":"string"},"salt":{"type":"string","format":"bytes32"},"verifyingContract":{"type":"string","format":"address"},"version":{"type":"string"}}},"TokenInfo":{"type":"object","description":"Public metadata for a token in the allowlist, returned by `/api/config`.","required":["address","symbol","decimals"],"properties":{"address":{"type":"string","format":"address"},"class":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/TokenClass"}]},"decimals":{"type":"integer","format":"int32","minimum":0},"oracles":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/OracleFeed"},"propertyNames":{"type":"string"}},"symbol":{"type":"string"}}},"TokenClass":{"type":"string","enum":["Stable","Regular","Meme"]},"OracleFeed":{"oneOf":[{"type":"object","description":"Binance WebSocket depth feed.","required":["ticker_symbol"],"properties":{"ticker_symbol":{"type":"string"}}},{"type":"object","description":"Coinbase WebSocket feed.","required":["product_id"],"properties":{"product_id":{"type":"string"}}},{"type":"object","description":"Kraken WebSocket feed.","required":["wsname"],"properties":{"wsname":{"type":"string"}}},{"type":"object","description":"Lighter WebSocket feed.","required":["market_id","symbol"],"properties":{"market_id":{"type":"string"},"symbol":{"type":"string"}}},{"type":"object","description":"Hyperliquid WebSocket feed.","required":["coin"],"properties":{"coin":{"type":"string"}}},{"type":"object","description":"Bitget WebSocket feed.","required":["symbol"],"properties":{"symbol":{"type":"string"}}}],"description":"OpenAPI schema for an oracle feed entry.\n\nThe wire format flattens the oracle configuration: the map key in\n`TokenInfo.oracles` identifies the oracle (e.g. `binance`, `kraken`),\nand the value carries only the provider-specific fields — the internal\n`type` discriminator is stripped by `serialize_feeds`.\n\nThis type is documentation-only: runtime (de)serialization goes through\n[`ExchangeFeedInfo`]. The `#[serde(untagged)]` attribute here merely keeps\n`ToSchema` output consistent with the flat wire shape."}}}}
```

## POST /api/eip712/add\_liquidity

> Submit an EIP-712 signed liquidity deposit intent

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"tags":[{"name":"turbine","description":"Turbine API endpoints"}],"paths":{"/api/eip712/add_liquidity":{"post":{"tags":["turbine"],"summary":"Submit an EIP-712 signed liquidity deposit intent","operationId":"add_liquidity_eip712","requestBody":{"description":"An LP deposit intent and the Permit2 batch permit funding it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Eip712RequestBody_AddLiquidityEip712"}}},"required":true},"responses":{"200":{"description":"Liquidity intent successfully submitted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddLiquidityResponse"}}}},"400":{"description":"Invalid intent, deadline, or pool","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}},"401":{"description":"Invalid signature or signer mismatch","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}},"409":{"description":"Nonce already used or reserved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}}}}}},"components":{"schemas":{"Eip712RequestBody_AddLiquidityEip712":{"type":"object","description":"EIP-712 request body: the semantic payload plus its auth block. The EIP-712\nsignature covers the payload's typed struct together with `nonce` and `deadline`.","required":["payload","auth"],"properties":{"auth":{"$ref":"#/components/schemas/AuthBlock"},"payload":{"allOf":[{"$ref":"#/components/schemas/AddLiquidity"}],"description":"Inner payload of add_liquidity endpoint."}}},"AuthBlock":{"type":"object","description":"Auth block carried by every EIP-712 request body.","required":["signer","nonce","deadline","signature"],"properties":{"deadline":{"type":"integer","format":"int64","description":"Unix seconds; not more than `maxSignatureLifetimeS` (from the config endpoint) into the\nfuture.","minimum":0},"nonce":{"type":"string","description":"Random per-request value, unique per signer among un-expired requests."},"signature":{"$ref":"#/components/schemas/PermitSignature"},"signer":{"type":"string","format":"address","description":"Claimed signer. Must match the recovered address."}}},"PermitSignature":{"type":"object","required":["r","s","yParity"],"properties":{"r":{"type":"string","format":"uint256"},"s":{"type":"string","format":"uint256"},"yParity":{"type":"boolean","format":"boolean"}}},"AddLiquidity":{"type":"object","description":"Represents a liquidity addition with SignatureTransfer permit functionality.\n\nThis struct encapsulates all necessary information needed to submit a new intent to add\nliquidity to the system, including the intent and SignatureTransfer permit data for approvals.\nUser authentication is handled via session-based SIWE verification.\n\n# Fields\n* `add_liquidity` The intent to add liquidity\n* `permit_tokens` The batch SignatureTransfer permit and signature for token0 and token1","required":["addLiquidity","permitTokens"],"properties":{"addLiquidity":{"$ref":"#/components/schemas/AddLiquidityIntent"},"permitTokens":{"$ref":"#/components/schemas/SignedBatchSignatureTransferDTO"}}},"AddLiquidityIntent":{"type":"object","required":["owner","token0","token1","fee","token0Amount","token1Amount","exact","salt"],"properties":{"exact":{"type":"boolean","description":"Whether the user wants to provide exactly the specified amounts (and is willing to\npay a swap fee first) or would rather like to provide liquidity in current ratio of pool\nreserves (in such case the specified token amounts are treated as maximum amounts)."},"fee":{"type":"integer","format":"uint32","description":"The fee for the pool","minimum":0},"owner":{"type":"string","format":"address","description":"The address of the user submitting the intent"},"salt":{"type":"string","format":"bytes32","description":"Used to differentiate between intents with the same parameters"},"token0":{"type":"string","format":"address","description":"The token0 of the pool. Tokens must be sorted ascending by address so that\n`token0 < token1`; unordered intents are rejected."},"token0Amount":{"type":"string","format":"uint256","description":"The amount of token0 to provide. If `exact` is true, this is the exact amount of token0 to\nprovide. If `exact` is false, this is the maximum amount of token0 to provide."},"token1":{"type":"string","format":"address","description":"The token1 of the pool. Must be greater than `token0` (see `token0`)."},"token1Amount":{"type":"string","format":"uint256","description":"The amount of token1 to provide. If `exact` is true, this is the exact amount of token1 to\nprovide. If `exact` is false, this is the maximum amount of token1 to provide."}}},"SignedBatchSignatureTransferDTO":{"type":"object","description":"Signed batch SignatureTransfer permit (for add liquidity token0 and token1 transfer)","required":["permit","signature"],"properties":{"permit":{"$ref":"#/components/schemas/PermitBatchTransferFrom"},"signature":{"$ref":"#/components/schemas/PermitSignature"}}},"PermitBatchTransferFrom":{"type":"object","description":"DTO for ISignatureTransfer.PermitBatchTransferFrom - batch token transfer authorization","required":["permitted","nonce","deadline"],"properties":{"deadline":{"type":"string","format":"uint256"},"nonce":{"type":"string","format":"uint256"},"permitted":{"type":"array","items":{"$ref":"#/components/schemas/TokenPermissions"}}}},"TokenPermissions":{"type":"object","description":"Token permissions for ISignatureTransfer permits.\nUsed in PermitTransferFrom and PermitBatchTransferFrom.","required":["token","amount"],"properties":{"amount":{"type":"string","format":"uint256","description":"The maximum amount that can be transferred"},"token":{"type":"string","format":"address","description":"The token address"}}},"AddLiquidityResponse":{"type":"object","description":"Response returned after successfully submitting a liquidity intent to the system.\nContains the unique identifier assigned to the submitted intent.\n\n# Fields\n* `hash` - Unique identifier assigned to the liquidity intent","required":["intentHash"],"properties":{"intentHash":{"type":"string","format":"bytes32"}}},"ErrorResponsePayload":{"type":"object","description":"Structured error response returned by the API","required":["code","message"],"properties":{"code":{"type":"string","description":"Error code in SNAKE_CASE format"},"inner":{"type":["array","null"],"items":{"$ref":"#/components/schemas/InnerErrorResponsePayload"},"description":"Optional array of nested errors (only present for validation errors)"},"message":{"type":"string","description":"Human-readable error message"}}},"InnerErrorResponsePayload":{"type":"object","description":"Individual error within a validation error response","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}
```

## POST /api/eip712/add\_order

> Submit a single EIP-712 signed order

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"tags":[{"name":"turbine","description":"Turbine API endpoints"}],"paths":{"/api/eip712/add_order":{"post":{"tags":["turbine"],"summary":"Submit a single EIP-712 signed order","operationId":"add_order_eip712","requestBody":{"description":"An order intent with its full spread curve, signed by the order's owner.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Eip712RequestBody_AddOrderEip712"}}},"required":true},"responses":{"200":{"description":"Order successfully submitted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddOrderResponse"}}}},"400":{"description":"Invalid order, deadline, or canonicalization","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}},"401":{"description":"Invalid signature or signer mismatch","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}},"409":{"description":"Nonce already used or reserved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}},"503":{"description":"Orderbook is full","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}}}}}},"components":{"schemas":{"Eip712RequestBody_AddOrderEip712":{"type":"object","description":"EIP-712 request body: the semantic payload plus its auth block. The EIP-712\nsignature covers the payload's typed struct together with `nonce` and `deadline`.","required":["payload","auth"],"properties":{"auth":{"$ref":"#/components/schemas/AuthBlock"},"payload":{"type":"object","description":"Inner payload of add_order endpoint.","required":["order","spreadCurve"],"properties":{"annotations":{"$ref":"#/components/schemas/OrderAnnotations","description":"Informational only; deliberately outside the signature, as in the session API."},"order":{"$ref":"#/components/schemas/OrderIntentEip712"},"signedPermit":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/SignedPermitDTO","description":"Permit2 signature funding the order; same semantics as the session API."}]},"spreadCurve":{"$ref":"#/components/schemas/SpreadCurveDto","description":"The full spread curve, signed as part of the inner payload."}}}}},"AuthBlock":{"type":"object","description":"Auth block carried by every EIP-712 request body.","required":["signer","nonce","deadline","signature"],"properties":{"deadline":{"type":"integer","format":"int64","description":"Unix seconds; not more than `maxSignatureLifetimeS` (from the config endpoint) into the\nfuture.","minimum":0},"nonce":{"type":"string","description":"Random per-request value, unique per signer among un-expired requests."},"signature":{"$ref":"#/components/schemas/PermitSignature"},"signer":{"type":"string","format":"address","description":"Claimed signer. Must match the recovered address."}}},"PermitSignature":{"type":"object","required":["r","s","yParity"],"properties":{"r":{"type":"string","format":"uint256"},"s":{"type":"string","format":"uint256"},"yParity":{"type":"boolean","format":"boolean"}}},"OrderAnnotations":{"type":"object","description":"Additional data attached to an order, provided by the user. Purely informational —\nnot part of the signed order and not used by the protocol.","properties":{"spreadAtSubmissionHbp":{"type":["integer","null"],"format":"int64","description":"The market spread (in hundredths of a basis point, 10_000 hbp = 1%) quoted to the\nuser at submission time, as returned by `/api/quote` (`amm_spread_hbp`).","minimum":0}}},"OrderIntentEip712":{"type":"object","description":"The EIP-712 API wire form of an order intent. Unlike the session-API DTO,\nit carries no spread curve inline. The curve is a required payload-level sibling.","required":["owner","sellToken","buyToken","sellAmount","minBuyAmount","startTime","endTime","partialFill","callData","callDataTarget","salt"],"properties":{"buyToken":{"type":"string","format":"address"},"callData":{"type":"string","format":"bytes"},"callDataTarget":{"type":"string","format":"address"},"endTime":{"type":"integer","format":"uint256","description":"Block timestamp until when the order is valid","minimum":0},"minBuyAmount":{"type":"string","format":"uint256"},"owner":{"type":"string","format":"address"},"partialFill":{"type":"boolean"},"salt":{"type":"string","format":"bytes32","description":"Differentiates orders that are otherwise identical (including their full spread\ncurve); SDKs should randomize it."},"sellAmount":{"type":"string","format":"uint256"},"sellToken":{"type":"string","format":"address"},"startTime":{"type":"integer","format":"uint256","description":"Block timestamp since when the order is valid","minimum":0}}},"SignedPermitDTO":{"type":"object","required":["permit","signature"],"properties":{"permit":{"$ref":"#/components/schemas/PermitSingle"},"signature":{"$ref":"#/components/schemas/PermitSignature"}}},"PermitSingle":{"type":"object","required":["details","spender","sigDeadline"],"properties":{"details":{"$ref":"#/components/schemas/PermitDetails"},"sigDeadline":{"type":"string","format":"uint256"},"spender":{"type":"string","format":"address"}}},"PermitDetails":{"type":"object","required":["token","amount","expiration","nonce"],"properties":{"amount":{"type":"string","format":"uint160"},"expiration":{"type":"integer","format":"uint64","minimum":0},"nonce":{"type":"integer","format":"uint64","minimum":0},"token":{"type":"string","format":"address"}}},"SpreadCurveDto":{"type":"object","description":"Piecewise-linear spread curve. Interior `points` express knot positions as a fraction\nof the order's `[start_time, end_time]` window in basis points (`windowBps`), so the\ncurve is portable across orders of different durations.","required":["startDeltaBps","endDeltaBps"],"properties":{"endDeltaBps":{"type":"integer","format":"int32","description":"Mid-price delta at the order's `end_time`, in basis points."},"points":{"type":"array","items":{"$ref":"#/components/schemas/CurvePointDto"},"description":"Interior knots. Each `windowBps` must lie in `[1, 9_999]` and values must be\nstrictly increasing across `points`. Defaults to `[]` if absent, yielding a single\nlinear segment from `startDeltaBps` to `endDeltaBps`."},"startDeltaBps":{"type":"integer","format":"int32","description":"Mid-price delta at the order's `start_time`, in basis points."}}},"CurvePointDto":{"type":"object","description":"A single interior knot in a [`SpreadCurveDto`].","required":["windowBps","deltaBps"],"properties":{"deltaBps":{"type":"integer","format":"int32","description":"Mid-price delta in basis points at this knot."},"windowBps":{"type":"integer","format":"int32","description":"Knot position as a fraction of the order window in basis points. Must lie in\n`(0, 10_000)` exclusive — `5_000` is the order midpoint.","maximum":9999,"minimum":1}}},"AddOrderResponse":{"type":"object","description":"Response returned after successfully submitting an order to the system.\nContains the unique identifier assigned to the submitted order.\n\n# Fields\n* `order_hash` - Unique identifier assigned to the submitted order","required":["orderHash"],"properties":{"orderHash":{"type":"string","format":"bytes32"}}},"ErrorResponsePayload":{"type":"object","description":"Structured error response returned by the API","required":["code","message"],"properties":{"code":{"type":"string","description":"Error code in SNAKE_CASE format"},"inner":{"type":["array","null"],"items":{"$ref":"#/components/schemas/InnerErrorResponsePayload"},"description":"Optional array of nested errors (only present for validation errors)"},"message":{"type":"string","description":"Human-readable error message"}}},"InnerErrorResponsePayload":{"type":"object","description":"Individual error within a validation error response","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}
```

## POST /api/eip712/add\_orders

> Submit multiple independently-signed orders

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"tags":[{"name":"turbine","description":"Turbine API endpoints"}],"paths":{"/api/eip712/add_orders":{"post":{"tags":["turbine"],"summary":"Submit multiple independently-signed orders","operationId":"add_orders_eip712","requestBody":{"description":"One independently signed request per order, all signed by the same signer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Eip712RequestBodyAddOrderBatch"}}},"required":true},"responses":{"200":{"description":"Orders successfully submitted","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AddOrderResponse"}}}}},"400":{"description":"All envelopes invalid (per-element errors inside)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}},"503":{"description":"Orderbook is full","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}}}}}},"components":{"schemas":{"Eip712RequestBodyAddOrderBatch":{"type":"array","items":{"$ref":"#/components/schemas/Eip712RequestBody_AddOrderEip712"},"description":"A batch of independently signed add-order requests."},"Eip712RequestBody_AddOrderEip712":{"type":"object","description":"EIP-712 request body: the semantic payload plus its auth block. The EIP-712\nsignature covers the payload's typed struct together with `nonce` and `deadline`.","required":["payload","auth"],"properties":{"auth":{"$ref":"#/components/schemas/AuthBlock"},"payload":{"type":"object","description":"Inner payload of add_order endpoint.","required":["order","spreadCurve"],"properties":{"annotations":{"$ref":"#/components/schemas/OrderAnnotations","description":"Informational only; deliberately outside the signature, as in the session API."},"order":{"$ref":"#/components/schemas/OrderIntentEip712"},"signedPermit":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/SignedPermitDTO","description":"Permit2 signature funding the order; same semantics as the session API."}]},"spreadCurve":{"$ref":"#/components/schemas/SpreadCurveDto","description":"The full spread curve, signed as part of the inner payload."}}}}},"AuthBlock":{"type":"object","description":"Auth block carried by every EIP-712 request body.","required":["signer","nonce","deadline","signature"],"properties":{"deadline":{"type":"integer","format":"int64","description":"Unix seconds; not more than `maxSignatureLifetimeS` (from the config endpoint) into the\nfuture.","minimum":0},"nonce":{"type":"string","description":"Random per-request value, unique per signer among un-expired requests."},"signature":{"$ref":"#/components/schemas/PermitSignature"},"signer":{"type":"string","format":"address","description":"Claimed signer. Must match the recovered address."}}},"PermitSignature":{"type":"object","required":["r","s","yParity"],"properties":{"r":{"type":"string","format":"uint256"},"s":{"type":"string","format":"uint256"},"yParity":{"type":"boolean","format":"boolean"}}},"OrderAnnotations":{"type":"object","description":"Additional data attached to an order, provided by the user. Purely informational —\nnot part of the signed order and not used by the protocol.","properties":{"spreadAtSubmissionHbp":{"type":["integer","null"],"format":"int64","description":"The market spread (in hundredths of a basis point, 10_000 hbp = 1%) quoted to the\nuser at submission time, as returned by `/api/quote` (`amm_spread_hbp`).","minimum":0}}},"OrderIntentEip712":{"type":"object","description":"The EIP-712 API wire form of an order intent. Unlike the session-API DTO,\nit carries no spread curve inline. The curve is a required payload-level sibling.","required":["owner","sellToken","buyToken","sellAmount","minBuyAmount","startTime","endTime","partialFill","callData","callDataTarget","salt"],"properties":{"buyToken":{"type":"string","format":"address"},"callData":{"type":"string","format":"bytes"},"callDataTarget":{"type":"string","format":"address"},"endTime":{"type":"integer","format":"uint256","description":"Block timestamp until when the order is valid","minimum":0},"minBuyAmount":{"type":"string","format":"uint256"},"owner":{"type":"string","format":"address"},"partialFill":{"type":"boolean"},"salt":{"type":"string","format":"bytes32","description":"Differentiates orders that are otherwise identical (including their full spread\ncurve); SDKs should randomize it."},"sellAmount":{"type":"string","format":"uint256"},"sellToken":{"type":"string","format":"address"},"startTime":{"type":"integer","format":"uint256","description":"Block timestamp since when the order is valid","minimum":0}}},"SignedPermitDTO":{"type":"object","required":["permit","signature"],"properties":{"permit":{"$ref":"#/components/schemas/PermitSingle"},"signature":{"$ref":"#/components/schemas/PermitSignature"}}},"PermitSingle":{"type":"object","required":["details","spender","sigDeadline"],"properties":{"details":{"$ref":"#/components/schemas/PermitDetails"},"sigDeadline":{"type":"string","format":"uint256"},"spender":{"type":"string","format":"address"}}},"PermitDetails":{"type":"object","required":["token","amount","expiration","nonce"],"properties":{"amount":{"type":"string","format":"uint160"},"expiration":{"type":"integer","format":"uint64","minimum":0},"nonce":{"type":"integer","format":"uint64","minimum":0},"token":{"type":"string","format":"address"}}},"SpreadCurveDto":{"type":"object","description":"Piecewise-linear spread curve. Interior `points` express knot positions as a fraction\nof the order's `[start_time, end_time]` window in basis points (`windowBps`), so the\ncurve is portable across orders of different durations.","required":["startDeltaBps","endDeltaBps"],"properties":{"endDeltaBps":{"type":"integer","format":"int32","description":"Mid-price delta at the order's `end_time`, in basis points."},"points":{"type":"array","items":{"$ref":"#/components/schemas/CurvePointDto"},"description":"Interior knots. Each `windowBps` must lie in `[1, 9_999]` and values must be\nstrictly increasing across `points`. Defaults to `[]` if absent, yielding a single\nlinear segment from `startDeltaBps` to `endDeltaBps`."},"startDeltaBps":{"type":"integer","format":"int32","description":"Mid-price delta at the order's `start_time`, in basis points."}}},"CurvePointDto":{"type":"object","description":"A single interior knot in a [`SpreadCurveDto`].","required":["windowBps","deltaBps"],"properties":{"deltaBps":{"type":"integer","format":"int32","description":"Mid-price delta in basis points at this knot."},"windowBps":{"type":"integer","format":"int32","description":"Knot position as a fraction of the order window in basis points. Must lie in\n`(0, 10_000)` exclusive — `5_000` is the order midpoint.","maximum":9999,"minimum":1}}},"AddOrderResponse":{"type":"object","description":"Response returned after successfully submitting an order to the system.\nContains the unique identifier assigned to the submitted order.\n\n# Fields\n* `order_hash` - Unique identifier assigned to the submitted order","required":["orderHash"],"properties":{"orderHash":{"type":"string","format":"bytes32"}}},"ErrorResponsePayload":{"type":"object","description":"Structured error response returned by the API","required":["code","message"],"properties":{"code":{"type":"string","description":"Error code in SNAKE_CASE format"},"inner":{"type":["array","null"],"items":{"$ref":"#/components/schemas/InnerErrorResponsePayload"},"description":"Optional array of nested errors (only present for validation errors)"},"message":{"type":"string","description":"Human-readable error message"}}},"InnerErrorResponsePayload":{"type":"object","description":"Individual error within a validation error response","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}
```

## POST /api/eip712/cancel\_order

> Cancel an order with an EIP-712 signed request

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"tags":[{"name":"turbine","description":"Turbine API endpoints"}],"paths":{"/api/eip712/cancel_order":{"post":{"tags":["turbine"],"summary":"Cancel an order with an EIP-712 signed request","operationId":"cancel_order_eip712","requestBody":{"description":"The hash of the order to cancel, signed by the order's owner.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Eip712RequestBody_CancelOrderEip712"}}},"required":true},"responses":{"200":{"description":"Order successfully queued for cancellation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelOrderResponse"}}}},"404":{"description":"Order not found or not owned by the signer","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}},"409":{"description":"Nonce already used or reserved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}}}}}},"components":{"schemas":{"Eip712RequestBody_CancelOrderEip712":{"type":"object","description":"EIP-712 request body: the semantic payload plus its auth block. The EIP-712\nsignature covers the payload's typed struct together with `nonce` and `deadline`.","required":["payload","auth"],"properties":{"auth":{"$ref":"#/components/schemas/AuthBlock"},"payload":{"type":"object","description":"Inner payload of cancel_order endpoint.","required":["orderHash"],"properties":{"orderHash":{"type":"string","format":"bytes32"}}}}},"AuthBlock":{"type":"object","description":"Auth block carried by every EIP-712 request body.","required":["signer","nonce","deadline","signature"],"properties":{"deadline":{"type":"integer","format":"int64","description":"Unix seconds; not more than `maxSignatureLifetimeS` (from the config endpoint) into the\nfuture.","minimum":0},"nonce":{"type":"string","description":"Random per-request value, unique per signer among un-expired requests."},"signature":{"$ref":"#/components/schemas/PermitSignature"},"signer":{"type":"string","format":"address","description":"Claimed signer. Must match the recovered address."}}},"PermitSignature":{"type":"object","required":["r","s","yParity"],"properties":{"r":{"type":"string","format":"uint256"},"s":{"type":"string","format":"uint256"},"yParity":{"type":"boolean","format":"boolean"}}},"CancelOrderResponse":{"type":"object","required":["orderHash"],"properties":{"orderHash":{"type":"string","format":"bytes32"}}},"ErrorResponsePayload":{"type":"object","description":"Structured error response returned by the API","required":["code","message"],"properties":{"code":{"type":"string","description":"Error code in SNAKE_CASE format"},"inner":{"type":["array","null"],"items":{"$ref":"#/components/schemas/InnerErrorResponsePayload"},"description":"Optional array of nested errors (only present for validation errors)"},"message":{"type":"string","description":"Human-readable error message"}}},"InnerErrorResponsePayload":{"type":"object","description":"Individual error within a validation error response","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}
```

## POST /api/eip712/liquidity\_intents

> Query the signer's liquidity intents by hash

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"tags":[{"name":"turbine","description":"Turbine API endpoints"}],"paths":{"/api/eip712/liquidity_intents":{"post":{"tags":["turbine"],"summary":"Query the signer's liquidity intents by hash","operationId":"get_liquidity_intents_eip712","requestBody":{"description":"The LP intent hashes to look up, at least one and at most 30; results are scoped to the recovered signer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Eip712RequestBody_LiquidityIntentsQueryEip712"}}},"required":true},"responses":{"200":{"description":"Liquidity intent states owned by the recovered signer","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/LiquidityIntentState"}}}}},"400":{"description":"Invalid deadline, or an empty or oversized hash list","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}},"401":{"description":"Invalid signature or signer mismatch","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}}}}}},"components":{"schemas":{"Eip712RequestBody_LiquidityIntentsQueryEip712":{"type":"object","description":"EIP-712 request body: the semantic payload plus its auth block. The EIP-712\nsignature covers the payload's typed struct together with `nonce` and `deadline`.","required":["payload","auth"],"properties":{"auth":{"$ref":"#/components/schemas/AuthBlock"},"payload":{"type":"object","description":"Inner payload for querying liquidity intents.","properties":{"hashes":{"type":"array","items":{"type":"string","format":"bytes32"},"description":"Intent hashes to look up. Max 30 per request; at least one is required."}}}}},"AuthBlock":{"type":"object","description":"Auth block carried by every EIP-712 request body.","required":["signer","nonce","deadline","signature"],"properties":{"deadline":{"type":"integer","format":"int64","description":"Unix seconds; not more than `maxSignatureLifetimeS` (from the config endpoint) into the\nfuture.","minimum":0},"nonce":{"type":"string","description":"Random per-request value, unique per signer among un-expired requests."},"signature":{"$ref":"#/components/schemas/PermitSignature"},"signer":{"type":"string","format":"address","description":"Claimed signer. Must match the recovered address."}}},"PermitSignature":{"type":"object","required":["r","s","yParity"],"properties":{"r":{"type":"string","format":"uint256"},"s":{"type":"string","format":"uint256"},"yParity":{"type":"boolean","format":"boolean"}}},"LiquidityIntentState":{"type":"object","required":["hash","intent","status"],"properties":{"hash":{"type":"string","format":"bytes32"},"intent":{"type":"object"},"status":{"$ref":"#/components/schemas/LiquidityIntentStatus"}}},"LiquidityIntentStatus":{"type":"string","enum":["Pending","Invalid","Expired","Executed","PendingCancellation","Canceled"]},"ErrorResponsePayload":{"type":"object","description":"Structured error response returned by the API","required":["code","message"],"properties":{"code":{"type":"string","description":"Error code in SNAKE_CASE format"},"inner":{"type":["array","null"],"items":{"$ref":"#/components/schemas/InnerErrorResponsePayload"},"description":"Optional array of nested errors (only present for validation errors)"},"message":{"type":"string","description":"Human-readable error message"}}},"InnerErrorResponsePayload":{"type":"object","description":"Individual error within a validation error response","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}
```

## POST /api/eip712/orders

> Query the signer's orders

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"tags":[{"name":"turbine","description":"Turbine API endpoints"}],"paths":{"/api/eip712/orders":{"post":{"tags":["turbine"],"summary":"Query the signer's orders","operationId":"get_orders_eip712","requestBody":{"description":"Filters for the signer's orders; results are scoped to the recovered signer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Eip712RequestBody_OrdersQueryEip712"}}},"required":true},"responses":{"200":{"description":"Orders for the recovered signer","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrdersResponse"}}}},"400":{"description":"Invalid deadline or query","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}},"401":{"description":"Invalid signature or signer mismatch","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}}}}}},"components":{"schemas":{"Eip712RequestBody_OrdersQueryEip712":{"type":"object","description":"EIP-712 request body: the semantic payload plus its auth block. The EIP-712\nsignature covers the payload's typed struct together with `nonce` and `deadline`.","required":["payload","auth"],"properties":{"auth":{"$ref":"#/components/schemas/AuthBlock"},"payload":{"type":"object","description":"Inner payload of GET orders endpoint.\nField semantics mirror the session API's query parameters; zero values mean \"unset\".","properties":{"cursor":{"type":"string","description":"Opaque cursor from a previous response; empty for the first page."},"hashes":{"type":"array","items":{"type":"string","format":"bytes32"},"description":"Direct lookup by order hash. Max 30 per request; pagination is skipped."},"limit":{"type":"integer","format":"int64","description":"Max orders per page; 0 selects the server default, and larger values are capped.","minimum":0},"statuses":{"type":"array","items":{"$ref":"#/components/schemas/OrderStatus"},"description":"Filter by order status. Multiple values are OR'd."}}}}},"AuthBlock":{"type":"object","description":"Auth block carried by every EIP-712 request body.","required":["signer","nonce","deadline","signature"],"properties":{"deadline":{"type":"integer","format":"int64","description":"Unix seconds; not more than `maxSignatureLifetimeS` (from the config endpoint) into the\nfuture.","minimum":0},"nonce":{"type":"string","description":"Random per-request value, unique per signer among un-expired requests."},"signature":{"$ref":"#/components/schemas/PermitSignature"},"signer":{"type":"string","format":"address","description":"Claimed signer. Must match the recovered address."}}},"PermitSignature":{"type":"object","required":["r","s","yParity"],"properties":{"r":{"type":"string","format":"uint256"},"s":{"type":"string","format":"uint256"},"yParity":{"type":"boolean","format":"boolean"}}},"OrderStatus":{"type":"string","enum":["Active","Invalid","Expired","Filled","PendingCancellation","Canceled","Incompatible"]},"OrdersResponse":{"type":"object","description":"Response body for `GET /api/orders`.","required":["orders","hasMore"],"properties":{"cursor":{"type":["string","null"],"description":"Opaque token to pass back as `cursor` in the next request. `null` on the last page."},"hasMore":{"type":"boolean","description":"Mirrors `cursor.is_some()`."},"orders":{"type":"array","items":{"$ref":"#/components/schemas/OrderState"}}}},"OrderState":{"type":"object","required":["hash","owner","status","execution","orderDetails"],"properties":{"execution":{"type":"array","items":{"$ref":"#/components/schemas/OrderExecution"}},"hash":{"type":"string","format":"bytes32"},"orderDetails":{"$ref":"#/components/schemas/OrderDetails"},"owner":{"type":"string","format":"address"},"status":{"$ref":"#/components/schemas/OrderStatus"}}},"OrderExecution":{"type":"object","required":["blockNumber","soldAmount","boughtAmount","surplusBuyAmount"],"properties":{"batchId":{"type":["integer","null"],"format":"int64","minimum":0},"blockNumber":{"type":"integer","format":"int64","minimum":0},"boughtAmount":{"type":"string","format":"uint256"},"midPrice":{"type":"object","description":"Mid-price for the order's (sell → buy) pair at the clearing batch."},"soldAmount":{"type":"string","format":"uint256"},"surplusBuyAmount":{"type":"string","format":"uint256"},"txHash":{"type":["string","null"],"format":"bytes32"}}},"OrderDetails":{"type":"object","description":"Display subset of an `Order`, copied verbatim onto `OrderState` at insertion time.\n\nCarrying these fields on the state record keeps display detail available even after the\nunderlying `Order` is dropped at terminal-status transition.","required":["sellToken","buyToken","sellAmount","limitPrice","startTime","endTime","spreadCurve","createdTimestamp"],"properties":{"annotations":{"$ref":"#/components/schemas/OrderAnnotations","description":"User-provided annotations, snapshotted at insertion. Defaults for order\nhistory entries persisted before annotations existed."},"buyToken":{"type":"string","format":"address"},"createdTimestamp":{"type":"string","format":"date-time"},"endTime":{"type":"string","format":"uint256"},"limitPrice":{"type":"object"},"sellAmount":{"type":"string","format":"uint256"},"sellToken":{"type":"string","format":"address"},"spreadCurve":{"$ref":"#/components/schemas/SpreadCurve","description":"Piecewise-linear curve defining the allowed mid-price deviation over the order\nwindow. Snapshotted from the submitted [`Order`] at insertion so the curve remains\navailable after the underlying `Order` is dropped."},"startTime":{"type":"string","format":"uint256"}}},"OrderAnnotations":{"type":"object","description":"Additional data attached to an order, provided by the user. Purely informational —\nnot part of the signed order and not used by the protocol.","properties":{"spreadAtSubmissionHbp":{"type":["integer","null"],"format":"int64","description":"The market spread (in hundredths of a basis point, 10_000 hbp = 1%) quoted to the\nuser at submission time, as returned by `/api/quote` (`amm_spread_hbp`).","minimum":0}}},"SpreadCurve":{"type":"object","description":"A piecewise-linear curve mapping wall-clock time to a mid-price delta in basis points.\n\nThe curve carries its own `[start_secs, end_secs]` window: the order's `start_time`\nand `end_time` must match these for validation to succeed (enforced at the API layer).\nBetween interior knots, `delta_bps` is interpolated linearly using i64 arithmetic.\nOutside the curve window the value clamps to `start_delta_bps` / `end_delta_bps`.","required":["startSecs","endSecs","startDeltaBps","endDeltaBps","points"],"properties":{"endDeltaBps":{"type":"integer","format":"int32"},"endSecs":{"type":"integer","format":"int64","minimum":0},"points":{"type":"array","items":{"$ref":"#/components/schemas/CurvePoint"}},"startDeltaBps":{"type":"integer","format":"int32"},"startSecs":{"type":"integer","format":"int64","minimum":0}}},"CurvePoint":{"type":"object","description":"A single knot in a [`SpreadCurve`], binding a wall-clock time to a delta in basis points.","required":["timeSecs","deltaBps"],"properties":{"deltaBps":{"type":"integer","format":"int32"},"timeSecs":{"type":"integer","format":"int64","minimum":0}}},"ErrorResponsePayload":{"type":"object","description":"Structured error response returned by the API","required":["code","message"],"properties":{"code":{"type":"string","description":"Error code in SNAKE_CASE format"},"inner":{"type":["array","null"],"items":{"$ref":"#/components/schemas/InnerErrorResponsePayload"},"description":"Optional array of nested errors (only present for validation errors)"},"message":{"type":"string","description":"Human-readable error message"}}},"InnerErrorResponsePayload":{"type":"object","description":"Individual error within a validation error response","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}
```

## POST /api/eip712/remove\_liquidity

> Submit an EIP-712 signed liquidity withdrawal intent

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"tags":[{"name":"turbine","description":"Turbine API endpoints"}],"paths":{"/api/eip712/remove_liquidity":{"post":{"tags":["turbine"],"summary":"Submit an EIP-712 signed liquidity withdrawal intent","operationId":"remove_liquidity_eip712","requestBody":{"description":"An LP withdrawal intent and the Permit2 transfer permit for the LP token it burns.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Eip712RequestBody_RemoveLiquidityEip712"}}},"required":true},"responses":{"200":{"description":"Liquidity intent successfully submitted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoveLiquidityResponse"}}}},"400":{"description":"Invalid intent, deadline, or pool","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}},"401":{"description":"Invalid signature or signer mismatch","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}},"409":{"description":"Nonce already used or reserved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}}}}}},"components":{"schemas":{"Eip712RequestBody_RemoveLiquidityEip712":{"type":"object","description":"EIP-712 request body: the semantic payload plus its auth block. The EIP-712\nsignature covers the payload's typed struct together with `nonce` and `deadline`.","required":["payload","auth"],"properties":{"auth":{"$ref":"#/components/schemas/AuthBlock"},"payload":{"allOf":[{"$ref":"#/components/schemas/RemoveLiquidity"}],"description":"Inner payload of remove_liquidity endpoint."}}},"AuthBlock":{"type":"object","description":"Auth block carried by every EIP-712 request body.","required":["signer","nonce","deadline","signature"],"properties":{"deadline":{"type":"integer","format":"int64","description":"Unix seconds; not more than `maxSignatureLifetimeS` (from the config endpoint) into the\nfuture.","minimum":0},"nonce":{"type":"string","description":"Random per-request value, unique per signer among un-expired requests."},"signature":{"$ref":"#/components/schemas/PermitSignature"},"signer":{"type":"string","format":"address","description":"Claimed signer. Must match the recovered address."}}},"PermitSignature":{"type":"object","required":["r","s","yParity"],"properties":{"r":{"type":"string","format":"uint256"},"s":{"type":"string","format":"uint256"},"yParity":{"type":"boolean","format":"boolean"}}},"RemoveLiquidity":{"type":"object","description":"Represents a liquidity removal with SignatureTransfer permit functionality.\n\nThis struct encapsulates all necessary information needed to submit a new intent to remove\nliquidity to the system, including the intent and SignatureTransfer permit data for approvals.\nUser authentication is handled via session-based SIWE verification.\n\n# Fields\n* `remove_liquidity` The intent to remove liquidity\n* `permit_lp_token` The SignatureTransfer permit and signature for the LP token","required":["removeLiquidity","permitLpToken"],"properties":{"permitLpToken":{"$ref":"#/components/schemas/SignedSignatureTransferDTO"},"removeLiquidity":{"$ref":"#/components/schemas/RemoveLiquidityIntent"}}},"SignedSignatureTransferDTO":{"type":"object","description":"Signed single SignatureTransfer permit (for remove liquidity LP token transfer)","required":["permit","signature"],"properties":{"permit":{"$ref":"#/components/schemas/PermitTransferFrom"},"signature":{"$ref":"#/components/schemas/PermitSignature"}}},"PermitTransferFrom":{"type":"object","description":"DTO for ISignatureTransfer.PermitTransferFrom - single token transfer authorization","required":["permitted","nonce","deadline"],"properties":{"deadline":{"type":"string","format":"uint256"},"nonce":{"type":"string","format":"uint256"},"permitted":{"$ref":"#/components/schemas/TokenPermissions"}}},"TokenPermissions":{"type":"object","description":"Token permissions for ISignatureTransfer permits.\nUsed in PermitTransferFrom and PermitBatchTransferFrom.","required":["token","amount"],"properties":{"amount":{"type":"string","format":"uint256","description":"The maximum amount that can be transferred"},"token":{"type":"string","format":"address","description":"The token address"}}},"RemoveLiquidityIntent":{"type":"object","required":["owner","token0","token1","fee","lpToken","lpTokenAmount","salt"],"properties":{"fee":{"type":"integer","format":"uint32","description":"The fee for the pool","minimum":0},"lpToken":{"type":"string","format":"address","description":"The LP token of the pool"},"lpTokenAmount":{"type":"string","format":"uint256","description":"The amount of LP tokens to burn"},"owner":{"type":"string","format":"address","description":"The address of the user submitting the intent"},"salt":{"type":"string","format":"bytes32","description":"Used to differentiate between intents with the same parameters"},"token0":{"type":"string","format":"address","description":"The token0 of the pool. Tokens must be sorted ascending by address so that\n`token0 < token1`; unordered intents are rejected."},"token1":{"type":"string","format":"address","description":"The token1 of the pool. Must be greater than `token0` (see `token0`)."}}},"RemoveLiquidityResponse":{"type":"object","description":"Response returned after successfully submitting an intent to the system.\nContains the unique identifier assigned to the submitted intent.\n\n# Fields\n* `hash` - Unique identifier assigned to the submitted intent","required":["intentHash"],"properties":{"intentHash":{"type":"string","format":"bytes32"}}},"ErrorResponsePayload":{"type":"object","description":"Structured error response returned by the API","required":["code","message"],"properties":{"code":{"type":"string","description":"Error code in SNAKE_CASE format"},"inner":{"type":["array","null"],"items":{"$ref":"#/components/schemas/InnerErrorResponsePayload"},"description":"Optional array of nested errors (only present for validation errors)"},"message":{"type":"string","description":"Human-readable error message"}}},"InnerErrorResponsePayload":{"type":"object","description":"Individual error within a validation error response","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}
```

## POST /api/liquidity\_intent\_states

> Get statuses for multiple liquidity intents

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"tags":[{"name":"turbine","description":"Turbine API endpoints"}],"paths":{"/api/liquidity_intent_states":{"post":{"tags":["turbine"],"summary":"Get statuses for multiple liquidity intents","operationId":"get_liquidity_intent_states","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetLiquidityIntentStates"}}},"required":true},"responses":{"200":{"description":"Liquidity intent states retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/LiquidityIntentState"}}}}},"401":{"description":"User not authorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}}}}}},"components":{"schemas":{"GetLiquidityIntentStates":{"type":"object","required":["intentHashes"],"properties":{"intentHashes":{"type":"array","items":{"type":"string","format":"bytes32"}}}},"LiquidityIntentState":{"type":"object","required":["hash","intent","status"],"properties":{"hash":{"type":"string","format":"bytes32"},"intent":{"type":"object"},"status":{"$ref":"#/components/schemas/LiquidityIntentStatus"}}},"LiquidityIntentStatus":{"type":"string","enum":["Pending","Invalid","Expired","Executed","PendingCancellation","Canceled"]},"ErrorResponsePayload":{"type":"object","description":"Structured error response returned by the API","required":["code","message"],"properties":{"code":{"type":"string","description":"Error code in SNAKE_CASE format"},"inner":{"type":["array","null"],"items":{"$ref":"#/components/schemas/InnerErrorResponsePayload"},"description":"Optional array of nested errors (only present for validation errors)"},"message":{"type":"string","description":"Human-readable error message"}}},"InnerErrorResponsePayload":{"type":"object","description":"Individual error within a validation error response","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}
```

## POST /api/logout

> Logout and clear all sessions for the current user

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"tags":[{"name":"turbine","description":"Turbine API endpoints"}],"paths":{"/api/logout":{"post":{"tags":["turbine"],"summary":"Logout and clear all sessions for the current user","operationId":"logout","responses":{"200":{"description":"Successfully logged out, all sessions cleared"},"400":{"description":"Invalid session state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}},"401":{"description":"User not authorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}}}}}},"components":{"schemas":{"ErrorResponsePayload":{"type":"object","description":"Structured error response returned by the API","required":["code","message"],"properties":{"code":{"type":"string","description":"Error code in SNAKE_CASE format"},"inner":{"type":["array","null"],"items":{"$ref":"#/components/schemas/InnerErrorResponsePayload"},"description":"Optional array of nested errors (only present for validation errors)"},"message":{"type":"string","description":"Human-readable error message"}}},"InnerErrorResponsePayload":{"type":"object","description":"Individual error within a validation error response","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}
```

## GET /api/me

> Get current user session information

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"tags":[{"name":"turbine","description":"Turbine API endpoints"}],"paths":{"/api/me":{"get":{"tags":["turbine"],"summary":"Get current user session information","operationId":"me","responses":{"200":{"description":"User session information retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeResponse"}}}},"400":{"description":"Invalid session state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}},"401":{"description":"User not authorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}}}}}},"components":{"schemas":{"MeResponse":{"type":"object","required":["address","authenticated"],"properties":{"address":{"type":"string","format":"address"},"authenticated":{"type":"boolean"}}},"ErrorResponsePayload":{"type":"object","description":"Structured error response returned by the API","required":["code","message"],"properties":{"code":{"type":"string","description":"Error code in SNAKE_CASE format"},"inner":{"type":["array","null"],"items":{"$ref":"#/components/schemas/InnerErrorResponsePayload"},"description":"Optional array of nested errors (only present for validation errors)"},"message":{"type":"string","description":"Human-readable error message"}}},"InnerErrorResponsePayload":{"type":"object","description":"Individual error within a validation error response","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}
```

## POST /api/nonce

> Generate a nonce for the SIWE flow.

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"tags":[{"name":"turbine","description":"Turbine API endpoints"}],"paths":{"/api/nonce":{"post":{"tags":["turbine"],"summary":"Generate a nonce for the SIWE flow.","operationId":"nonce","responses":{"200":{"description":"Nonce generated successfully","content":{"text/plain":{"schema":{"type":"string"}}}},"400":{"description":"Cannot generate nonce: user is already authenticated or nonce already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}}}}}},"components":{"schemas":{"ErrorResponsePayload":{"type":"object","description":"Structured error response returned by the API","required":["code","message"],"properties":{"code":{"type":"string","description":"Error code in SNAKE_CASE format"},"inner":{"type":["array","null"],"items":{"$ref":"#/components/schemas/InnerErrorResponsePayload"},"description":"Optional array of nested errors (only present for validation errors)"},"message":{"type":"string","description":"Human-readable error message"}}},"InnerErrorResponsePayload":{"type":"object","description":"Individual error within a validation error response","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}
```

## POST /api/order\_fees

>

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"tags":[{"name":"turbine","description":"Turbine API endpoints"}],"paths":{"/api/order_fees":{"post":{"tags":["turbine"],"operationId":"get_order_fees","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderIntent"}}},"required":true},"responses":{"200":{"description":"Order fees retrieved successfully","content":{"text/plain":{"schema":{"type":"string"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}}}}}},"components":{"schemas":{"OrderIntent":{"type":"object","description":"The swap intent for a user","required":["owner","sellToken","buyToken","sellAmount","minBuyAmount","spreadCurve","startTime","endTime","partialFill","callData","callDataTarget","calldataSellAmountOffset","calldataBuyAmountOffset","salt"],"properties":{"buyToken":{"type":"string","format":"address","description":"The token being bought"},"callData":{"type":"string","format":"bytes","description":"Optional call data for smart orders, allowing custom routing"},"callDataTarget":{"type":"string","format":"address","description":"The address of the target contract for the calldata"},"calldataBuyAmountOffset":{"type":"integer","format":"uint64","description":"The offset of the buy amount in the calldata\nIf Some, used to update the calldata with the actual buy amount","minimum":0},"calldataSellAmountOffset":{"type":"integer","format":"uint64","description":"The offset of the sell amount in the calldata\nIf Some, used to update the calldata with the actual sell amount","minimum":0},"endTime":{"type":"integer","format":"uint256","description":"Block timestamp until when the order is valid.","minimum":0},"minBuyAmount":{"type":"string","format":"uint256","description":"The minimum amount of tokens being bought, effectively defining limit price."},"owner":{"type":"string","format":"address","description":"The address of the user submitting the order"},"partialFill":{"type":"boolean","format":"boolean","description":"Flag allowing partial fills"},"salt":{"type":"string","format":"bytes32","description":"The offset of the buy amount in the calldata\nUsed to differentiate between orders with the same parameters"},"sellAmount":{"type":"string","format":"uint196","description":"The amount of tokens being sold"},"sellToken":{"type":"string","format":"address","description":"The token being sold"},"spreadCurve":{"$ref":"#/components/schemas/SpreadCurveDto","description":"A piecewise-linear spread curve mapping a fraction of the order window to a mid-price\ndelta in basis points. Each interior point's `windowBps` is a fraction of the order\nduration in basis points (5_000 = halfway)."},"startTime":{"type":"integer","format":"uint256","description":"Block timestamp since when the order is valid","minimum":0}}},"SpreadCurveDto":{"type":"object","description":"Piecewise-linear spread curve. Interior `points` express knot positions as a fraction\nof the order's `[start_time, end_time]` window in basis points (`windowBps`), so the\ncurve is portable across orders of different durations.","required":["startDeltaBps","endDeltaBps"],"properties":{"endDeltaBps":{"type":"integer","format":"int32","description":"Mid-price delta at the order's `end_time`, in basis points."},"points":{"type":"array","items":{"$ref":"#/components/schemas/CurvePointDto"},"description":"Interior knots. Each `windowBps` must lie in `[1, 9_999]` and values must be\nstrictly increasing across `points`. Defaults to `[]` if absent, yielding a single\nlinear segment from `startDeltaBps` to `endDeltaBps`."},"startDeltaBps":{"type":"integer","format":"int32","description":"Mid-price delta at the order's `start_time`, in basis points."}}},"CurvePointDto":{"type":"object","description":"A single interior knot in a [`SpreadCurveDto`].","required":["windowBps","deltaBps"],"properties":{"deltaBps":{"type":"integer","format":"int32","description":"Mid-price delta in basis points at this knot."},"windowBps":{"type":"integer","format":"int32","description":"Knot position as a fraction of the order window in basis points. Must lie in\n`(0, 10_000)` exclusive — `5_000` is the order midpoint.","maximum":9999,"minimum":1}}},"ErrorResponsePayload":{"type":"object","description":"Structured error response returned by the API","required":["code","message"],"properties":{"code":{"type":"string","description":"Error code in SNAKE_CASE format"},"inner":{"type":["array","null"],"items":{"$ref":"#/components/schemas/InnerErrorResponsePayload"},"description":"Optional array of nested errors (only present for validation errors)"},"message":{"type":"string","description":"Human-readable error message"}}},"InnerErrorResponsePayload":{"type":"object","description":"Individual error within a validation error response","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}
```

## GET /api/order\_state

>

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"tags":[{"name":"turbine","description":"Turbine API endpoints"}],"paths":{"/api/order_state":{"get":{"tags":["turbine"],"operationId":"get_order_state","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetOrderStatus"}}},"required":true},"responses":{"200":{"description":"Order state retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderState"}}}},"401":{"description":"User not authorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}},"404":{"description":"Order not found or not owned by caller","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}}}}}},"components":{"schemas":{"GetOrderStatus":{"type":"object","required":["orderHash"],"properties":{"orderHash":{"type":"string","format":"bytes32"}}},"OrderState":{"type":"object","required":["hash","owner","status","execution","orderDetails"],"properties":{"execution":{"type":"array","items":{"$ref":"#/components/schemas/OrderExecution"}},"hash":{"type":"string","format":"bytes32"},"orderDetails":{"$ref":"#/components/schemas/OrderDetails"},"owner":{"type":"string","format":"address"},"status":{"$ref":"#/components/schemas/OrderStatus"}}},"OrderExecution":{"type":"object","required":["blockNumber","soldAmount","boughtAmount","surplusBuyAmount"],"properties":{"batchId":{"type":["integer","null"],"format":"int64","minimum":0},"blockNumber":{"type":"integer","format":"int64","minimum":0},"boughtAmount":{"type":"string","format":"uint256"},"midPrice":{"type":"object","description":"Mid-price for the order's (sell → buy) pair at the clearing batch."},"soldAmount":{"type":"string","format":"uint256"},"surplusBuyAmount":{"type":"string","format":"uint256"},"txHash":{"type":["string","null"],"format":"bytes32"}}},"OrderDetails":{"type":"object","description":"Display subset of an `Order`, copied verbatim onto `OrderState` at insertion time.\n\nCarrying these fields on the state record keeps display detail available even after the\nunderlying `Order` is dropped at terminal-status transition.","required":["sellToken","buyToken","sellAmount","limitPrice","startTime","endTime","spreadCurve","createdTimestamp"],"properties":{"annotations":{"$ref":"#/components/schemas/OrderAnnotations","description":"User-provided annotations, snapshotted at insertion. Defaults for order\nhistory entries persisted before annotations existed."},"buyToken":{"type":"string","format":"address"},"createdTimestamp":{"type":"string","format":"date-time"},"endTime":{"type":"string","format":"uint256"},"limitPrice":{"type":"object"},"sellAmount":{"type":"string","format":"uint256"},"sellToken":{"type":"string","format":"address"},"spreadCurve":{"$ref":"#/components/schemas/SpreadCurve","description":"Piecewise-linear curve defining the allowed mid-price deviation over the order\nwindow. Snapshotted from the submitted [`Order`] at insertion so the curve remains\navailable after the underlying `Order` is dropped."},"startTime":{"type":"string","format":"uint256"}}},"OrderAnnotations":{"type":"object","description":"Additional data attached to an order, provided by the user. Purely informational —\nnot part of the signed order and not used by the protocol.","properties":{"spreadAtSubmissionHbp":{"type":["integer","null"],"format":"int64","description":"The market spread (in hundredths of a basis point, 10_000 hbp = 1%) quoted to the\nuser at submission time, as returned by `/api/quote` (`amm_spread_hbp`).","minimum":0}}},"SpreadCurve":{"type":"object","description":"A piecewise-linear curve mapping wall-clock time to a mid-price delta in basis points.\n\nThe curve carries its own `[start_secs, end_secs]` window: the order's `start_time`\nand `end_time` must match these for validation to succeed (enforced at the API layer).\nBetween interior knots, `delta_bps` is interpolated linearly using i64 arithmetic.\nOutside the curve window the value clamps to `start_delta_bps` / `end_delta_bps`.","required":["startSecs","endSecs","startDeltaBps","endDeltaBps","points"],"properties":{"endDeltaBps":{"type":"integer","format":"int32"},"endSecs":{"type":"integer","format":"int64","minimum":0},"points":{"type":"array","items":{"$ref":"#/components/schemas/CurvePoint"}},"startDeltaBps":{"type":"integer","format":"int32"},"startSecs":{"type":"integer","format":"int64","minimum":0}}},"CurvePoint":{"type":"object","description":"A single knot in a [`SpreadCurve`], binding a wall-clock time to a delta in basis points.","required":["timeSecs","deltaBps"],"properties":{"deltaBps":{"type":"integer","format":"int32"},"timeSecs":{"type":"integer","format":"int64","minimum":0}}},"OrderStatus":{"type":"string","enum":["Active","Invalid","Expired","Filled","PendingCancellation","Canceled","Incompatible"]},"ErrorResponsePayload":{"type":"object","description":"Structured error response returned by the API","required":["code","message"],"properties":{"code":{"type":"string","description":"Error code in SNAKE_CASE format"},"inner":{"type":["array","null"],"items":{"$ref":"#/components/schemas/InnerErrorResponsePayload"},"description":"Optional array of nested errors (only present for validation errors)"},"message":{"type":"string","description":"Human-readable error message"}}},"InnerErrorResponsePayload":{"type":"object","description":"Individual error within a validation error response","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}
```

## POST /api/order\_states

> Get states for multiple orders

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"tags":[{"name":"turbine","description":"Turbine API endpoints"}],"paths":{"/api/order_states":{"post":{"tags":["turbine"],"summary":"Get states for multiple orders","operationId":"get_order_states","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetOrderStates"}}},"required":true},"responses":{"200":{"description":"Order states retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OrderState"}}}}},"401":{"description":"User not authorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}}}}}},"components":{"schemas":{"GetOrderStates":{"type":"object","required":["orderHashes"],"properties":{"orderHashes":{"type":"array","items":{"type":"string","format":"bytes32"}}}},"OrderState":{"type":"object","required":["hash","owner","status","execution","orderDetails"],"properties":{"execution":{"type":"array","items":{"$ref":"#/components/schemas/OrderExecution"}},"hash":{"type":"string","format":"bytes32"},"orderDetails":{"$ref":"#/components/schemas/OrderDetails"},"owner":{"type":"string","format":"address"},"status":{"$ref":"#/components/schemas/OrderStatus"}}},"OrderExecution":{"type":"object","required":["blockNumber","soldAmount","boughtAmount","surplusBuyAmount"],"properties":{"batchId":{"type":["integer","null"],"format":"int64","minimum":0},"blockNumber":{"type":"integer","format":"int64","minimum":0},"boughtAmount":{"type":"string","format":"uint256"},"midPrice":{"type":"object","description":"Mid-price for the order's (sell → buy) pair at the clearing batch."},"soldAmount":{"type":"string","format":"uint256"},"surplusBuyAmount":{"type":"string","format":"uint256"},"txHash":{"type":["string","null"],"format":"bytes32"}}},"OrderDetails":{"type":"object","description":"Display subset of an `Order`, copied verbatim onto `OrderState` at insertion time.\n\nCarrying these fields on the state record keeps display detail available even after the\nunderlying `Order` is dropped at terminal-status transition.","required":["sellToken","buyToken","sellAmount","limitPrice","startTime","endTime","spreadCurve","createdTimestamp"],"properties":{"annotations":{"$ref":"#/components/schemas/OrderAnnotations","description":"User-provided annotations, snapshotted at insertion. Defaults for order\nhistory entries persisted before annotations existed."},"buyToken":{"type":"string","format":"address"},"createdTimestamp":{"type":"string","format":"date-time"},"endTime":{"type":"string","format":"uint256"},"limitPrice":{"type":"object"},"sellAmount":{"type":"string","format":"uint256"},"sellToken":{"type":"string","format":"address"},"spreadCurve":{"$ref":"#/components/schemas/SpreadCurve","description":"Piecewise-linear curve defining the allowed mid-price deviation over the order\nwindow. Snapshotted from the submitted [`Order`] at insertion so the curve remains\navailable after the underlying `Order` is dropped."},"startTime":{"type":"string","format":"uint256"}}},"OrderAnnotations":{"type":"object","description":"Additional data attached to an order, provided by the user. Purely informational —\nnot part of the signed order and not used by the protocol.","properties":{"spreadAtSubmissionHbp":{"type":["integer","null"],"format":"int64","description":"The market spread (in hundredths of a basis point, 10_000 hbp = 1%) quoted to the\nuser at submission time, as returned by `/api/quote` (`amm_spread_hbp`).","minimum":0}}},"SpreadCurve":{"type":"object","description":"A piecewise-linear curve mapping wall-clock time to a mid-price delta in basis points.\n\nThe curve carries its own `[start_secs, end_secs]` window: the order's `start_time`\nand `end_time` must match these for validation to succeed (enforced at the API layer).\nBetween interior knots, `delta_bps` is interpolated linearly using i64 arithmetic.\nOutside the curve window the value clamps to `start_delta_bps` / `end_delta_bps`.","required":["startSecs","endSecs","startDeltaBps","endDeltaBps","points"],"properties":{"endDeltaBps":{"type":"integer","format":"int32"},"endSecs":{"type":"integer","format":"int64","minimum":0},"points":{"type":"array","items":{"$ref":"#/components/schemas/CurvePoint"}},"startDeltaBps":{"type":"integer","format":"int32"},"startSecs":{"type":"integer","format":"int64","minimum":0}}},"CurvePoint":{"type":"object","description":"A single knot in a [`SpreadCurve`], binding a wall-clock time to a delta in basis points.","required":["timeSecs","deltaBps"],"properties":{"deltaBps":{"type":"integer","format":"int32"},"timeSecs":{"type":"integer","format":"int64","minimum":0}}},"OrderStatus":{"type":"string","enum":["Active","Invalid","Expired","Filled","PendingCancellation","Canceled","Incompatible"]},"ErrorResponsePayload":{"type":"object","description":"Structured error response returned by the API","required":["code","message"],"properties":{"code":{"type":"string","description":"Error code in SNAKE_CASE format"},"inner":{"type":["array","null"],"items":{"$ref":"#/components/schemas/InnerErrorResponsePayload"},"description":"Optional array of nested errors (only present for validation errors)"},"message":{"type":"string","description":"Human-readable error message"}}},"InnerErrorResponsePayload":{"type":"object","description":"Individual error within a validation error response","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}
```

## List orders for the authenticated user.

> Returns active and terminal orders in a single time-sorted, cursor-paginated list. Each entry\
> carries full display detail via \`orderDetails\`, regardless of status. Supports multi-value\
> \`hash\` and \`status\` filters; when \`hash\` is provided, pagination is skipped.

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"tags":[{"name":"turbine","description":"Turbine API endpoints"}],"paths":{"/api/orders":{"get":{"tags":["turbine"],"summary":"List orders for the authenticated user.","description":"Returns active and terminal orders in a single time-sorted, cursor-paginated list. Each entry\ncarries full display detail via `orderDetails`, regardless of status. Supports multi-value\n`hash` and `status` filters; when `hash` is provided, pagination is skipped.","operationId":"get_orders","parameters":[{"name":"hash","in":"query","description":"Direct lookup by order hash, comma-separated. Max 30 per request. When provided,\npagination is skipped.","required":false,"schema":{"type":"string","format":"bytes32"}},{"name":"status","in":"query","description":"Filter by order status, comma-separated. Multiple values are OR'd.","required":false,"schema":{"type":"string"}},{"name":"cursor","in":"query","description":"Opaque cursor from a previous response. Ignored when `hash` is provided.","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","description":"Max orders per page (default 50, cap 200). Ignored when `hash` is provided.","required":false,"schema":{"type":"integer","format":"int32","minimum":0}}],"responses":{"200":{"description":"Orders retrieved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrdersResponse"}}}},"400":{"description":"Invalid query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}},"401":{"description":"User not authorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}}}}}},"components":{"schemas":{"OrdersResponse":{"type":"object","description":"Response body for `GET /api/orders`.","required":["orders","hasMore"],"properties":{"cursor":{"type":["string","null"],"description":"Opaque token to pass back as `cursor` in the next request. `null` on the last page."},"hasMore":{"type":"boolean","description":"Mirrors `cursor.is_some()`."},"orders":{"type":"array","items":{"$ref":"#/components/schemas/OrderState"}}}},"OrderState":{"type":"object","required":["hash","owner","status","execution","orderDetails"],"properties":{"execution":{"type":"array","items":{"$ref":"#/components/schemas/OrderExecution"}},"hash":{"type":"string","format":"bytes32"},"orderDetails":{"$ref":"#/components/schemas/OrderDetails"},"owner":{"type":"string","format":"address"},"status":{"$ref":"#/components/schemas/OrderStatus"}}},"OrderExecution":{"type":"object","required":["blockNumber","soldAmount","boughtAmount","surplusBuyAmount"],"properties":{"batchId":{"type":["integer","null"],"format":"int64","minimum":0},"blockNumber":{"type":"integer","format":"int64","minimum":0},"boughtAmount":{"type":"string","format":"uint256"},"midPrice":{"type":"object","description":"Mid-price for the order's (sell → buy) pair at the clearing batch."},"soldAmount":{"type":"string","format":"uint256"},"surplusBuyAmount":{"type":"string","format":"uint256"},"txHash":{"type":["string","null"],"format":"bytes32"}}},"OrderDetails":{"type":"object","description":"Display subset of an `Order`, copied verbatim onto `OrderState` at insertion time.\n\nCarrying these fields on the state record keeps display detail available even after the\nunderlying `Order` is dropped at terminal-status transition.","required":["sellToken","buyToken","sellAmount","limitPrice","startTime","endTime","spreadCurve","createdTimestamp"],"properties":{"annotations":{"$ref":"#/components/schemas/OrderAnnotations","description":"User-provided annotations, snapshotted at insertion. Defaults for order\nhistory entries persisted before annotations existed."},"buyToken":{"type":"string","format":"address"},"createdTimestamp":{"type":"string","format":"date-time"},"endTime":{"type":"string","format":"uint256"},"limitPrice":{"type":"object"},"sellAmount":{"type":"string","format":"uint256"},"sellToken":{"type":"string","format":"address"},"spreadCurve":{"$ref":"#/components/schemas/SpreadCurve","description":"Piecewise-linear curve defining the allowed mid-price deviation over the order\nwindow. Snapshotted from the submitted [`Order`] at insertion so the curve remains\navailable after the underlying `Order` is dropped."},"startTime":{"type":"string","format":"uint256"}}},"OrderAnnotations":{"type":"object","description":"Additional data attached to an order, provided by the user. Purely informational —\nnot part of the signed order and not used by the protocol.","properties":{"spreadAtSubmissionHbp":{"type":["integer","null"],"format":"int64","description":"The market spread (in hundredths of a basis point, 10_000 hbp = 1%) quoted to the\nuser at submission time, as returned by `/api/quote` (`amm_spread_hbp`).","minimum":0}}},"SpreadCurve":{"type":"object","description":"A piecewise-linear curve mapping wall-clock time to a mid-price delta in basis points.\n\nThe curve carries its own `[start_secs, end_secs]` window: the order's `start_time`\nand `end_time` must match these for validation to succeed (enforced at the API layer).\nBetween interior knots, `delta_bps` is interpolated linearly using i64 arithmetic.\nOutside the curve window the value clamps to `start_delta_bps` / `end_delta_bps`.","required":["startSecs","endSecs","startDeltaBps","endDeltaBps","points"],"properties":{"endDeltaBps":{"type":"integer","format":"int32"},"endSecs":{"type":"integer","format":"int64","minimum":0},"points":{"type":"array","items":{"$ref":"#/components/schemas/CurvePoint"}},"startDeltaBps":{"type":"integer","format":"int32"},"startSecs":{"type":"integer","format":"int64","minimum":0}}},"CurvePoint":{"type":"object","description":"A single knot in a [`SpreadCurve`], binding a wall-clock time to a delta in basis points.","required":["timeSecs","deltaBps"],"properties":{"deltaBps":{"type":"integer","format":"int32"},"timeSecs":{"type":"integer","format":"int64","minimum":0}}},"OrderStatus":{"type":"string","enum":["Active","Invalid","Expired","Filled","PendingCancellation","Canceled","Incompatible"]},"ErrorResponsePayload":{"type":"object","description":"Structured error response returned by the API","required":["code","message"],"properties":{"code":{"type":"string","description":"Error code in SNAKE_CASE format"},"inner":{"type":["array","null"],"items":{"$ref":"#/components/schemas/InnerErrorResponsePayload"},"description":"Optional array of nested errors (only present for validation errors)"},"message":{"type":"string","description":"Human-readable error message"}}},"InnerErrorResponsePayload":{"type":"object","description":"Individual error within a validation error response","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}
```

## Request a single-order quote from the Fynd solver.

> Public, unauthenticated. Returns a trimmed view of the Fynd response\
> (amounts + gas). Any non-success status or transport error is returned as\
> a 502 with a descriptive message. If Quoting is disabled, 501 is returned.

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"tags":[{"name":"turbine","description":"Turbine API endpoints"}],"paths":{"/api/quote":{"post":{"tags":["turbine"],"summary":"Request a single-order quote from the Fynd solver.","description":"Public, unauthenticated. Returns a trimmed view of the Fynd response\n(amounts + gas). Any non-success status or transport error is returned as\na 502 with a descriptive message. If Quoting is disabled, 501 is returned.","operationId":"quote","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteRequest"}}},"required":true},"responses":{"200":{"description":"Quote successful","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteResponse"}}}},"501":{"description":"Fynd solver not enabled on this instance","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}},"502":{"description":"Quote failed upstream","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}}}}}},"components":{"schemas":{"QuoteRequest":{"type":"object","description":"Minimal inputs for a Fynd passthrough quote.","required":["sellToken","buyToken","sellAmount"],"properties":{"buyToken":{"type":"string","format":"address"},"sellAmount":{"type":"string","format":"uint256"},"sellToken":{"type":"string","format":"address"}}},"QuoteResponse":{"type":"object","description":"Pared-down Fynd quote: amounts, gas, and a reference midprice.","required":["sellToken","buyToken","sellAmount","buyAmount","ammSpreadHbp","spread","midPrice","fee"],"properties":{"ammSpreadHbp":{"type":"integer","format":"int64","description":"DEX-derived spread in hundredths of a basis point. 10_000 hbp = 1%.\nDeprecated: use `spread` instead.","minimum":0},"buyAmount":{"type":"string","format":"uint256"},"buyToken":{"type":"string","format":"address"},"fee":{"$ref":"#/components/schemas/QuoteFee","description":"Protocol fee for this trade."},"midPrice":{"$ref":"#/components/schemas/MidPrice","description":"Oracle-aggregated reference midprice for `sell_token → buy_token`."},"sellAmount":{"type":"string","format":"uint256"},"sellToken":{"type":"string","format":"address"},"spread":{"$ref":"#/components/schemas/QuoteResponseSpread"}}},"QuoteFee":{"type":"object","description":"Protocol fee for a quoted trade.\n\n`total_amount` is in `buy_token` atomic units, computed against the\ngross `buy_amount` Fynd quoted.","required":["totalHbp","totalAmount"],"properties":{"totalAmount":{"type":"string","format":"uint256","description":"Absolute fee in `buy_token` atomic units."},"totalHbp":{"type":"integer","format":"int64","description":"Sum of fee components in hundredths of a basis point. 10_000 hbp = 1%.","minimum":0}}},"MidPrice":{"type":"object","description":"Reference midprice for a token pair, expressed as the rational\n`numerator / denominator` in atomic units (buy-wei per sell-wei).","required":["numerator","denominator"],"properties":{"denominator":{"type":"string","format":"uint256"},"numerator":{"type":"string","format":"uint256"}}},"QuoteResponseSpread":{"type":"object","required":["ammSpreadHbp","gasHbp"],"properties":{"ammSpreadHbp":{"type":"integer","format":"int64","description":"DEX-derived spread in hundredths of a basis point. 10_000 hbp = 1%.","minimum":0},"gasHbp":{"type":"integer","format":"int64","description":"Extra spread due to gas cost. Add to `amm_spread_hbp` to get the spread accounting for gas.","minimum":0}}},"ErrorResponsePayload":{"type":"object","description":"Structured error response returned by the API","required":["code","message"],"properties":{"code":{"type":"string","description":"Error code in SNAKE_CASE format"},"inner":{"type":["array","null"],"items":{"$ref":"#/components/schemas/InnerErrorResponsePayload"},"description":"Optional array of nested errors (only present for validation errors)"},"message":{"type":"string","description":"Human-readable error message"}}},"InnerErrorResponsePayload":{"type":"object","description":"Individual error within a validation error response","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}
```

## POST /api/remove\_liquidity

> Submit a new liquidity removal request to the system

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"tags":[{"name":"turbine","description":"Turbine API endpoints"}],"paths":{"/api/remove_liquidity":{"post":{"tags":["turbine"],"summary":"Submit a new liquidity removal request to the system","operationId":"remove_liquidity","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoveLiquidity"}}},"required":true},"responses":{"200":{"description":"Liquidity intent successfully submitted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoveLiquidityResponse"}}}},"400":{"description":"Invalid intent, signatures, or session state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}},"401":{"description":"User not authorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}}}}}},"components":{"schemas":{"RemoveLiquidity":{"type":"object","description":"Represents a liquidity removal with SignatureTransfer permit functionality.\n\nThis struct encapsulates all necessary information needed to submit a new intent to remove\nliquidity to the system, including the intent and SignatureTransfer permit data for approvals.\nUser authentication is handled via session-based SIWE verification.\n\n# Fields\n* `remove_liquidity` The intent to remove liquidity\n* `permit_lp_token` The SignatureTransfer permit and signature for the LP token","required":["removeLiquidity","permitLpToken"],"properties":{"permitLpToken":{"$ref":"#/components/schemas/SignedSignatureTransferDTO"},"removeLiquidity":{"$ref":"#/components/schemas/RemoveLiquidityIntent"}}},"SignedSignatureTransferDTO":{"type":"object","description":"Signed single SignatureTransfer permit (for remove liquidity LP token transfer)","required":["permit","signature"],"properties":{"permit":{"$ref":"#/components/schemas/PermitTransferFrom"},"signature":{"$ref":"#/components/schemas/PermitSignature"}}},"PermitTransferFrom":{"type":"object","description":"DTO for ISignatureTransfer.PermitTransferFrom - single token transfer authorization","required":["permitted","nonce","deadline"],"properties":{"deadline":{"type":"string","format":"uint256"},"nonce":{"type":"string","format":"uint256"},"permitted":{"$ref":"#/components/schemas/TokenPermissions"}}},"TokenPermissions":{"type":"object","description":"Token permissions for ISignatureTransfer permits.\nUsed in PermitTransferFrom and PermitBatchTransferFrom.","required":["token","amount"],"properties":{"amount":{"type":"string","format":"uint256","description":"The maximum amount that can be transferred"},"token":{"type":"string","format":"address","description":"The token address"}}},"PermitSignature":{"type":"object","required":["r","s","yParity"],"properties":{"r":{"type":"string","format":"uint256"},"s":{"type":"string","format":"uint256"},"yParity":{"type":"boolean","format":"boolean"}}},"RemoveLiquidityIntent":{"type":"object","required":["owner","token0","token1","fee","lpToken","lpTokenAmount","salt"],"properties":{"fee":{"type":"integer","format":"uint32","description":"The fee for the pool","minimum":0},"lpToken":{"type":"string","format":"address","description":"The LP token of the pool"},"lpTokenAmount":{"type":"string","format":"uint256","description":"The amount of LP tokens to burn"},"owner":{"type":"string","format":"address","description":"The address of the user submitting the intent"},"salt":{"type":"string","format":"bytes32","description":"Used to differentiate between intents with the same parameters"},"token0":{"type":"string","format":"address","description":"The token0 of the pool. Tokens must be sorted ascending by address so that\n`token0 < token1`; unordered intents are rejected."},"token1":{"type":"string","format":"address","description":"The token1 of the pool. Must be greater than `token0` (see `token0`)."}}},"RemoveLiquidityResponse":{"type":"object","description":"Response returned after successfully submitting an intent to the system.\nContains the unique identifier assigned to the submitted intent.\n\n# Fields\n* `hash` - Unique identifier assigned to the submitted intent","required":["intentHash"],"properties":{"intentHash":{"type":"string","format":"bytes32"}}},"ErrorResponsePayload":{"type":"object","description":"Structured error response returned by the API","required":["code","message"],"properties":{"code":{"type":"string","description":"Error code in SNAKE_CASE format"},"inner":{"type":["array","null"],"items":{"$ref":"#/components/schemas/InnerErrorResponsePayload"},"description":"Optional array of nested errors (only present for validation errors)"},"message":{"type":"string","description":"Human-readable error message"}}},"InnerErrorResponsePayload":{"type":"object","description":"Individual error within a validation error response","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}
```

## GET /api/status

> Get the status of the Turbine service

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"tags":[{"name":"turbine","description":"Turbine API endpoints"}],"paths":{"/api/status":{"get":{"tags":["turbine"],"summary":"Get the status of the Turbine service","operationId":"status","responses":{"200":{"description":"Service is running","content":{"text/plain":{"schema":{"type":"string"}}}}}}}}}
```

## Verify a SIWE message and create/update the session.

> Cycles the session id to prevent session fixation attacks.

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"tags":[{"name":"turbine","description":"Turbine API endpoints"}],"paths":{"/api/verify":{"post":{"tags":["turbine"],"summary":"Verify a SIWE message and create/update the session.","description":"Cycles the session id to prevent session fixation attacks.","operationId":"verify","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SiweVerify"}}},"required":true},"responses":{"200":{"description":"SIWE verification successful, session created"},"400":{"description":"SIWE verification failed or invalid session state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponsePayload"}}}}}}}},"components":{"schemas":{"SiweVerify":{"type":"object","required":["message","signature"],"properties":{"message":{"type":"string"},"signature":{"type":"string"}}},"ErrorResponsePayload":{"type":"object","description":"Structured error response returned by the API","required":["code","message"],"properties":{"code":{"type":"string","description":"Error code in SNAKE_CASE format"},"inner":{"type":["array","null"],"items":{"$ref":"#/components/schemas/InnerErrorResponsePayload"},"description":"Optional array of nested errors (only present for validation errors)"},"message":{"type":"string","description":"Human-readable error message"}}},"InnerErrorResponsePayload":{"type":"object","description":"Individual error within a validation error response","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}
```


# Models

## The AddLiquidity object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"AddLiquidity":{"type":"object","description":"Represents a liquidity addition with SignatureTransfer permit functionality.\n\nThis struct encapsulates all necessary information needed to submit a new intent to add\nliquidity to the system, including the intent and SignatureTransfer permit data for approvals.\nUser authentication is handled via session-based SIWE verification.\n\n# Fields\n* `add_liquidity` The intent to add liquidity\n* `permit_tokens` The batch SignatureTransfer permit and signature for token0 and token1","required":["addLiquidity","permitTokens"],"properties":{"addLiquidity":{"$ref":"#/components/schemas/AddLiquidityIntent"},"permitTokens":{"$ref":"#/components/schemas/SignedBatchSignatureTransferDTO"}}},"AddLiquidityIntent":{"type":"object","required":["owner","token0","token1","fee","token0Amount","token1Amount","exact","salt"],"properties":{"exact":{"type":"boolean","description":"Whether the user wants to provide exactly the specified amounts (and is willing to\npay a swap fee first) or would rather like to provide liquidity in current ratio of pool\nreserves (in such case the specified token amounts are treated as maximum amounts)."},"fee":{"type":"integer","format":"uint32","description":"The fee for the pool","minimum":0},"owner":{"type":"string","format":"address","description":"The address of the user submitting the intent"},"salt":{"type":"string","format":"bytes32","description":"Used to differentiate between intents with the same parameters"},"token0":{"type":"string","format":"address","description":"The token0 of the pool. Tokens must be sorted ascending by address so that\n`token0 < token1`; unordered intents are rejected."},"token0Amount":{"type":"string","format":"uint256","description":"The amount of token0 to provide. If `exact` is true, this is the exact amount of token0 to\nprovide. If `exact` is false, this is the maximum amount of token0 to provide."},"token1":{"type":"string","format":"address","description":"The token1 of the pool. Must be greater than `token0` (see `token0`)."},"token1Amount":{"type":"string","format":"uint256","description":"The amount of token1 to provide. If `exact` is true, this is the exact amount of token1 to\nprovide. If `exact` is false, this is the maximum amount of token1 to provide."}}},"SignedBatchSignatureTransferDTO":{"type":"object","description":"Signed batch SignatureTransfer permit (for add liquidity token0 and token1 transfer)","required":["permit","signature"],"properties":{"permit":{"$ref":"#/components/schemas/PermitBatchTransferFrom"},"signature":{"$ref":"#/components/schemas/PermitSignature"}}},"PermitBatchTransferFrom":{"type":"object","description":"DTO for ISignatureTransfer.PermitBatchTransferFrom - batch token transfer authorization","required":["permitted","nonce","deadline"],"properties":{"deadline":{"type":"string","format":"uint256"},"nonce":{"type":"string","format":"uint256"},"permitted":{"type":"array","items":{"$ref":"#/components/schemas/TokenPermissions"}}}},"TokenPermissions":{"type":"object","description":"Token permissions for ISignatureTransfer permits.\nUsed in PermitTransferFrom and PermitBatchTransferFrom.","required":["token","amount"],"properties":{"amount":{"type":"string","format":"uint256","description":"The maximum amount that can be transferred"},"token":{"type":"string","format":"address","description":"The token address"}}},"PermitSignature":{"type":"object","required":["r","s","yParity"],"properties":{"r":{"type":"string","format":"uint256"},"s":{"type":"string","format":"uint256"},"yParity":{"type":"boolean","format":"boolean"}}}}}}
```

## The AddLiquidityEip712 object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"AddLiquidityEip712":{"allOf":[{"$ref":"#/components/schemas/AddLiquidity"}],"description":"Inner payload of add_liquidity endpoint."},"AddLiquidity":{"type":"object","description":"Represents a liquidity addition with SignatureTransfer permit functionality.\n\nThis struct encapsulates all necessary information needed to submit a new intent to add\nliquidity to the system, including the intent and SignatureTransfer permit data for approvals.\nUser authentication is handled via session-based SIWE verification.\n\n# Fields\n* `add_liquidity` The intent to add liquidity\n* `permit_tokens` The batch SignatureTransfer permit and signature for token0 and token1","required":["addLiquidity","permitTokens"],"properties":{"addLiquidity":{"$ref":"#/components/schemas/AddLiquidityIntent"},"permitTokens":{"$ref":"#/components/schemas/SignedBatchSignatureTransferDTO"}}},"AddLiquidityIntent":{"type":"object","required":["owner","token0","token1","fee","token0Amount","token1Amount","exact","salt"],"properties":{"exact":{"type":"boolean","description":"Whether the user wants to provide exactly the specified amounts (and is willing to\npay a swap fee first) or would rather like to provide liquidity in current ratio of pool\nreserves (in such case the specified token amounts are treated as maximum amounts)."},"fee":{"type":"integer","format":"uint32","description":"The fee for the pool","minimum":0},"owner":{"type":"string","format":"address","description":"The address of the user submitting the intent"},"salt":{"type":"string","format":"bytes32","description":"Used to differentiate between intents with the same parameters"},"token0":{"type":"string","format":"address","description":"The token0 of the pool. Tokens must be sorted ascending by address so that\n`token0 < token1`; unordered intents are rejected."},"token0Amount":{"type":"string","format":"uint256","description":"The amount of token0 to provide. If `exact` is true, this is the exact amount of token0 to\nprovide. If `exact` is false, this is the maximum amount of token0 to provide."},"token1":{"type":"string","format":"address","description":"The token1 of the pool. Must be greater than `token0` (see `token0`)."},"token1Amount":{"type":"string","format":"uint256","description":"The amount of token1 to provide. If `exact` is true, this is the exact amount of token1 to\nprovide. If `exact` is false, this is the maximum amount of token1 to provide."}}},"SignedBatchSignatureTransferDTO":{"type":"object","description":"Signed batch SignatureTransfer permit (for add liquidity token0 and token1 transfer)","required":["permit","signature"],"properties":{"permit":{"$ref":"#/components/schemas/PermitBatchTransferFrom"},"signature":{"$ref":"#/components/schemas/PermitSignature"}}},"PermitBatchTransferFrom":{"type":"object","description":"DTO for ISignatureTransfer.PermitBatchTransferFrom - batch token transfer authorization","required":["permitted","nonce","deadline"],"properties":{"deadline":{"type":"string","format":"uint256"},"nonce":{"type":"string","format":"uint256"},"permitted":{"type":"array","items":{"$ref":"#/components/schemas/TokenPermissions"}}}},"TokenPermissions":{"type":"object","description":"Token permissions for ISignatureTransfer permits.\nUsed in PermitTransferFrom and PermitBatchTransferFrom.","required":["token","amount"],"properties":{"amount":{"type":"string","format":"uint256","description":"The maximum amount that can be transferred"},"token":{"type":"string","format":"address","description":"The token address"}}},"PermitSignature":{"type":"object","required":["r","s","yParity"],"properties":{"r":{"type":"string","format":"uint256"},"s":{"type":"string","format":"uint256"},"yParity":{"type":"boolean","format":"boolean"}}}}}}
```

## The AddLiquidityIntent object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"AddLiquidityIntent":{"type":"object","required":["owner","token0","token1","fee","token0Amount","token1Amount","exact","salt"],"properties":{"exact":{"type":"boolean","description":"Whether the user wants to provide exactly the specified amounts (and is willing to\npay a swap fee first) or would rather like to provide liquidity in current ratio of pool\nreserves (in such case the specified token amounts are treated as maximum amounts)."},"fee":{"type":"integer","format":"uint32","description":"The fee for the pool","minimum":0},"owner":{"type":"string","format":"address","description":"The address of the user submitting the intent"},"salt":{"type":"string","format":"bytes32","description":"Used to differentiate between intents with the same parameters"},"token0":{"type":"string","format":"address","description":"The token0 of the pool. Tokens must be sorted ascending by address so that\n`token0 < token1`; unordered intents are rejected."},"token0Amount":{"type":"string","format":"uint256","description":"The amount of token0 to provide. If `exact` is true, this is the exact amount of token0 to\nprovide. If `exact` is false, this is the maximum amount of token0 to provide."},"token1":{"type":"string","format":"address","description":"The token1 of the pool. Must be greater than `token0` (see `token0`)."},"token1Amount":{"type":"string","format":"uint256","description":"The amount of token1 to provide. If `exact` is true, this is the exact amount of token1 to\nprovide. If `exact` is false, this is the maximum amount of token1 to provide."}}}}}}
```

## The AddLiquidityResponse object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"AddLiquidityResponse":{"type":"object","description":"Response returned after successfully submitting a liquidity intent to the system.\nContains the unique identifier assigned to the submitted intent.\n\n# Fields\n* `hash` - Unique identifier assigned to the liquidity intent","required":["intentHash"],"properties":{"intentHash":{"type":"string","format":"bytes32"}}}}}}
```

## The AddOrder object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"AddOrder":{"type":"object","description":"Represents an order submission with optional permit functionality.\n\nThis struct encapsulates all necessary information needed to submit a new order to the system.\n`signed_permit` is required only for non-smart orders.","required":["order"],"properties":{"annotations":{"$ref":"#/components/schemas/OrderAnnotations","description":"Optional informational annotations stored alongside the order in order history\nand echoed back from the order-state endpoints. Not part of the signed order."},"order":{"$ref":"#/components/schemas/OrderIntent"},"signedPermit":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/SignedPermitDTO","description":"The optional signed permit. Only required for non-smart orders.\nDefaults to None in the JSON input.\nBut defaults to a permit in the API schema, as an example."}],"default":{"permit":{"details":{"token":"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48","amount":"1461501637330902918203684832716283019655932542975","expiration":1630003600,"nonce":0},"spender":"0x346a33009161ed9085b75c9194a523322236f74e","sigDeadline":"1630003600"},"signature":{"r":"93806474433608546370310852361160557641089548055391845313596357890102999855022","s":"44967894251262473816400307202805291026637879676677543909166458416059363900630","yParity":true}}}}},"OrderAnnotations":{"type":"object","description":"Additional data attached to an order, provided by the user. Purely informational —\nnot part of the signed order and not used by the protocol.","properties":{"spreadAtSubmissionHbp":{"type":["integer","null"],"format":"int64","description":"The market spread (in hundredths of a basis point, 10_000 hbp = 1%) quoted to the\nuser at submission time, as returned by `/api/quote` (`amm_spread_hbp`).","minimum":0}}},"OrderIntent":{"type":"object","description":"The swap intent for a user","required":["owner","sellToken","buyToken","sellAmount","minBuyAmount","spreadCurve","startTime","endTime","partialFill","callData","callDataTarget","calldataSellAmountOffset","calldataBuyAmountOffset","salt"],"properties":{"buyToken":{"type":"string","format":"address","description":"The token being bought"},"callData":{"type":"string","format":"bytes","description":"Optional call data for smart orders, allowing custom routing"},"callDataTarget":{"type":"string","format":"address","description":"The address of the target contract for the calldata"},"calldataBuyAmountOffset":{"type":"integer","format":"uint64","description":"The offset of the buy amount in the calldata\nIf Some, used to update the calldata with the actual buy amount","minimum":0},"calldataSellAmountOffset":{"type":"integer","format":"uint64","description":"The offset of the sell amount in the calldata\nIf Some, used to update the calldata with the actual sell amount","minimum":0},"endTime":{"type":"integer","format":"uint256","description":"Block timestamp until when the order is valid.","minimum":0},"minBuyAmount":{"type":"string","format":"uint256","description":"The minimum amount of tokens being bought, effectively defining limit price."},"owner":{"type":"string","format":"address","description":"The address of the user submitting the order"},"partialFill":{"type":"boolean","format":"boolean","description":"Flag allowing partial fills"},"salt":{"type":"string","format":"bytes32","description":"The offset of the buy amount in the calldata\nUsed to differentiate between orders with the same parameters"},"sellAmount":{"type":"string","format":"uint196","description":"The amount of tokens being sold"},"sellToken":{"type":"string","format":"address","description":"The token being sold"},"spreadCurve":{"$ref":"#/components/schemas/SpreadCurveDto","description":"A piecewise-linear spread curve mapping a fraction of the order window to a mid-price\ndelta in basis points. Each interior point's `windowBps` is a fraction of the order\nduration in basis points (5_000 = halfway)."},"startTime":{"type":"integer","format":"uint256","description":"Block timestamp since when the order is valid","minimum":0}}},"SpreadCurveDto":{"type":"object","description":"Piecewise-linear spread curve. Interior `points` express knot positions as a fraction\nof the order's `[start_time, end_time]` window in basis points (`windowBps`), so the\ncurve is portable across orders of different durations.","required":["startDeltaBps","endDeltaBps"],"properties":{"endDeltaBps":{"type":"integer","format":"int32","description":"Mid-price delta at the order's `end_time`, in basis points."},"points":{"type":"array","items":{"$ref":"#/components/schemas/CurvePointDto"},"description":"Interior knots. Each `windowBps` must lie in `[1, 9_999]` and values must be\nstrictly increasing across `points`. Defaults to `[]` if absent, yielding a single\nlinear segment from `startDeltaBps` to `endDeltaBps`."},"startDeltaBps":{"type":"integer","format":"int32","description":"Mid-price delta at the order's `start_time`, in basis points."}}},"CurvePointDto":{"type":"object","description":"A single interior knot in a [`SpreadCurveDto`].","required":["windowBps","deltaBps"],"properties":{"deltaBps":{"type":"integer","format":"int32","description":"Mid-price delta in basis points at this knot."},"windowBps":{"type":"integer","format":"int32","description":"Knot position as a fraction of the order window in basis points. Must lie in\n`(0, 10_000)` exclusive — `5_000` is the order midpoint.","maximum":9999,"minimum":1}}},"SignedPermitDTO":{"type":"object","required":["permit","signature"],"properties":{"permit":{"$ref":"#/components/schemas/PermitSingle"},"signature":{"$ref":"#/components/schemas/PermitSignature"}}},"PermitSingle":{"type":"object","required":["details","spender","sigDeadline"],"properties":{"details":{"$ref":"#/components/schemas/PermitDetails"},"sigDeadline":{"type":"string","format":"uint256"},"spender":{"type":"string","format":"address"}}},"PermitDetails":{"type":"object","required":["token","amount","expiration","nonce"],"properties":{"amount":{"type":"string","format":"uint160"},"expiration":{"type":"integer","format":"uint64","minimum":0},"nonce":{"type":"integer","format":"uint64","minimum":0},"token":{"type":"string","format":"address"}}},"PermitSignature":{"type":"object","required":["r","s","yParity"],"properties":{"r":{"type":"string","format":"uint256"},"s":{"type":"string","format":"uint256"},"yParity":{"type":"boolean","format":"boolean"}}}}}}
```

## The AddOrderEip712 object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"AddOrderEip712":{"type":"object","description":"Inner payload of add_order endpoint.","required":["order","spreadCurve"],"properties":{"annotations":{"$ref":"#/components/schemas/OrderAnnotations","description":"Informational only; deliberately outside the signature, as in the session API."},"order":{"$ref":"#/components/schemas/OrderIntentEip712"},"signedPermit":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/SignedPermitDTO","description":"Permit2 signature funding the order; same semantics as the session API."}]},"spreadCurve":{"$ref":"#/components/schemas/SpreadCurveDto","description":"The full spread curve, signed as part of the inner payload."}}},"OrderAnnotations":{"type":"object","description":"Additional data attached to an order, provided by the user. Purely informational —\nnot part of the signed order and not used by the protocol.","properties":{"spreadAtSubmissionHbp":{"type":["integer","null"],"format":"int64","description":"The market spread (in hundredths of a basis point, 10_000 hbp = 1%) quoted to the\nuser at submission time, as returned by `/api/quote` (`amm_spread_hbp`).","minimum":0}}},"OrderIntentEip712":{"type":"object","description":"The EIP-712 API wire form of an order intent. Unlike the session-API DTO,\nit carries no spread curve inline. The curve is a required payload-level sibling.","required":["owner","sellToken","buyToken","sellAmount","minBuyAmount","startTime","endTime","partialFill","callData","callDataTarget","salt"],"properties":{"buyToken":{"type":"string","format":"address"},"callData":{"type":"string","format":"bytes"},"callDataTarget":{"type":"string","format":"address"},"endTime":{"type":"integer","format":"uint256","description":"Block timestamp until when the order is valid","minimum":0},"minBuyAmount":{"type":"string","format":"uint256"},"owner":{"type":"string","format":"address"},"partialFill":{"type":"boolean"},"salt":{"type":"string","format":"bytes32","description":"Differentiates orders that are otherwise identical (including their full spread\ncurve); SDKs should randomize it."},"sellAmount":{"type":"string","format":"uint256"},"sellToken":{"type":"string","format":"address"},"startTime":{"type":"integer","format":"uint256","description":"Block timestamp since when the order is valid","minimum":0}}},"SignedPermitDTO":{"type":"object","required":["permit","signature"],"properties":{"permit":{"$ref":"#/components/schemas/PermitSingle"},"signature":{"$ref":"#/components/schemas/PermitSignature"}}},"PermitSingle":{"type":"object","required":["details","spender","sigDeadline"],"properties":{"details":{"$ref":"#/components/schemas/PermitDetails"},"sigDeadline":{"type":"string","format":"uint256"},"spender":{"type":"string","format":"address"}}},"PermitDetails":{"type":"object","required":["token","amount","expiration","nonce"],"properties":{"amount":{"type":"string","format":"uint160"},"expiration":{"type":"integer","format":"uint64","minimum":0},"nonce":{"type":"integer","format":"uint64","minimum":0},"token":{"type":"string","format":"address"}}},"PermitSignature":{"type":"object","required":["r","s","yParity"],"properties":{"r":{"type":"string","format":"uint256"},"s":{"type":"string","format":"uint256"},"yParity":{"type":"boolean","format":"boolean"}}},"SpreadCurveDto":{"type":"object","description":"Piecewise-linear spread curve. Interior `points` express knot positions as a fraction\nof the order's `[start_time, end_time]` window in basis points (`windowBps`), so the\ncurve is portable across orders of different durations.","required":["startDeltaBps","endDeltaBps"],"properties":{"endDeltaBps":{"type":"integer","format":"int32","description":"Mid-price delta at the order's `end_time`, in basis points."},"points":{"type":"array","items":{"$ref":"#/components/schemas/CurvePointDto"},"description":"Interior knots. Each `windowBps` must lie in `[1, 9_999]` and values must be\nstrictly increasing across `points`. Defaults to `[]` if absent, yielding a single\nlinear segment from `startDeltaBps` to `endDeltaBps`."},"startDeltaBps":{"type":"integer","format":"int32","description":"Mid-price delta at the order's `start_time`, in basis points."}}},"CurvePointDto":{"type":"object","description":"A single interior knot in a [`SpreadCurveDto`].","required":["windowBps","deltaBps"],"properties":{"deltaBps":{"type":"integer","format":"int32","description":"Mid-price delta in basis points at this knot."},"windowBps":{"type":"integer","format":"int32","description":"Knot position as a fraction of the order window in basis points. Must lie in\n`(0, 10_000)` exclusive — `5_000` is the order midpoint.","maximum":9999,"minimum":1}}}}}}
```

## The AddOrderResponse object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"AddOrderResponse":{"type":"object","description":"Response returned after successfully submitting an order to the system.\nContains the unique identifier assigned to the submitted order.\n\n# Fields\n* `order_hash` - Unique identifier assigned to the submitted order","required":["orderHash"],"properties":{"orderHash":{"type":"string","format":"bytes32"}}}}}}
```

## The AuthBlock object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"AuthBlock":{"type":"object","description":"Auth block carried by every EIP-712 request body.","required":["signer","nonce","deadline","signature"],"properties":{"deadline":{"type":"integer","format":"int64","description":"Unix seconds; not more than `maxSignatureLifetimeS` (from the config endpoint) into the\nfuture.","minimum":0},"nonce":{"type":"string","description":"Random per-request value, unique per signer among un-expired requests."},"signature":{"$ref":"#/components/schemas/PermitSignature"},"signer":{"type":"string","format":"address","description":"Claimed signer. Must match the recovered address."}}},"PermitSignature":{"type":"object","required":["r","s","yParity"],"properties":{"r":{"type":"string","format":"uint256"},"s":{"type":"string","format":"uint256"},"yParity":{"type":"boolean","format":"boolean"}}}}}}
```

## The CancelOrder object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"CancelOrder":{"type":"object","required":["orderHash"],"properties":{"orderHash":{"type":"string","format":"bytes32"}}}}}}
```

## The CancelOrderEip712 object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"CancelOrderEip712":{"type":"object","description":"Inner payload of cancel_order endpoint.","required":["orderHash"],"properties":{"orderHash":{"type":"string","format":"bytes32"}}}}}}
```

## The CancelOrderResponse object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"CancelOrderResponse":{"type":"object","required":["orderHash"],"properties":{"orderHash":{"type":"string","format":"bytes32"}}}}}}
```

## The ConfigResponse object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"ConfigResponse":{"type":"object","required":["version","turbineSettlerAddress","turbineSignerAddress","lpHookAddress","lpRouterAddress","poolManagerAddress","submitSettlements","siweDomain","siweUri","minTradeSizeUsdc","minLiquidityVolumeUsdc","eip712Domain","maxSignatureLifetimeS","tokens"],"properties":{"eip712Domain":{"$ref":"#/components/schemas/Eip712DomainInfo","description":"The EIP-712 domain that `/api/eip712/` request signatures are verified against."},"lpHookAddress":{"type":"string","format":"address"},"lpRouterAddress":{"type":"string","format":"address"},"maxSignatureLifetimeS":{"type":"integer","format":"int64","description":"Maximum allowed remaining validity of an EIP-712 auth signature, in seconds.","minimum":0},"minLiquidityVolumeUsdc":{"type":"string","format":"uint256","description":"Minimum total USDC volume for a liquidity provision intent, in native USDC units\n(6 decimals). A value of `10000000` is 10 USDC."},"minTradeSizeUsdc":{"type":"string","format":"uint256","description":"Minimum trade size in native USDC units (6 decimals). A value of `10000000`\nis 10 USDC."},"poolManagerAddress":{"type":"string","format":"address"},"siweDomain":{"type":"string"},"siweUri":{"type":"string"},"submitSettlements":{"type":"boolean"},"tokens":{"type":"array","items":{"$ref":"#/components/schemas/TokenInfo"}},"turbineSettlerAddress":{"type":"string","format":"address"},"turbineSignerAddress":{"type":"string","format":"address"},"version":{"type":"string"}}},"Eip712DomainInfo":{"type":"object","description":"Wire form of the EIP-712 signing domain served by `/api/config`. This is the single\nsource of truth for the domain: both `/api/config` and signature verification read\ntheir alloy [`Eip712Domain`] from it, so what clients sign against is exactly what\nthe server verifies.","required":["name","version","chainId","verifyingContract","salt"],"properties":{"chainId":{"type":"integer","format":"int64","minimum":0},"name":{"type":"string"},"salt":{"type":"string","format":"bytes32"},"verifyingContract":{"type":"string","format":"address"},"version":{"type":"string"}}},"TokenInfo":{"type":"object","description":"Public metadata for a token in the allowlist, returned by `/api/config`.","required":["address","symbol","decimals"],"properties":{"address":{"type":"string","format":"address"},"class":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/TokenClass"}]},"decimals":{"type":"integer","format":"int32","minimum":0},"oracles":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/OracleFeed"},"propertyNames":{"type":"string"}},"symbol":{"type":"string"}}},"TokenClass":{"type":"string","enum":["Stable","Regular","Meme"]},"OracleFeed":{"oneOf":[{"type":"object","description":"Binance WebSocket depth feed.","required":["ticker_symbol"],"properties":{"ticker_symbol":{"type":"string"}}},{"type":"object","description":"Coinbase WebSocket feed.","required":["product_id"],"properties":{"product_id":{"type":"string"}}},{"type":"object","description":"Kraken WebSocket feed.","required":["wsname"],"properties":{"wsname":{"type":"string"}}},{"type":"object","description":"Lighter WebSocket feed.","required":["market_id","symbol"],"properties":{"market_id":{"type":"string"},"symbol":{"type":"string"}}},{"type":"object","description":"Hyperliquid WebSocket feed.","required":["coin"],"properties":{"coin":{"type":"string"}}},{"type":"object","description":"Bitget WebSocket feed.","required":["symbol"],"properties":{"symbol":{"type":"string"}}}],"description":"OpenAPI schema for an oracle feed entry.\n\nThe wire format flattens the oracle configuration: the map key in\n`TokenInfo.oracles` identifies the oracle (e.g. `binance`, `kraken`),\nand the value carries only the provider-specific fields — the internal\n`type` discriminator is stripped by `serialize_feeds`.\n\nThis type is documentation-only: runtime (de)serialization goes through\n[`ExchangeFeedInfo`]. The `#[serde(untagged)]` attribute here merely keeps\n`ToSchema` output consistent with the flat wire shape."}}}}
```

## The CurvePoint object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"CurvePoint":{"type":"object","description":"A single knot in a [`SpreadCurve`], binding a wall-clock time to a delta in basis points.","required":["timeSecs","deltaBps"],"properties":{"deltaBps":{"type":"integer","format":"int32"},"timeSecs":{"type":"integer","format":"int64","minimum":0}}}}}}
```

## The CurvePointDto object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"CurvePointDto":{"type":"object","description":"A single interior knot in a [`SpreadCurveDto`].","required":["windowBps","deltaBps"],"properties":{"deltaBps":{"type":"integer","format":"int32","description":"Mid-price delta in basis points at this knot."},"windowBps":{"type":"integer","format":"int32","description":"Knot position as a fraction of the order window in basis points. Must lie in\n`(0, 10_000)` exclusive — `5_000` is the order midpoint.","maximum":9999,"minimum":1}}}}}}
```

## The Eip712DomainInfo object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"Eip712DomainInfo":{"type":"object","description":"Wire form of the EIP-712 signing domain served by `/api/config`. This is the single\nsource of truth for the domain: both `/api/config` and signature verification read\ntheir alloy [`Eip712Domain`] from it, so what clients sign against is exactly what\nthe server verifies.","required":["name","version","chainId","verifyingContract","salt"],"properties":{"chainId":{"type":"integer","format":"int64","minimum":0},"name":{"type":"string"},"salt":{"type":"string","format":"bytes32"},"verifyingContract":{"type":"string","format":"address"},"version":{"type":"string"}}}}}}
```

## The Eip712RequestBodyAddOrderBatch object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"Eip712RequestBodyAddOrderBatch":{"type":"array","items":{"$ref":"#/components/schemas/Eip712RequestBody_AddOrderEip712"},"description":"A batch of independently signed add-order requests."},"Eip712RequestBody_AddOrderEip712":{"type":"object","description":"EIP-712 request body: the semantic payload plus its auth block. The EIP-712\nsignature covers the payload's typed struct together with `nonce` and `deadline`.","required":["payload","auth"],"properties":{"auth":{"$ref":"#/components/schemas/AuthBlock"},"payload":{"type":"object","description":"Inner payload of add_order endpoint.","required":["order","spreadCurve"],"properties":{"annotations":{"$ref":"#/components/schemas/OrderAnnotations","description":"Informational only; deliberately outside the signature, as in the session API."},"order":{"$ref":"#/components/schemas/OrderIntentEip712"},"signedPermit":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/SignedPermitDTO","description":"Permit2 signature funding the order; same semantics as the session API."}]},"spreadCurve":{"$ref":"#/components/schemas/SpreadCurveDto","description":"The full spread curve, signed as part of the inner payload."}}}}},"AuthBlock":{"type":"object","description":"Auth block carried by every EIP-712 request body.","required":["signer","nonce","deadline","signature"],"properties":{"deadline":{"type":"integer","format":"int64","description":"Unix seconds; not more than `maxSignatureLifetimeS` (from the config endpoint) into the\nfuture.","minimum":0},"nonce":{"type":"string","description":"Random per-request value, unique per signer among un-expired requests."},"signature":{"$ref":"#/components/schemas/PermitSignature"},"signer":{"type":"string","format":"address","description":"Claimed signer. Must match the recovered address."}}},"PermitSignature":{"type":"object","required":["r","s","yParity"],"properties":{"r":{"type":"string","format":"uint256"},"s":{"type":"string","format":"uint256"},"yParity":{"type":"boolean","format":"boolean"}}},"OrderAnnotations":{"type":"object","description":"Additional data attached to an order, provided by the user. Purely informational —\nnot part of the signed order and not used by the protocol.","properties":{"spreadAtSubmissionHbp":{"type":["integer","null"],"format":"int64","description":"The market spread (in hundredths of a basis point, 10_000 hbp = 1%) quoted to the\nuser at submission time, as returned by `/api/quote` (`amm_spread_hbp`).","minimum":0}}},"OrderIntentEip712":{"type":"object","description":"The EIP-712 API wire form of an order intent. Unlike the session-API DTO,\nit carries no spread curve inline. The curve is a required payload-level sibling.","required":["owner","sellToken","buyToken","sellAmount","minBuyAmount","startTime","endTime","partialFill","callData","callDataTarget","salt"],"properties":{"buyToken":{"type":"string","format":"address"},"callData":{"type":"string","format":"bytes"},"callDataTarget":{"type":"string","format":"address"},"endTime":{"type":"integer","format":"uint256","description":"Block timestamp until when the order is valid","minimum":0},"minBuyAmount":{"type":"string","format":"uint256"},"owner":{"type":"string","format":"address"},"partialFill":{"type":"boolean"},"salt":{"type":"string","format":"bytes32","description":"Differentiates orders that are otherwise identical (including their full spread\ncurve); SDKs should randomize it."},"sellAmount":{"type":"string","format":"uint256"},"sellToken":{"type":"string","format":"address"},"startTime":{"type":"integer","format":"uint256","description":"Block timestamp since when the order is valid","minimum":0}}},"SignedPermitDTO":{"type":"object","required":["permit","signature"],"properties":{"permit":{"$ref":"#/components/schemas/PermitSingle"},"signature":{"$ref":"#/components/schemas/PermitSignature"}}},"PermitSingle":{"type":"object","required":["details","spender","sigDeadline"],"properties":{"details":{"$ref":"#/components/schemas/PermitDetails"},"sigDeadline":{"type":"string","format":"uint256"},"spender":{"type":"string","format":"address"}}},"PermitDetails":{"type":"object","required":["token","amount","expiration","nonce"],"properties":{"amount":{"type":"string","format":"uint160"},"expiration":{"type":"integer","format":"uint64","minimum":0},"nonce":{"type":"integer","format":"uint64","minimum":0},"token":{"type":"string","format":"address"}}},"SpreadCurveDto":{"type":"object","description":"Piecewise-linear spread curve. Interior `points` express knot positions as a fraction\nof the order's `[start_time, end_time]` window in basis points (`windowBps`), so the\ncurve is portable across orders of different durations.","required":["startDeltaBps","endDeltaBps"],"properties":{"endDeltaBps":{"type":"integer","format":"int32","description":"Mid-price delta at the order's `end_time`, in basis points."},"points":{"type":"array","items":{"$ref":"#/components/schemas/CurvePointDto"},"description":"Interior knots. Each `windowBps` must lie in `[1, 9_999]` and values must be\nstrictly increasing across `points`. Defaults to `[]` if absent, yielding a single\nlinear segment from `startDeltaBps` to `endDeltaBps`."},"startDeltaBps":{"type":"integer","format":"int32","description":"Mid-price delta at the order's `start_time`, in basis points."}}},"CurvePointDto":{"type":"object","description":"A single interior knot in a [`SpreadCurveDto`].","required":["windowBps","deltaBps"],"properties":{"deltaBps":{"type":"integer","format":"int32","description":"Mid-price delta in basis points at this knot."},"windowBps":{"type":"integer","format":"int32","description":"Knot position as a fraction of the order window in basis points. Must lie in\n`(0, 10_000)` exclusive — `5_000` is the order midpoint.","maximum":9999,"minimum":1}}}}}}
```

## The Eip712RequestBody\_AddLiquidityEip712 object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"Eip712RequestBody_AddLiquidityEip712":{"type":"object","description":"EIP-712 request body: the semantic payload plus its auth block. The EIP-712\nsignature covers the payload's typed struct together with `nonce` and `deadline`.","required":["payload","auth"],"properties":{"auth":{"$ref":"#/components/schemas/AuthBlock"},"payload":{"allOf":[{"$ref":"#/components/schemas/AddLiquidity"}],"description":"Inner payload of add_liquidity endpoint."}}},"AuthBlock":{"type":"object","description":"Auth block carried by every EIP-712 request body.","required":["signer","nonce","deadline","signature"],"properties":{"deadline":{"type":"integer","format":"int64","description":"Unix seconds; not more than `maxSignatureLifetimeS` (from the config endpoint) into the\nfuture.","minimum":0},"nonce":{"type":"string","description":"Random per-request value, unique per signer among un-expired requests."},"signature":{"$ref":"#/components/schemas/PermitSignature"},"signer":{"type":"string","format":"address","description":"Claimed signer. Must match the recovered address."}}},"PermitSignature":{"type":"object","required":["r","s","yParity"],"properties":{"r":{"type":"string","format":"uint256"},"s":{"type":"string","format":"uint256"},"yParity":{"type":"boolean","format":"boolean"}}},"AddLiquidity":{"type":"object","description":"Represents a liquidity addition with SignatureTransfer permit functionality.\n\nThis struct encapsulates all necessary information needed to submit a new intent to add\nliquidity to the system, including the intent and SignatureTransfer permit data for approvals.\nUser authentication is handled via session-based SIWE verification.\n\n# Fields\n* `add_liquidity` The intent to add liquidity\n* `permit_tokens` The batch SignatureTransfer permit and signature for token0 and token1","required":["addLiquidity","permitTokens"],"properties":{"addLiquidity":{"$ref":"#/components/schemas/AddLiquidityIntent"},"permitTokens":{"$ref":"#/components/schemas/SignedBatchSignatureTransferDTO"}}},"AddLiquidityIntent":{"type":"object","required":["owner","token0","token1","fee","token0Amount","token1Amount","exact","salt"],"properties":{"exact":{"type":"boolean","description":"Whether the user wants to provide exactly the specified amounts (and is willing to\npay a swap fee first) or would rather like to provide liquidity in current ratio of pool\nreserves (in such case the specified token amounts are treated as maximum amounts)."},"fee":{"type":"integer","format":"uint32","description":"The fee for the pool","minimum":0},"owner":{"type":"string","format":"address","description":"The address of the user submitting the intent"},"salt":{"type":"string","format":"bytes32","description":"Used to differentiate between intents with the same parameters"},"token0":{"type":"string","format":"address","description":"The token0 of the pool. Tokens must be sorted ascending by address so that\n`token0 < token1`; unordered intents are rejected."},"token0Amount":{"type":"string","format":"uint256","description":"The amount of token0 to provide. If `exact` is true, this is the exact amount of token0 to\nprovide. If `exact` is false, this is the maximum amount of token0 to provide."},"token1":{"type":"string","format":"address","description":"The token1 of the pool. Must be greater than `token0` (see `token0`)."},"token1Amount":{"type":"string","format":"uint256","description":"The amount of token1 to provide. If `exact` is true, this is the exact amount of token1 to\nprovide. If `exact` is false, this is the maximum amount of token1 to provide."}}},"SignedBatchSignatureTransferDTO":{"type":"object","description":"Signed batch SignatureTransfer permit (for add liquidity token0 and token1 transfer)","required":["permit","signature"],"properties":{"permit":{"$ref":"#/components/schemas/PermitBatchTransferFrom"},"signature":{"$ref":"#/components/schemas/PermitSignature"}}},"PermitBatchTransferFrom":{"type":"object","description":"DTO for ISignatureTransfer.PermitBatchTransferFrom - batch token transfer authorization","required":["permitted","nonce","deadline"],"properties":{"deadline":{"type":"string","format":"uint256"},"nonce":{"type":"string","format":"uint256"},"permitted":{"type":"array","items":{"$ref":"#/components/schemas/TokenPermissions"}}}},"TokenPermissions":{"type":"object","description":"Token permissions for ISignatureTransfer permits.\nUsed in PermitTransferFrom and PermitBatchTransferFrom.","required":["token","amount"],"properties":{"amount":{"type":"string","format":"uint256","description":"The maximum amount that can be transferred"},"token":{"type":"string","format":"address","description":"The token address"}}}}}}
```

## The Eip712RequestBody\_AddOrderEip712 object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"Eip712RequestBody_AddOrderEip712":{"type":"object","description":"EIP-712 request body: the semantic payload plus its auth block. The EIP-712\nsignature covers the payload's typed struct together with `nonce` and `deadline`.","required":["payload","auth"],"properties":{"auth":{"$ref":"#/components/schemas/AuthBlock"},"payload":{"type":"object","description":"Inner payload of add_order endpoint.","required":["order","spreadCurve"],"properties":{"annotations":{"$ref":"#/components/schemas/OrderAnnotations","description":"Informational only; deliberately outside the signature, as in the session API."},"order":{"$ref":"#/components/schemas/OrderIntentEip712"},"signedPermit":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/SignedPermitDTO","description":"Permit2 signature funding the order; same semantics as the session API."}]},"spreadCurve":{"$ref":"#/components/schemas/SpreadCurveDto","description":"The full spread curve, signed as part of the inner payload."}}}}},"AuthBlock":{"type":"object","description":"Auth block carried by every EIP-712 request body.","required":["signer","nonce","deadline","signature"],"properties":{"deadline":{"type":"integer","format":"int64","description":"Unix seconds; not more than `maxSignatureLifetimeS` (from the config endpoint) into the\nfuture.","minimum":0},"nonce":{"type":"string","description":"Random per-request value, unique per signer among un-expired requests."},"signature":{"$ref":"#/components/schemas/PermitSignature"},"signer":{"type":"string","format":"address","description":"Claimed signer. Must match the recovered address."}}},"PermitSignature":{"type":"object","required":["r","s","yParity"],"properties":{"r":{"type":"string","format":"uint256"},"s":{"type":"string","format":"uint256"},"yParity":{"type":"boolean","format":"boolean"}}},"OrderAnnotations":{"type":"object","description":"Additional data attached to an order, provided by the user. Purely informational —\nnot part of the signed order and not used by the protocol.","properties":{"spreadAtSubmissionHbp":{"type":["integer","null"],"format":"int64","description":"The market spread (in hundredths of a basis point, 10_000 hbp = 1%) quoted to the\nuser at submission time, as returned by `/api/quote` (`amm_spread_hbp`).","minimum":0}}},"OrderIntentEip712":{"type":"object","description":"The EIP-712 API wire form of an order intent. Unlike the session-API DTO,\nit carries no spread curve inline. The curve is a required payload-level sibling.","required":["owner","sellToken","buyToken","sellAmount","minBuyAmount","startTime","endTime","partialFill","callData","callDataTarget","salt"],"properties":{"buyToken":{"type":"string","format":"address"},"callData":{"type":"string","format":"bytes"},"callDataTarget":{"type":"string","format":"address"},"endTime":{"type":"integer","format":"uint256","description":"Block timestamp until when the order is valid","minimum":0},"minBuyAmount":{"type":"string","format":"uint256"},"owner":{"type":"string","format":"address"},"partialFill":{"type":"boolean"},"salt":{"type":"string","format":"bytes32","description":"Differentiates orders that are otherwise identical (including their full spread\ncurve); SDKs should randomize it."},"sellAmount":{"type":"string","format":"uint256"},"sellToken":{"type":"string","format":"address"},"startTime":{"type":"integer","format":"uint256","description":"Block timestamp since when the order is valid","minimum":0}}},"SignedPermitDTO":{"type":"object","required":["permit","signature"],"properties":{"permit":{"$ref":"#/components/schemas/PermitSingle"},"signature":{"$ref":"#/components/schemas/PermitSignature"}}},"PermitSingle":{"type":"object","required":["details","spender","sigDeadline"],"properties":{"details":{"$ref":"#/components/schemas/PermitDetails"},"sigDeadline":{"type":"string","format":"uint256"},"spender":{"type":"string","format":"address"}}},"PermitDetails":{"type":"object","required":["token","amount","expiration","nonce"],"properties":{"amount":{"type":"string","format":"uint160"},"expiration":{"type":"integer","format":"uint64","minimum":0},"nonce":{"type":"integer","format":"uint64","minimum":0},"token":{"type":"string","format":"address"}}},"SpreadCurveDto":{"type":"object","description":"Piecewise-linear spread curve. Interior `points` express knot positions as a fraction\nof the order's `[start_time, end_time]` window in basis points (`windowBps`), so the\ncurve is portable across orders of different durations.","required":["startDeltaBps","endDeltaBps"],"properties":{"endDeltaBps":{"type":"integer","format":"int32","description":"Mid-price delta at the order's `end_time`, in basis points."},"points":{"type":"array","items":{"$ref":"#/components/schemas/CurvePointDto"},"description":"Interior knots. Each `windowBps` must lie in `[1, 9_999]` and values must be\nstrictly increasing across `points`. Defaults to `[]` if absent, yielding a single\nlinear segment from `startDeltaBps` to `endDeltaBps`."},"startDeltaBps":{"type":"integer","format":"int32","description":"Mid-price delta at the order's `start_time`, in basis points."}}},"CurvePointDto":{"type":"object","description":"A single interior knot in a [`SpreadCurveDto`].","required":["windowBps","deltaBps"],"properties":{"deltaBps":{"type":"integer","format":"int32","description":"Mid-price delta in basis points at this knot."},"windowBps":{"type":"integer","format":"int32","description":"Knot position as a fraction of the order window in basis points. Must lie in\n`(0, 10_000)` exclusive — `5_000` is the order midpoint.","maximum":9999,"minimum":1}}}}}}
```

## The Eip712RequestBody\_CancelOrderEip712 object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"Eip712RequestBody_CancelOrderEip712":{"type":"object","description":"EIP-712 request body: the semantic payload plus its auth block. The EIP-712\nsignature covers the payload's typed struct together with `nonce` and `deadline`.","required":["payload","auth"],"properties":{"auth":{"$ref":"#/components/schemas/AuthBlock"},"payload":{"type":"object","description":"Inner payload of cancel_order endpoint.","required":["orderHash"],"properties":{"orderHash":{"type":"string","format":"bytes32"}}}}},"AuthBlock":{"type":"object","description":"Auth block carried by every EIP-712 request body.","required":["signer","nonce","deadline","signature"],"properties":{"deadline":{"type":"integer","format":"int64","description":"Unix seconds; not more than `maxSignatureLifetimeS` (from the config endpoint) into the\nfuture.","minimum":0},"nonce":{"type":"string","description":"Random per-request value, unique per signer among un-expired requests."},"signature":{"$ref":"#/components/schemas/PermitSignature"},"signer":{"type":"string","format":"address","description":"Claimed signer. Must match the recovered address."}}},"PermitSignature":{"type":"object","required":["r","s","yParity"],"properties":{"r":{"type":"string","format":"uint256"},"s":{"type":"string","format":"uint256"},"yParity":{"type":"boolean","format":"boolean"}}}}}}
```

## The Eip712RequestBody\_LiquidityIntentsQueryEip712 object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"Eip712RequestBody_LiquidityIntentsQueryEip712":{"type":"object","description":"EIP-712 request body: the semantic payload plus its auth block. The EIP-712\nsignature covers the payload's typed struct together with `nonce` and `deadline`.","required":["payload","auth"],"properties":{"auth":{"$ref":"#/components/schemas/AuthBlock"},"payload":{"type":"object","description":"Inner payload for querying liquidity intents.","properties":{"hashes":{"type":"array","items":{"type":"string","format":"bytes32"},"description":"Intent hashes to look up. Max 30 per request; at least one is required."}}}}},"AuthBlock":{"type":"object","description":"Auth block carried by every EIP-712 request body.","required":["signer","nonce","deadline","signature"],"properties":{"deadline":{"type":"integer","format":"int64","description":"Unix seconds; not more than `maxSignatureLifetimeS` (from the config endpoint) into the\nfuture.","minimum":0},"nonce":{"type":"string","description":"Random per-request value, unique per signer among un-expired requests."},"signature":{"$ref":"#/components/schemas/PermitSignature"},"signer":{"type":"string","format":"address","description":"Claimed signer. Must match the recovered address."}}},"PermitSignature":{"type":"object","required":["r","s","yParity"],"properties":{"r":{"type":"string","format":"uint256"},"s":{"type":"string","format":"uint256"},"yParity":{"type":"boolean","format":"boolean"}}}}}}
```

## The Eip712RequestBody\_OrdersQueryEip712 object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"Eip712RequestBody_OrdersQueryEip712":{"type":"object","description":"EIP-712 request body: the semantic payload plus its auth block. The EIP-712\nsignature covers the payload's typed struct together with `nonce` and `deadline`.","required":["payload","auth"],"properties":{"auth":{"$ref":"#/components/schemas/AuthBlock"},"payload":{"type":"object","description":"Inner payload of GET orders endpoint.\nField semantics mirror the session API's query parameters; zero values mean \"unset\".","properties":{"cursor":{"type":"string","description":"Opaque cursor from a previous response; empty for the first page."},"hashes":{"type":"array","items":{"type":"string","format":"bytes32"},"description":"Direct lookup by order hash. Max 30 per request; pagination is skipped."},"limit":{"type":"integer","format":"int64","description":"Max orders per page; 0 selects the server default, and larger values are capped.","minimum":0},"statuses":{"type":"array","items":{"$ref":"#/components/schemas/OrderStatus"},"description":"Filter by order status. Multiple values are OR'd."}}}}},"AuthBlock":{"type":"object","description":"Auth block carried by every EIP-712 request body.","required":["signer","nonce","deadline","signature"],"properties":{"deadline":{"type":"integer","format":"int64","description":"Unix seconds; not more than `maxSignatureLifetimeS` (from the config endpoint) into the\nfuture.","minimum":0},"nonce":{"type":"string","description":"Random per-request value, unique per signer among un-expired requests."},"signature":{"$ref":"#/components/schemas/PermitSignature"},"signer":{"type":"string","format":"address","description":"Claimed signer. Must match the recovered address."}}},"PermitSignature":{"type":"object","required":["r","s","yParity"],"properties":{"r":{"type":"string","format":"uint256"},"s":{"type":"string","format":"uint256"},"yParity":{"type":"boolean","format":"boolean"}}},"OrderStatus":{"type":"string","enum":["Active","Invalid","Expired","Filled","PendingCancellation","Canceled","Incompatible"]}}}}
```

## The Eip712RequestBody\_RemoveLiquidityEip712 object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"Eip712RequestBody_RemoveLiquidityEip712":{"type":"object","description":"EIP-712 request body: the semantic payload plus its auth block. The EIP-712\nsignature covers the payload's typed struct together with `nonce` and `deadline`.","required":["payload","auth"],"properties":{"auth":{"$ref":"#/components/schemas/AuthBlock"},"payload":{"allOf":[{"$ref":"#/components/schemas/RemoveLiquidity"}],"description":"Inner payload of remove_liquidity endpoint."}}},"AuthBlock":{"type":"object","description":"Auth block carried by every EIP-712 request body.","required":["signer","nonce","deadline","signature"],"properties":{"deadline":{"type":"integer","format":"int64","description":"Unix seconds; not more than `maxSignatureLifetimeS` (from the config endpoint) into the\nfuture.","minimum":0},"nonce":{"type":"string","description":"Random per-request value, unique per signer among un-expired requests."},"signature":{"$ref":"#/components/schemas/PermitSignature"},"signer":{"type":"string","format":"address","description":"Claimed signer. Must match the recovered address."}}},"PermitSignature":{"type":"object","required":["r","s","yParity"],"properties":{"r":{"type":"string","format":"uint256"},"s":{"type":"string","format":"uint256"},"yParity":{"type":"boolean","format":"boolean"}}},"RemoveLiquidity":{"type":"object","description":"Represents a liquidity removal with SignatureTransfer permit functionality.\n\nThis struct encapsulates all necessary information needed to submit a new intent to remove\nliquidity to the system, including the intent and SignatureTransfer permit data for approvals.\nUser authentication is handled via session-based SIWE verification.\n\n# Fields\n* `remove_liquidity` The intent to remove liquidity\n* `permit_lp_token` The SignatureTransfer permit and signature for the LP token","required":["removeLiquidity","permitLpToken"],"properties":{"permitLpToken":{"$ref":"#/components/schemas/SignedSignatureTransferDTO"},"removeLiquidity":{"$ref":"#/components/schemas/RemoveLiquidityIntent"}}},"SignedSignatureTransferDTO":{"type":"object","description":"Signed single SignatureTransfer permit (for remove liquidity LP token transfer)","required":["permit","signature"],"properties":{"permit":{"$ref":"#/components/schemas/PermitTransferFrom"},"signature":{"$ref":"#/components/schemas/PermitSignature"}}},"PermitTransferFrom":{"type":"object","description":"DTO for ISignatureTransfer.PermitTransferFrom - single token transfer authorization","required":["permitted","nonce","deadline"],"properties":{"deadline":{"type":"string","format":"uint256"},"nonce":{"type":"string","format":"uint256"},"permitted":{"$ref":"#/components/schemas/TokenPermissions"}}},"TokenPermissions":{"type":"object","description":"Token permissions for ISignatureTransfer permits.\nUsed in PermitTransferFrom and PermitBatchTransferFrom.","required":["token","amount"],"properties":{"amount":{"type":"string","format":"uint256","description":"The maximum amount that can be transferred"},"token":{"type":"string","format":"address","description":"The token address"}}},"RemoveLiquidityIntent":{"type":"object","required":["owner","token0","token1","fee","lpToken","lpTokenAmount","salt"],"properties":{"fee":{"type":"integer","format":"uint32","description":"The fee for the pool","minimum":0},"lpToken":{"type":"string","format":"address","description":"The LP token of the pool"},"lpTokenAmount":{"type":"string","format":"uint256","description":"The amount of LP tokens to burn"},"owner":{"type":"string","format":"address","description":"The address of the user submitting the intent"},"salt":{"type":"string","format":"bytes32","description":"Used to differentiate between intents with the same parameters"},"token0":{"type":"string","format":"address","description":"The token0 of the pool. Tokens must be sorted ascending by address so that\n`token0 < token1`; unordered intents are rejected."},"token1":{"type":"string","format":"address","description":"The token1 of the pool. Must be greater than `token0` (see `token0`)."}}}}}}
```

## The ErrorResponsePayload object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"ErrorResponsePayload":{"type":"object","description":"Structured error response returned by the API","required":["code","message"],"properties":{"code":{"type":"string","description":"Error code in SNAKE_CASE format"},"inner":{"type":["array","null"],"items":{"$ref":"#/components/schemas/InnerErrorResponsePayload"},"description":"Optional array of nested errors (only present for validation errors)"},"message":{"type":"string","description":"Human-readable error message"}}},"InnerErrorResponsePayload":{"type":"object","description":"Individual error within a validation error response","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}
```

## The GetLiquidityIntentStates object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"GetLiquidityIntentStates":{"type":"object","required":["intentHashes"],"properties":{"intentHashes":{"type":"array","items":{"type":"string","format":"bytes32"}}}}}}}
```

## The GetOrderStates object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"GetOrderStates":{"type":"object","required":["orderHashes"],"properties":{"orderHashes":{"type":"array","items":{"type":"string","format":"bytes32"}}}}}}}
```

## The GetOrderStatus object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"GetOrderStatus":{"type":"object","required":["orderHash"],"properties":{"orderHash":{"type":"string","format":"bytes32"}}}}}}
```

## The InnerErrorResponsePayload object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"InnerErrorResponsePayload":{"type":"object","description":"Individual error within a validation error response","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}
```

## The LiquidityIntentState object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"LiquidityIntentState":{"type":"object","required":["hash","intent","status"],"properties":{"hash":{"type":"string","format":"bytes32"},"intent":{"type":"object"},"status":{"$ref":"#/components/schemas/LiquidityIntentStatus"}}},"LiquidityIntentStatus":{"type":"string","enum":["Pending","Invalid","Expired","Executed","PendingCancellation","Canceled"]}}}}
```

## The LiquidityIntentStatus object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"LiquidityIntentStatus":{"type":"string","enum":["Pending","Invalid","Expired","Executed","PendingCancellation","Canceled"]}}}}
```

## The LiquidityIntentsQueryEip712 object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"LiquidityIntentsQueryEip712":{"type":"object","description":"Inner payload for querying liquidity intents.","properties":{"hashes":{"type":"array","items":{"type":"string","format":"bytes32"},"description":"Intent hashes to look up. Max 30 per request; at least one is required."}}}}}}
```

## The MeResponse object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"MeResponse":{"type":"object","required":["address","authenticated"],"properties":{"address":{"type":"string","format":"address"},"authenticated":{"type":"boolean"}}}}}}
```

## The MidPrice object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"MidPrice":{"type":"object","description":"Reference midprice for a token pair, expressed as the rational\n`numerator / denominator` in atomic units (buy-wei per sell-wei).","required":["numerator","denominator"],"properties":{"denominator":{"type":"string","format":"uint256"},"numerator":{"type":"string","format":"uint256"}}}}}}
```

## The OracleFeed object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"OracleFeed":{"oneOf":[{"type":"object","description":"Binance WebSocket depth feed.","required":["ticker_symbol"],"properties":{"ticker_symbol":{"type":"string"}}},{"type":"object","description":"Coinbase WebSocket feed.","required":["product_id"],"properties":{"product_id":{"type":"string"}}},{"type":"object","description":"Kraken WebSocket feed.","required":["wsname"],"properties":{"wsname":{"type":"string"}}},{"type":"object","description":"Lighter WebSocket feed.","required":["market_id","symbol"],"properties":{"market_id":{"type":"string"},"symbol":{"type":"string"}}},{"type":"object","description":"Hyperliquid WebSocket feed.","required":["coin"],"properties":{"coin":{"type":"string"}}},{"type":"object","description":"Bitget WebSocket feed.","required":["symbol"],"properties":{"symbol":{"type":"string"}}}],"description":"OpenAPI schema for an oracle feed entry.\n\nThe wire format flattens the oracle configuration: the map key in\n`TokenInfo.oracles` identifies the oracle (e.g. `binance`, `kraken`),\nand the value carries only the provider-specific fields — the internal\n`type` discriminator is stripped by `serialize_feeds`.\n\nThis type is documentation-only: runtime (de)serialization goes through\n[`ExchangeFeedInfo`]. The `#[serde(untagged)]` attribute here merely keeps\n`ToSchema` output consistent with the flat wire shape."}}}}
```

## The OrderAnnotations object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"OrderAnnotations":{"type":"object","description":"Additional data attached to an order, provided by the user. Purely informational —\nnot part of the signed order and not used by the protocol.","properties":{"spreadAtSubmissionHbp":{"type":["integer","null"],"format":"int64","description":"The market spread (in hundredths of a basis point, 10_000 hbp = 1%) quoted to the\nuser at submission time, as returned by `/api/quote` (`amm_spread_hbp`).","minimum":0}}}}}}
```

## The OrderDetails object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"OrderDetails":{"type":"object","description":"Display subset of an `Order`, copied verbatim onto `OrderState` at insertion time.\n\nCarrying these fields on the state record keeps display detail available even after the\nunderlying `Order` is dropped at terminal-status transition.","required":["sellToken","buyToken","sellAmount","limitPrice","startTime","endTime","spreadCurve","createdTimestamp"],"properties":{"annotations":{"$ref":"#/components/schemas/OrderAnnotations","description":"User-provided annotations, snapshotted at insertion. Defaults for order\nhistory entries persisted before annotations existed."},"buyToken":{"type":"string","format":"address"},"createdTimestamp":{"type":"string","format":"date-time"},"endTime":{"type":"string","format":"uint256"},"limitPrice":{"type":"object"},"sellAmount":{"type":"string","format":"uint256"},"sellToken":{"type":"string","format":"address"},"spreadCurve":{"$ref":"#/components/schemas/SpreadCurve","description":"Piecewise-linear curve defining the allowed mid-price deviation over the order\nwindow. Snapshotted from the submitted [`Order`] at insertion so the curve remains\navailable after the underlying `Order` is dropped."},"startTime":{"type":"string","format":"uint256"}}},"OrderAnnotations":{"type":"object","description":"Additional data attached to an order, provided by the user. Purely informational —\nnot part of the signed order and not used by the protocol.","properties":{"spreadAtSubmissionHbp":{"type":["integer","null"],"format":"int64","description":"The market spread (in hundredths of a basis point, 10_000 hbp = 1%) quoted to the\nuser at submission time, as returned by `/api/quote` (`amm_spread_hbp`).","minimum":0}}},"SpreadCurve":{"type":"object","description":"A piecewise-linear curve mapping wall-clock time to a mid-price delta in basis points.\n\nThe curve carries its own `[start_secs, end_secs]` window: the order's `start_time`\nand `end_time` must match these for validation to succeed (enforced at the API layer).\nBetween interior knots, `delta_bps` is interpolated linearly using i64 arithmetic.\nOutside the curve window the value clamps to `start_delta_bps` / `end_delta_bps`.","required":["startSecs","endSecs","startDeltaBps","endDeltaBps","points"],"properties":{"endDeltaBps":{"type":"integer","format":"int32"},"endSecs":{"type":"integer","format":"int64","minimum":0},"points":{"type":"array","items":{"$ref":"#/components/schemas/CurvePoint"}},"startDeltaBps":{"type":"integer","format":"int32"},"startSecs":{"type":"integer","format":"int64","minimum":0}}},"CurvePoint":{"type":"object","description":"A single knot in a [`SpreadCurve`], binding a wall-clock time to a delta in basis points.","required":["timeSecs","deltaBps"],"properties":{"deltaBps":{"type":"integer","format":"int32"},"timeSecs":{"type":"integer","format":"int64","minimum":0}}}}}}
```

## The OrderExecution object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"OrderExecution":{"type":"object","required":["blockNumber","soldAmount","boughtAmount","surplusBuyAmount"],"properties":{"batchId":{"type":["integer","null"],"format":"int64","minimum":0},"blockNumber":{"type":"integer","format":"int64","minimum":0},"boughtAmount":{"type":"string","format":"uint256"},"midPrice":{"type":"object","description":"Mid-price for the order's (sell → buy) pair at the clearing batch."},"soldAmount":{"type":"string","format":"uint256"},"surplusBuyAmount":{"type":"string","format":"uint256"},"txHash":{"type":["string","null"],"format":"bytes32"}}}}}}
```

## The OrderIntent object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"OrderIntent":{"type":"object","description":"The swap intent for a user","required":["owner","sellToken","buyToken","sellAmount","minBuyAmount","spreadCurve","startTime","endTime","partialFill","callData","callDataTarget","calldataSellAmountOffset","calldataBuyAmountOffset","salt"],"properties":{"buyToken":{"type":"string","format":"address","description":"The token being bought"},"callData":{"type":"string","format":"bytes","description":"Optional call data for smart orders, allowing custom routing"},"callDataTarget":{"type":"string","format":"address","description":"The address of the target contract for the calldata"},"calldataBuyAmountOffset":{"type":"integer","format":"uint64","description":"The offset of the buy amount in the calldata\nIf Some, used to update the calldata with the actual buy amount","minimum":0},"calldataSellAmountOffset":{"type":"integer","format":"uint64","description":"The offset of the sell amount in the calldata\nIf Some, used to update the calldata with the actual sell amount","minimum":0},"endTime":{"type":"integer","format":"uint256","description":"Block timestamp until when the order is valid.","minimum":0},"minBuyAmount":{"type":"string","format":"uint256","description":"The minimum amount of tokens being bought, effectively defining limit price."},"owner":{"type":"string","format":"address","description":"The address of the user submitting the order"},"partialFill":{"type":"boolean","format":"boolean","description":"Flag allowing partial fills"},"salt":{"type":"string","format":"bytes32","description":"The offset of the buy amount in the calldata\nUsed to differentiate between orders with the same parameters"},"sellAmount":{"type":"string","format":"uint196","description":"The amount of tokens being sold"},"sellToken":{"type":"string","format":"address","description":"The token being sold"},"spreadCurve":{"$ref":"#/components/schemas/SpreadCurveDto","description":"A piecewise-linear spread curve mapping a fraction of the order window to a mid-price\ndelta in basis points. Each interior point's `windowBps` is a fraction of the order\nduration in basis points (5_000 = halfway)."},"startTime":{"type":"integer","format":"uint256","description":"Block timestamp since when the order is valid","minimum":0}}},"SpreadCurveDto":{"type":"object","description":"Piecewise-linear spread curve. Interior `points` express knot positions as a fraction\nof the order's `[start_time, end_time]` window in basis points (`windowBps`), so the\ncurve is portable across orders of different durations.","required":["startDeltaBps","endDeltaBps"],"properties":{"endDeltaBps":{"type":"integer","format":"int32","description":"Mid-price delta at the order's `end_time`, in basis points."},"points":{"type":"array","items":{"$ref":"#/components/schemas/CurvePointDto"},"description":"Interior knots. Each `windowBps` must lie in `[1, 9_999]` and values must be\nstrictly increasing across `points`. Defaults to `[]` if absent, yielding a single\nlinear segment from `startDeltaBps` to `endDeltaBps`."},"startDeltaBps":{"type":"integer","format":"int32","description":"Mid-price delta at the order's `start_time`, in basis points."}}},"CurvePointDto":{"type":"object","description":"A single interior knot in a [`SpreadCurveDto`].","required":["windowBps","deltaBps"],"properties":{"deltaBps":{"type":"integer","format":"int32","description":"Mid-price delta in basis points at this knot."},"windowBps":{"type":"integer","format":"int32","description":"Knot position as a fraction of the order window in basis points. Must lie in\n`(0, 10_000)` exclusive — `5_000` is the order midpoint.","maximum":9999,"minimum":1}}}}}}
```

## The OrderIntentEip712 object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"OrderIntentEip712":{"type":"object","description":"The EIP-712 API wire form of an order intent. Unlike the session-API DTO,\nit carries no spread curve inline. The curve is a required payload-level sibling.","required":["owner","sellToken","buyToken","sellAmount","minBuyAmount","startTime","endTime","partialFill","callData","callDataTarget","salt"],"properties":{"buyToken":{"type":"string","format":"address"},"callData":{"type":"string","format":"bytes"},"callDataTarget":{"type":"string","format":"address"},"endTime":{"type":"integer","format":"uint256","description":"Block timestamp until when the order is valid","minimum":0},"minBuyAmount":{"type":"string","format":"uint256"},"owner":{"type":"string","format":"address"},"partialFill":{"type":"boolean"},"salt":{"type":"string","format":"bytes32","description":"Differentiates orders that are otherwise identical (including their full spread\ncurve); SDKs should randomize it."},"sellAmount":{"type":"string","format":"uint256"},"sellToken":{"type":"string","format":"address"},"startTime":{"type":"integer","format":"uint256","description":"Block timestamp since when the order is valid","minimum":0}}}}}}
```

## The OrderState object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"OrderState":{"type":"object","required":["hash","owner","status","execution","orderDetails"],"properties":{"execution":{"type":"array","items":{"$ref":"#/components/schemas/OrderExecution"}},"hash":{"type":"string","format":"bytes32"},"orderDetails":{"$ref":"#/components/schemas/OrderDetails"},"owner":{"type":"string","format":"address"},"status":{"$ref":"#/components/schemas/OrderStatus"}}},"OrderExecution":{"type":"object","required":["blockNumber","soldAmount","boughtAmount","surplusBuyAmount"],"properties":{"batchId":{"type":["integer","null"],"format":"int64","minimum":0},"blockNumber":{"type":"integer","format":"int64","minimum":0},"boughtAmount":{"type":"string","format":"uint256"},"midPrice":{"type":"object","description":"Mid-price for the order's (sell → buy) pair at the clearing batch."},"soldAmount":{"type":"string","format":"uint256"},"surplusBuyAmount":{"type":"string","format":"uint256"},"txHash":{"type":["string","null"],"format":"bytes32"}}},"OrderDetails":{"type":"object","description":"Display subset of an `Order`, copied verbatim onto `OrderState` at insertion time.\n\nCarrying these fields on the state record keeps display detail available even after the\nunderlying `Order` is dropped at terminal-status transition.","required":["sellToken","buyToken","sellAmount","limitPrice","startTime","endTime","spreadCurve","createdTimestamp"],"properties":{"annotations":{"$ref":"#/components/schemas/OrderAnnotations","description":"User-provided annotations, snapshotted at insertion. Defaults for order\nhistory entries persisted before annotations existed."},"buyToken":{"type":"string","format":"address"},"createdTimestamp":{"type":"string","format":"date-time"},"endTime":{"type":"string","format":"uint256"},"limitPrice":{"type":"object"},"sellAmount":{"type":"string","format":"uint256"},"sellToken":{"type":"string","format":"address"},"spreadCurve":{"$ref":"#/components/schemas/SpreadCurve","description":"Piecewise-linear curve defining the allowed mid-price deviation over the order\nwindow. Snapshotted from the submitted [`Order`] at insertion so the curve remains\navailable after the underlying `Order` is dropped."},"startTime":{"type":"string","format":"uint256"}}},"OrderAnnotations":{"type":"object","description":"Additional data attached to an order, provided by the user. Purely informational —\nnot part of the signed order and not used by the protocol.","properties":{"spreadAtSubmissionHbp":{"type":["integer","null"],"format":"int64","description":"The market spread (in hundredths of a basis point, 10_000 hbp = 1%) quoted to the\nuser at submission time, as returned by `/api/quote` (`amm_spread_hbp`).","minimum":0}}},"SpreadCurve":{"type":"object","description":"A piecewise-linear curve mapping wall-clock time to a mid-price delta in basis points.\n\nThe curve carries its own `[start_secs, end_secs]` window: the order's `start_time`\nand `end_time` must match these for validation to succeed (enforced at the API layer).\nBetween interior knots, `delta_bps` is interpolated linearly using i64 arithmetic.\nOutside the curve window the value clamps to `start_delta_bps` / `end_delta_bps`.","required":["startSecs","endSecs","startDeltaBps","endDeltaBps","points"],"properties":{"endDeltaBps":{"type":"integer","format":"int32"},"endSecs":{"type":"integer","format":"int64","minimum":0},"points":{"type":"array","items":{"$ref":"#/components/schemas/CurvePoint"}},"startDeltaBps":{"type":"integer","format":"int32"},"startSecs":{"type":"integer","format":"int64","minimum":0}}},"CurvePoint":{"type":"object","description":"A single knot in a [`SpreadCurve`], binding a wall-clock time to a delta in basis points.","required":["timeSecs","deltaBps"],"properties":{"deltaBps":{"type":"integer","format":"int32"},"timeSecs":{"type":"integer","format":"int64","minimum":0}}},"OrderStatus":{"type":"string","enum":["Active","Invalid","Expired","Filled","PendingCancellation","Canceled","Incompatible"]}}}}
```

## The OrderStatus object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"OrderStatus":{"type":"string","enum":["Active","Invalid","Expired","Filled","PendingCancellation","Canceled","Incompatible"]}}}}
```

## The OrdersQueryEip712 object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"OrdersQueryEip712":{"type":"object","description":"Inner payload of GET orders endpoint.\nField semantics mirror the session API's query parameters; zero values mean \"unset\".","properties":{"cursor":{"type":"string","description":"Opaque cursor from a previous response; empty for the first page."},"hashes":{"type":"array","items":{"type":"string","format":"bytes32"},"description":"Direct lookup by order hash. Max 30 per request; pagination is skipped."},"limit":{"type":"integer","format":"int64","description":"Max orders per page; 0 selects the server default, and larger values are capped.","minimum":0},"statuses":{"type":"array","items":{"$ref":"#/components/schemas/OrderStatus"},"description":"Filter by order status. Multiple values are OR'd."}}},"OrderStatus":{"type":"string","enum":["Active","Invalid","Expired","Filled","PendingCancellation","Canceled","Incompatible"]}}}}
```

## The OrdersResponse object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"OrdersResponse":{"type":"object","description":"Response body for `GET /api/orders`.","required":["orders","hasMore"],"properties":{"cursor":{"type":["string","null"],"description":"Opaque token to pass back as `cursor` in the next request. `null` on the last page."},"hasMore":{"type":"boolean","description":"Mirrors `cursor.is_some()`."},"orders":{"type":"array","items":{"$ref":"#/components/schemas/OrderState"}}}},"OrderState":{"type":"object","required":["hash","owner","status","execution","orderDetails"],"properties":{"execution":{"type":"array","items":{"$ref":"#/components/schemas/OrderExecution"}},"hash":{"type":"string","format":"bytes32"},"orderDetails":{"$ref":"#/components/schemas/OrderDetails"},"owner":{"type":"string","format":"address"},"status":{"$ref":"#/components/schemas/OrderStatus"}}},"OrderExecution":{"type":"object","required":["blockNumber","soldAmount","boughtAmount","surplusBuyAmount"],"properties":{"batchId":{"type":["integer","null"],"format":"int64","minimum":0},"blockNumber":{"type":"integer","format":"int64","minimum":0},"boughtAmount":{"type":"string","format":"uint256"},"midPrice":{"type":"object","description":"Mid-price for the order's (sell → buy) pair at the clearing batch."},"soldAmount":{"type":"string","format":"uint256"},"surplusBuyAmount":{"type":"string","format":"uint256"},"txHash":{"type":["string","null"],"format":"bytes32"}}},"OrderDetails":{"type":"object","description":"Display subset of an `Order`, copied verbatim onto `OrderState` at insertion time.\n\nCarrying these fields on the state record keeps display detail available even after the\nunderlying `Order` is dropped at terminal-status transition.","required":["sellToken","buyToken","sellAmount","limitPrice","startTime","endTime","spreadCurve","createdTimestamp"],"properties":{"annotations":{"$ref":"#/components/schemas/OrderAnnotations","description":"User-provided annotations, snapshotted at insertion. Defaults for order\nhistory entries persisted before annotations existed."},"buyToken":{"type":"string","format":"address"},"createdTimestamp":{"type":"string","format":"date-time"},"endTime":{"type":"string","format":"uint256"},"limitPrice":{"type":"object"},"sellAmount":{"type":"string","format":"uint256"},"sellToken":{"type":"string","format":"address"},"spreadCurve":{"$ref":"#/components/schemas/SpreadCurve","description":"Piecewise-linear curve defining the allowed mid-price deviation over the order\nwindow. Snapshotted from the submitted [`Order`] at insertion so the curve remains\navailable after the underlying `Order` is dropped."},"startTime":{"type":"string","format":"uint256"}}},"OrderAnnotations":{"type":"object","description":"Additional data attached to an order, provided by the user. Purely informational —\nnot part of the signed order and not used by the protocol.","properties":{"spreadAtSubmissionHbp":{"type":["integer","null"],"format":"int64","description":"The market spread (in hundredths of a basis point, 10_000 hbp = 1%) quoted to the\nuser at submission time, as returned by `/api/quote` (`amm_spread_hbp`).","minimum":0}}},"SpreadCurve":{"type":"object","description":"A piecewise-linear curve mapping wall-clock time to a mid-price delta in basis points.\n\nThe curve carries its own `[start_secs, end_secs]` window: the order's `start_time`\nand `end_time` must match these for validation to succeed (enforced at the API layer).\nBetween interior knots, `delta_bps` is interpolated linearly using i64 arithmetic.\nOutside the curve window the value clamps to `start_delta_bps` / `end_delta_bps`.","required":["startSecs","endSecs","startDeltaBps","endDeltaBps","points"],"properties":{"endDeltaBps":{"type":"integer","format":"int32"},"endSecs":{"type":"integer","format":"int64","minimum":0},"points":{"type":"array","items":{"$ref":"#/components/schemas/CurvePoint"}},"startDeltaBps":{"type":"integer","format":"int32"},"startSecs":{"type":"integer","format":"int64","minimum":0}}},"CurvePoint":{"type":"object","description":"A single knot in a [`SpreadCurve`], binding a wall-clock time to a delta in basis points.","required":["timeSecs","deltaBps"],"properties":{"deltaBps":{"type":"integer","format":"int32"},"timeSecs":{"type":"integer","format":"int64","minimum":0}}},"OrderStatus":{"type":"string","enum":["Active","Invalid","Expired","Filled","PendingCancellation","Canceled","Incompatible"]}}}}
```

## The PermitBatchTransferFrom object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"PermitBatchTransferFrom":{"type":"object","description":"DTO for ISignatureTransfer.PermitBatchTransferFrom - batch token transfer authorization","required":["permitted","nonce","deadline"],"properties":{"deadline":{"type":"string","format":"uint256"},"nonce":{"type":"string","format":"uint256"},"permitted":{"type":"array","items":{"$ref":"#/components/schemas/TokenPermissions"}}}},"TokenPermissions":{"type":"object","description":"Token permissions for ISignatureTransfer permits.\nUsed in PermitTransferFrom and PermitBatchTransferFrom.","required":["token","amount"],"properties":{"amount":{"type":"string","format":"uint256","description":"The maximum amount that can be transferred"},"token":{"type":"string","format":"address","description":"The token address"}}}}}}
```

## The PermitDetails object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"PermitDetails":{"type":"object","required":["token","amount","expiration","nonce"],"properties":{"amount":{"type":"string","format":"uint160"},"expiration":{"type":"integer","format":"uint64","minimum":0},"nonce":{"type":"integer","format":"uint64","minimum":0},"token":{"type":"string","format":"address"}}}}}}
```

## The PermitSignature object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"PermitSignature":{"type":"object","required":["r","s","yParity"],"properties":{"r":{"type":"string","format":"uint256"},"s":{"type":"string","format":"uint256"},"yParity":{"type":"boolean","format":"boolean"}}}}}}
```

## The PermitSingle object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"PermitSingle":{"type":"object","required":["details","spender","sigDeadline"],"properties":{"details":{"$ref":"#/components/schemas/PermitDetails"},"sigDeadline":{"type":"string","format":"uint256"},"spender":{"type":"string","format":"address"}}},"PermitDetails":{"type":"object","required":["token","amount","expiration","nonce"],"properties":{"amount":{"type":"string","format":"uint160"},"expiration":{"type":"integer","format":"uint64","minimum":0},"nonce":{"type":"integer","format":"uint64","minimum":0},"token":{"type":"string","format":"address"}}}}}}
```

## The PermitTransferFrom object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"PermitTransferFrom":{"type":"object","description":"DTO for ISignatureTransfer.PermitTransferFrom - single token transfer authorization","required":["permitted","nonce","deadline"],"properties":{"deadline":{"type":"string","format":"uint256"},"nonce":{"type":"string","format":"uint256"},"permitted":{"$ref":"#/components/schemas/TokenPermissions"}}},"TokenPermissions":{"type":"object","description":"Token permissions for ISignatureTransfer permits.\nUsed in PermitTransferFrom and PermitBatchTransferFrom.","required":["token","amount"],"properties":{"amount":{"type":"string","format":"uint256","description":"The maximum amount that can be transferred"},"token":{"type":"string","format":"address","description":"The token address"}}}}}}
```

## The QuoteFee object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"QuoteFee":{"type":"object","description":"Protocol fee for a quoted trade.\n\n`total_amount` is in `buy_token` atomic units, computed against the\ngross `buy_amount` Fynd quoted.","required":["totalHbp","totalAmount"],"properties":{"totalAmount":{"type":"string","format":"uint256","description":"Absolute fee in `buy_token` atomic units."},"totalHbp":{"type":"integer","format":"int64","description":"Sum of fee components in hundredths of a basis point. 10_000 hbp = 1%.","minimum":0}}}}}}
```

## The QuoteRequest object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"QuoteRequest":{"type":"object","description":"Minimal inputs for a Fynd passthrough quote.","required":["sellToken","buyToken","sellAmount"],"properties":{"buyToken":{"type":"string","format":"address"},"sellAmount":{"type":"string","format":"uint256"},"sellToken":{"type":"string","format":"address"}}}}}}
```

## The QuoteResponse object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"QuoteResponse":{"type":"object","description":"Pared-down Fynd quote: amounts, gas, and a reference midprice.","required":["sellToken","buyToken","sellAmount","buyAmount","ammSpreadHbp","spread","midPrice","fee"],"properties":{"ammSpreadHbp":{"type":"integer","format":"int64","description":"DEX-derived spread in hundredths of a basis point. 10_000 hbp = 1%.\nDeprecated: use `spread` instead.","minimum":0},"buyAmount":{"type":"string","format":"uint256"},"buyToken":{"type":"string","format":"address"},"fee":{"$ref":"#/components/schemas/QuoteFee","description":"Protocol fee for this trade."},"midPrice":{"$ref":"#/components/schemas/MidPrice","description":"Oracle-aggregated reference midprice for `sell_token → buy_token`."},"sellAmount":{"type":"string","format":"uint256"},"sellToken":{"type":"string","format":"address"},"spread":{"$ref":"#/components/schemas/QuoteResponseSpread"}}},"QuoteFee":{"type":"object","description":"Protocol fee for a quoted trade.\n\n`total_amount` is in `buy_token` atomic units, computed against the\ngross `buy_amount` Fynd quoted.","required":["totalHbp","totalAmount"],"properties":{"totalAmount":{"type":"string","format":"uint256","description":"Absolute fee in `buy_token` atomic units."},"totalHbp":{"type":"integer","format":"int64","description":"Sum of fee components in hundredths of a basis point. 10_000 hbp = 1%.","minimum":0}}},"MidPrice":{"type":"object","description":"Reference midprice for a token pair, expressed as the rational\n`numerator / denominator` in atomic units (buy-wei per sell-wei).","required":["numerator","denominator"],"properties":{"denominator":{"type":"string","format":"uint256"},"numerator":{"type":"string","format":"uint256"}}},"QuoteResponseSpread":{"type":"object","required":["ammSpreadHbp","gasHbp"],"properties":{"ammSpreadHbp":{"type":"integer","format":"int64","description":"DEX-derived spread in hundredths of a basis point. 10_000 hbp = 1%.","minimum":0},"gasHbp":{"type":"integer","format":"int64","description":"Extra spread due to gas cost. Add to `amm_spread_hbp` to get the spread accounting for gas.","minimum":0}}}}}}
```

## The QuoteResponseSpread object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"QuoteResponseSpread":{"type":"object","required":["ammSpreadHbp","gasHbp"],"properties":{"ammSpreadHbp":{"type":"integer","format":"int64","description":"DEX-derived spread in hundredths of a basis point. 10_000 hbp = 1%.","minimum":0},"gasHbp":{"type":"integer","format":"int64","description":"Extra spread due to gas cost. Add to `amm_spread_hbp` to get the spread accounting for gas.","minimum":0}}}}}}
```

## The RemoveLiquidity object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"RemoveLiquidity":{"type":"object","description":"Represents a liquidity removal with SignatureTransfer permit functionality.\n\nThis struct encapsulates all necessary information needed to submit a new intent to remove\nliquidity to the system, including the intent and SignatureTransfer permit data for approvals.\nUser authentication is handled via session-based SIWE verification.\n\n# Fields\n* `remove_liquidity` The intent to remove liquidity\n* `permit_lp_token` The SignatureTransfer permit and signature for the LP token","required":["removeLiquidity","permitLpToken"],"properties":{"permitLpToken":{"$ref":"#/components/schemas/SignedSignatureTransferDTO"},"removeLiquidity":{"$ref":"#/components/schemas/RemoveLiquidityIntent"}}},"SignedSignatureTransferDTO":{"type":"object","description":"Signed single SignatureTransfer permit (for remove liquidity LP token transfer)","required":["permit","signature"],"properties":{"permit":{"$ref":"#/components/schemas/PermitTransferFrom"},"signature":{"$ref":"#/components/schemas/PermitSignature"}}},"PermitTransferFrom":{"type":"object","description":"DTO for ISignatureTransfer.PermitTransferFrom - single token transfer authorization","required":["permitted","nonce","deadline"],"properties":{"deadline":{"type":"string","format":"uint256"},"nonce":{"type":"string","format":"uint256"},"permitted":{"$ref":"#/components/schemas/TokenPermissions"}}},"TokenPermissions":{"type":"object","description":"Token permissions for ISignatureTransfer permits.\nUsed in PermitTransferFrom and PermitBatchTransferFrom.","required":["token","amount"],"properties":{"amount":{"type":"string","format":"uint256","description":"The maximum amount that can be transferred"},"token":{"type":"string","format":"address","description":"The token address"}}},"PermitSignature":{"type":"object","required":["r","s","yParity"],"properties":{"r":{"type":"string","format":"uint256"},"s":{"type":"string","format":"uint256"},"yParity":{"type":"boolean","format":"boolean"}}},"RemoveLiquidityIntent":{"type":"object","required":["owner","token0","token1","fee","lpToken","lpTokenAmount","salt"],"properties":{"fee":{"type":"integer","format":"uint32","description":"The fee for the pool","minimum":0},"lpToken":{"type":"string","format":"address","description":"The LP token of the pool"},"lpTokenAmount":{"type":"string","format":"uint256","description":"The amount of LP tokens to burn"},"owner":{"type":"string","format":"address","description":"The address of the user submitting the intent"},"salt":{"type":"string","format":"bytes32","description":"Used to differentiate between intents with the same parameters"},"token0":{"type":"string","format":"address","description":"The token0 of the pool. Tokens must be sorted ascending by address so that\n`token0 < token1`; unordered intents are rejected."},"token1":{"type":"string","format":"address","description":"The token1 of the pool. Must be greater than `token0` (see `token0`)."}}}}}}
```

## The RemoveLiquidityEip712 object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"RemoveLiquidityEip712":{"allOf":[{"$ref":"#/components/schemas/RemoveLiquidity"}],"description":"Inner payload of remove_liquidity endpoint."},"RemoveLiquidity":{"type":"object","description":"Represents a liquidity removal with SignatureTransfer permit functionality.\n\nThis struct encapsulates all necessary information needed to submit a new intent to remove\nliquidity to the system, including the intent and SignatureTransfer permit data for approvals.\nUser authentication is handled via session-based SIWE verification.\n\n# Fields\n* `remove_liquidity` The intent to remove liquidity\n* `permit_lp_token` The SignatureTransfer permit and signature for the LP token","required":["removeLiquidity","permitLpToken"],"properties":{"permitLpToken":{"$ref":"#/components/schemas/SignedSignatureTransferDTO"},"removeLiquidity":{"$ref":"#/components/schemas/RemoveLiquidityIntent"}}},"SignedSignatureTransferDTO":{"type":"object","description":"Signed single SignatureTransfer permit (for remove liquidity LP token transfer)","required":["permit","signature"],"properties":{"permit":{"$ref":"#/components/schemas/PermitTransferFrom"},"signature":{"$ref":"#/components/schemas/PermitSignature"}}},"PermitTransferFrom":{"type":"object","description":"DTO for ISignatureTransfer.PermitTransferFrom - single token transfer authorization","required":["permitted","nonce","deadline"],"properties":{"deadline":{"type":"string","format":"uint256"},"nonce":{"type":"string","format":"uint256"},"permitted":{"$ref":"#/components/schemas/TokenPermissions"}}},"TokenPermissions":{"type":"object","description":"Token permissions for ISignatureTransfer permits.\nUsed in PermitTransferFrom and PermitBatchTransferFrom.","required":["token","amount"],"properties":{"amount":{"type":"string","format":"uint256","description":"The maximum amount that can be transferred"},"token":{"type":"string","format":"address","description":"The token address"}}},"PermitSignature":{"type":"object","required":["r","s","yParity"],"properties":{"r":{"type":"string","format":"uint256"},"s":{"type":"string","format":"uint256"},"yParity":{"type":"boolean","format":"boolean"}}},"RemoveLiquidityIntent":{"type":"object","required":["owner","token0","token1","fee","lpToken","lpTokenAmount","salt"],"properties":{"fee":{"type":"integer","format":"uint32","description":"The fee for the pool","minimum":0},"lpToken":{"type":"string","format":"address","description":"The LP token of the pool"},"lpTokenAmount":{"type":"string","format":"uint256","description":"The amount of LP tokens to burn"},"owner":{"type":"string","format":"address","description":"The address of the user submitting the intent"},"salt":{"type":"string","format":"bytes32","description":"Used to differentiate between intents with the same parameters"},"token0":{"type":"string","format":"address","description":"The token0 of the pool. Tokens must be sorted ascending by address so that\n`token0 < token1`; unordered intents are rejected."},"token1":{"type":"string","format":"address","description":"The token1 of the pool. Must be greater than `token0` (see `token0`)."}}}}}}
```

## The RemoveLiquidityIntent object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"RemoveLiquidityIntent":{"type":"object","required":["owner","token0","token1","fee","lpToken","lpTokenAmount","salt"],"properties":{"fee":{"type":"integer","format":"uint32","description":"The fee for the pool","minimum":0},"lpToken":{"type":"string","format":"address","description":"The LP token of the pool"},"lpTokenAmount":{"type":"string","format":"uint256","description":"The amount of LP tokens to burn"},"owner":{"type":"string","format":"address","description":"The address of the user submitting the intent"},"salt":{"type":"string","format":"bytes32","description":"Used to differentiate between intents with the same parameters"},"token0":{"type":"string","format":"address","description":"The token0 of the pool. Tokens must be sorted ascending by address so that\n`token0 < token1`; unordered intents are rejected."},"token1":{"type":"string","format":"address","description":"The token1 of the pool. Must be greater than `token0` (see `token0`)."}}}}}}
```

## The RemoveLiquidityResponse object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"RemoveLiquidityResponse":{"type":"object","description":"Response returned after successfully submitting an intent to the system.\nContains the unique identifier assigned to the submitted intent.\n\n# Fields\n* `hash` - Unique identifier assigned to the submitted intent","required":["intentHash"],"properties":{"intentHash":{"type":"string","format":"bytes32"}}}}}}
```

## The SignedBatchSignatureTransferDTO object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"SignedBatchSignatureTransferDTO":{"type":"object","description":"Signed batch SignatureTransfer permit (for add liquidity token0 and token1 transfer)","required":["permit","signature"],"properties":{"permit":{"$ref":"#/components/schemas/PermitBatchTransferFrom"},"signature":{"$ref":"#/components/schemas/PermitSignature"}}},"PermitBatchTransferFrom":{"type":"object","description":"DTO for ISignatureTransfer.PermitBatchTransferFrom - batch token transfer authorization","required":["permitted","nonce","deadline"],"properties":{"deadline":{"type":"string","format":"uint256"},"nonce":{"type":"string","format":"uint256"},"permitted":{"type":"array","items":{"$ref":"#/components/schemas/TokenPermissions"}}}},"TokenPermissions":{"type":"object","description":"Token permissions for ISignatureTransfer permits.\nUsed in PermitTransferFrom and PermitBatchTransferFrom.","required":["token","amount"],"properties":{"amount":{"type":"string","format":"uint256","description":"The maximum amount that can be transferred"},"token":{"type":"string","format":"address","description":"The token address"}}},"PermitSignature":{"type":"object","required":["r","s","yParity"],"properties":{"r":{"type":"string","format":"uint256"},"s":{"type":"string","format":"uint256"},"yParity":{"type":"boolean","format":"boolean"}}}}}}
```

## The SignedPermitDTO object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"SignedPermitDTO":{"type":"object","required":["permit","signature"],"properties":{"permit":{"$ref":"#/components/schemas/PermitSingle"},"signature":{"$ref":"#/components/schemas/PermitSignature"}}},"PermitSingle":{"type":"object","required":["details","spender","sigDeadline"],"properties":{"details":{"$ref":"#/components/schemas/PermitDetails"},"sigDeadline":{"type":"string","format":"uint256"},"spender":{"type":"string","format":"address"}}},"PermitDetails":{"type":"object","required":["token","amount","expiration","nonce"],"properties":{"amount":{"type":"string","format":"uint160"},"expiration":{"type":"integer","format":"uint64","minimum":0},"nonce":{"type":"integer","format":"uint64","minimum":0},"token":{"type":"string","format":"address"}}},"PermitSignature":{"type":"object","required":["r","s","yParity"],"properties":{"r":{"type":"string","format":"uint256"},"s":{"type":"string","format":"uint256"},"yParity":{"type":"boolean","format":"boolean"}}}}}}
```

## The SignedSignatureTransferDTO object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"SignedSignatureTransferDTO":{"type":"object","description":"Signed single SignatureTransfer permit (for remove liquidity LP token transfer)","required":["permit","signature"],"properties":{"permit":{"$ref":"#/components/schemas/PermitTransferFrom"},"signature":{"$ref":"#/components/schemas/PermitSignature"}}},"PermitTransferFrom":{"type":"object","description":"DTO for ISignatureTransfer.PermitTransferFrom - single token transfer authorization","required":["permitted","nonce","deadline"],"properties":{"deadline":{"type":"string","format":"uint256"},"nonce":{"type":"string","format":"uint256"},"permitted":{"$ref":"#/components/schemas/TokenPermissions"}}},"TokenPermissions":{"type":"object","description":"Token permissions for ISignatureTransfer permits.\nUsed in PermitTransferFrom and PermitBatchTransferFrom.","required":["token","amount"],"properties":{"amount":{"type":"string","format":"uint256","description":"The maximum amount that can be transferred"},"token":{"type":"string","format":"address","description":"The token address"}}},"PermitSignature":{"type":"object","required":["r","s","yParity"],"properties":{"r":{"type":"string","format":"uint256"},"s":{"type":"string","format":"uint256"},"yParity":{"type":"boolean","format":"boolean"}}}}}}
```

## The SiweVerify object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"SiweVerify":{"type":"object","required":["message","signature"],"properties":{"message":{"type":"string"},"signature":{"type":"string"}}}}}}
```

## The SpreadCurve object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"SpreadCurve":{"type":"object","description":"A piecewise-linear curve mapping wall-clock time to a mid-price delta in basis points.\n\nThe curve carries its own `[start_secs, end_secs]` window: the order's `start_time`\nand `end_time` must match these for validation to succeed (enforced at the API layer).\nBetween interior knots, `delta_bps` is interpolated linearly using i64 arithmetic.\nOutside the curve window the value clamps to `start_delta_bps` / `end_delta_bps`.","required":["startSecs","endSecs","startDeltaBps","endDeltaBps","points"],"properties":{"endDeltaBps":{"type":"integer","format":"int32"},"endSecs":{"type":"integer","format":"int64","minimum":0},"points":{"type":"array","items":{"$ref":"#/components/schemas/CurvePoint"}},"startDeltaBps":{"type":"integer","format":"int32"},"startSecs":{"type":"integer","format":"int64","minimum":0}}},"CurvePoint":{"type":"object","description":"A single knot in a [`SpreadCurve`], binding a wall-clock time to a delta in basis points.","required":["timeSecs","deltaBps"],"properties":{"deltaBps":{"type":"integer","format":"int32"},"timeSecs":{"type":"integer","format":"int64","minimum":0}}}}}}
```

## The SpreadCurveDto object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"SpreadCurveDto":{"type":"object","description":"Piecewise-linear spread curve. Interior `points` express knot positions as a fraction\nof the order's `[start_time, end_time]` window in basis points (`windowBps`), so the\ncurve is portable across orders of different durations.","required":["startDeltaBps","endDeltaBps"],"properties":{"endDeltaBps":{"type":"integer","format":"int32","description":"Mid-price delta at the order's `end_time`, in basis points."},"points":{"type":"array","items":{"$ref":"#/components/schemas/CurvePointDto"},"description":"Interior knots. Each `windowBps` must lie in `[1, 9_999]` and values must be\nstrictly increasing across `points`. Defaults to `[]` if absent, yielding a single\nlinear segment from `startDeltaBps` to `endDeltaBps`."},"startDeltaBps":{"type":"integer","format":"int32","description":"Mid-price delta at the order's `start_time`, in basis points."}}},"CurvePointDto":{"type":"object","description":"A single interior knot in a [`SpreadCurveDto`].","required":["windowBps","deltaBps"],"properties":{"deltaBps":{"type":"integer","format":"int32","description":"Mid-price delta in basis points at this knot."},"windowBps":{"type":"integer","format":"int32","description":"Knot position as a fraction of the order window in basis points. Must lie in\n`(0, 10_000)` exclusive — `5_000` is the order midpoint.","maximum":9999,"minimum":1}}}}}}
```

## The TokenClass object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"TokenClass":{"type":"string","enum":["Stable","Regular","Meme"]}}}}
```

## The TokenInfo object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"TokenInfo":{"type":"object","description":"Public metadata for a token in the allowlist, returned by `/api/config`.","required":["address","symbol","decimals"],"properties":{"address":{"type":"string","format":"address"},"class":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/TokenClass"}]},"decimals":{"type":"integer","format":"int32","minimum":0},"oracles":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/OracleFeed"},"propertyNames":{"type":"string"}},"symbol":{"type":"string"}}},"TokenClass":{"type":"string","enum":["Stable","Regular","Meme"]},"OracleFeed":{"oneOf":[{"type":"object","description":"Binance WebSocket depth feed.","required":["ticker_symbol"],"properties":{"ticker_symbol":{"type":"string"}}},{"type":"object","description":"Coinbase WebSocket feed.","required":["product_id"],"properties":{"product_id":{"type":"string"}}},{"type":"object","description":"Kraken WebSocket feed.","required":["wsname"],"properties":{"wsname":{"type":"string"}}},{"type":"object","description":"Lighter WebSocket feed.","required":["market_id","symbol"],"properties":{"market_id":{"type":"string"},"symbol":{"type":"string"}}},{"type":"object","description":"Hyperliquid WebSocket feed.","required":["coin"],"properties":{"coin":{"type":"string"}}},{"type":"object","description":"Bitget WebSocket feed.","required":["symbol"],"properties":{"symbol":{"type":"string"}}}],"description":"OpenAPI schema for an oracle feed entry.\n\nThe wire format flattens the oracle configuration: the map key in\n`TokenInfo.oracles` identifies the oracle (e.g. `binance`, `kraken`),\nand the value carries only the provider-specific fields — the internal\n`type` discriminator is stripped by `serialize_feeds`.\n\nThis type is documentation-only: runtime (de)serialization goes through\n[`ExchangeFeedInfo`]. The `#[serde(untagged)]` attribute here merely keeps\n`ToSchema` output consistent with the flat wire shape."}}}}
```

## The TokenPermissions object

```json
{"openapi":"3.1.0","info":{"title":"turbine","version":"0.156.0"},"components":{"schemas":{"TokenPermissions":{"type":"object","description":"Token permissions for ISignatureTransfer permits.\nUsed in PermitTransferFrom and PermitBatchTransferFrom.","required":["token","amount"],"properties":{"amount":{"type":"string","format":"uint256","description":"The maximum amount that can be transferred"},"token":{"type":"string","format":"address","description":"The token address"}}}}}}
```


# Authentication

Turbine supports two types of authentication: SIWE and EIP-712

## Session-Based SIWE Authentication

Turbine authenticates users with sessions. The session ID arrives in a `Set-Cookie` response header. Send `id=<SESSION_ID>` as a cookie with each subsequent request.

{% hint style="success" %}
The [Typescript SDK](https://github.com/propeller-heads/turbine-sdk) handles authentication automatically.

The authentication flow below is implemented in [`TurbineClient.ensureAuthenticated()`](https://github.com/propeller-heads/turbine-sdk/blob/main/src/turbineClient.ts#L1057).
{% endhint %}

### Check Authentication Status

Make a GET request to `/api/me` (see the [Swagger UI](https://api.turbine.exchange/api/swagger-ui/)). Include the session ID in a `Cookie` header if you have one.

```bash
curl 'https://staging-api.turbine.exchange/api/me' \
  -H 'content-type: application/json' \
  -b 'id=<SESSION_ID>'
```

A `200 OK` response means you are authenticated.

A `401 Unauthorized` response means you must authenticate.

### Authenticate

{% stepper %}
{% step %}
**Get nonce**

Make a POST request to `/api/nonce` (see the [Swagger UI](https://api.turbine.exchange/api/swagger-ui/)).

```bash
curl 'https://staging-api.turbine.exchange/api/nonce' \
  -X 'POST'
```

The response contains a nonce, for example:

```bash
"uE6LoICEKw01JjApm"
```

{% endstep %}

{% step %}
**Sign a SIWE message**

Turbine uses the [Sign-In With Ethereum](https://login.xyz/) (SIWE) standard.

Sign a specific message containing the nonce with your Ethereum wallet.

The message format is:

```
app.turbine.exchange wants you to sign in with your Ethereum account:
<YOUR_WALLET_ADDRESS>

Sign in to Turbine with your Ethereum wallet

URI: https://api.turbine.exchange/api
Version: 1
Chain ID: 1
Nonce: <YOUR_NONCE>
Issued At: <TIMESTAMP>
```

{% endstep %}

{% step %}
**Verify the signature**

Make a POST request to `/api/verify` (see the [Swagger UI](https://api.turbine.exchange/api/swagger-ui/)).

The body must contain the signed message and the signature:

```json
{
    "message": "...message with newlines represented as \n...",
    "signature": {
        "r": "0x...",
        "s": "0x...",
        "yParity": "0x...",
        "v": "0x..."
    }
}
```

If verification passes, the response includes a `Set-Cookie` header with session ID `id=...`.
{% endstep %}
{% endstepper %}

### Make an Authenticated Request

Include a `Cookie` header with `id=<YOUR_SESSION_ID>` in subsequent requests. For example, check your authentication status again with `/api/me`.

### Session Expiration

A session is valid for 5 minutes.

Keep it active by making authenticated requests before it expires, for example by calling `/api/me`. Each call before expiration extends the session by another 5 minutes.

## EIP-712 Authentication

Authenticate by signing each message according to [EIP-712 standard](https://eips.ethereum.org/EIPS/eip-712). No session, no cookies. Useful for automated workflows.

{% hint style="success" %}
The [Typescript SDK](/reference/api/turbine-sdk) handles EIP-712 authentication automatically starting with version 0.33.0.
{% endhint %}

### Endpoints supporting EIP-712

Endpoints that support EIP-712 authentication have an `/api/eip712/` prefix.

Each endpoint that requires authentication has an EIP-712 sibling. E.g. there is `/api/add_order` that supports SIWE authentication and `/api/eip712/add_order` that supports EIP-712 authentication.

See [API Specification](/reference) for the list of all endpoints.

### Construct a Request Body

Body of a request to each EIP-712 endpoint has the following structure:

```json
{
  "auth": {
    "deadline": 1760000600,
    "nonce": "8421337650123",
    "signature": {
      "r": "70329708892606663774035422698344599545311796398002379995821142342730166717036",
      "s": "55059184936589248349789125767339054890704358390709680883308715042461925991610",
      "yParity": false
    },
    "signer": "0xBE69d72ca5f88aCba033a063dF5DBe43a4148De0"
  },
  "payload": {
    "field1": ["string"],
    "field2": 123
  }
}
```

#### What to put in \`signature\`?

For each EIP-712 endpoint, there is a specific struct to sign.

In most cases, the signed struct consists of all `payload`'s fields + `deadline` and `nonce`.

The exact structs are defined in a Solidity code snippet below.

<details>

<summary>Signed Types definitions</summary>

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// Canonical EIP-712 typed-data structs for the Turbine signed API (`/api/eip712/*`).
//
// This is the single source of truth for the signing types. SDK authors can copy these struct
// definitions verbatim to build the EIP-712 `types` their signing library expects. Field names,
// order, and types here define the EIP-712 type hashes.

/// Sign this: EIP-712 signed envelope for order submission.
struct AddOrder {
    OrderIntent order;
    uint64 nonce;
    uint64 deadline;
}

/// A struct for the order intent
///
/// # Fields
/// * `owner` - The address of the order creator and owner
/// * `sellToken` - The token address being sold
/// * `buyToken` - The token address being bought
/// * `sellAmount` - The amount of sellToken to sell
/// * `minBuyAmount` - The minimum amount of buyToken to receive
/// * `startDeltaBps` - The spread curve's delta at the order start, in basis points (100 = 1%)
/// * `endDeltaBps` - The spread curve's delta at the order end, in basis points (100 = 1%)
/// * `points` - The spread curve's interior knots
/// * `startTime` - Unix timestamp when the order becomes valid
/// * `endTime` - Unix timestamp when the order expires
/// * `partialFill` - Whether partial fills of the order are allowed
/// * `callData` - Optional call data for smart orders
/// * `callDataTarget` - Target contract address for the call data
/// * `salt` - Arbitrary value differentiating orders whose other fields are the same
struct OrderIntent {
    address owner;
    address sellToken;
    address buyToken;
    uint256 sellAmount;
    uint256 minBuyAmount;
    int32 startDeltaBps;
    int32 endDeltaBps;
    SpreadCurvePoint[] points;
    uint256 startTime;
    uint256 endTime;
    bool partialFill;
    bytes callData;
    address callDataTarget;
    bytes32 salt;
}

/// A single knot of an order's spread curve
///
/// # Fields
/// * `timeSecs` - Absolute unix timestamp of the knot
/// * `deltaBps` - Mid-price delta at the knot, in basis points (100 = 1%)
struct SpreadCurvePoint {
    uint64 timeSecs;
    int32 deltaBps;
}

/// Sign this: EIP-712 signed envelope for order cancellation.
struct CancelOrder {
    bytes32 orderHash;
    uint64 nonce;
    uint64 deadline;
}

/// Sign this: EIP-712 signed envelope for adding liquidity.
struct AddLiquidityEip712 {
    AddLiquidityIntent intent;
    uint64 nonce;
    uint64 deadline;
}

/// A struct for the intent to add liquidity
///
/// # Fields
/// * `owner` - The account providing the liquidity
/// * `token0` - token0 of the pool to which the liquidity is provided
/// * `token1` - token1 of the pool to which the liquidity is provided
/// * `fee` - fee of the pool to which the liquidity is provided, in 1/100 of bip (3000=0.3%)
/// * `token0Amount` - Maximum amount of token0 of the pool that the user is willing to provide
/// * `token1Amount` - Maximum amount of token1 of the pool that the user is willing to provide
/// * `exact` - If true, provide exactly the specified amounts (paying a swap fee to rebalance);
///   if false, treat them as maximums and provide in the pool's current reserve ratio.
/// * `salt` - Arbitrary value differentiating intents whose other fields are the same
struct AddLiquidityIntent {
    address owner;
    address token0;
    address token1;
    uint24 fee;
    uint256 token0Amount;
    uint256 token1Amount;
    bool exact;
    bytes32 salt;
}

/// Sign this: EIP-712 signed envelope for removing liquidity.
struct RemoveLiquidityEip712 {
    RemoveLiquidityIntent intent;
    uint64 nonce;
    uint64 deadline;
}

/// A struct for the intent to remove liquidity
///
/// # Fields
/// * `owner` - The account withdrawing the liquidity
/// * `token0` - token0 of the pool to which the liquidity is withdrawn
/// * `token1` - token1 of the pool to which the liquidity is withdrawing
/// * `fee` - fee of the pool to which the liquidity is withdrawing, in 1/100 of bip (3000=0.3%)
/// * `lpToken` - Address of the LP token that the user wants to burn.
/// * `lpTokenAmount` - Quantity of LP tokens that the user wants to burn.
/// * `salt` - Arbitrary value differentiating intents whose other fields are the same
struct RemoveLiquidityIntent {
    address owner;
    address token0;
    address token1;
    uint24 fee;
    address lpToken;
    uint256 lpTokenAmount;
    bytes32 salt;
}

/// Sign this: EIP-712 signed order query.
/// Zero values mean "unset": empty arrays are no filter, empty `cursor` is the first page, 
/// `limit` 0 is the server default page size.
struct QueryOrders {
    bytes32[] hashes;
    string[] statuses;
    string cursor;
    uint64 limit;
    uint64 nonce;
    uint64 deadline;
}

/// Sign this: EIP-712 signed liquidity-intent lookup.
struct QueryLiquidityIntents {
    bytes32[] hashes;
    uint64 nonce;
    uint64 deadline;
}
```

</details>

<table data-search="false"><thead><tr><th>endpoint</th><th>struct to sign</th></tr></thead><tbody><tr><td><code>add_order</code></td><td><code>AddOrder</code></td></tr><tr><td><code>add_orders</code></td><td>sign one <code>AddOrder</code> per submitted order</td></tr><tr><td><code>cancel_order</code></td><td><code>CancelOrder</code></td></tr><tr><td><code>orders</code></td><td><code>QueryOrders</code></td></tr><tr><td><code>add_liquidity</code></td><td><code>AddLiquidityEip712</code></td></tr><tr><td><code>remove_liquidity</code></td><td><code>RemoveLiquidityEip712</code></td></tr><tr><td><code>liquidity_intents</code></td><td><code>QueryLiquidityIntents</code></td></tr></tbody></table>

### Examples

{% hint style="info" %}
The Typescript SDK handles EIP-712 authentication for you. These examples show how to do it manually.
{% endhint %}

#### Submitting an Order

Submit an order via `/api/eip712/add_order` using Typescript and [viem](https://viem.sh/).

{% hint style="warning" %}
**The `spreadCurve` gotcha**: in the wire payload, `spreadCurve` is a sibling of `order` and its knots are relative to the order window (`windowBps`). But the signed `OrderIntent` inlines the curve: `startDeltaBps`, `endDeltaBps`, and `points` with each knot resolved to an absolute timestamp:

`timeSecs = startTime + windowBps * (endTime - startTime) / 10000`, rounded down.

This will be simplified in a future API version.
{% endhint %}

```typescript
import { parseSignature, bytesToHex, bytesToBigInt, maxUint160 } from "viem";
import { privateKeyToAccount } from "viem/accounts";

const API = "https://staging-api.turbine.exchange/api";
const account = privateKeyToAccount(PRIVATE_KEY);
const { eip712Domain, maxSignatureLifetimeS, turbineSettlerAddress } = await (
    await fetch(`${API}/config`)
).json();

const now = BigInt(Math.floor(Date.now() / 1000));

// The wire `order` carries no spread fields...
const order = {
    owner: account.address,
    sellToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
    buyToken: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // WETH
    sellAmount: 30n * 10n ** 6n, // 30 USDC
    minBuyAmount: 7n * 10n ** 15n, // 0.007 WETH
    startTime: now,
    endTime: now + 300n,
    partialFill: true,
    callData: "0x",
    callDataTarget: "0x0000000000000000000000000000000000000000",
    salt: bytesToHex(crypto.getRandomValues(new Uint8Array(32))),
};
// ...the spread curve is its sibling in the payload, with window-relative
// knots: `windowBps: 5000` is the middle of the order window.
const spreadCurve = {
    startDeltaBps: 200,
    endDeltaBps: 100,
    points: [{ windowBps: 5000, deltaBps: 100 }],
};

// The signed OrderIntent inlines the curve, knots resolved to absolute timestamps.
const signedOrder = {
    ...order,
    startDeltaBps: spreadCurve.startDeltaBps,
    endDeltaBps: spreadCurve.endDeltaBps,
    points: spreadCurve.points.map((p) => ({
        timeSecs:
            order.startTime +
            (BigInt(p.windowBps) * (order.endTime - order.startTime)) / 10_000n,
        deltaBps: p.deltaBps,
    })),
};

const nonce = bytesToBigInt(crypto.getRandomValues(new Uint8Array(8)));
const deadline = BigInt(Math.floor(Date.now() / 1000) + maxSignatureLifetimeS - 5);

const signature = await account.signTypedData({
    domain: eip712Domain,
    primaryType: "AddOrder",
    types: {
        // Copied from the Signed Types definitions above
        AddOrder: [
            { name: "order", type: "OrderIntent" },
            { name: "nonce", type: "uint64" },
            { name: "deadline", type: "uint64" },
        ],
        OrderIntent: [
            { name: "owner", type: "address" },
            { name: "sellToken", type: "address" },
            { name: "buyToken", type: "address" },
            { name: "sellAmount", type: "uint256" },
            { name: "minBuyAmount", type: "uint256" },
            { name: "startDeltaBps", type: "int32" },
            { name: "endDeltaBps", type: "int32" },
            { name: "points", type: "SpreadCurvePoint[]" },
            { name: "startTime", type: "uint256" },
            { name: "endTime", type: "uint256" },
            { name: "partialFill", type: "bool" },
            { name: "callData", type: "bytes" },
            { name: "callDataTarget", type: "address" },
            { name: "salt", type: "bytes32" },
        ],
        SpreadCurvePoint: [
            { name: "timeSecs", type: "uint64" },
            { name: "deltaBps", type: "int32" },
        ],
    },
    message: { order: signedOrder, nonce, deadline },
});
const { r, s, yParity } = parseSignature(signature);

// Permit2 permit funding the order. Assuming the order owner has a Permit2 allowance for the sell token.
const permit = {
    details: {
        token: order.sellToken,
        amount: maxUint160, // unlimited, but expires with the order
        expiration: Number(order.endTime),
        // The wallet's current Permit2 AllowanceTransfer nonce:
        // cast call 0x000000000022D473030F116dDEE9F6B43aC78BA3 \
        //   "allowance(address,address,address)(uint160,uint48,uint48)" \
        //   <OWNER> <SELL_TOKEN> <SETTLER>
        nonce: 0,
    },
    spender: turbineSettlerAddress,
    sigDeadline: order.endTime,
};
const permitSignature = parseSignature(
    await account.signTypedData({
        domain: {
            name: "Permit2",
            chainId: 1,
            verifyingContract: "0x000000000022D473030F116dDEE9F6B43aC78BA3",
        },
        primaryType: "PermitSingle",
        types: {
            PermitSingle: [
                { name: "details", type: "PermitDetails" },
                { name: "spender", type: "address" },
                { name: "sigDeadline", type: "uint256" },
            ],
            PermitDetails: [
                { name: "token", type: "address" },
                { name: "amount", type: "uint160" },
                { name: "expiration", type: "uint48" },
                { name: "nonce", type: "uint48" },
            ],
        },
        message: permit,
    })
);

const response = await fetch(`${API}/eip712/add_order`, {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify(
        {
            // The wire payload keeps the curve beside the order,
            // defined relative to the order window.
            payload: {
                order,
                spreadCurve,
                signedPermit: {
                    permit,
                    signature: {
                        r: permitSignature.r,
                        s: permitSignature.s,
                        yParity: permitSignature.yParity === 1,
                    },
                },
            },
            auth: {
                signer: account.address,
                nonce: nonce.toString(), // decimal string on the wire
                deadline: Number(deadline),
                signature: { r, s, yParity: yParity === 1 },
            },
        },
        (_, v) => (typeof v === "bigint" ? v.toString() : v)
    ),
});
```

The response contains the order hash (yours will be different):

```json
{"orderHash":"0xc7c768292e401d9d5b963b3250fcd0b7033a30b950373d9c74780dec512548c5"}
```

#### Listing Orders

Look up your orders via `/api/eip712/orders` using only shell tools: [Foundry's](https://getfoundry.sh/) `cast`, `curl` and `jq`.

In this example, we'll request just a single order by its hash.

Build the `QueryOrders` typed data. The domain comes from `/api/config` (field `eip712Domain`):

```bash
API='https://staging-api.turbine.exchange/api'
HASH=0xc7c768292e401d9d5b963b3250fcd0b7033a30b950373d9c74780dec512548c5
NONCE=$RANDOM$RANDOM$RANDOM      # random u64, fresh per request
DEADLINE=$(($(date +%s) + 590))  # at most maxSignatureLifetimeS (see /api/config) ahead

cat > typed-data.json <<EOF
{
  "domain": $(curl -s "$API/config" | jq .eip712Domain),
  "primaryType": "QueryOrders",
  "types": {
    "QueryOrders": [
      { "name": "hashes", "type": "bytes32[]" },
      { "name": "statuses", "type": "string[]" },
      { "name": "cursor", "type": "string" },
      { "name": "limit", "type": "uint64" },
      { "name": "nonce", "type": "uint64" },
      { "name": "deadline", "type": "uint64" }
    ]
  },
  "message": {
    "hashes": ["$HASH"],
    "statuses": [],
    "cursor": "",
    "limit": 0,
    "nonce": $NONCE,
    "deadline": $DEADLINE
  }
}
EOF
```

Sign it and split the 65-byte signature into `r`, `s` and `yParity`:

```bash
SIG=$(cast wallet sign --data --from-file typed-data.json --private-key "$PRIVATE_KEY")
R=0x${SIG:2:64}
S=0x${SIG:66:64}
YPARITY=$([ "${SIG:130:2}" = "1c" ] && echo true || echo false)
```

Send the envelope. The `payload` must match the signed message exactly, minus `nonce` and `deadline`, which move to `auth`:

```bash
curl "$API/eip712/orders" \
  -H 'content-type: application/json' \
  -d "{
    \"payload\": { \"hashes\": [\"$HASH\"], \"statuses\": [], \"cursor\": \"\", \"limit\": 0 },
    \"auth\": {
      \"signer\": \"$(cast wallet address --private-key "$PRIVATE_KEY")\",
      \"nonce\": \"$NONCE\",
      \"deadline\": $DEADLINE,
      \"signature\": { \"r\": \"$R\", \"s\": \"$S\", \"yParity\": $YPARITY }
    }
  }"
```

The response contains the matching orders, including their details:

```json
{
  "orders": [
    {
      "hash": "0xc7c768292e401d9d5b963b3250fcd0b7033a30b950373d9c74780dec512548c5",
      "owner": "0xbe69d72ca5f88acba033a063df5dbe43a4148de0",
      "status": "Active",
      "execution": [],
      "orderDetails": {
        "sellToken": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
        "buyToken": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
        "sellAmount": "30000000",
        "limitPrice": { "numerator": "700000000", "denominator": "3" },
        "startTime": "1786354256",
        "endTime": "1786354556",
        "spreadCurve": {
          "startSecs": 1786354256,
          "endSecs": 1786354556,
          "startDeltaBps": 200,
          "endDeltaBps": 100,
          "points": [{ "timeSecs": 1786354406, "deltaBps": 100 }]
        },
        "createdTimestamp": "2026-08-10T09:30:56.910647104",
        "annotations": { "spreadAtSubmissionHbp": null }
      }
    }
  ],
  "cursor": null,
  "hasMore": false
}
```

{% hint style="info" %}
`auth.nonce` is a decimal string on the wire, but a `uint64` number in the signed message — both must hold the same value.
{% endhint %}


# Rate Limiting

Turbine API rate limit: **250 requests per second per IP**.

Custom rate limits aren't offered by default. If your use case needs higher limits, reach out to `@tanay_j` on Telegram.


# Turbine SDK

**Get the SDK at** [**github.com/propeller-heads/turbine-sdk**](https://github.com/propeller-heads/turbine-sdk)

The Turbine SDK is the official TypeScript client. It wraps every Turbine HTTP endpoint and handles authentication for you. The Turbine frontend uses this same SDK.

It is a recommended tool for building automations that interact with Turbine.

PropellerHeads provide support on using the SDK. Join the [Telegram group](https://t.me/+ZRoSLw0ikZlkM2Fk).

See the repository for SDK's documentation.

#### See also

{% content-ref url="/pages/ejRyUUm4z8T1hDAfR95n" %}
[API Specification](/reference)
{% endcontent-ref %}

{% content-ref url="/pages/xKrdEO1eKeSF4zjbG3rU" %}
[Authentication](/reference/api/authentication)
{% endcontent-ref %}

{% content-ref url="/pages/PNYY3TrbzyTxpZ0WVpmS" %}
[Rate Limiting](/reference/api/rate-limiting)
{% endcontent-ref %}


# Contract Addresses

### Ethereum Mainnet

<table><thead><tr><th width="250">Smart Contract</th><th>Address</th></tr></thead><tbody><tr><td><a data-mention href="/reference/contracts/solidity-documentation/settler/contract.turbinesettler">TurbineSettler</a></td><td><code>0x2aadb59279619cb33d34ad1a3696e23a2effb394</code></td></tr><tr><td><a data-mention href="/reference/contracts/solidity-documentation/univ4-hook/contract.turbinehook">TurbineHook</a></td><td><code>0x5858ecf7ba160485037d9432d548556070c9a088</code></td></tr><tr><td><a data-mention href="/reference/contracts/solidity-documentation/univ4-hook/contract.turbineliquidityrouter">TurbineLiquidityRouter</a></td><td><code>0x769ead430c4d613ef1852a3c7b88371588602bcf</code></td></tr></tbody></table>

{% hint style="info" %}
Up-to-date addresses can always be found at: <https://api.turbine.exchange/api/config>
{% endhint %}


# Solidity Documentation

Turbine protocol consists of an offchain backend and a set of smart contracts.

There are two main groups of contracts:

* **Order Settlement** (`settler`): The main contract - `TurbineSettler` - is responsible for settling orders. Turbine backend submits transactions directly to this contract.
* **Liquidity Provision** (`univ4-hook`): Liquidity provision is implemented by a Uniswap V4 Hook - `TurbineHook` - and a Router - `TurbineLiquidityRouter`.

## Overview

### TurbineHook

* Implements a Uniswap V4 Hook interface to intercept and customize pool actions (e.g., swaps, liquidity additions).
* Uses an internal ERC20 token to represent liquidity shares (similar to Uniswap V2 LP tokens).
* Tracks the reserves internally (like Uniswap V2’s `reserves0` and `reserves1`) to issue or burn liquidity tokens accordingly.
* Restricts direct pool operations so that only the designated router can invoke critical functions like minting, burning, or swapping.

### Router

* Orchestrates liquidity addition (`addLiquidity`), removal (`removeLiquidity`), and swaps (`swapExactTokens`) by interacting with both TurbineHook and the Uniswap V4 PoolManager.
* Implements the `IUnlockCallback` interface, which is used by the Uniswap V4 `PoolManager` during certain operations.
* Allows for a simplified user interface, controlling the flow of tokens during swaps and liquidity changes.

### Swap flow

1. `TurbineSettler` calls `LiquidityRouter`.
2. `LiquidityRouter` unlocks `PoolManager` to begin an operation. `PoolManager` invokes `LiquidityRouter.unlockCallback(...)`.
3. `LiquidityRouter` calls `PoolManager.swap(...)` to perform the swap.
4. `PoolManager` delegates the accounting to `TurbineHook` via `beforeSwap(...)` and `afterSwap(...)` functions.
5. `LiquidityRouter` transfers tokens to and from `PoolManager` and `TurbineSettler`.

## Development with Foundry

### Installation

```shell
forge install
git submodule update --init --recursive --remote
```

### Build

```shell
$ forge build
```

or to also include dev contracts used by Rust, use the makefile:

```shell
$ make build
```

### Test

```shell
$ forge test -vvv
```

### Fuzz & Invariant Tests

Fuzz and invariant tests live in `test/fuzz/` and run in CI with default settings (256 fuzz runs, 64 invariant runs). To run locally with more iterations:

```shell
# All fuzz + invariant tests
forge test --match-path "*/fuzz/*" -vv

# With more iterations
forge test --match-path "*/fuzz/*" --fuzz-runs 5000

# Only stateful invariant tests (requires RPC_URL for fork)
forge test --match-path "*/fuzz/invariant/*" -vv
```

### Format

```shell
$ forge fmt
```

### Deploy

See the specific page in the documentation for more information on how to deploy the contracts.


# Selectors and topics

## TurbineSettler

### Functions

* `0x01ffc9a7` supportsInterface(bytes4)
* `0x110bda25` updateFeeWithdrawer(address)
* `0x200a1a77` turbineLiquidityRouter()
* `0x248a9ca3` getRoleAdmin(bytes32)
* `0x2f2ff15d` grantRole(bytes32,address)
* `0x32b4fa6b` feeWithdrawer()
* `0x36568abe` renounceRole(bytes32,address)
* `0x3f4ba83a` unpause()
* `0x49551976` getTurbineHook()
* `0x51617258` UNIV4\_POOL\_MANAGER\_CONTRACT()
* `0x5c975abb` paused()
* `0x69fe8806` batchExecutedSellAmounts(bytes32\[])
* `0x8456cb59` pause()
* `0x8bad0c0a` renounceAdmin()
* `0x91d14854` hasRole(bytes32,address)
* `0x91dd7346` unlockCallback(bytes)
* `0xa217fddf` DEFAULT\_ADMIN\_ROLE()
* `0xb42b5bf8` getTurbineLiquidityRouter()
* `0xd547741f` revokeRole(bytes32,address)
* `0xe2fa47f0` sweepFees(address\[],address)
* `0xe5c94437` executedSellAmounts(bytes32)
* `0xe63ab1e9` PAUSER\_ROLE()
* `0xe9fc0e01` turbineSigner()
* `0xf840f3fc` PERMIT2\_CONTRACT()
* `0xfb1bb9de` UNPAUSER\_ROLE()
* `0xfdcc8090` executeLiquidityActionsAndSettlement((address,bytes32,uint256,uint256,uint256,address,address,uint24,(((address,uint256)\[],uint256,uint256),bytes))\[],(address,bytes32,uint256,address,address,uint24,(((address,uint256),uint256,uint256),bytes))\[],bytes32\[],((address,uint256)\[],(bytes32,address,address,address,uint256,uint256,uint256,(bytes,((address,uint160,uint48,uint48),address,uint256)))\[],(bytes32,address,address,bytes,address,address,uint256,uint256)\[],(address,bytes,address,address,uint256,uint256)\[],(address,address,uint256,uint256,uint24,uint256)\[],(address,uint256)\[]),uint256,uint256)

### Errors

* `0x179ce99f` RouterNotSet()
* `0x1b886509` OnlyTurbineSigner()
* `0x400d5838` ExcessiveTokensDistributed(uint256,uint256)
* `0x4730d0b1` InvalidCalldataTarget(address)
* `0x4e47846c` InvalidBlockNumber()
* `0x5274afe7` SafeERC20FailedOperation(address)
* `0x6697b232` AccessControlBadConfirmation()
* `0x7ef7897c` MissingAllowance(bytes32)
* `0x8989e031` InsufficientTokensReceived(uint256,uint256)
* `0x8dfc202b` ExpectedPause()
* `0x9996b315` AddressEmptyCode(address)
* `0xbb231d53` InsufficientBuyTokenBalance(address,uint256,uint256)
* `0xd6bda275` FailedCall()
* `0xd92e233d` ZeroAddress()
* `0xd93c0665` EnforcedPause()
* `0xdcdd32f1` OnlyFeeWithdrawer()
* `0xe2517d3f` AccessControlUnauthorizedAccount(address,bytes32)

### Events

* `0x013d96381264780a74f0a1460532619bcc42953580ff6ad2c45d63725eae5ada` FeesSwept(address\[],address)
* `0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d` RoleGranted(bytes32,address,address)
* `0x5a8a721d8fc5affcedff701436e92f6e3f555ec3866f2f3124f2b1a404c4fabe` FeeWithdrawerUpdated(address,address)
* `0x5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa` Unpaused(address)
* `0x62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258` Paused(address)
* `0x7fac4012cf4d9412941be83feebe299813fec2e27da8a0e9e9e83e348b7d47d0` DEXSwapExecuted(address,address,address,uint256,uint256,uint256)
* `0xbcf47a47f4778664d2bba984df51717ed82a59e0ae7ee1c8f8693c37700ab262` OrderSettled(address,bytes32,address,address,uint256,uint256)
* `0xbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff` RoleAdminChanged(bytes32,bytes32,bytes32)
* `0xcb9f64d6e4ccba3245693379bb306eff237e8ebc224a478ad6b82660b85b5d5c` TurbinePoolSwapExecuted(address,address,uint24,bool,uint256,uint256)
* `0xd1e5c0d026a890a7118e935fd0dcb810a5466e8f6f085f43a35845be57a77325` TurbineSettlement(uint256)
* `0xeadfba4a6dcfd600e2dfac65f349a17f655af81eba00936e9a367c63d21f821d` TurbineSignerSet(address)
* `0xeeef375f2a6eac8a885084e8818b615ae63b323dbe600a499aa8d719b09fc2ad` SmartOrderSettled(address,bytes32,address,address,uint256,uint256,uint256)
* `0xf6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b` RoleRevoked(bytes32,address,address)

## TurbineLiquidityRouter

### Functions

* `0x208dc3d8` getRemoveLiquidityIntents(bytes32\[])
* `0x234d57d9` \_swapCallback(uint256,uint256,(address,address,uint24,int24,address),bool)
* `0x39e298ac` turbineSettler()
* `0x415e982d` \_TOKEN\_PERMISSIONS\_TYPEHASH()
* `0x41d5d2b6` removeLiquidityIntents(bytes32)
* `0x49551976` getTurbineHook()
* `0x5264753b` \_addLiquidityCallback(address,uint256,uint256,(address,address,uint24,int24,address),(((address,uint256)\[],uint256,uint256),bytes))
* `0x5f4e78b6` submitRemoveLiquidityIntent((address,bytes32,uint256,bytes32),(((address,uint256),uint256,uint256),bytes))
* `0x62308e85` POOL\_MANAGER()
* `0x74535c63` executePendingIntents(bytes32\[])
* `0x78f16830` \_PERMIT\_TRANSFER\_FROM\_TYPEHASH()
* `0x7f5a7c7b` hook()
* `0x816c154f` getTurbineSettler()
* `0x91dd7346` unlockCallback(bytes)
* `0xaa93d10e` removeLiquidityPermits(bytes32)
* `0xb0f45cad` \_removeLiquidityCallback(address,uint256,(address,address,uint24,int24,address),uint256,uint256,(((address,uint256),uint256,uint256),bytes))
* `0xb7e62136` SPEEDBUMP\_DURATION()
* `0xb9661025` removeLiquidityCreatedAt(bytes32)
* `0xc3fbd579` removeLiquidity(address,uint256,address,address,uint24,(((address,uint256),uint256,uint256),bytes))
* `0xc5df0a68` swapExactTokens(uint256,uint256,address,address,uint24,bool,uint256)
* `0xc6abc0c6` MIN\_INTENT\_EXPIRY()
* `0xcab7e8eb` isNonceUsed(address,uint256)
* `0xe0e826aa` MAX\_INTENT\_EXPIRY()
* `0xe8a507e8` addLiquidity(address,uint256,uint256,address,address,uint24,uint256,(((address,uint256)\[],uint256,uint256),bytes))
* `0xf840f3fc` PERMIT2\_CONTRACT()

### Errors

* `0x01c6a053` InvalidIntentOwner()
* `0x065729ff` InvalidOutputReceived()
* `0x10074548` ZeroLiquidity()
* `0x1a8470c7` InsufficientLpTokenBalance(address,uint256,uint256)
* `0x23c5628a` InvalidPermitToken(address,address)
* `0x24775e06` SafeCastOverflowedUintToInt(uint256)
* `0x2b32713d` SwapExpired()
* `0x34682d42` InvalidPermitAmount(uint256,uint256)
* `0x41035b5a` PermitDeadlineTooShort(uint256,uint256)
* `0x4be6321b` InvalidSignatureLength()
* `0x5274afe7` SafeERC20FailedOperation(address)
* `0x76a9cc3e` NotTurbineSettler()
* `0x77ebef4d` OutOfGas()
* `0x8125e874` InvalidLpTokenAmount()
* `0x815e1d64` InvalidSigner()
* `0x8404e40f` PermitDeadlineTooLong(uint256,uint256)
* `0x8baa579f` InvalidSignature()
* `0x911285a3` NotSettlerOrPoolManager()
* `0xae18210a` NotPoolManager()
* `0xb0669cbc` InvalidContractSignature()
* `0xc237b320` DuplicateIntent(RemoveLiquidityIntentHash)
* `0xe57902cd` InsufficientInitialLiquidity()
* `0xe86ac8a6` InsufficientLiquidityBurnt()
* `0xe90aded4` NonceAlreadyUsed(address,uint256)

### Events

* `0x488857d55d5699a92f14db37ec65228eb0e3344dde0d38345a26464abf9a32ac` RemoveLiquidityIntentDeleted(RemoveLiquidityIntentHash)
* `0x91b7b87cc50ee71ce8dc7cdc2c2d6434b8525023c067481a16db26fa5b5be9ac` SettlerSet(address)
* `0xafafbdfafb615362738fb792c73736c166b3e3f4578451596a235e0260e50914` RemoveLiquidityIntentSubmitted(address,PoolId,RemoveLiquidityIntentHash,uint256,uint256)

## TurbineHook

### Functions

* `0x1cc186bd` getNumberOfRegisteredPools()
* `0x21d0ee70` beforeRemoveLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),bytes)
* `0x259982e5` beforeAddLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),bytes)
* `0x33f1d4b5` getReserves(address,address,uint24)
* `0x399ff28f` getPoolStates(bytes32\[])
* `0x46df4d45` burn(address,uint256,uint256,uint256,(address,address,uint24,int24,address))
* `0x575e24b4` beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)
* `0x5874f699` getLpTokenAddress(address,address,uint24)
* `0x62308e85` POOL\_MANAGER()
* `0x6c2bbe7e` afterRemoveLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),int256,int256,bytes)
* `0x6fe7e6eb` afterInitialize(address,(address,address,uint24,int24,address),uint160,int24)
* `0x9e50efea` getRegisteredPoolsSlice(uint256,uint256)
* `0x9f063efc` afterAddLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),int256,int256,bytes)
* `0xa9a091bb` validateHookAddress()
* `0xab6291fe` lockAcquired(bytes)
* `0xb46c6fb7` isRegisteredPool(address,address,uint24)
* `0xb47b2fb1` afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)
* `0xb5217bb4` pools(bytes32)
* `0xb6a8b0fa` beforeDonate(address,(address,address,uint24,int24,address),uint256,uint256,bytes)
* `0xba9a7a56` MINIMUM\_LIQUIDITY()
* `0xc40ac54c` computePoolId(address,address,uint24)
* `0xc4e833ce` getHookPermissions()
* `0xcc1471df` getPoolLpToken(bytes32)
* `0xdc98354e` beforeInitialize(address,(address,address,uint24,int24,address),uint160)
* `0xe1b4af69` afterDonate(address,(address,address,uint24,int24,address),uint256,uint256,bytes)
* `0xf887ea40` router()
* `0xf9e3e6a0` mint(address,uint256,uint256,(address,address,uint24,int24,address),uint256)

### Errors

* `0x074e997e` ZeroCurrencyAddress()
* `0x0a85dc29` HookNotImplemented()
* `0x2083cd40` InvalidPool()
* `0x24775e06` SafeCastOverflowedUintToInt(uint256)
* `0x29c3b7ee` NotSelf()
* `0x327269a7` SafeCastOverflowedIntDowncast(uint8,int256)
* `0x739f4185` PoolNotRegistered()
* `0x89560ca1` BalanceOverflow()
* `0xa40afa38` LockFailure()
* `0xae18210a` NotPoolManager()
* `0xb3e8301e` PoolAlreadyRegistered()
* `0xd829ca58` RouterOnly()
* `0xe57902cd` InsufficientInitialLiquidity()

### Events

* `0x11b5b9d3e5e311261e5cde9d782ee1d28232178782e0b0b214ce4116e2896d94` Mint(bytes32,address,uint256,uint256)
* `0x8b5b231c3c70a76583e59da845256a0dfaa38bc9d8395f2068761e92e60faf48` Burn(bytes32,address,uint256,uint256,address)
* `0x922f85ecc7a399e9175cd0333de2f8fe2363d2fd59ab77f709437a22adf7a017` Swap(bytes32,uint256,uint256)
* `0xc6b438e6a8a59579ce6a4406cbd203b740e0d47b458aae6596339bcd40c40d15` RouterSet(address)
* `0xde3ff08ff95b98c67eeff3f890d8feae4ee5a3bce2ea568929d244155ddfab49` Sync(bytes32,uint256,uint256)
* `0xe450dd63de6f7928a9cf7761d1dcbf5c287ee9fc1fb46fa00c7c9412413bc352` LPTokenDeployed(bytes32,address,string,string)
* `0xe4c5c61c09aa451db9aa36c6f882a19e93473d68d2cc6ce5e6f5eede02d9d3b5` PoolRegistered(bytes32,address,address,uint24)


# ❱ interfaces

* [AllowanceParams](/reference/contracts/solidity-documentation/interfaces/struct.allowanceparams)
* [IAllowanceTransfer](/reference/contracts/solidity-documentation/interfaces/interface.iallowancetransfer)
* [IEIP712](/reference/contracts/solidity-documentation/interfaces/interface.ieip712)
* [IERC1271](/reference/contracts/solidity-documentation/interfaces/interface.ierc1271)
* [IPermit2](/reference/contracts/solidity-documentation/interfaces/interface.ipermit2)
* [ISignatureTransfer](/reference/contracts/solidity-documentation/interfaces/interface.isignaturetransfer)


# AllowanceParams

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/interfaces/IAllowanceTransfer.sol)

Holds the permit signature and permit data for a given user and sell token.

```solidity
struct AllowanceParams {
bytes signature;
IAllowanceTransfer.PermitSingle permit;
}
```


# IAllowanceTransfer

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/interfaces/IAllowanceTransfer.sol)

**Inherits:** [IEIP712](https://github.com/propeller-heads/turbine-docs/blob/master/src/interfaces/IEIP712.sol/interface.IEIP712.md)

**Title:** AllowanceTransfer

Handles ERC20 token permissions through signature based allowance setting and ERC20 token transfers by checking allowed amounts

Requires user's token approval on the Permit2 contract

## Functions

### allowance

A mapping from owner address to token address to spender address to PackedAllowance struct, which contains details and conditions of the approval.

The mapping is indexed in the above order see: allowance\[ownerAddress]\[tokenAddress]\[spenderAddress]

The packed slot holds the allowed amount, expiration at which the allowed amount is no longer valid, and current nonce thats updated on any signature based approvals.

```solidity
function allowance(
    address user,
    address token,
    address spender
) external view returns (uint160 amount, uint48 expiration, uint48 nonce);
```

### approve

Approves the spender to use up to amount of the specified token up until the expiration

The packed allowance also holds a nonce, which will stay unchanged in approve

Setting amount to type(uint160).max sets an unlimited approval

```solidity
function approve(address token, address spender, uint160 amount, uint48 expiration) external;
```

**Parameters**

| Name         | Type      | Description                                            |
| ------------ | --------- | ------------------------------------------------------ |
| `token`      | `address` | The token to approve                                   |
| `spender`    | `address` | The spender address to approve                         |
| `amount`     | `uint160` | The approved amount of the token                       |
| `expiration` | `uint48`  | The timestamp at which the approval is no longer valid |

### permit

Permit a spender to a given amount of the owners token via the owner's EIP-712 signature

May fail if the owner's nonce was invalidated in-flight by invalidateNonce

```solidity
function permit(address owner, PermitSingle memory permitSingle, bytes calldata signature) external;
```

**Parameters**

| Name           | Type           | Description                                                    |
| -------------- | -------------- | -------------------------------------------------------------- |
| `owner`        | `address`      | The owner of the tokens being approved                         |
| `permitSingle` | `PermitSingle` | Data signed over by the owner specifying the terms of approval |
| `signature`    | `bytes`        | The owner's signature over the permit data                     |

### permit

Permit a spender to the signed amounts of the owners tokens via the owner's EIP-712 signature

May fail if the owner's nonce was invalidated in-flight by invalidateNonce

```solidity
function permit(address owner, PermitBatch memory permitBatch, bytes calldata signature) external;
```

**Parameters**

| Name          | Type          | Description                                                    |
| ------------- | ------------- | -------------------------------------------------------------- |
| `owner`       | `address`     | The owner of the tokens being approved                         |
| `permitBatch` | `PermitBatch` | Data signed over by the owner specifying the terms of approval |
| `signature`   | `bytes`       | The owner's signature over the permit data                     |

### transferFrom

Transfer approved tokens from one address to another

Requires the from address to have approved at least the desired amount of tokens to msg.sender.

```solidity
function transferFrom(address from, address to, uint160 amount, address token) external;
```

**Parameters**

| Name     | Type      | Description                         |
| -------- | --------- | ----------------------------------- |
| `from`   | `address` | The address to transfer from        |
| `to`     | `address` | The address of the recipient        |
| `amount` | `uint160` | The amount of the token to transfer |
| `token`  | `address` | The token address to transfer       |

### transferFrom

Transfer approved tokens in a batch

Requires the from addresses to have approved at least the desired amount of tokens to msg.sender.

```solidity
function transferFrom(AllowanceTransferDetails[] calldata transferDetails) external;
```

**Parameters**

| Name              | Type                         | Description                                                        |
| ----------------- | ---------------------------- | ------------------------------------------------------------------ |
| `transferDetails` | `AllowanceTransferDetails[]` | Array of owners, recipients, amounts, and tokens for the transfers |

### lockdown

Enables performing a "lockdown" of the sender's Permit2 identity by batch revoking approvals

```solidity
function lockdown(TokenSpenderPair[] calldata approvals) external;
```

**Parameters**

| Name        | Type                 | Description                   |
| ----------- | -------------------- | ----------------------------- |
| `approvals` | `TokenSpenderPair[]` | Array of approvals to revoke. |

### invalidateNonces

Invalidate nonces for a given (token, spender) pair

Can't invalidate more than 2\*\*16 nonces per transaction.

```solidity
function invalidateNonces(address token, address spender, uint48 newNonce) external;
```

**Parameters**

| Name       | Type      | Description                                                |
| ---------- | --------- | ---------------------------------------------------------- |
| `token`    | `address` | The token to invalidate nonces for                         |
| `spender`  | `address` | The spender to invalidate nonces for                       |
| `newNonce` | `uint48`  | The new nonce to set. Invalidates all nonces less than it. |

## Events

### NonceInvalidation

Emits an event when the owner successfully invalidates an ordered nonce.

```solidity
event NonceInvalidation(
    address indexed owner, address indexed token, address indexed spender, uint48 newNonce, uint48 oldNonce
);
```

### Approval

Emits an event when the owner successfully sets permissions on a token for the spender.

```solidity
event Approval(
    address indexed owner, address indexed token, address indexed spender, uint160 amount, uint48 expiration
);
```

### Permit

Emits an event when the owner successfully sets permissions using a permit signature on a token for the spender.

```solidity
event Permit(
    address indexed owner,
    address indexed token,
    address indexed spender,
    uint160 amount,
    uint48 expiration,
    uint48 nonce
);
```

### Lockdown

Emits an event when the owner sets the allowance back to 0 with the lockdown function.

```solidity
event Lockdown(address indexed owner, address token, address spender);
```

## Errors

### AllowanceExpired

Thrown when an allowance on a token has expired.

```solidity
error AllowanceExpired(uint256 deadline);
```

**Parameters**

| Name       | Type      | Description                                                  |
| ---------- | --------- | ------------------------------------------------------------ |
| `deadline` | `uint256` | The timestamp at which the allowed amount is no longer valid |

### InsufficientAllowance

Thrown when an allowance on a token has been depleted.

```solidity
error InsufficientAllowance(uint256 amount);
```

**Parameters**

| Name     | Type      | Description                |
| -------- | --------- | -------------------------- |
| `amount` | `uint256` | The maximum amount allowed |

### ExcessiveInvalidation

Thrown when too many nonces are invalidated.

```solidity
error ExcessiveInvalidation();
```

## Structs

### PermitDetails

The permit data for a token

```solidity
struct PermitDetails {
    // ERC20 token address
    address token;
    // the maximum amount allowed to spend
    uint160 amount;
    // timestamp at which a spender's token allowances become invalid
    uint48 expiration;
    // an incrementing value indexed per owner,token,and spender for each signature
    uint48 nonce;
}
```

### PermitSingle

The permit message signed for a single token allowance

```solidity
struct PermitSingle {
    // the permit data for a single token alownce
    PermitDetails details;
    // address permissioned on the allowed tokens
    address spender;
    // deadline on the permit signature
    uint256 sigDeadline;
}
```

### PermitBatch

The permit message signed for multiple token allowances

```solidity
struct PermitBatch {
    // the permit data for multiple token allowances
    PermitDetails[] details;
    // address permissioned on the allowed tokens
    address spender;
    // deadline on the permit signature
    uint256 sigDeadline;
}
```

### PackedAllowance

The saved permissions

This info is saved per owner, per token, per spender and all signed over in the permit message

Setting amount to type(uint160).max sets an unlimited approval

```solidity
struct PackedAllowance {
    // amount allowed
    uint160 amount;
    // permission expiry
    uint48 expiration;
    // an incrementing value indexed per owner,token,and spender for each signature
    uint48 nonce;
}
```

### TokenSpenderPair

A token spender pair.

```solidity
struct TokenSpenderPair {
    // the token the spender is approved
    address token;
    // the spender address
    address spender;
}
```

### AllowanceTransferDetails

Details for a token transfer.

```solidity
struct AllowanceTransferDetails {
    // the owner of the token
    address from;
    // the recipient of the token
    address to;
    // the amount of the token
    uint160 amount;
    // the token to be transferred
    address token;
}
```


# IEIP712

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/interfaces/IEIP712.sol)

## Functions

### DOMAIN\_SEPARATOR

forge-lint: disable-next-line(mixed-case-function)

```solidity
function DOMAIN_SEPARATOR() external view returns (bytes32);
```


# IERC1271

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/interfaces/IERC1271.sol)

## Functions

### isValidSignature

Should return whether the signature provided is valid for the provided data

```solidity
function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
```

**Parameters**

| Name        | Type      | Description                                 |
| ----------- | --------- | ------------------------------------------- |
| `hash`      | `bytes32` | Hash of the data to be signed               |
| `signature` | `bytes`   | Signature byte array associated with \_data |

**Returns**

| Name         | Type     | Description                       |
| ------------ | -------- | --------------------------------- |
| `magicValue` | `bytes4` | The bytes4 magic value 0x1626ba7e |


# IPermit2

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/interfaces/IPermit2.sol)

**Inherits:** [ISignatureTransfer](https://github.com/propeller-heads/turbine-docs/blob/master/src/interfaces/ISignatureTransfer.sol/interface.ISignatureTransfer.md), [IAllowanceTransfer](https://github.com/propeller-heads/turbine-docs/blob/master/src/interfaces/IAllowanceTransfer.sol/interface.IAllowanceTransfer.md)

Permit2 handles signature-based transfers in SignatureTransfer and allowance-based transfers in AllowanceTransfer.

Users must approve Permit2 before calling any of the transfer functions.


# ISignatureTransfer

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/interfaces/ISignatureTransfer.sol)

**Inherits:** [IEIP712](https://github.com/propeller-heads/turbine-docs/blob/master/src/interfaces/IEIP712.sol/interface.IEIP712.md)

**Title:** SignatureTransfer

Handles ERC20 token transfers through signature based actions

Requires user's token approval on the Permit2 contract

## Functions

### nonceBitmap

A map from token owner address and a caller specified word index to a bitmap. Used to set bits in the bitmap to prevent against signature replay protection

Uses unordered nonces so that permit messages do not need to be spent in a certain order

The mapping is indexed first by the token owner, then by an index specified in the nonce

It returns a uint256 bitmap

The index, or wordPosition is capped at type(uint248).max

```solidity
function nonceBitmap(address, uint256) external view returns (uint256);
```

### permitTransferFrom

Transfers a token using a signed permit message

Reverts if the requested amount is greater than the permitted signed amount

```solidity
function permitTransferFrom(
    PermitTransferFrom memory permit,
    SignatureTransferDetails calldata transferDetails,
    address owner,
    bytes calldata signature
) external;
```

**Parameters**

| Name              | Type                       | Description                                                      |
| ----------------- | -------------------------- | ---------------------------------------------------------------- |
| `permit`          | `PermitTransferFrom`       | The permit data signed over by the owner                         |
| `transferDetails` | `SignatureTransferDetails` | The spender's requested transfer details for the permitted token |
| `owner`           | `address`                  | The owner of the tokens to transfer                              |
| `signature`       | `bytes`                    | The signature to verify                                          |

### permitWitnessTransferFrom

Transfers a token using a signed permit message

Includes extra data provided by the caller to verify signature over

The witness type string must follow EIP712 ordering of nested structs and must include the TokenPermissions type definition

Reverts if the requested amount is greater than the permitted signed amount

```solidity
function permitWitnessTransferFrom(
    PermitTransferFrom memory permit,
    SignatureTransferDetails calldata transferDetails,
    address owner,
    bytes32 witness,
    string calldata witnessTypeString,
    bytes calldata signature
) external;
```

**Parameters**

| Name                | Type                       | Description                                                           |
| ------------------- | -------------------------- | --------------------------------------------------------------------- |
| `permit`            | `PermitTransferFrom`       | The permit data signed over by the owner                              |
| `transferDetails`   | `SignatureTransferDetails` | The spender's requested transfer details for the permitted token      |
| `owner`             | `address`                  | The owner of the tokens to transfer                                   |
| `witness`           | `bytes32`                  | Extra data to include when checking the user signature                |
| `witnessTypeString` | `string`                   | The EIP-712 type definition for remaining string stub of the typehash |
| `signature`         | `bytes`                    | The signature to verify                                               |

### permitTransferFrom

Transfers multiple tokens using a signed permit message

```solidity
function permitTransferFrom(
    PermitBatchTransferFrom memory permit,
    SignatureTransferDetails[] calldata transferDetails,
    address owner,
    bytes calldata signature
) external;
```

**Parameters**

| Name              | Type                         | Description                                                         |
| ----------------- | ---------------------------- | ------------------------------------------------------------------- |
| `permit`          | `PermitBatchTransferFrom`    | The permit data signed over by the owner                            |
| `transferDetails` | `SignatureTransferDetails[]` | Specifies the recipient and requested amount for the token transfer |
| `owner`           | `address`                    | The owner of the tokens to transfer                                 |
| `signature`       | `bytes`                      | The signature to verify                                             |

### permitWitnessTransferFrom

Transfers multiple tokens using a signed permit message

Includes extra data provided by the caller to verify signature over

The witness type string must follow EIP712 ordering of nested structs and must include the TokenPermissions type definition

```solidity
function permitWitnessTransferFrom(
    PermitBatchTransferFrom memory permit,
    SignatureTransferDetails[] calldata transferDetails,
    address owner,
    bytes32 witness,
    string calldata witnessTypeString,
    bytes calldata signature
) external;
```

**Parameters**

| Name                | Type                         | Description                                                           |
| ------------------- | ---------------------------- | --------------------------------------------------------------------- |
| `permit`            | `PermitBatchTransferFrom`    | The permit data signed over by the owner                              |
| `transferDetails`   | `SignatureTransferDetails[]` | Specifies the recipient and requested amount for the token transfer   |
| `owner`             | `address`                    | The owner of the tokens to transfer                                   |
| `witness`           | `bytes32`                    | Extra data to include when checking the user signature                |
| `witnessTypeString` | `string`                     | The EIP-712 type definition for remaining string stub of the typehash |
| `signature`         | `bytes`                      | The signature to verify                                               |

### invalidateUnorderedNonces

Invalidates the bits specified in mask for the bitmap at the word position

The wordPos is maxed at type(uint248).max

```solidity
function invalidateUnorderedNonces(uint256 wordPos, uint256 mask) external;
```

**Parameters**

| Name      | Type      | Description                                                              |
| --------- | --------- | ------------------------------------------------------------------------ |
| `wordPos` | `uint256` | A number to index the nonceBitmap at                                     |
| `mask`    | `uint256` | A bitmap masked against msg.sender's current bitmap at the word position |

## Events

### UnorderedNonceInvalidation

Emits an event when the owner successfully invalidates an unordered nonce.

```solidity
event UnorderedNonceInvalidation(address indexed owner, uint256 word, uint256 mask);
```

## Errors

### InvalidAmount

Thrown when the requested amount for a transfer is larger than the permissioned amount

```solidity
error InvalidAmount(uint256 maxAmount);
```

**Parameters**

| Name        | Type      | Description                                          |
| ----------- | --------- | ---------------------------------------------------- |
| `maxAmount` | `uint256` | The maximum amount a spender can request to transfer |

### LengthMismatch

Thrown when the number of tokens permissioned to a spender does not match the number of tokens being transferred

If the spender does not need to transfer the number of tokens permitted, the spender can request amount 0 to be transferred

```solidity
error LengthMismatch();
```

## Structs

### TokenPermissions

The token and amount details for a transfer signed in the permit transfer signature

```solidity
struct TokenPermissions {
    // ERC20 token address
    address token;
    // the maximum amount that can be spent
    uint256 amount;
}
```

### PermitTransferFrom

The signed permit message for a single token transfer

```solidity
struct PermitTransferFrom {
    TokenPermissions permitted;
    // a unique value for every token owner's signature to prevent signature replays
    uint256 nonce;
    // deadline on the permit signature
    uint256 deadline;
}
```

### SignatureTransferDetails

Specifies the recipient address and amount for batched transfers.

Recipients and amounts correspond to the index of the signed token permissions array.

Reverts if the requested amount is greater than the permitted signed amount.

```solidity
struct SignatureTransferDetails {
    // recipient address
    address to;
    // spender requested amount
    uint256 requestedAmount;
}
```

### PermitBatchTransferFrom

Used to reconstruct the signed permit message for multiple token transfers

Do not need to pass in spender address as it is required that it is msg.sender

Note that a user still signs over a spender address

```solidity
struct PermitBatchTransferFrom {
    // the tokens and corresponding amounts permitted for a transfer
    TokenPermissions[] permitted;
    // a unique value for every token owner's signature to prevent signature replays
    uint256 nonce;
    // deadline on the permit signature
    uint256 deadline;
}
```


# ❱ settler

* [interfaces](https://github.com/propeller-heads/turbine-docs/blob/master/src/settler/interfaces/README.md)
* [libraries](https://github.com/propeller-heads/turbine-docs/blob/master/src/settler/libraries/README.md)
* [structs](https://github.com/propeller-heads/turbine-docs/blob/master/src/settler/structs/README.md)
* [Constants](/reference/contracts/solidity-documentation/settler/constants.constants)
* [TurbineSettler](/reference/contracts/solidity-documentation/settler/contract.turbinesettler)


# ❱ interfaces

* [ITurbineSettler](/reference/contracts/solidity-documentation/settler/interfaces/interface.iturbinesettler)
* [AddLiquidityAppliedAction](/reference/contracts/solidity-documentation/settler/interfaces/struct.addliquidityappliedaction)
* [RemoveLiquidityAppliedAction](/reference/contracts/solidity-documentation/settler/interfaces/struct.removeliquidityappliedaction)


# ITurbineSettler

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/settler/interfaces/ITurbineSettler.sol)

## Functions

### executedSellAmounts

\------------------------- EXTERNAL METHODS ------------------------- ///

Returns the cumulative executed sell amount for an order over all settlements

```solidity
function executedSellAmounts(bytes32 orderHash) external view returns (uint256);
```

**Parameters**

| Name        | Type      | Description           |
| ----------- | --------- | --------------------- |
| `orderHash` | `bytes32` | The hash of the order |

**Returns**

| Name     | Type      | Description              |
| -------- | --------- | ------------------------ |
| `<none>` | `uint256` | The executed sell amount |

### batchExecutedSellAmounts

Returns the cumulative executed sell amount for a batch of orders over all settlements

```solidity
function batchExecutedSellAmounts(bytes32[] calldata orderHashes) external view returns (uint256[] memory);
```

**Parameters**

| Name          | Type        | Description              |
| ------------- | ----------- | ------------------------ |
| `orderHashes` | `bytes32[]` | The hashes of the orders |

**Returns**

| Name     | Type        | Description               |
| -------- | ----------- | ------------------------- |
| `<none>` | `uint256[]` | The executed sell amounts |

### renounceAdmin

Renounces the caller's admin role

Can only be called by an admin. If multiple admins exist, other admins retain their roles.

```solidity
function renounceAdmin() external;
```

### getTurbineLiquidityRouter

Returns the address of the TurbineLiquidityRouter

Return type is address to avoid unnecessary imports

```solidity
function getTurbineLiquidityRouter() external view returns (address);
```

**Returns**

| Name     | Type      | Description                               |
| -------- | --------- | ----------------------------------------- |
| `<none>` | `address` | The address of the TurbineLiquidityRouter |

### getTurbineHook

Returns the address of the TurbineHook (via the TurbineLiquidityRouter)

Return type is address to avoid unnecessary imports

```solidity
function getTurbineHook() external view returns (address);
```

**Returns**

| Name     | Type      | Description                    |
| -------- | --------- | ------------------------------ |
| `<none>` | `address` | The address of the TurbineHook |

### turbineSigner

Returns the address of the Turbine signer

```solidity
function turbineSigner() external view returns (address);
```

**Returns**

| Name     | Type      | Description                                  |
| -------- | --------- | -------------------------------------------- |
| `<none>` | `address` | The address authorized to submit settlements |

### executeLiquidityActionsAndSettlement

Executes liquidity actions and a batch settlement

```solidity
function executeLiquidityActionsAndSettlement(
    AddLiquidityAppliedAction[] calldata addLiquidityActions,
    RemoveLiquidityAppliedAction[] calldata removeLiquidityOffchainActions,
    RemoveLiquidityIntentHash[] calldata removeLiquidityOnchainActions,
    BatchSettlement calldata settlement,
    uint256 targetBlockNumber,
    uint256 batchId
) external;
```

**Parameters**

| Name                             | Type                             | Description                                                |
| -------------------------------- | -------------------------------- | ---------------------------------------------------------- |
| `addLiquidityActions`            | `AddLiquidityAppliedAction[]`    | The liquidity actions to add liquidity                     |
| `removeLiquidityOffchainActions` | `RemoveLiquidityAppliedAction[]` | The offchain-applied liquidity actions to remove liquidity |
| `removeLiquidityOnchainActions`  | `RemoveLiquidityIntentHash[]`    | Intent hashes for onchain liquidity removals               |
| `settlement`                     | `BatchSettlement`                | The settlement to execute                                  |
| `targetBlockNumber`              | `uint256`                        | The block number at which the settlement is valid          |
| `batchId`                        | `uint256`                        | The unique identifier for this settlement batch            |

### feeWithdrawer

Returns the address of the fee withdrawer

```solidity
function feeWithdrawer() external view returns (address);
```

**Returns**

| Name     | Type      | Description                             |
| -------- | --------- | --------------------------------------- |
| `<none>` | `address` | The address authorized to withdraw fees |

### updateFeeWithdrawer

Updates the fee withdrawer address

Only callable by the admin

```solidity
function updateFeeWithdrawer(address newFeeWithdrawer) external;
```

**Parameters**

| Name               | Type      | Description                    |
| ------------------ | --------- | ------------------------------ |
| `newFeeWithdrawer` | `address` | The new fee withdrawer address |

### sweepFees

Sweeps all available balance of specified tokens to a specified recipient

Only callable by the fee withdrawer

```solidity
function sweepFees(address[] calldata tokens, address to) external;
```

**Parameters**

| Name     | Type        | Description                           |
| -------- | ----------- | ------------------------------------- |
| `tokens` | `address[]` | The addresses of the tokens to sweep  |
| `to`     | `address`   | The address to send the swept fees to |

## Events

### TurbineSignerSet

\------------------------- EVENTS ------------------------- ///

Emitted when the Turbine signer is set during construction

```solidity
event TurbineSignerSet(address indexed signer);
```

**Parameters**

| Name     | Type      | Description                                                    |
| -------- | --------- | -------------------------------------------------------------- |
| `signer` | `address` | The address of the Turbine signer (immutable after deployment) |

### TurbineSettlement

Emitted when a Turbine settlement is executed

```solidity
event TurbineSettlement(uint256 indexed batchId);
```

**Parameters**

| Name      | Type      | Description                                     |
| --------- | --------- | ----------------------------------------------- |
| `batchId` | `uint256` | The unique identifier for this settlement batch |

### OrderSettled

Emitted when an order is settled

```solidity
event OrderSettled(
    address indexed owner,
    bytes32 indexed orderHash,
    address sellToken,
    address buyToken,
    uint256 sellAmount,
    uint256 buyAmount
);
```

**Parameters**

| Name         | Type      | Description                    |
| ------------ | --------- | ------------------------------ |
| `owner`      | `address` | The owner of the settled order |
| `orderHash`  | `bytes32` | The hash of the settled order  |
| `sellToken`  | `address` |                                |
| `buyToken`   | `address` |                                |
| `sellAmount` | `uint256` | The amount of sell token       |
| `buyAmount`  | `uint256` | The amount of buy token        |

### SmartOrderSettled

Emitted when a smart order is executed

```solidity
event SmartOrderSettled(
    address indexed smartOrderTarget,
    bytes32 indexed orderHash,
    address sellToken,
    address buyToken,
    uint256 expectedSellAmount,
    uint256 receivedSellAmount,
    uint256 buyAmount
);
```

**Parameters**

| Name                 | Type      | Description                                                             |
| -------------------- | --------- | ----------------------------------------------------------------------- |
| `smartOrderTarget`   | `address` | The contract that executed the calldata                                 |
| `orderHash`          | `bytes32` | The hash of this smart order. In case of pool swaps, this hash is zero. |
| `sellToken`          | `address` | The sell token                                                          |
| `buyToken`           | `address` | The buy token                                                           |
| `expectedSellAmount` | `uint256` | The amount of sell token we expect from this order                      |
| `receivedSellAmount` | `uint256` | The amount of sell token we received from this order                    |
| `buyAmount`          | `uint256` | The amount of buy token                                                 |

### DEXSwapExecuted

Emitted when a DEX swap has been executed (typically via a Smart Order Router / aggregator).

```solidity
event DEXSwapExecuted(
    address indexed target,
    address sellToken,
    address buyToken,
    uint256 expectedSellAmount,
    uint256 receivedSellAmount,
    uint256 buyAmount
);
```

**Parameters**

| Name                 | Type      | Description                                                                |
| -------------------- | --------- | -------------------------------------------------------------------------- |
| `target`             | `address` | The target contract used to execute the swap (router/aggregator/exchange). |
| `sellToken`          | `address` | The token address being sold.                                              |
| `buyToken`           | `address` | The token address being purchased.                                         |
| `expectedSellAmount` | `uint256` | The expected amount of `sellToken` to be sold.                             |
| `receivedSellAmount` | `uint256` | The actual amount of `sellToken` sold by the pool/received by the settler. |
| `buyAmount`          | `uint256` | The amount of `buyToken` received from the swap.                           |

### TurbinePoolSwapExecuted

Emitted when a Turbine pool execution occurs

```solidity
event TurbinePoolSwapExecuted(
    address poolToken0, address poolToken1, uint24 poolFee, bool zeroForOne, uint256 amountIn, uint256 amountOut
);
```

**Parameters**

| Name         | Type      | Description                                               |
| ------------ | --------- | --------------------------------------------------------- |
| `poolToken0` | `address` | The address of token0 in the executed pool                |
| `poolToken1` | `address` | The address of token1 in the executed pool                |
| `poolFee`    | `uint24`  | The fee tier of the executed pool                         |
| `zeroForOne` | `bool`    | Direction of the swap, true if swapping token0 for token1 |
| `amountIn`   | `uint256` | Input token amount                                        |
| `amountOut`  | `uint256` | Output token amount                                       |

### FeeWithdrawerUpdated

Emitted when the fee withdrawer is updated

```solidity
event FeeWithdrawerUpdated(address indexed previous, address indexed newFeeWithdrawer);
```

**Parameters**

| Name               | Type      | Description                         |
| ------------------ | --------- | ----------------------------------- |
| `previous`         | `address` | The previous fee withdrawer address |
| `newFeeWithdrawer` | `address` | The new fee withdrawer address      |

### FeesSwept

Emitted when protocol fees are swept

```solidity
event FeesSwept(address[] tokens, address indexed recipient);
```

**Parameters**

| Name        | Type        | Description                       |
| ----------- | ----------- | --------------------------------- |
| `tokens`    | `address[]` | The addresses of the tokens swept |
| `recipient` | `address`   | The address receiving the fees    |

## Errors

### OnlyTurbineSigner

\------------------------- ERRORS ------------------------- ///

Only the turbine signer can call this function

```solidity
error OnlyTurbineSigner();
```

### OnlyFeeWithdrawer

Only the fee withdrawer can call this function

```solidity
error OnlyFeeWithdrawer();
```

### InsufficientTokensReceived

Insufficient amount of tokens received by the ITurbineSettler

```solidity
error InsufficientTokensReceived(uint256 expected, uint256 actual);
```

**Parameters**

| Name       | Type      | Description                                    |
| ---------- | --------- | ---------------------------------------------- |
| `expected` | `uint256` | The expected amount                            |
| `actual`   | `uint256` | The actual amount, smaller value than expected |

### InvalidCalldataTarget

Invalid calldata target. The calldata target cannot be the ITurbineSettler or the ITurbineLiquidityRouter.

```solidity
error InvalidCalldataTarget(address calldataTarget);
```

**Parameters**

| Name             | Type      | Description         |
| ---------------- | --------- | ------------------- |
| `calldataTarget` | `address` | The calldata target |

### ExcessiveTokensDistributed

More than expected amount of tokens sent out from the ITurbineSettler

```solidity
error ExcessiveTokensDistributed(uint256 expected, uint256 actual);
```

**Parameters**

| Name       | Type      | Description                                   |
| ---------- | --------- | --------------------------------------------- |
| `expected` | `uint256` | The expected amount                           |
| `actual`   | `uint256` | The actual amount, larger value than expected |

### InvalidBlockNumber

Current block number is not the target block number

```solidity
error InvalidBlockNumber();
```

### InsufficientBuyTokenBalance

There is not enough buy token balance to pay out users

```solidity
error InsufficientBuyTokenBalance(address token, uint256 available, uint256 required);
```

### ZeroAddress

Zero address provided where not allowed

```solidity
error ZeroAddress();
```

### RouterNotSet

TurbineLiquidityRouter must be set before renouncing admin

```solidity
error RouterNotSet();
```

### MissingAllowance

The allowance for a limit order is missing, and the onchain allowance is not enough to fulfill the order.

```solidity
error MissingAllowance(bytes32 orderHash);
```


# AddLiquidityAppliedAction

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/settler/interfaces/ITurbineSettler.sol)

\------------------------- STRUCTS ------------------------- ///

Liquidity action representing provision of liquidity already applied to pool states, with all necessary arguments.

```solidity
struct AddLiquidityAppliedAction {
address owner;
bytes32 hash;
uint256 token0Amount;
uint256 token1Amount;
uint256 lpTokenAmount;
address token0;
address token1;
uint24 fee;
BatchSignatureTransferParams signatureTransferParams;
}
```

**Properties**

| Name                      | Type                           | Description                                                                    |
| ------------------------- | ------------------------------ | ------------------------------------------------------------------------------ |
| `owner`                   | `address`                      | The account providing the liquidity                                            |
| `hash`                    | `bytes32`                      | The hash of the liquidity action                                               |
| `token0Amount`            | `uint256`                      | The amount of token0 provided                                                  |
| `token1Amount`            | `uint256`                      | The amount of token1 provided                                                  |
| `lpTokenAmount`           | `uint256`                      | The amount of LP tokens to mint                                                |
| `token0`                  | `address`                      | The address of token0 of the Turbine pool                                      |
| `token1`                  | `address`                      | The address of token1 of the Turbine pool                                      |
| `fee`                     | `uint24`                       | The fee of the Turbine pool                                                    |
| `signatureTransferParams` | `BatchSignatureTransferParams` | The Permit2 batch SignatureTransfer permit and signature for token0 and token1 |


# RemoveLiquidityAppliedAction

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/settler/interfaces/ITurbineSettler.sol)

Liquidity action representing removal of liquidity already applied to pool states, with all necessary arguments.

```solidity
struct RemoveLiquidityAppliedAction {
address owner;
bytes32 hash;
uint256 lpTokenAmount;
address token0;
address token1;
uint24 fee;
SignatureTransferParams signatureTransferParams;
}
```

**Properties**

| Name                      | Type                      | Description                                                         |
| ------------------------- | ------------------------- | ------------------------------------------------------------------- |
| `owner`                   | `address`                 | The account removing the liquidity                                  |
| `hash`                    | `bytes32`                 | The hash of the liquidity action                                    |
| `lpTokenAmount`           | `uint256`                 | The amount of LP tokens to burn                                     |
| `token0`                  | `address`                 | The address of token0 of the Turbine pool                           |
| `token1`                  | `address`                 | The address of token1 of the Turbine pool                           |
| `fee`                     | `uint24`                  | The fee of the Turbine pool                                         |
| `signatureTransferParams` | `SignatureTransferParams` | The Permit2 SignatureTransfer permit and signature for the LP token |


# ❱ libraries

* [Address](/reference/contracts/solidity-documentation/settler/libraries/library.address)
* [OrderHashParams](/reference/contracts/solidity-documentation/settler/libraries/struct.orderhashparams)
* [OrderLib](/reference/contracts/solidity-documentation/settler/libraries/library.orderlib)


# Address

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/settler/libraries/Address.sol)

## Functions

### functionCallWithGasLimit

Performs a Solidity function call using a low level `call`. A plain `call` is an unsafe replacement for a function call: use this function instead. If `target` reverts with a revert reason or custom error, it is bubbled up by this function (like regular Solidity function calls). However, if the call reverted with no returned reason, this function reverts with a {Errors.FailedCall} error. Also limits the gas usage. Returns the raw returned data. To convert to the expected return value, use <https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions\\[`abi.decode`>]. Requirements:

* `target` must be a contract.
* calling `target` with `data` must not revert.

```solidity
function functionCallWithGasLimit(
    address target,
    bytes memory data,
    uint256 gasLimit
) internal returns (bytes memory);
```


# OrderHashParams

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/settler/libraries/OrderLib.sol)

Struct that defines the key for storing an order.

Includes all order parameters except the allowances and creation timestamp.

```solidity
struct OrderHashParams {
address owner;
address sellToken;
address buyToken;
uint256 sellAmount;
uint256 minBuyAmount;
uint32 maxGas;
uint32 startMidPriceDelta;
uint32 endMidPriceDelta;
uint32 startTime;
uint32 endTime;
bytes32 salt;
bool partialFill;
bytes callData;
address callDataTarget;
}
```


# OrderLib

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/settler/libraries/OrderLib.sol)

## Functions

### hash

Calculates the hash of an order

```solidity
function hash(Order memory order) internal pure returns (bytes32);
```

**Parameters**

| Name    | Type    | Description       |
| ------- | ------- | ----------------- |
| `order` | `Order` | The order to hash |

**Returns**

| Name     | Type      | Description                     |
| -------- | --------- | ------------------------------- |
| `<none>` | `bytes32` | orderHash The hash of the order |


# ❱ structs

* [BatchSettlement](/reference/contracts/solidity-documentation/settler/structs/struct.batchsettlement)
* [Loan](/reference/contracts/solidity-documentation/settler/structs/struct.loan)
* [LimitAmount](/reference/contracts/solidity-documentation/settler/structs/struct.limitamount)
* [LimitOrderExecution](/reference/contracts/solidity-documentation/settler/structs/struct.limitorderexecution)
* [SmartOrderExecution](/reference/contracts/solidity-documentation/settler/structs/struct.smartorderexecution)
* [TurbinePoolExecution](/reference/contracts/solidity-documentation/settler/structs/struct.turbinepoolexecution)
* [DEXSwap](/reference/contracts/solidity-documentation/settler/structs/struct.dexswap)
* [ReceivedTokenAmount](/reference/contracts/solidity-documentation/settler/structs/struct.receivedtokenamount)
* [Order](/reference/contracts/solidity-documentation/settler/structs/struct.order)
* [OrderGroup](/reference/contracts/solidity-documentation/settler/structs/struct.ordergroup)
* [Price](/reference/contracts/solidity-documentation/settler/structs/struct.price)


# BatchSettlement

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/settler/structs/BatchSettlement.sol)

```solidity
struct BatchSettlement {
Loan[] loans;
LimitOrderExecution[] limitOrders;
SmartOrderExecution[] smartOrders;
DEXSwap[] dexSwaps;
TurbinePoolExecution[] poolExecutions;
// TODO: Fynd integration
// FyndExecution[] fyndExecutions;
LimitAmount[] limitAmounts;
}
```


# Loan

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/settler/structs/BatchSettlement.sol)

```solidity
struct Loan {
address token;
uint256 amount;
}
```


# LimitAmount

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/settler/structs/BatchSettlement.sol)

```solidity
struct LimitAmount {
address token;
uint256 amount;
}
```


# LimitOrderExecution

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/settler/structs/Execution.sol)

**Title:** LimitOrderExecution

Instructions on how to execute a limit order.

```solidity
struct LimitOrderExecution {
bytes32 orderHash;
address owner;
address buyToken;
address sellToken;
uint256 minBuyAmount;
uint256 sellAmount;
uint256 buyTokenFees;
AllowanceParams allowanceParams;
}
```

**Properties**

| Name              | Type              | Description                                                                                                                                                                                                         |
| ----------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `orderHash`       | `bytes32`         | The hash of the order that this swap represents.                                                                                                                                                                    |
| `owner`           | `address`         |                                                                                                                                                                                                                     |
| `buyToken`        | `address`         | Address of the token that the user is buying                                                                                                                                                                        |
| `sellToken`       | `address`         | Address of the token that the user is selling                                                                                                                                                                       |
| `minBuyAmount`    | `uint256`         | The minimum amount of `buyToken` that the user expects to receive                                                                                                                                                   |
| `sellAmount`      | `uint256`         | The amount of `sellToken` that the user will sell                                                                                                                                                                   |
| `buyTokenFees`    | `uint256`         | The amount of `buyToken` fees that will be paid for the swap.                                                                                                                                                       |
| `allowanceParams` | `AllowanceParams` | The allowance parameters for the order. If empty, it means that at the time of the offchain validation there was already enough Permit2 allowance onchain to execute this order, so new allowance is not necessary. |


# SmartOrderExecution

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/settler/structs/Execution.sol)

**Title:** SmartOrderExecution

Instructions on how to execute a smart order.

```solidity
struct SmartOrderExecution {
bytes32 orderHash;
address target;
address callDataTarget;
bytes callData;
address buyToken;
address sellToken;
uint256 buyAmount;
uint256 minSellAmount;
}
```

**Properties**

| Name             | Type      | Description                                                        |
| ---------------- | --------- | ------------------------------------------------------------------ |
| `orderHash`      | `bytes32` | The hash of the order that this swap represents.                   |
| `target`         | `address` | Where the funds will be sent to.                                   |
| `callDataTarget` | `address` | The address to call. Usually a pool or a router address.           |
| `callData`       | `bytes`   | The calldata to call the target it with.                           |
| `buyToken`       | `address` | Address of the token that the smart order is buying.               |
| `sellToken`      | `address` | Address of the token that the smart order is selling.              |
| `buyAmount`      | `uint256` | The amount of `buyToken` that will be sent to target.              |
| `minSellAmount`  | `uint256` | The minimum amount of `sellToken` that Turbine expects to receive. |


# TurbinePoolExecution

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/settler/structs/Execution.sol)

**Title:** TurbinePoolExecution

Instructions on how to execute a swap on a Turbine pool. The buyToken and sellToken meaning stays the same as SmartOrderExecution.

```solidity
struct TurbinePoolExecution {
address sellToken;
address buyToken;
uint256 sellAmount;
uint256 buyAmount;
uint24 poolFee;
uint256 deadline;
}
```

**Properties**

| Name         | Type      | Description                            |
| ------------ | --------- | -------------------------------------- |
| `sellToken`  | `address` | The token being sold by the order.     |
| `buyToken`   | `address` | The token being bought by the order.   |
| `sellAmount` | `uint256` | The amount of sellToken being swapped. |
| `buyAmount`  | `uint256` | The amount of buyToken being swapped.  |
| `poolFee`    | `uint24`  | The fee tier of the pool.              |
| `deadline`   | `uint256` | The deadline for the swap execution.   |


# DEXSwap

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/settler/structs/Execution.sol)

**Title:** DEXSwap

Instructions on how to execute a swap on an external DEX.

```solidity
struct DEXSwap {
address callDataTarget;
bytes callData;
address buyToken;
address sellToken;
uint256 buyAmount;
uint256 expectedSellAmount;
}
```

**Properties**

| Name                 | Type      | Description                                                     |
| -------------------- | --------- | --------------------------------------------------------------- |
| `callDataTarget`     | `address` | The address to call. Usually a pool or a router address.        |
| `callData`           | `bytes`   | The calldata to call the target with.                           |
| `buyToken`           | `address` | Address of the token being bought in the swap.                  |
| `sellToken`          | `address` | Address of the token being sold in the swap.                    |
| `buyAmount`          | `uint256` | The amount of `buyToken` expected to be received from the swap. |
| `expectedSellAmount` | `uint256` | The amount of `sellToken` expected to be sold in the swap.      |


# ReceivedTokenAmount

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/settler/structs/Execution.sol)

**Title:** ReceivedTokenAmount

Details about the amount of a token received from an execution.

```solidity
struct ReceivedTokenAmount {
address token;
uint256 expected;
uint256 surplus;
}
```

**Properties**

| Name       | Type      | Description                                                                |
| ---------- | --------- | -------------------------------------------------------------------------- |
| `token`    | `address` | The address of the token.                                                  |
| `expected` | `uint256` | The expected amount of the token to be received.                           |
| `surplus`  | `uint256` | The surplus amount of the token received (i.e. amount received - expected) |


# Order

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/settler/structs/Order.sol)

**Title:** Order

A user's intent to trade tokens with price defined as a delta to the mid price.

```solidity
struct Order {
address owner;
address sellToken;
address buyToken;
uint256 sellAmount;
uint256 minBuyAmount;
uint32 maxGas;
uint32 startMidPriceDelta;
uint32 endMidPriceDelta;
uint32 startTime;
uint32 endTime;
uint64 createdTimestamp;
bool partialFill;
bytes callData;
address callDataTarget;
AllowanceParams allowanceParams;
bytes32 salt;
}
```

**Properties**

| Name                 | Type              | Description                                                                  |
| -------------------- | ----------------- | ---------------------------------------------------------------------------- |
| `owner`              | `address`         | The address that originally created the order and holds the sellToken amount |
| `sellToken`          | `address`         | The token to be sold                                                         |
| `buyToken`           | `address`         | The token to be bought                                                       |
| `sellAmount`         | `uint256`         | The amount of sellToken to be sold                                           |
| `minBuyAmount`       | `uint256`         | The minimum amount of buyToken to be bought                                  |
| `maxGas`             | `uint32`          | The maximum amount of gas to be used for the order                           |
| `startMidPriceDelta` | `uint32`          | The minimum delta to the mid price (units are basis points)                  |
| `endMidPriceDelta`   | `uint32`          | The maximum delta to the mid price (units are basis points)                  |
| `startTime`          | `uint32`          | The block timestamp at which the order became active                         |
| `endTime`            | `uint32`          | The block timestamp at which the order expires                               |
| `createdTimestamp`   | `uint64`          | The timestamp at which the order was added to the orderbook                  |
| `partialFill`        | `bool`            | Whether the order can be partially filled                                    |
| `callData`           | `bytes`           | The calldata for the order, allows for custom routing                        |
| `callDataTarget`     | `address`         |                                                                              |
| `allowanceParams`    | `AllowanceParams` | The permit signature and permit data for the order                           |
| `salt`               | `bytes32`         | Used to differentiate between orders with the same parameters                |


# OrderGroup

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/settler/structs/OrderGroup.sol)

Group of orders defined by the same sellToken and buyToken

```solidity
struct OrderGroup {
address sellToken;
address buyToken;
Order[] orders;
Price midPrice;
}
```


# Price

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/settler/structs/Price.sol)

A struct representing a price as a fraction \[numerator/denominator]

```solidity
struct Price {
uint256 numerator;
uint256 denominator;
}
```


# Constants

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/settler/Constants.sol)

### BYTES32\_LENGTH

```solidity
uint8 constant BYTES32_LENGTH = 32
```

### BASIS\_POINTS

```solidity
uint32 constant BASIS_POINTS = 10_000
```

### PRICE\_SCALING

```solidity
uint256 constant PRICE_SCALING = 1e36
```

### PRICE\_SCALING\_SQUARED

```solidity
uint256 constant PRICE_SCALING_SQUARED = PRICE_SCALING * PRICE_SCALING
```

### PERMIT2

```solidity
address constant PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3
```

### UNIV4\_POOL\_MANAGER

```solidity
address constant UNIV4_POOL_MANAGER = 0x000000000004444c5dc75cB358380D2e3dE08A90
```


# TurbineSettler

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/settler/TurbineSettler.sol)

**Inherits:** [ITurbineSettler](https://github.com/propeller-heads/turbine-docs/blob/master/src/settler/interfaces/ITurbineSettler.sol/interface.ITurbineSettler.md), AccessControl, Pausable, IUnlockCallback

## State Variables

### PERMIT2\_CONTRACT

```solidity
IPermit2 public constant PERMIT2_CONTRACT = IPermit2(PERMIT2)
```

### UNIV4\_POOL\_MANAGER\_CONTRACT

```solidity
IPoolManager public constant UNIV4_POOL_MANAGER_CONTRACT = IPoolManager(UNIV4_POOL_MANAGER)
```

### PAUSER\_ROLE

```solidity
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE")
```

### UNPAUSER\_ROLE

```solidity
bytes32 public constant UNPAUSER_ROLE = keccak256("UNPAUSER_ROLE")
```

### CALL\_DATA\_EXECUTION\_GAS\_LIMIT

```solidity
uint256 private constant CALL_DATA_EXECUTION_GAS_LIMIT = 200000
```

### turbineSigner

Address that signs the settlement solutions

```solidity
address public immutable turbineSigner
```

### feeWithdrawer

Address authorized to withdraw accumulated fees

```solidity
address public feeWithdrawer
```

### turbineLiquidityRouter

Contract that handles liquidity provisions and removals

```solidity
ITurbineLiquidityRouter public immutable turbineLiquidityRouter
```

### executedSellAmounts

Executed settled sell amounts of orders

```solidity
mapping(bytes32 orderHash => uint256 executedSellAmount) public executedSellAmounts
```

## Functions

### constructor

\---------------------------- 🛠️ CONSTRUCTOR ----------------------------- ///

```solidity
constructor(address _turbineSigner, address _maintainer, address _turbineLiquidityRouter) ;
```

### onlyTurbineSigner

Restricts function access to the turbine signer only

```solidity
modifier onlyTurbineSigner() ;
```

### \_onlyTurbineSigner

```solidity
function _onlyTurbineSigner() internal view;
```

### onlyFeeWithdrawer

Restricts function access to the fee withdrawer only

```solidity
modifier onlyFeeWithdrawer() ;
```

### \_onlyFeeWithdrawer

```solidity
function _onlyFeeWithdrawer() internal view;
```

### pause

\------------------------ 🌐 EXTERNAL METHODS ------------------------- ///

Pauses the contract

```solidity
function pause() external onlyRole(PAUSER_ROLE);
```

### unpause

Unpauses the contract

```solidity
function unpause() external onlyRole(UNPAUSER_ROLE);
```

### renounceAdmin

Renounces the caller's admin role

Can only be called by an admin. If multiple admins exist, other admins retain their roles.

```solidity
function renounceAdmin() external onlyRole(DEFAULT_ADMIN_ROLE);
```

### updateFeeWithdrawer

Updates the fee withdrawer address

Only callable by the admin

```solidity
function updateFeeWithdrawer(address newFeeWithdrawer) external onlyRole(DEFAULT_ADMIN_ROLE);
```

**Parameters**

| Name               | Type      | Description                    |
| ------------------ | --------- | ------------------------------ |
| `newFeeWithdrawer` | `address` | The new fee withdrawer address |

### sweepFees

Sweeps all available balance of specified tokens to a specified recipient

Only callable by the fee withdrawer

```solidity
function sweepFees(address[] calldata tokens, address to) external onlyFeeWithdrawer;
```

**Parameters**

| Name     | Type        | Description                           |
| -------- | ----------- | ------------------------------------- |
| `tokens` | `address[]` | The addresses of the tokens to sweep  |
| `to`     | `address`   | The address to send the swept fees to |

### executeLiquidityActionsAndSettlement

Executes liquidity actions and a batch settlement

```solidity
function executeLiquidityActionsAndSettlement(
    AddLiquidityAppliedAction[] calldata addLiquidityActions,
    RemoveLiquidityAppliedAction[] calldata removeLiquidityOffchainActions,
    RemoveLiquidityIntentHash[] calldata removeLiquidityOnchainActions,
    BatchSettlement calldata settlement,
    uint256 targetBlockNumber,
    uint256 batchId
) external onlyTurbineSigner whenNotPaused;
```

**Parameters**

| Name                             | Type                             | Description                                                |
| -------------------------------- | -------------------------------- | ---------------------------------------------------------- |
| `addLiquidityActions`            | `AddLiquidityAppliedAction[]`    | The liquidity actions to add liquidity                     |
| `removeLiquidityOffchainActions` | `RemoveLiquidityAppliedAction[]` | The offchain-applied liquidity actions to remove liquidity |
| `removeLiquidityOnchainActions`  | `RemoveLiquidityIntentHash[]`    | Intent hashes for onchain liquidity removals               |
| `settlement`                     | `BatchSettlement`                | The settlement to execute                                  |
| `targetBlockNumber`              | `uint256`                        | The block number at which the settlement is valid          |
| `batchId`                        | `uint256`                        | The unique identifier for this settlement batch            |

### unlockCallback

Take loans, fill orders, repay the loans

This function is called by the UniV4PoolManager contract.

```solidity
function unlockCallback(bytes calldata data) external override returns (bytes memory);
```

### batchExecutedSellAmounts

Returns the cumulative executed sell amount for a batch of orders over all settlements

```solidity
function batchExecutedSellAmounts(bytes32[] calldata orderHashes) external view returns (uint256[] memory);
```

**Parameters**

| Name          | Type        | Description              |
| ------------- | ----------- | ------------------------ |
| `orderHashes` | `bytes32[]` | The hashes of the orders |

**Returns**

| Name     | Type        | Description               |
| -------- | ----------- | ------------------------- |
| `<none>` | `uint256[]` | The executed sell amounts |

### getTurbineLiquidityRouter

Returns the address of the TurbineLiquidityRouter

Return type is address to avoid unnecessary imports

```solidity
function getTurbineLiquidityRouter() external view returns (address);
```

**Returns**

| Name     | Type      | Description                               |
| -------- | --------- | ----------------------------------------- |
| `<none>` | `address` | The address of the TurbineLiquidityRouter |

### getTurbineHook

Returns the address of the TurbineHook (via the TurbineLiquidityRouter)

Return type is address to avoid unnecessary imports

```solidity
function getTurbineHook() external view returns (address);
```

**Returns**

| Name     | Type      | Description                    |
| -------- | --------- | ------------------------------ |
| `<none>` | `address` | The address of the TurbineHook |

### \_executeBatchSettlement

\------------------------- 🛠️ INTERNAL METHODS ------------------------- ///

Execute a settlement, potentially taking flashloans

```solidity
function _executeBatchSettlement(BatchSettlement calldata settlement) internal;
```

### \_executeSmartOrderSwaps

Executes smart orders

```solidity
function _executeSmartOrderSwaps(SmartOrderExecution[] memory swaps) internal;
```

### \_executeDEXSwaps

Executes DEX pool swaps

```solidity
function _executeDEXSwaps(DEXSwap[] memory swaps) internal;
```

### \_executeTurbinePoolExecutions

Execute Turbine pool executions

```solidity
function _executeTurbinePoolExecutions(TurbinePoolExecution[] memory executions) internal;
```

### \_receiveSellTokens

```solidity
function _receiveSellTokens(LimitOrderExecution[] memory swaps) internal;
```

### \_sendBuyTokens

```solidity
function _sendBuyTokens(LimitOrderExecution[] memory swaps, ReceivedTokenAmount[] memory tokenAmounts) internal;
```

### \_computeReceivedTokenAmounts

```solidity
function _computeReceivedTokenAmounts(
    BatchSettlement memory settlement,
    uint256[] memory initialBalances
) internal view returns (ReceivedTokenAmount[] memory);
```

### \_executeSmartOrderSwap

Execute swaps on external contracts

This is used both to execute smart orders and to execute swaps on liquidity pools. These two cases are almost identical, but they might diverge in the future. We may decide later to merge them.

```solidity
function _executeSmartOrderSwap(SmartOrderExecution memory swap) internal returns (uint256);
```

**Parameters**

| Name   | Type                  | Description          |
| ------ | --------------------- | -------------------- |
| `swap` | `SmartOrderExecution` | The swap to execute. |

**Returns**

| Name     | Type      | Description                                                       |
| -------- | --------- | ----------------------------------------------------------------- |
| `<none>` | `uint256` | Returns the received sell amount from this smart order execution. |

### \_executeDEXSwap

Execute a swap on a DEX pool

We don't check for amounts transferred because we do these checks at a token level

```solidity
function _executeDEXSwap(DEXSwap memory swap) internal returns (uint256);
```

**Parameters**

| Name   | Type      | Description          |
| ------ | --------- | -------------------- |
| `swap` | `DEXSwap` | The swap to execute. |


# ❱ univ4-hook

* [interfaces](https://github.com/propeller-heads/turbine-docs/blob/master/src/univ4-hook/interfaces/README.md)
* [libraries](https://github.com/propeller-heads/turbine-docs/blob/master/src/univ4-hook/libraries/README.md)
* [BaseHook](/reference/contracts/solidity-documentation/univ4-hook/abstract.basehook)
* [LPToken](/reference/contracts/solidity-documentation/univ4-hook/contract.lptoken)
* [TurbineHook](/reference/contracts/solidity-documentation/univ4-hook/contract.turbinehook)
* [TurbineLiquidityRouter](/reference/contracts/solidity-documentation/univ4-hook/contract.turbineliquidityrouter)


# ❱ interfaces

* [RemoveLiquidityIntentHash](/reference/contracts/solidity-documentation/univ4-hook/interfaces/type.removeliquidityintenthash)
* [PoolId](/reference/contracts/solidity-documentation/univ4-hook/interfaces/type.poolid)
* [SignatureTransferParams](/reference/contracts/solidity-documentation/univ4-hook/interfaces/struct.signaturetransferparams)
* [BatchSignatureTransferParams](/reference/contracts/solidity-documentation/univ4-hook/interfaces/struct.batchsignaturetransferparams)
* [RemoveLiquidityIntent](/reference/contracts/solidity-documentation/univ4-hook/interfaces/struct.removeliquidityintent)
* [RemoveLiquidityData](/reference/contracts/solidity-documentation/univ4-hook/interfaces/struct.removeliquiditydata)
* [ITurbineLiquidityRouter](/reference/contracts/solidity-documentation/univ4-hook/interfaces/interface.iturbineliquidityrouter)
* [equalsIntentHash](/reference/contracts/solidity-documentation/univ4-hook/interfaces/function.equalsintenthash)
* [equalsPoolId](/reference/contracts/solidity-documentation/univ4-hook/interfaces/function.equalspoolid)


# RemoveLiquidityIntentHash

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/univ4-hook/interfaces/ITurbineLiquidityRouter.sol)

```solidity
type RemoveLiquidityIntentHash is bytes32
```


# PoolId

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/univ4-hook/interfaces/ITurbineLiquidityRouter.sol)

```solidity
type PoolId is bytes32
```


# SignatureTransferParams

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/univ4-hook/interfaces/ITurbineLiquidityRouter.sol)

Holds the Permit2 SignatureTransfer permit and signature for a single token transfer.

Used for remove-liquidity intents to authorize LP token transfers.

```solidity
struct SignatureTransferParams {
/// @notice The permit data for a single token transfer.
ISignatureTransfer.PermitTransferFrom permit;
/// @notice The signature authorizing the transfer.
bytes signature;
}
```


# BatchSignatureTransferParams

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/univ4-hook/interfaces/ITurbineLiquidityRouter.sol)

Holds the Permit2 SignatureTransfer batch permit and signature for multiple token transfers.

Used for add-liquidity intents to authorize token0 and token1 transfers in one signature.

```solidity
struct BatchSignatureTransferParams {
/// @notice The permit data for a batch token transfer.
ISignatureTransfer.PermitBatchTransferFrom permit;
/// @notice The signature authorizing the transfer.
bytes signature;
}
```


# RemoveLiquidityIntent

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/univ4-hook/interfaces/ITurbineLiquidityRouter.sol)

Intent describing a user's request to remove liquidity from a specific pool.

```solidity
struct RemoveLiquidityIntent {
/// @notice The address that owns the LP tokens and submits the intent.
address owner;
/// @notice The identifier of the pool from which liquidity should be removed.
PoolId poolId;
/// @notice Amount of LP tokens to burn for withdrawal.
uint256 lpTokenAmount;
/// @notice Arbitrary user-provided salt to make the intent hash unique.
bytes32 salt;
}
```


# RemoveLiquidityData

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/univ4-hook/interfaces/ITurbineLiquidityRouter.sol)

Full on-chain metadata associated with a remove-liquidity intent.

Bundles the original intent, its Permit2 SignatureTransfer permit, creation timestamp and hash. Pool metadata (token0, token1, fee, lpToken) is excluded — callers enrich from their own cache.

```solidity
struct RemoveLiquidityData {
/// @notice The original remove-liquidity intent.
RemoveLiquidityIntent intent;
/// @notice Permit2 SignatureTransfer parameters authorizing LP token transfer.
SignatureTransferParams signatureTransferParams;
/// @notice Block timestamp when the intent was recorded.
uint256 createdAt;
/// @notice The keccak256 hash uniquely identifying this intent.
RemoveLiquidityIntentHash hash;
}
```


# ITurbineLiquidityRouter

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/univ4-hook/interfaces/ITurbineLiquidityRouter.sol)

## Functions

### addLiquidity

Adds liquidity to the pair represented by the given hook.

```solidity
function addLiquidity(
    address to,
    uint256 amount0,
    uint256 amount1,
    address poolToken0,
    address poolToken1,
    uint24 poolFee,
    uint256 liquidity,
    BatchSignatureTransferParams memory batchSignatureTransferParams
) external payable returns (uint256);
```

**Parameters**

| Name                           | Type                           | Description                                                                     |
| ------------------------------ | ------------------------------ | ------------------------------------------------------------------------------- |
| `to`                           | `address`                      | The address receiving the minted liquidity.                                     |
| `amount0`                      | `uint256`                      | The amount of token0 to add as liquidity.                                       |
| `amount1`                      | `uint256`                      | The amount of token1 to add as liquidity.                                       |
| `poolToken0`                   | `address`                      | The address of the first token in the pool.                                     |
| `poolToken1`                   | `address`                      | The address of the second token in the pool.                                    |
| `poolFee`                      | `uint24`                       | The fee of the pool.                                                            |
| `liquidity`                    | `uint256`                      | The liquidity amount to mint for the caller.                                    |
| `batchSignatureTransferParams` | `BatchSignatureTransferParams` | The Permit2 batch SignatureTransfer permit and signature for token0 and token1. |

**Returns**

| Name     | Type      | Description                                           |
| -------- | --------- | ----------------------------------------------------- |
| `<none>` | `uint256` | liquidity The minted liquidity amount for the caller. |

### removeLiquidity

Burns liquidity to the pair represented by the given hook.

```solidity
function removeLiquidity(
    address to,
    uint256 liquidity,
    address poolToken0,
    address poolToken1,
    uint24 poolFee,
    SignatureTransferParams memory signatureTransferParams
) external payable returns (uint256, uint256);
```

**Parameters**

| Name                      | Type                      | Description                                                          |
| ------------------------- | ------------------------- | -------------------------------------------------------------------- |
| `to`                      | `address`                 | The address receiving the burnt liquidity.                           |
| `liquidity`               | `uint256`                 | The burnt liquidity amount from the caller.                          |
| `poolToken0`              | `address`                 | The address of the first token in the pool.                          |
| `poolToken1`              | `address`                 | The address of the second token in the pool.                         |
| `poolFee`                 | `uint24`                  | The fee of the pool.                                                 |
| `signatureTransferParams` | `SignatureTransferParams` | The Permit2 SignatureTransfer permit and signature for the LP token. |

**Returns**

| Name     | Type      | Description                                                   |
| -------- | --------- | ------------------------------------------------------------- |
| `<none>` | `uint256` | amount0 The amount of token0 received from burning liquidity. |
| `<none>` | `uint256` | amount1 The amount of token1 received from burning liquidity. |

### submitRemoveLiquidityIntent

Submits a new remove-liquidity intent for later execution.

The caller must be `intent.owner`. The provided Permit2 authorization must match the LP token and cover at least `intent.lpTokenAmount`.

The signature stored onchain can only be used by the spender address (this router) because the msg.sender is included in the PermitTransferFrom.hash() see <https://github.com/Uniswap/permit2/blob/cc56ad0f3439c502c246fc5cfcc3db92bb8b7219/src/libraries/PermitHash.sol#L62>

```solidity
function submitRemoveLiquidityIntent(
    RemoveLiquidityIntent calldata intent,
    SignatureTransferParams calldata signatureTransferParams
) external returns (RemoveLiquidityIntentHash);
```

**Parameters**

| Name                      | Type                      | Description                                                                       |
| ------------------------- | ------------------------- | --------------------------------------------------------------------------------- |
| `intent`                  | `RemoveLiquidityIntent`   | The remove-liquidity instruction containing pool and LP token amount details.     |
| `signatureTransferParams` | `SignatureTransferParams` | Permit2 SignatureTransfer parameters and signature authorizing LP token transfer. |

**Returns**

| Name     | Type                        | Description                                        |
| -------- | --------------------------- | -------------------------------------------------- |
| `<none>` | `RemoveLiquidityIntentHash` | The intent hash used to reference this submission. |

### MIN\_INTENT\_EXPIRY

Returns the minimum expiry for the TurbineLiquidityRouter

```solidity
function MIN_INTENT_EXPIRY() external view returns (uint256);
```

**Returns**

| Name     | Type      | Description                   |
| -------- | --------- | ----------------------------- |
| `<none>` | `uint256` | The minimum expiry in seconds |

### getTurbineSettler

Returns the address of the TurbineSettler

```solidity
function getTurbineSettler() external view returns (address);
```

**Returns**

| Name     | Type      | Description                       |
| -------- | --------- | --------------------------------- |
| `<none>` | `address` | The address of the TurbineSettler |

### getTurbineHook

Returns the address of the TurbineHook

Return type is address to avoid unnecessary imports

```solidity
function getTurbineHook() external view returns (address);
```

**Returns**

| Name     | Type      | Description                    |
| -------- | --------- | ------------------------------ |
| `<none>` | `address` | The address of the TurbineHook |

### executePendingIntents

Executes the provided intent hashes and deletes the executed intents.

```solidity
function executePendingIntents(RemoveLiquidityIntentHash[] calldata hashes) external;
```

**Parameters**

| Name     | Type                          | Description                        |
| -------- | ----------------------------- | ---------------------------------- |
| `hashes` | `RemoveLiquidityIntentHash[]` | Array of intent hashes to execute. |

### getRemoveLiquidityIntents

Returns remove-liquidity intent data for the given hashes. Skips deleted/unknown intents (owner == address(0)).

```solidity
function getRemoveLiquidityIntents(RemoveLiquidityIntentHash[] calldata hashes)
    external
    view
    returns (RemoveLiquidityData[] memory result);
```

**Parameters**

| Name     | Type                          | Description                      |
| -------- | ----------------------------- | -------------------------------- |
| `hashes` | `RemoveLiquidityIntentHash[]` | Array of intent hashes to query. |

**Returns**

| Name     | Type                    | Description                                          |
| -------- | ----------------------- | ---------------------------------------------------- |
| `result` | `RemoveLiquidityData[]` | Array of RemoveLiquidityData for valid intents only. |

### swapExactTokens

Swaps an exact amount of a token for an exact amount of another token.

This function is only callable by the `turbineSettler` address and the output tokens are transferred to the `turbineSettler`

* This function assumes that amountIn is already transferred to this contract.
* This function transfers tokens to the `poolManager` via `poolManager.unlock(...)`.
* The `poolManager` will perform the swap and return the output amount via the callback (`unlockCallback`).

```solidity
function swapExactTokens(
    uint256 amountIn,
    uint256 amountOut,
    address poolToken0,
    address poolToken1,
    uint24 poolFee,
    bool zeroForOne,
    uint256 deadline
) external payable;
```

**Parameters**

| Name         | Type      | Description                                                       |
| ------------ | --------- | ----------------------------------------------------------------- |
| `amountIn`   | `uint256` | The exact amount of input tokens to be sent for the swap.         |
| `amountOut`  | `uint256` | The exact amount of tokens expected to be received from the swap. |
| `poolToken0` | `address` | The address of the first token in the pool.                       |
| `poolToken1` | `address` | The address of the second token in the pool.                      |
| `poolFee`    | `uint24`  | The fee of the pool.                                              |
| `zeroForOne` | `bool`    | The direction of the swap.                                        |
| `deadline`   | `uint256` | A timestamp by which the call be finished to be valid.            |

## Events

### SettlerSet

```solidity
event SettlerSet(address indexed settler);
```

### RemoveLiquidityIntentSubmitted

```solidity
event RemoveLiquidityIntentSubmitted(
    address indexed owner,
    PoolId indexed poolId,
    RemoveLiquidityIntentHash intentHash,
    uint256 createdAt,
    uint256 deadline
);
```

### RemoveLiquidityIntentDeleted

```solidity
event RemoveLiquidityIntentDeleted(RemoveLiquidityIntentHash indexed intentHash);
```

## Errors

### InvalidOutputReceived

```solidity
error InvalidOutputReceived();
```

### OutOfGas

```solidity
error OutOfGas();
```

### ZeroLiquidity

```solidity
error ZeroLiquidity();
```

### InsufficientInitialLiquidity

```solidity
error InsufficientInitialLiquidity();
```

### InsufficientLiquidityBurnt

```solidity
error InsufficientLiquidityBurnt();
```

### SwapExpired

```solidity
error SwapExpired();
```

### NotTurbineSettler

```solidity
error NotTurbineSettler();
```

### NotPoolManager

```solidity
error NotPoolManager();
```

### NotSettlerOrPoolManager

```solidity
error NotSettlerOrPoolManager();
```

### InvalidIntentOwner

```solidity
error InvalidIntentOwner();
```

### InsufficientLpTokenBalance

```solidity
error InsufficientLpTokenBalance(address lpToken, uint256 requested, uint256 balance);
```

### DuplicateIntent

```solidity
error DuplicateIntent(RemoveLiquidityIntentHash intentHash);
```

### InvalidLpTokenAmount

```solidity
error InvalidLpTokenAmount();
```

### InvalidPermitToken

```solidity
error InvalidPermitToken(address expected, address actual);
```

### InvalidPermitAmount

```solidity
error InvalidPermitAmount(uint256 amount, uint256 required);
```

### PermitDeadlineTooShort

```solidity
error PermitDeadlineTooShort(uint256 deadline, uint256 minimumDeadline);
```

### PermitDeadlineTooLong

```solidity
error PermitDeadlineTooLong(uint256 deadline, uint256 maximumDeadline);
```

### NonceAlreadyUsed

```solidity
error NonceAlreadyUsed(address owner, uint256 nonce);
```


# equalsIntentHash

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/univ4-hook/interfaces/ITurbineLiquidityRouter.sol)

```solidity
function equalsIntentHash(RemoveLiquidityIntentHash intentHash, RemoveLiquidityIntentHash other) pure returns (bool);
```


# equalsPoolId

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/univ4-hook/interfaces/ITurbineLiquidityRouter.sol)

```solidity
function equalsPoolId(PoolId poolId, PoolId other) pure returns (bool);
```


# ❱ libraries

* [PoolMetadata](/reference/contracts/solidity-documentation/univ4-hook/libraries/struct.poolmetadata)
* [PoolMetadataLib](/reference/contracts/solidity-documentation/univ4-hook/libraries/library.poolmetadatalib)
* [SignatureVerification](/reference/contracts/solidity-documentation/univ4-hook/libraries/library.signatureverification)


# PoolMetadata

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/univ4-hook/libraries/PoolMetadata.sol)

Structure to hold metadata for a Turbine liquidity pool

```solidity
struct PoolMetadata {
address token0;
address token1;
uint24 fee;
address lpToken;
}
```

**Properties**

| Name      | Type      | Description                                                                                           |
| --------- | --------- | ----------------------------------------------------------------------------------------------------- |
| `token0`  | `address` | First token of a pool                                                                                 |
| `token1`  | `address` | Second token of a pool (tokens must be sorted, i.e. token1 > token0)                                  |
| `fee`     | `uint24`  | How much above the mid-price the pool is willing to trade for (in hundredths of bip, i.e. 10000 = 1%) |
| `lpToken` | `address` | Address of the LP token of the pool                                                                   |


# PoolMetadataLib

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/univ4-hook/libraries/PoolMetadata.sol)

**Title:** PoolMetadataLib

Library for handling pool metadata and computing pool identifiers

## Functions

### computeOnchainId

Computes the onchain ID for a pool based on its components

```solidity
function computeOnchainId(address token0, address token1, uint24 fee) internal pure returns (bytes32);
```

**Parameters**

| Name     | Type      | Description                           |
| -------- | --------- | ------------------------------------- |
| `token0` | `address` | First token address (must be sorted)  |
| `token1` | `address` | Second token address (must be sorted) |
| `fee`    | `uint24`  | Fee tier for the pool                 |

**Returns**

| Name     | Type      | Description                                               |
| -------- | --------- | --------------------------------------------------------- |
| `<none>` | `bytes32` | The keccak256 hash of the encoded token addresses and fee |

### computeOnchainId

Computes the onchain ID for a pool based on its metadata

```solidity
function computeOnchainId(PoolMetadata memory metadata) internal pure returns (bytes32);
```

**Parameters**

| Name       | Type           | Description       |
| ---------- | -------------- | ----------------- |
| `metadata` | `PoolMetadata` | The pool metadata |

**Returns**

| Name     | Type      | Description                                               |
| -------- | --------- | --------------------------------------------------------- |
| `<none>` | `bytes32` | The keccak256 hash of the encoded token addresses and fee |


# SignatureVerification

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/univ4-hook/libraries/SignatureVerification.sol)

Library for verifying signatures using ECDSA and ERC-1271

Copied from <https://github.com/Uniswap/permit2/blob/cc56ad0f3439c502c246fc5cfcc3db92bb8b7219/src/libraries/SignatureVerification.sol>

## State Variables

### UPPER\_BIT\_MASK

```solidity
bytes32 constant UPPER_BIT_MASK = (0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
```

## Functions

### verify

```solidity
function verify(bytes calldata signature, bytes32 hash, address claimedSigner) internal view;
```

## Errors

### InvalidSignatureLength

Thrown when the passed in signature is not a valid length

```solidity
error InvalidSignatureLength();
```

### InvalidSignature

Thrown when the recovered signer is equal to the zero address

```solidity
error InvalidSignature();
```

### InvalidSigner

Thrown when the recovered signer does not equal the claimedSigner

```solidity
error InvalidSigner();
```

### InvalidContractSignature

Thrown when the recovered contract signature is incorrect

```solidity
error InvalidContractSignature();
```


# BaseHook

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/univ4-hook/BaseHook.sol)

**Inherits:** IHooks

## State Variables

### POOL\_MANAGER

```solidity
IPoolManager public immutable POOL_MANAGER = IPoolManager(UNIV4_POOL_MANAGER)
```

## Functions

### poolManagerOnly

Only the pool manager may call this function

```solidity
modifier poolManagerOnly() ;
```

### \_poolManagerOnly

```solidity
function _poolManagerOnly() internal view;
```

### selfOnly

Only this address may call this function

```solidity
modifier selfOnly() ;
```

### \_selfOnly

```solidity
function _selfOnly() internal view;
```

### onlyValidPools

Only pools with hooks set to this contract may call this function

```solidity
modifier onlyValidPools(IHooks hooks) ;
```

### \_onlyValidPools

```solidity
function _onlyValidPools(IHooks hooks) internal view;
```

### getHookPermissions

```solidity
function getHookPermissions() public pure virtual returns (Hooks.Permissions memory);
```

### validateHookAddress

```solidity
function validateHookAddress() public view virtual;
```

### lockAcquired

```solidity
function lockAcquired(bytes calldata data) external virtual poolManagerOnly returns (bytes memory);
```

### beforeInitialize

```solidity
function beforeInitialize(address, PoolKey calldata, uint160) external virtual returns (bytes4);
```

### afterInitialize

```solidity
function afterInitialize(address, PoolKey calldata, uint160, int24) external virtual returns (bytes4);
```

### beforeAddLiquidity

```solidity
function beforeAddLiquidity(
    address,
    PoolKey calldata,
    IPoolManager.ModifyLiquidityParams calldata,
    bytes calldata
) external virtual returns (bytes4);
```

### beforeRemoveLiquidity

```solidity
function beforeRemoveLiquidity(
    address,
    PoolKey calldata,
    IPoolManager.ModifyLiquidityParams calldata,
    bytes calldata
) external virtual returns (bytes4);
```

### afterAddLiquidity

```solidity
function afterAddLiquidity(
    address, /* sender **/
    PoolKey calldata, /* key **/
    IPoolManager.ModifyLiquidityParams calldata, /* params **/
    BalanceDelta, /* delta **/
    BalanceDelta, /* feeDelta **/
    bytes calldata /* hookData **/
) external virtual returns (bytes4, BalanceDelta);
```

### afterRemoveLiquidity

```solidity
function afterRemoveLiquidity(
    address,
    PoolKey calldata,
    IPoolManager.ModifyLiquidityParams calldata,
    BalanceDelta,
    BalanceDelta,
    bytes calldata
) external virtual returns (bytes4, BalanceDelta);
```

### beforeSwap

```solidity
function beforeSwap(
    address,
    PoolKey calldata,
    IPoolManager.SwapParams calldata,
    bytes calldata
) external virtual returns (bytes4, BeforeSwapDelta, uint24);
```

### afterSwap

```solidity
function afterSwap(
    address,
    PoolKey calldata,
    IPoolManager.SwapParams calldata,
    BalanceDelta,
    bytes calldata
) external virtual returns (bytes4, int128);
```

### beforeDonate

```solidity
function beforeDonate(
    address,
    PoolKey calldata,
    uint256,
    uint256,
    bytes calldata
) external virtual returns (bytes4);
```

### afterDonate

```solidity
function afterDonate(
    address,
    PoolKey calldata,
    uint256,
    uint256,
    bytes calldata
) external virtual returns (bytes4);
```

## Errors

### NotPoolManager

```solidity
error NotPoolManager();
```

### NotSelf

```solidity
error NotSelf();
```

### InvalidPool

```solidity
error InvalidPool();
```

### LockFailure

```solidity
error LockFailure();
```

### HookNotImplemented

```solidity
error HookNotImplemented();
```


# LPToken

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/univ4-hook/LPToken.sol)

**Inherits:** ERC20

## State Variables

### hook

```solidity
address public hook
```

## Functions

### constructor

```solidity
constructor(string memory _name, string memory _symbol, address _hook) ERC20(_name, _symbol, 18);
```

### hookOnly

Restricts function access to the hook only.

```solidity
modifier hookOnly() ;
```

### \_hookOnly

```solidity
function _hookOnly() internal view;
```

### mint

Mints LP tokens. Can only be called by the authorized hook.

```solidity
function mint(address to, uint256 amount) external hookOnly;
```

### burn

Burns LP tokens. Can only be called by the authorized hook.

```solidity
function burn(address from, uint256 amount) external hookOnly;
```

## Errors

### InvalidCaller

```solidity
error InvalidCaller(string message);
```


# TurbineHook

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/univ4-hook/TurbineHook.sol)

**Inherits:** [BaseHook](https://github.com/propeller-heads/turbine-docs/blob/master/src/univ4-hook/BaseHook.sol/abstract.BaseHook.md)

**Title:** TurbineHook

Implements a Uniswap V4 hook that acts as an intermediary between the pool manager and the router for performing swaps and liquidity operations. It also mints an ERC20 token (representing LP shares) to track liquidity positions. The hook maintains per-pool reserves and restricts all swap and liquidity operations to the designated router.

See the readme for deployment instructions.

## State Variables

### router

Address of the Turbine Router which is allowed to perform swap and liquidity operations.

```solidity
address public immutable router
```

### MINIMUM\_LIQUIDITY

Minimum liquidity burned on first mint to prevent inflation attacks.

```solidity
uint256 public constant MINIMUM_LIQUIDITY = 10 ** 17
```

### registeredPoolIds

Array of all registered pool IDs for enumeration

```solidity
bytes32[] internal registeredPoolIds
```

### pools

Mapping from pool ID to comprehensive pool data

```solidity
mapping(bytes32 => PoolData) public pools
```

## Functions

### constructor

Initializes the TurbineHook.

Sets the router as an operator in the pool manager so that the router can later burn liquidity on behalf of the hook.

```solidity
constructor(address _router) ;
```

**Parameters**

| Name      | Type      | Description                                 |
| --------- | --------- | ------------------------------------------- |
| `_router` | `address` | The address of the Turbine Router contract. |

### routerOnly

Restricts function access to the router only.

```solidity
modifier routerOnly(address sender) ;
```

**Parameters**

| Name     | Type      | Description                       |
| -------- | --------- | --------------------------------- |
| `sender` | `address` | The caller address to be checked. |

### \_routerOnly

```solidity
function _routerOnly(address sender) internal view;
```

### getPoolStates

Returns pool states for multiple pools in a single call

```solidity
function getPoolStates(bytes32[] calldata poolIds) public view returns (PoolState[] memory states);
```

**Parameters**

| Name      | Type        | Description                |
| --------- | ----------- | -------------------------- |
| `poolIds` | `bytes32[]` | Array of pool IDs to query |

**Returns**

| Name     | Type          | Description                                           |
| -------- | ------------- | ----------------------------------------------------- |
| `states` | `PoolState[]` | Array of PoolStates containing reserves and liquidity |

### getReserves

Retrieves the current reserves for a registered pool.

Reverts if the pool is not registered.

```solidity
function getReserves(address currency0, address currency1, uint24 fee) public view returns (uint256, uint256);
```

**Parameters**

| Name        | Type      | Description                         |
| ----------- | --------- | ----------------------------------- |
| `currency0` | `address` | The address of the first currency.  |
| `currency1` | `address` | The address of the second currency. |
| `fee`       | `uint24`  | The fee associated with the pool.   |

**Returns**

| Name     | Type      | Description                                          |
| -------- | --------- | ---------------------------------------------------- |
| `<none>` | `uint256` | reserve0 The current reserve of the first currency.  |
| `<none>` | `uint256` | reserve1 The current reserve of the second currency. |

### getLpTokenAddress

Retrieves the lpToken address of a registered pool.

Reverts if the pool is not registered.

```solidity
function getLpTokenAddress(address currency0, address currency1, uint24 fee) public view returns (address);
```

**Parameters**

| Name        | Type      | Description                         |
| ----------- | --------- | ----------------------------------- |
| `currency0` | `address` | The address of the first currency.  |
| `currency1` | `address` | The address of the second currency. |
| `fee`       | `uint24`  | The fee associated with the pool.   |

**Returns**

| Name     | Type      | Description                               |
| -------- | --------- | ----------------------------------------- |
| `<none>` | `address` | The address of the LP token for the pool. |

### isRegisteredPool

Checks if a pool is registered with the TurbineHook.

```solidity
function isRegisteredPool(address currency0, address currency1, uint24 fee) public view returns (bool);
```

**Parameters**

| Name        | Type      | Description                         |
| ----------- | --------- | ----------------------------------- |
| `currency0` | `address` | The address of the first currency.  |
| `currency1` | `address` | The address of the second currency. |
| `fee`       | `uint24`  | The fee associated with the pool.   |

**Returns**

| Name     | Type   | Description                                      |
| -------- | ------ | ------------------------------------------------ |
| `<none>` | `bool` | True if the pool is registered, False otherwise. |

### getNumberOfRegisteredPools

Returns the number of registered pools

```solidity
function getNumberOfRegisteredPools() public view returns (uint256);
```

**Returns**

| Name     | Type      | Description                    |
| -------- | --------- | ------------------------------ |
| `<none>` | `uint256` | The number of registered pools |

### getRegisteredPoolsSlice

Returns all registered pools with their complete information

```solidity
function getRegisteredPoolsSlice(
    uint256 startIndex,
    uint256 endIndex
) public view returns (PoolInfo[] memory poolsInfo);
```

**Parameters**

| Name         | Type      | Description                                      |
| ------------ | --------- | ------------------------------------------------ |
| `startIndex` | `uint256` | The index of the first pool to return            |
| `endIndex`   | `uint256` | The index of the last pool to return (exclusive) |

**Returns**

| Name        | Type         | Description                                       |
| ----------- | ------------ | ------------------------------------------------- |
| `poolsInfo` | `PoolInfo[]` | Array of PoolInfo structs containing pool details |

### mint

Mints liquidity tokens corresponding to deposited tokens.

Can only be called by the router. Emits a [Mint](https://github.com/propeller-heads/turbine-docs/blob/master/src/univ4-hook/TurbineHook.sol/contract.TurbineHook.md#mint) event and updates pool reserves.

```solidity
function mint(
    address to,
    uint256 amount0,
    uint256 amount1,
    PoolKey calldata key,
    uint256 liquidity
) external routerOnly(msg.sender) returns (uint256);
```

**Parameters**

| Name        | Type      | Description                                                |
| ----------- | --------- | ---------------------------------------------------------- |
| `to`        | `address` | The address that will receive the minted liquidity tokens. |
| `amount0`   | `uint256` | The amount of the first currency to be deposited.          |
| `amount1`   | `uint256` | The amount of the second currency to be deposited.         |
| `key`       | `PoolKey` | The PoolKey structure containing pool details.             |
| `liquidity` | `uint256` | The amount of liquidity tokens to mint.                    |

**Returns**

| Name     | Type      | Description                                      |
| -------- | --------- | ------------------------------------------------ |
| `<none>` | `uint256` | liquidity The amount of liquidity tokens minted. |

### burn

Burns liquidity tokens, returning the underlying assets.

We expect the router to have already transferred the lpTokens from the user to the hook.

Can only be called by the router. Retrieves current balances from the hook's reserves, burns the liquidity tokens, updates reserves, and emits a [Burn](https://github.com/propeller-heads/turbine-docs/blob/master/src/univ4-hook/TurbineHook.sol/contract.TurbineHook.md#burn) event.

```solidity
function burn(
    address to,
    uint256 liquidity,
    uint256 amount0,
    uint256 amount1,
    PoolKey calldata key
) external routerOnly(msg.sender);
```

**Parameters**

| Name        | Type      | Description                                          |
| ----------- | --------- | ---------------------------------------------------- |
| `to`        | `address` | The address that will receive the underlying tokens. |
| `liquidity` | `uint256` | The amount of liquidity to burn.                     |
| `amount0`   | `uint256` | The amount of the first currency to be removed.      |
| `amount1`   | `uint256` | The amount of the second currency to be removed.     |
| `key`       | `PoolKey` | The PoolKey structure containing pool details.       |

### beforeInitialize

```solidity
function beforeInitialize(
    address,
    PoolKey calldata key,
    uint160
) external override poolManagerOnly returns (bytes4);
```

### \_registerPool

Registers a new pool with the TurbineHook.

Reverts if either currency address is zero, if the addresses are not ordered (currency0 must be less than currency1), or if the pool has already been registered. Emits a [PoolRegistered](https://github.com/propeller-heads/turbine-docs/blob/master/src/univ4-hook/TurbineHook.sol/contract.TurbineHook.md#poolregistered) event upon successful registration.

```solidity
function _registerPool(address currency0, address currency1, uint24 fee) private;
```

**Parameters**

| Name        | Type      | Description                                     |
| ----------- | --------- | ----------------------------------------------- |
| `currency0` | `address` | The address of the first currency in the pool.  |
| `currency1` | `address` | The address of the second currency in the pool. |
| `fee`       | `uint24`  | The fee associated with the pool.               |

### beforeSwap

Called by the poolManager before a swap occurs.

Performs the following steps:

* Decodes the expected output amount from hookData.
* Determines input and output currencies based on swap direction.
* Mints input tokens and burns output tokens from the poolManager.
* Updates internal reserves and emits a [Swap](https://github.com/propeller-heads/turbine-docs/blob/master/src/univ4-hook/TurbineHook.sol/contract.TurbineHook.md#swap) event.
* Calculates swap deltas via {\_calculateDeltas} and converts them using toBeforeSwapDelta.

```solidity
function beforeSwap(
    address sender,
    PoolKey calldata key,
    IPoolManager.SwapParams calldata params,
    bytes calldata hookData
) external override routerOnly(sender) poolManagerOnly returns (bytes4, BeforeSwapDelta, uint24);
```

**Parameters**

| Name       | Type                      | Description                                                                 |
| ---------- | ------------------------- | --------------------------------------------------------------------------- |
| `sender`   | `address`                 | The caller (must be the router).                                            |
| `key`      | `PoolKey`                 | The PoolKey structure with pool details.                                    |
| `params`   | `IPoolManager.SwapParams` | The swap parameters from the poolManager.                                   |
| `hookData` | `bytes`                   | Encoded data carrying additional swap parameters (e.g. expected amountOut). |

**Returns**

| Name     | Type              | Description                                                                 |
| -------- | ----------------- | --------------------------------------------------------------------------- |
| `<none>` | `bytes4`          | selector The function selector for beforeSwap (IHooks.beforeSwap.selector). |
| `<none>` | `BeforeSwapDelta` | hookDelta The BeforeSwapDelta struct that encodes the swap deltas.          |
| `<none>` | `uint24`          | feeProtocol A fee value (unused, returns 0).                                |

### getHookPermissions

Returns the hook permissions indicating which callbacks are implemented.

Only beforeSwap and beforeSwapReturnDelta are enabled in this hook.

```solidity
function getHookPermissions() public pure override returns (Hooks.Permissions memory);
```

**Returns**

| Name     | Type                | Description                                                      |
| -------- | ------------------- | ---------------------------------------------------------------- |
| `<none>` | `Hooks.Permissions` | A Hooks.Permissions struct with booleans for each callback type. |

### getPoolLpToken

```solidity
function getPoolLpToken(bytes32 poolId) public view returns (address);
```

### \_updateReserves

Updates the stored reserves for a pool based on new token balances.

Emits a [Sync](https://github.com/propeller-heads/turbine-docs/blob/master/src/univ4-hook/TurbineHook.sol/contract.TurbineHook.md#sync) event with the updated reserve values.

```solidity
function _updateReserves(PoolKey calldata key, uint256 balance0, uint256 balance1) private;
```

**Parameters**

| Name       | Type      | Description                                    |
| ---------- | --------- | ---------------------------------------------- |
| `key`      | `PoolKey` | The PoolKey structure containing pool details. |
| `balance0` | `uint256` | The new balance for the first currency.        |
| `balance1` | `uint256` | The new balance for the second currency.       |

### computePoolId

Computes a unique pool identifier based on currency addresses and fee.

Uses keccak256 to encode the parameters.

```solidity
function computePoolId(address currency0, address currency1, uint24 fee) public pure returns (bytes32);
```

**Parameters**

| Name        | Type      | Description                         |
| ----------- | --------- | ----------------------------------- |
| `currency0` | `address` | The address of the first currency.  |
| `currency1` | `address` | The address of the second currency. |
| `fee`       | `uint24`  | The fee associated with the pool.   |

**Returns**

| Name     | Type      | Description                                   |
| -------- | --------- | --------------------------------------------- |
| `<none>` | `bytes32` | A bytes32 hash serving as the unique pool ID. |

## Events

### Mint

```solidity
event Mint(bytes32 indexed poolId, address indexed sender, uint256 amount0, uint256 amount1);
```

### Burn

```solidity
event Burn(bytes32 indexed poolId, address indexed sender, uint256 amount0, uint256 amount1, address indexed to);
```

### Swap

```solidity
event Swap(bytes32 indexed poolId, uint256 amountIn, uint256 amountOut);
```

### Sync

```solidity
event Sync(bytes32 indexed poolId, uint256 reserves0, uint256 reserves1);
```

### PoolRegistered

```solidity
event PoolRegistered(bytes32 indexed poolId, address indexed currency0, address indexed currency1, uint24 fee);
```

### LPTokenDeployed

```solidity
event LPTokenDeployed(bytes32 indexed poolId, address lpToken, string name, string symbol);
```

### RouterSet

```solidity
event RouterSet(address indexed router);
```

## Errors

### RouterOnly

```solidity
error RouterOnly();
```

### PoolNotRegistered

```solidity
error PoolNotRegistered();
```

### PoolAlreadyRegistered

```solidity
error PoolAlreadyRegistered();
```

### ZeroCurrencyAddress

```solidity
error ZeroCurrencyAddress();
```

### BalanceOverflow

```solidity
error BalanceOverflow();
```

### InsufficientInitialLiquidity

Liquidity minted must be at least MINIMUM\_LIQUIDITY for initial mint

```solidity
error InsufficientInitialLiquidity();
```

## Structs

### PoolState

Structure to hold pool state information.

```solidity
struct PoolState {
    uint256 reserve0;
    uint256 reserve1;
    uint256 liquidity;
}
```

### PoolData

Structure to hold pool information in storage

```solidity
struct PoolData {
    address token0;
    address token1;
    uint24 fee;
    address lpToken;
    uint256 reserve0;
    uint256 reserve1;
}
```

### PoolInfo

Comprehensive structure to hold all pool data for returning from view functions

```solidity
struct PoolInfo {
    bytes32 poolId;
    address token0;
    address token1;
    uint24 fee;
    address lpToken;
    uint256 reserve0;
    uint256 reserve1;
    uint256 liquidity;
}
```


# TurbineLiquidityRouter

[Git Source](https://github.com/propeller-heads/turbine/blob/90514973167f1eb25cb9eb7756354fb3de34824d/src/univ4-hook/TurbineLiquidityRouter.sol)

**Inherits:** IUnlockCallback, [ITurbineLiquidityRouter](https://github.com/propeller-heads/turbine-docs/blob/master/src/univ4-hook/interfaces/ITurbineLiquidityRouter.sol/interface.ITurbineLiquidityRouter.md)

## State Variables

### PERMIT2\_CONTRACT

```solidity
IPermit2 public constant PERMIT2_CONTRACT = IPermit2(0x000000000022D473030F116dDEE9F6B43aC78BA3)
```

### POOL\_MANAGER

```solidity
IPoolManager public immutable POOL_MANAGER = IPoolManager(UNIV4_POOL_MANAGER)
```

### turbineSettler

```solidity
address public immutable turbineSettler
```

### MIN\_INTENT\_EXPIRY

```solidity
uint256 public constant MIN_INTENT_EXPIRY = 36 seconds
```

### SPEEDBUMP\_DURATION

```solidity
uint256 public constant SPEEDBUMP_DURATION = 12 seconds
```

### MAX\_INTENT\_EXPIRY

```solidity
uint256 public constant MAX_INTENT_EXPIRY = 3 hours
```

### \_TOKEN\_PERMISSIONS\_TYPEHASH

```solidity
bytes32 public constant _TOKEN_PERMISSIONS_TYPEHASH = keccak256("TokenPermissions(address token,uint256 amount)")
```

### \_PERMIT\_TRANSFER\_FROM\_TYPEHASH

```solidity
bytes32 public constant _PERMIT_TRANSFER_FROM_TYPEHASH = keccak256(
    "PermitTransferFrom(TokenPermissions permitted,address spender,uint256 nonce,uint256 deadline)TokenPermissions(address token,uint256 amount)"
)
```

### hook

```solidity
TurbineHook public immutable hook
```

### removeLiquidityIntents

```solidity
mapping(RemoveLiquidityIntentHash => RemoveLiquidityIntent) public removeLiquidityIntents
```

### removeLiquidityPermits

```solidity
mapping(RemoveLiquidityIntentHash => SignatureTransferParams) public removeLiquidityPermits
```

### removeLiquidityCreatedAt

```solidity
mapping(RemoveLiquidityIntentHash => uint256) public removeLiquidityCreatedAt
```

## Functions

### constructor

```solidity
constructor(address _turbineSettler, address _turbineHook) ;
```

### ensure

```solidity
modifier ensure(uint256 deadline) ;
```

### \_ensure

```solidity
function _ensure(uint256 deadline) internal view;
```

### turbineSettlerOnly

Only Turbine OrderSettler may call this function

```solidity
modifier turbineSettlerOnly() virtual;
```

### \_turbineSettlerOnly

```solidity
function _turbineSettlerOnly() internal view;
```

### poolManagerOnly

Only PoolManager may call this function

```solidity
modifier poolManagerOnly() virtual;
```

### \_poolManagerOnly

```solidity
function _poolManagerOnly() internal view;
```

### settlerOrPoolManagerOnly

Only TurbineSettler or the PoolManager may call this function

Some functions are marked as external to allow calling them via delegatecall, but they are not intended to be called externally. They can only be called by the PoolManager, or by this contract itself in the context of a call from the TurbineSettler (msg.sender is therefore TurbineSettler).

```solidity
modifier settlerOrPoolManagerOnly() ;
```

### \_settlerOrPoolManagerOnly

```solidity
function _settlerOrPoolManagerOnly() internal view;
```

### getTurbineSettler

Returns the address of the TurbineSettler

```solidity
function getTurbineSettler() external view returns (address);
```

**Returns**

| Name     | Type      | Description                       |
| -------- | --------- | --------------------------------- |
| `<none>` | `address` | The address of the TurbineSettler |

### getTurbineHook

Returns the address of the TurbineHook

Return type is address to avoid unnecessary imports

```solidity
function getTurbineHook() external view returns (address);
```

**Returns**

| Name     | Type      | Description                    |
| -------- | --------- | ------------------------------ |
| `<none>` | `address` | The address of the TurbineHook |

### submitRemoveLiquidityIntent

Submits a new remove-liquidity intent for later execution.

The caller must be `intent.owner`. The provided Permit2 authorization must match the LP token and cover at least `intent.lpTokenAmount`.

```solidity
function submitRemoveLiquidityIntent(
    RemoveLiquidityIntent calldata intent,
    SignatureTransferParams calldata signatureTransferParams
) external returns (RemoveLiquidityIntentHash intentHash);
```

**Parameters**

| Name                      | Type                      | Description                                                                       |
| ------------------------- | ------------------------- | --------------------------------------------------------------------------------- |
| `intent`                  | `RemoveLiquidityIntent`   | The remove-liquidity instruction containing pool and LP token amount details.     |
| `signatureTransferParams` | `SignatureTransferParams` | Permit2 SignatureTransfer parameters and signature authorizing LP token transfer. |

**Returns**

| Name         | Type                        | Description                                        |
| ------------ | --------------------------- | -------------------------------------------------- |
| `intentHash` | `RemoveLiquidityIntentHash` | The intent hash used to reference this submission. |

### executePendingIntents

Executes the provided intent hashes and deletes the executed intents.

```solidity
function executePendingIntents(RemoveLiquidityIntentHash[] calldata hashes) external;
```

**Parameters**

| Name     | Type                          | Description                        |
| -------- | ----------------------------- | ---------------------------------- |
| `hashes` | `RemoveLiquidityIntentHash[]` | Array of intent hashes to execute. |

### getRemoveLiquidityIntents

Returns remove-liquidity intent data for the given hashes. Skips deleted/unknown intents (owner == address(0)).

```solidity
function getRemoveLiquidityIntents(RemoveLiquidityIntentHash[] calldata hashes)
    external
    view
    returns (RemoveLiquidityData[] memory result);
```

**Parameters**

| Name     | Type                          | Description                      |
| -------- | ----------------------------- | -------------------------------- |
| `hashes` | `RemoveLiquidityIntentHash[]` | Array of intent hashes to query. |

**Returns**

| Name     | Type                    | Description                                          |
| -------- | ----------------------- | ---------------------------------------------------- |
| `result` | `RemoveLiquidityData[]` | Array of RemoveLiquidityData for valid intents only. |

### \_buildRemoveLiquidityData

```solidity
function _buildRemoveLiquidityData(
    RemoveLiquidityIntentHash intentHash,
    RemoveLiquidityIntent storage intent
) internal view returns (RemoveLiquidityData memory);
```

### \_executePendingIntent

```solidity
function _executePendingIntent(
    RemoveLiquidityIntentHash intentHash,
    RemoveLiquidityIntent storage intent
) internal;
```

### unlockCallback

Callback function invoked by the `poolManager` when `unlock` is called.

* Only the `poolManager` can call this function (`require(msg.sender == address(poolManager))`).
* Reverts if the delegatecall fails.

```solidity
function unlockCallback(bytes calldata encodedData) external poolManagerOnly returns (bytes memory);
```

**Parameters**

| Name          | Type    | Description                                                                                                                       |
| ------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `encodedData` | `bytes` | Encoded data passed from the `poolManager`. The first parameter is the command (uint256), and the second is the raw data (bytes). |

**Returns**

| Name     | Type    | Description                                         |
| -------- | ------- | --------------------------------------------------- |
| `<none>` | `bytes` | The return data from the invoked callback function. |

### \_maybeUnlockPoolManager

Wrapper function around poolManager.unlock that checks if the manager is already unlocked (because we are taking loans from the pool manager)

If the manager is already unlocked, it will delegatecall directly. Otherwise, it will unlock and pass the calldata.

```solidity
function _maybeUnlockPoolManager(bytes memory data) internal returns (bytes memory result);
```

**Parameters**

| Name   | Type    | Description                             |
| ------ | ------- | --------------------------------------- |
| `data` | `bytes` | The data to pass to the unlock callback |

**Returns**

| Name     | Type    | Description                         |
| -------- | ------- | ----------------------------------- |
| `result` | `bytes` | The result from the unlock callback |

### swapExactTokens

Swaps an exact amount of a token for an exact amount of another token.

* This function assumes that amountIn is already transferred to this contract.
* This function transfers tokens to the `poolManager` via `poolManager.unlock(...)`.
* The `poolManager` will perform the swap and return the output amount via the callback (`unlockCallback`).

```solidity
function swapExactTokens(
    uint256 amountIn,
    uint256 amountOut,
    address poolToken0,
    address poolToken1,
    uint24 poolFee,
    bool zeroForOne,
    uint256 deadline
) external payable turbineSettlerOnly ensure(deadline);
```

**Parameters**

| Name         | Type      | Description                                                       |
| ------------ | --------- | ----------------------------------------------------------------- |
| `amountIn`   | `uint256` | The exact amount of input tokens to be sent for the swap.         |
| `amountOut`  | `uint256` | The exact amount of tokens expected to be received from the swap. |
| `poolToken0` | `address` | The address of the first token in the pool.                       |
| `poolToken1` | `address` | The address of the second token in the pool.                      |
| `poolFee`    | `uint24`  | The fee of the pool.                                              |
| `zeroForOne` | `bool`    | The direction of the swap.                                        |
| `deadline`   | `uint256` | A timestamp by which the call be finished to be valid.            |

### \_swapCallback

Callback function invoked by the `poolManager` during the swap execution.

* Only the `poolManager` (or this contract if the pool manager is already unlocked) can call this function.
* Performs a swap via `poolManager.swap(...)`, and ensures the output is exactly `amountOut`.
* Handles token transfers: input tokens are transferred from `turbineAddress` to `poolManager`, then output tokens are taken from the `poolManager` and sent to `turbineAddress`.

```solidity
function _swapCallback(
    uint256 amountIn,
    uint256 amountOut,
    PoolKey memory key,
    bool zeroForOne
) external settlerOrPoolManagerOnly returns (bytes memory);
```

**Parameters**

| Name         | Type      | Description                                               |
| ------------ | --------- | --------------------------------------------------------- |
| `amountIn`   | `uint256` | The exact amount of input tokens to be sent for the swap. |
| `amountOut`  | `uint256` | The exact amount of tokens to be received from the swap.  |
| `key`        | `PoolKey` | The pool key containing the token addresses and fee.      |
| `zeroForOne` | `bool`    | The direction of the swap.                                |

**Returns**

| Name     | Type    | Description  |
| -------- | ------- | ------------ |
| `<none>` | `bytes` | Empty bytes. |

### addLiquidity

Adds liquidity to the pair represented by the given hook.

```solidity
function addLiquidity(
    address to,
    uint256 amount0,
    uint256 amount1,
    address poolToken0,
    address poolToken1,
    uint24 poolFee,
    uint256 liquidity,
    BatchSignatureTransferParams memory batchSignatureTransferParams
) external payable turbineSettlerOnly returns (uint256);
```

**Parameters**

| Name                           | Type                           | Description                                                                     |
| ------------------------------ | ------------------------------ | ------------------------------------------------------------------------------- |
| `to`                           | `address`                      | The address receiving the minted liquidity.                                     |
| `amount0`                      | `uint256`                      | The amount of token0 to add as liquidity.                                       |
| `amount1`                      | `uint256`                      | The amount of token1 to add as liquidity.                                       |
| `poolToken0`                   | `address`                      | The address of the first token in the pool.                                     |
| `poolToken1`                   | `address`                      | The address of the second token in the pool.                                    |
| `poolFee`                      | `uint24`                       | The fee of the pool.                                                            |
| `liquidity`                    | `uint256`                      | The liquidity amount to mint for the caller.                                    |
| `batchSignatureTransferParams` | `BatchSignatureTransferParams` | The Permit2 batch SignatureTransfer permit and signature for token0 and token1. |

**Returns**

| Name     | Type      | Description                                           |
| -------- | --------- | ----------------------------------------------------- |
| `<none>` | `uint256` | liquidity The minted liquidity amount for the caller. |

### \_addLiquidityCallback

Callback function invoked by the PoolManager when adding liquidity.

* Only the PoolManager should call this function.
* Uses a single batch permitTransferFrom to transfer both tokens to this router.
* Then settles each token individually with the PoolManager via sync -> transfer -> settle.
* Mints the corresponding amounts to the `hook` contract via `poolManager.mint(...)`.

```solidity
function _addLiquidityCallback(
    address caller,
    uint256 amount0,
    uint256 amount1,
    PoolKey memory key,
    BatchSignatureTransferParams memory batchSignatureTransferParams
) external poolManagerOnly returns (bytes memory);
```

**Parameters**

| Name                           | Type                           | Description                                                                     |
| ------------------------------ | ------------------------------ | ------------------------------------------------------------------------------- |
| `caller`                       | `address`                      | The address that initiated the liquidity addition.                              |
| `amount0`                      | `uint256`                      | The amount of token0 to add as liquidity.                                       |
| `amount1`                      | `uint256`                      | The amount of token1 to add as liquidity.                                       |
| `key`                          | `PoolKey`                      | The pool key containing the token addresses and fee.                            |
| `batchSignatureTransferParams` | `BatchSignatureTransferParams` | The Permit2 batch SignatureTransfer permit and signature for token0 and token1. |

**Returns**

| Name     | Type    | Description                                                      |
| -------- | ------- | ---------------------------------------------------------------- |
| `<none>` | `bytes` | Encoded data containing the hook reference (`abi.encode(hook)`). |

### removeLiquidity

Burns liquidity to the pair represented by the given hook.

```solidity
function removeLiquidity(
    address to,
    uint256 liquidity,
    address poolToken0,
    address poolToken1,
    uint24 poolFee,
    SignatureTransferParams memory signatureTransferParams
) external payable turbineSettlerOnly returns (uint256, uint256);
```

**Parameters**

| Name                      | Type                      | Description                                                          |
| ------------------------- | ------------------------- | -------------------------------------------------------------------- |
| `to`                      | `address`                 | The address receiving the burnt liquidity.                           |
| `liquidity`               | `uint256`                 | The burnt liquidity amount from the caller.                          |
| `poolToken0`              | `address`                 | The address of the first token in the pool.                          |
| `poolToken1`              | `address`                 | The address of the second token in the pool.                         |
| `poolFee`                 | `uint24`                  | The fee of the pool.                                                 |
| `signatureTransferParams` | `SignatureTransferParams` | The Permit2 SignatureTransfer permit and signature for the LP token. |

**Returns**

| Name     | Type      | Description                                                   |
| -------- | --------- | ------------------------------------------------------------- |
| `<none>` | `uint256` | amount0 The amount of token0 received from burning liquidity. |
| `<none>` | `uint256` | amount1 The amount of token1 received from burning liquidity. |

### \_removeLiquidity

```solidity
function _removeLiquidity(
    address to,
    uint256 liquidity,
    address poolToken0,
    address poolToken1,
    uint24 poolFee,
    SignatureTransferParams memory signatureTransferParams
) internal returns (uint256, uint256);
```

### calculateWithdrawnAmounts

```solidity
function calculateWithdrawnAmounts(
    uint256 reserve0,
    uint256 reserve1,
    uint256 lpTokenTotalSupply,
    uint256 lpTokenBurnt
) internal pure returns (uint256 amount0, uint256 amount1);
```

### \_removeLiquidityCallback

Callback function invoked by the PoolManager when removing liquidity.

* Only the PoolManager should call this function.
* Takes both token0 and token1 from the PoolManager to the caller.
* Burns the corresponding amount from the `hook` contract via `poolManager.burn(...)`.

```solidity
function _removeLiquidityCallback(
    address caller,
    uint256 liquidity,
    PoolKey memory key,
    uint256 amount0,
    uint256 amount1,
    SignatureTransferParams memory signatureTransferParams
) external poolManagerOnly returns (bytes memory);
```

**Parameters**

| Name                      | Type                      | Description                                                          |
| ------------------------- | ------------------------- | -------------------------------------------------------------------- |
| `caller`                  | `address`                 | The address that initiated the liquidity removal.                    |
| `liquidity`               | `uint256`                 | The amount of liquidity to remove.                                   |
| `key`                     | `PoolKey`                 | The pool key containing the token addresses and fee.                 |
| `amount0`                 | `uint256`                 | The amount of token0 to receive from burning liquidity.              |
| `amount1`                 | `uint256`                 | The amount of token1 to receive from burning liquidity.              |
| `signatureTransferParams` | `SignatureTransferParams` | The Permit2 SignatureTransfer permit and signature for the LP token. |

**Returns**

| Name     | Type    | Description                                                           |
| -------- | ------- | --------------------------------------------------------------------- |
| `<none>` | `bytes` | Encoded data containing the amounts of tokens from burning liquidity. |

### \_settleFromRouter

Settle a currency with the PoolManager when tokens are already held by the router

Used after batch permitTransferFrom has transferred tokens to this contract.

```solidity
function _settleFromRouter(Currency currency, uint256 amount) internal;
```

**Parameters**

| Name       | Type       | Description        |
| ---------- | ---------- | ------------------ |
| `currency` | `Currency` | Currency to settle |
| `amount`   | `uint256`  | Amount to send     |

### isNonceUsed

Check if a SignatureTransfer nonce has already been used

```solidity
function isNonceUsed(address owner, uint256 nonce) public view returns (bool);
```

**Parameters**

| Name    | Type      | Description            |
| ------- | --------- | ---------------------- |
| `owner` | `address` | The owner of the nonce |
| `nonce` | `uint256` | The nonce to check     |

**Returns**

| Name     | Type   | Description                                      |
| -------- | ------ | ------------------------------------------------ |
| `<none>` | `bool` | True if the nonce has been used, false otherwise |

### \_hashRemoveLiquidityIntent

```solidity
function _hashRemoveLiquidityIntent(RemoveLiquidityIntent calldata intent)
    internal
    pure
    returns (RemoveLiquidityIntentHash);
```

### \_isTooYoung

```solidity
function _isTooYoung(RemoveLiquidityIntentHash intentHash, uint256 createdBefore) internal view returns (bool);
```

### \_isExpired

```solidity
function _isExpired(RemoveLiquidityIntentHash intentHash, uint256 validUntil) internal view returns (bool);
```

### \_deleteIntentData

Deletes intent mapping data for the given hash.

```solidity
function _deleteIntentData(RemoveLiquidityIntentHash intentHash) internal;
```

### hash

Hash a PermitTransferFrom struct to create a unique identifier for the permit.

Adapted from <https://github.com/Uniswap/permit2/blob/cc56ad0f3439c502c246fc5cfcc3db92bb8b7219/src/libraries/PermitHash.sol#L59> replacing the `spender` address with this router contract (address(this)), instead of msg.sender.

```solidity
function hash(ISignatureTransfer.PermitTransferFrom memory permit) internal view returns (bytes32);
```

**Parameters**

| Name     | Type                                    | Description                            |
| -------- | --------------------------------------- | -------------------------------------- |
| `permit` | `ISignatureTransfer.PermitTransferFrom` | The PermitTransferFrom struct to hash. |

**Returns**

| Name     | Type      | Description                                |
| -------- | --------- | ------------------------------------------ |
| `<none>` | `bytes32` | The hash of the PermitTransferFrom struct. |


# Clearing Algorithm (APEX)

APEX is Turbine's batch clearing algorithm. Each batch, APEX finds a single exchange rate for every token that maximizes settled volume. APEX draws on all open orders, [market maker quotes](broken://spaces/usbDsiWDhwRR4sob2ROl/pages/LgxN2qWJul4UFuMsotOv), [Turbine LP pools](broken://spaces/gxFOZ85U3R3v2cPka9gM/pages/u7zWyMUKXDnn1eSGg3BW), and every DEX pool indexed by Tycho. Every order in the batch clears at that rate minus the platform fee.

APEX is Turbine's Rust implementation of [SPEEDEX](https://www.scs.stanford.edu/~geoff/papers/speedex.pdf), a batch clearing algorithm originally developed at Stanford.

### How APEX solves a batch

APEX solves each batch in two steps.

**Price search.** Starting from current market prices, APEX adjusts token prices iteratively until it finds a set where supply and demand balance across the full orderbook. At each step it checks how orders respond to a price change and keeps adjusting until it minimizes the imbalance.

**Volume maximization.** With prices fixed, APEX runs a linear program that maximizes total trade volume, subject to every order filling at or better than its limit price. Orders that match peer-to-peer settle without touching DEX liquidity. Remaining imbalances route to on-chain pools.

### Ring-CoWs: multi-party circular trades

APEX solves the entire batch as one problem across all token pairs. This lets it find ring-CoWs: circular multi-party trades where Alice sells ETH for USDC, Bob sells USDC for WBTC, and Carol sells WBTC for ETH, all netting out at zero price impact without any DEX interaction.

Solvers that work one pair at a time miss these. APEX finds them because it sees the whole batch.

### Uniform clearing prices

All orders for the same token pair in the same batch clear at one price. If APEX sets a clearing rate of 3800 USDC/ETH, every order in that batch gets exactly 3800, whether the limit was 3800 or 3750. There is no queue advantage: the first order to arrive gets the same price as the last.

### DEX liquidity

APEX routes through all DEX pools indexed by Tycho, including Uniswap v2, v3, and v4, Curve, Balancer, and others. When peer-to-peer matching is not enough, remaining volume settles at the best available on-chain rate.


# Liquidity Pools in Depth

## Swap price and fee tier

Turbine Pools always swap at current market mid-price plus the pool's fee.

For example, if the current price of a USDC/USDT pair is 1.00, and the pool's fee tier is 1%, you can swap 100 USDC into 99 USDT, or 100 USDT into 99 USDC.

## Two modes of providing liquidity

### Providing liquidity in pool ratio

At any point in time, each Turbine Pool has some ratio of its reserves. For example, if a pool holds 10,000 DAI and 2 WETH, its reserves ratio is 5000:1.

In *pool ratio* mode, you specify *maximum* amounts of tokens to provide in your intent submitted to Turbine.

After your intent passes the speedbump, Turbine executes the intent. It provides liquidity on your behalf in the *current* pool ratio, without exceeding the specified maximum amounts.

The pool ratio can change between the time you submit your intent and the time Turbine executes it.

Turbine gives you a certain amount of LP token, specific to the pool, that represents your share in it. For example, if you provided 1,000 DAI to a pool that held 9,000 DAI (so the pool now holds 10,000 DAI and you own 10% of it), you receive an amount of LP token that represents 10% of its total supply. (The amount of the second token provided is not relevant, because the ratio is the same either way.)

To provide liquidity in pool ratio, specify `exact: false` in the intent submitted to `POST /api/add_liquidity` (see the [Swagger UI](https://api.turbine.exchange/api/swagger-ui/)).

### Providing liquidity in any ratio

You can provide any amounts of both tokens to a pool, regardless of the current reserve ratio, for a fee.

Providing liquidity in any ratio is equivalent to first making a swap on the pool to bring the pool's reserves and your provided amounts to the same ratio, then providing in pool ratio.

**Paying the fee:** When you provide liquidity in a ratio different from the pool ratio, you pay the swap fee on the amount the protocol must swap to align reserves with your provided amounts. The fee is subtracted from the LP tokens you receive.

<details>

<summary>Mathematical Foundation</summary>

**Notation**

* `r0`, `r1`: Current reserves of token0 and token1
* `p0`, `p1`: Amounts user wants to provide
* `Rr = r0/r1`: Pool reserve ratio
* `Rp = p0/p1`: Provision ratio
* `mp`: Mid-price (token0 in terms of token1, units are \[token1/token0])
* `f`: Swap fee (as a multiplier, e.g., 0.99 for 1% fee)
* `emp`: Effective mid-price (accounts for fee)
* `s0`, `s1`: Swapped amounts (amounts that the user **gets** in a virtual swap)

> **Conclusion:**
>
> All tokens provided by the user are added to pool reserves.
>
> The amount of liquidity tokens minted is calculated as `lp_total_supply * (emp*p0 + p1) / (emp*r0 + r1)`, where `emp = mp/f` if `Rp < Rr` and `emp = mp*f` if `Rp > Rr`.

To provide liquidity in a ratio different from the pool, the protocol first performs a virtual swap to bring the provided amounts and pool reserves to the same ratio. When making this swap, the pool charges a fee. Then equal ratio provision logic is applied.

The following mathematical derivations calculate the final amount of liquidity tokens to mint, as if the virtual swap happened before provision in equal ratio.

1. **Determine swap direction:**
   * If `Rp < Rr`, swap token1 to token0.\
     `s1` is given, and `s0` is unknown.\
     Calculate `s0` applying swap fee: `s0 = -s1 / mp * f`, so `s1 = -s0 * mp / f`
   * If `Rp > Rr`, swap token0 to token1.\
     `s0` is given, and `s1` is unknown.\
     Calculate `s1` applying swap fee: `s1 = -s0 * mp * f`
   * This can be simplified to one case by introducing *effective mid-price* `emp`:

     ```
     s1 = -s0 * emp
     emp = mp/f if Rp < Rr
           mp*f if Rp > Rr
     ```
2. **Calculate swapped amounts:**
   * User's provided amounts after swap: `p0 + s0` and `p1 + s1` i.e. `Rp*p1 + s0` and `p1 - emp*s0`
   * Reserves after swap: `r0 - s0` and `r1 - s1` i.e. `Rr*r1 - s0` and `r1 + emp*s0`
   * We want the ratio of reserves after swap to equal the ratio of provided amounts after swap. Solving for `s0`:

     ```
     (r0 - s0) / (r1 - s1)  = (p0 + s0) / (p1 + s1)
     (r0 - s0)(p1 + s1)     = (p0 + s0)(r1 - s1)
     (r0 - s0)(p1 - emp*s0) = (p0 + s0)(r1 + emp*s0)
     p1*r0 - emp*r0*s0 - p1*s0 + emp*s0*s0 = p0*r1 + emp*p0*s0 + r1*s0 + emp*s0*s0
     p1*r0 - emp*r0*s0 - p1*s0             = p0*r1 + emp*p0*s0 + r1*s0
     p1*r0 - p0*r1 = emp*p0*s0 + r1*s0 + emp*r0*s0 + p1*s0
     s0(emp*p0 + r1 + emp*r0 + p1) = p1*r0 - p0*r1
     s0 = (p1*r0 - p0*r1) / (emp*p0 + emp*r0 + p1 + r1)
     ```
3. **Calculate amount of liquidity tokens to mint:**
   * Reserve of token0 after swap (before provision):\
     `r0 - s0 = r0 - (p1*r0 - p0*r1) / (emp*p0 + emp*r0 + p1 + r1)`
   * Provided amount of token0 after swap:\
     `p0 + s0 = p0 + (p1*r0 - p0*r1) / (emp*p0 + emp*r0 + p1 + r1)`
   * Ratio of provided amount to reserve (this, times liquidity token total supply, is the amount of liquidity token that needs to be minted):

     ```
     provided_to_reserve_ratio = (p0 + s0) / (r0 - s0) =

       p0 + (p1*r0 - p0*r1) / (emp*p0 + emp*r0 + p1 + r1)
     = -------------------------------------------------- =
       r0 - (p1*r0 - p0*r1) / (emp*p0 + emp*r0 + p1 + r1)
     ```

     Multiplying numerator and denominator by `emp*p0 + emp*r0 + p1 + r1`, we get:

     ```
       emp*p0*p0 + emp*p0*r0 + p0*p1 + p0*r1 + p1*r0 - p0*r1
     = ----------------------------------------------------- =
       emp*p0*r0 + emp*r0*r0 + p1*r0 + r0*r1 + p0*r1 - p1*r0

       emp*p0*p0 + emp*p0*r0 + p0*p1         + p1*r0
     = ----------------------------------------------------- =
       emp*p0*r0 + emp*r0*r0         + r0*r1 + p0*r1

       emp*p0*(p0 + r0) + p1*(p0 + r0)
     = ------------------------------- =
       emp*r0*(p0 + r0) + r1*(p0 + r0)

       emp*p0 + p1
     = -----------
       emp*r0 + r1
     ```
   * To avoid numerical integer divisions, assuming that effective mid-price is a fraction `emp = emp_num / emp_den`, we can multiply numerator and denominator by effective mid-price's denominator:

     ```
                                 (emp*p0 + p1) * emp_den
     provided_to_reserve_ratio = ----------------------- =
                                 (emp*r0 + r1) * emp_den

       emp_num*p0 + p1*emp_den
     = -----------------------
       emp_num*r0 + r1*emp_den

     ```
   * Finally, the amount of LP tokens to mint is:

     ```
     lp_to_mint = lp_total_supply * (emp_num*p0 + p1*emp_den) / (emp_num*r0 + r1*emp_den)
     ```

</details>

## See also

{% columns %}
{% column %}
{% content-ref url="/spaces/gxFOZ85U3R3v2cPka9gM/pages/u7zWyMUKXDnn1eSGg3BW" %}
[Broken mention](broken://spaces/gxFOZ85U3R3v2cPka9gM/pages/u7zWyMUKXDnn1eSGg3BW)
{% endcontent-ref %}
{% endcolumn %}

{% column %}
{% content-ref url="/spaces/usbDsiWDhwRR4sob2ROl/pages/nTwimP6eLkw1g057D6fT" %}
[Broken mention](broken://spaces/usbDsiWDhwRR4sob2ROl/pages/nTwimP6eLkw1g057D6fT)
{% endcontent-ref %}
{% endcolumn %}
{% endcolumns %}


# Audit

Turbine backend and frontend (including SDK) have been audited by [Trail of Bits](https://www.trailofbits.com/).

### Backend audit

Trail of Bits audited the backend at commit [9644d88d5aee50a4f93feaeb646123b74730b015](https://github.com/propeller-heads/turbine/tree/9644d88d5aee50a4f93feaeb646123b74730b015) (2025-11-17).

Audit report: [Propeller Heads Turbine Security Review](https://github.com/trailofbits/publications/blob/master/reviews/2026-03-propellerheads-turbine-securityreview.pdf)

### Frontend and SDK audit

Trail of Bits audited the frontend at commit [fba934df4eca0c11f4b84cfaa1860e4142e3a680](https://github.com/propeller-heads/propellerswap-frontend/tree/fba934df4eca0c11f4b84cfaa1860e4142e3a680) (2026-01-05), and SDK at commit [d5ed0b7615afdf2dcac3faae2eacd02342878960](https://github.com/propeller-heads/turbine-sdk/tree/d5ed0b7615afdf2dcac3faae2eacd02342878960) (2026-01-05; equivalent to version [0.13.2](https://github.com/propeller-heads/turbine-sdk/tree/0.13.2) with some documentation commits cherry-picked on top).

Audit report: [Propeller Heads Turbine Frontend & SDK Security Review](https://github.com/trailofbits/publications/blob/master/reviews/2026-03-propellerheads-turbinefrontendsdk-securityreview.pdf)

### Changes after audit

PropellerHeads applied fixes after receiving the audit report. Trail of Bits then audited the fixes; the findings are in the reports linked above.

PropellerHeads kept developing Turbine while Trail of Bits reviewed the code. Trail of Bits has not audited some newer parts. PropellerHeads consults an internal security consultant on all significant changes. A formal audit of post-review changes will follow.


# Trusted Execution Environment

The Turbine backend runs inside a Trusted Execution Environment (TEE).

Nobody, including PropellerHeads or anyone with physical access to the node, can read Turbine's data or alter the program.

PropellerHeads can update the application. Each update is visible on-chain for full transparency.

Turbine runs in a secure Intel TDX enclave, powered by the open-source TEE framework [DStack](https://phala.com/dstack), provided by [Phala](https://phala.com/).

## Track Turbine updates

Each update of Turbine backend must be allowed by an onchain transaction.

Allowing an update means calling `addComposeHash` function on Turbine [DstackApp smart contract](https://etherscan.io/address/0x48da3ef94f5c5409c2c6df70f95474f41eb763a5). PropellerHeads cannot update Turbine without making a public transaction.

## Verify Turbine

### Verify with Trust Center

The Trust Center lets you verify all components forming the chain of trust:

{% embed url="<https://trust.phala.com/app/48da3ef94f5c5409c2c6df70f95474f41eb763a5>" %}

The App Source Code block contains the docker-compose file the TEE runs, which references the Turbine Docker image. Confirm that an image with the same sha256 digest was built on GitHub.

{% stepper %}
{% step %}

#### **Find the** [**App Source Code**](https://trust.phala.com/app/48da3ef94f5c5409c2c6df70f95474f41eb763a5?selected=app-code) **block**

<figure><img src="https://2394990052-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpYOzGnfXqOVgAkSx1kE2%2Fuploads%2FX1rLbPVk5TLYGYTi4HeB%2Fimage.png?alt=media&amp;token=4db1fd7e-af36-449a-95b0-9f4fe76f37b5" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}
**Click on compose\_file**

<figure><img src="https://2394990052-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpYOzGnfXqOVgAkSx1kE2%2Fuploads%2Ffw036HJxQ4IlB5dOF4zE%2FScreenshot%202026-04-09%2016%3A19%3A42.png?alt=media&amp;token=ee35fa85-45e6-4936-b3e6-8d8a244adf7c" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}

#### **Find docker\_compose\_file key**

<figure><img src="https://2394990052-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpYOzGnfXqOVgAkSx1kE2%2Fuploads%2FGh1aIktaLGKRIRNbLTEA%2FScreenshot%202026-04-09%2016%3A25%3A02.png?alt=media&amp;token=a100dd25-2b60-4c5f-96d1-40ed7a011f63" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}

#### **Find Docker image**

<figure><img src="https://2394990052-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpYOzGnfXqOVgAkSx1kE2%2Fuploads%2FYRw3r4hhjqB9LNPc3Zzp%2FScreenshot%202026-04-09%2016%3A27%3A33.png?alt=media&amp;token=f1f3ff57-4312-4258-b9ab-7caf8bf9b831" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}

#### **Locate the image on** [**GitHub**](https://github.com/propeller-heads/turbine/actions/workflows/prod-deployment.yaml)

{% hint style="info" %}
Turbine will be open sourced after Beta stage.
{% endhint %}

<figure><img src="https://2394990052-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpYOzGnfXqOVgAkSx1kE2%2Fuploads%2FUEV2ddFyKHBUTXXRoh4E%2Fimage.png?alt=media&amp;token=0530b4c6-7a10-4a25-a663-d7c474b9b513" alt=""><figcaption></figcaption></figure>
{% endstep %}
{% endstepper %}

### Verify manually

This guide describes how to verify that the Turbine API you are interacting with runs genuine Turbine code inside a Trusted Execution Environment.

{% stepper %}
{% step %}

#### Get a quote

Go to `https://api.turbine.exchange/api/attestation/<report_data>`, substituting `<report_data>` with a string (up to 64 characters), for example `https://api.turbine.exchange/api/attestation/MyReportData`.

The generated quote is in the `quote` field of the response.
{% endstep %}

{% step %}

#### Confirm that the quote was produced in a TEE

Validate the quote by pasting it (the value of the `quote` field, without quotation marks) at [TEE Attestation Explorer by Phala](https://proof.t16z.com/). You can also use other Intel TDX attestation verification tools, but the one linked above is specialised for DStack and takes more data into account.
{% endstep %}

{% step %}

#### Confirm that the TEE is running genuine Turbine

Quote validation extracts several values from the quote, including RTMR3. This value reflects the docker-compose file used to run containers inside the TEE.

Obtain the expected docker-compose file and TEE event log from the Turbine API. Both values are available at the `https://api.turbine.exchange/api/info` endpoint. To avoid formatting errors, use the following commands to save them as text files:

```bash
curl https://api.turbine.exchange/api/info | jq .tcb_info.app_compose -j > app_compose.json
curl https://api.turbine.exchange/api/info | jq .tcb_info.event_log -j > event_log.json
```

In `app_compose.json`, you can find the Docker image hash of the container running Turbine.

Now calculate the expected RTMR3 value. The easiest way is to use [https://rtmr3-calculator.vercel.app](https://rtmr3-calculator.vercel.app/) (source code [here](https://github.com/propeller-heads/rtmr3-calculator)). Paste the contents of `app_compose.json` and `event_log.json` into the calculator. It verifies that the compose file matches the event log and calculates the expected RTMR3 value.

{% hint style="info" %}
Calculation of RTMR3 value is explained in [Phala documentation](https://phala.network/posts/truth-of-AI-Agent).
{% endhint %}

Compare the RTMR3 value you calculated with the RTMR3 value extracted from the quote during verification. They must match exactly. **This proves that the quote was generated by a DStack deployment running Turbine in a confidential virtual machine.**
{% endstep %}
{% endstepper %}

## Read more

{% embed url="<https://phala.com/learn/What-Is-TEE>" %}

{% embed url="<https://www.intel.com/content/www/us/en/developer/tools/trust-domain-extensions/overview.html>" %}


