# Introduction

Open-source, permissionless, free-by-default AMM infrastructure

Ekubo Protocol is AMM infrastructure featuring super-concentrated liquidity, a singleton architecture with flash accounting, and extensions. It is deployed on [Starknet](/reference/contracts/starknet) and, with Ekubo V3, on [EVM chains](/reference/contracts/evm-v3) — Ethereum, Base, Arbitrum, Robinhood Chain, and more — at the same contract addresses on every chain.

The contracts are open source under the [Ekubo DAO Shared Revenue License](https://ekubo-license-v1.eth.link/), and are heavily optimized for gas and for precise control over where liquidity sits.

### Where to go

* **New to Ekubo?** Start with [Features](/about-ekubo/features), then browse the [products](/products/products).
* **Trading or providing liquidity?** See [Trading](/products/trading), [Providing liquidity](/products/liquidity), and the [user guides](/user-guides/add-liquidity).
* **Building on Ekubo?** Start with [Integrating Ekubo](/integration-guides/integration-guides).
* **Curious how it works?** Read [Protocol architecture](/concepts/architecture).
* **Looking up an address, API, or audit?** See the [reference](/reference/contracts).

Questions? Ask in the [Discord](https://discord.ekubo.org).


# Features

What sets Ekubo Protocol apart from other AMM protocols

### Free by default

The core contracts are ownerless and permissionless. They are deployed to the same address on every chain using a script that anyone can run. Core itself collects no protocol fee — swap fees accrue entirely to the pool. Protocol fees, where they exist, are applied at the periphery, for example by the Positions contract when a provider collects their fees. This is the key design decision that allows Ekubo V3 to serve many different [licensees](/about-ekubo/v3-whitepaper) on one shared liquidity layer.

### Gas efficiency

Ekubo uses the ["till" pattern](/concepts/architecture) and a singleton design to provide the cheapest trades across many pools. All pools live in a single contract, and token transfers are deferred until the end of the transaction. Advanced users don't have to transfer tokens at all: balances can be saved inside Ekubo for later use, avoiding repeated token transfers entirely.

The result is that you can execute many actions across many pools while making only the minimum number of token transfers. Combined with contracts optimized down to the storage-slot and calldata level (see [Price representation](/reference/price-representation)), this keeps the gas cost of a trade low, which matters most for routes that touch several pools.

### Concentrated liquidity

Concentrated liquidity allows market makers to [provide liquidity](/user-guides/add-liquidity) within a specified price range. Each liquidity provider chooses the exact parameters of their position, but from a swapper's perspective all positions in a pool are aggregated into a single curve. Concentrating liquidity near the market price deepens the book where trades actually happen, so swappers see less price impact — and providers are free to deploy the capital they did not have to post elsewhere.

Ekubo's ticks are 1/100th of a basis point — 100 times finer than the usual concentrated-liquidity convention. Finer ticks make narrower ranges expressible, so a position can be placed precisely where the provider wants it. A narrower range concentrates the same capital into less price space, which raises the fees earned per dollar of principal *while the price stays in range* — and equally raises the rate at which the position converts into the losing asset when the price moves against it, and the frequency with which it goes out of range entirely.

### Multiple pool types

Beyond concentrated liquidity, Ekubo V3 pools can be configured as stableswap pools (liquidity concentrated around a center price with an amplification factor) or full-range pools — all in the same Core contract, sharing the same routing and integration surface.

### Extensions

[Extensions](/concepts/extensions) allow third-party developers to permissionlessly create new kinds of pools on Ekubo that plug into the same ecosystem of aggregators and interfaces. Deployed extensions include price [oracles](/reference/contracts/evm-v3), TWAMM ([DCA orders](/user-guides/dollar-cost-average-orders)), limit orders, MEV capture, and [Ve33](/products/ve33) token-governed liquidity.

For what you can actually do with all of this — trading, providing liquidity, running incentive campaigns, indexing the data — see [Products](/products/products).


# Vision

Shared Liquidity as a Public Good

Ekubo is a next-generation AMM protocol built around a **singleton Core contract** that all deployments share. Instead of fragmenting liquidity across countless forks and versions, every licensee — each with its own branding, economics, and frontend — settles directly in the same Core. This design transforms liquidity itself into a **public good**, eliminating redundant work, consolidating integrations, and maximizing on-chain efficiency.

At the heart of Ekubo Core is a high-precision, concentrated-liquidity AMM engineered for extreme gas efficiency. The Core contract encodes all swap and pool logic, while **licensees** build products, interfaces, and fee models on top. This separation keeps the Core neutral and durable, while allowing permissionless innovation at the edges. Shared extensions — like on-chain oracles, TWAMMs, and MEV-capture modules — further expand the protocol's capabilities without fragmenting liquidity.

By unifying liquidity, tooling, and integrations under one canonical implementation, Ekubo creates strong network effects across the DeFi ecosystem. Traders benefit from deeper pools and lower costs; liquidity providers can serve order flow from every venue built on the same Core rather than splitting capital across forks; and builders can focus on user experience instead of re-implementing AMMs. Ekubo turns the AMM layer into open, composable infrastructure — a foundation for many brands, products, and communities to thrive together.

Read the full [V3 whitepaper](/about-ekubo/v3-whitepaper).


# V3 Whitepaper

Ekubo V3: Shared Liquidity as a Public Good — the whitepaper for the open-source, multichain V3 release

## 1. Motivation

Most AMMs today follow the same pattern:

* Each team forks or reimplements an AMM.
* Each deployment holds its own liquidity and token balances.
* Each “version” requires new integrations, new analytics, and new approvals.

From users' and integrators' perspectives this leads to:

* **Fragmented liquidity** – the same pair trades in many unrelated pools.
* **Duplicated work** – every fork needs its own indexers, explorers, and tooling.
* **Unnecessary gas costs** – tokens are transferred in and out of many different contracts that are all doing roughly the same thing.

Ekubo Core is a response to this duplication. Instead of many unrelated AMM contracts, Ekubo defines **one canonical Core contract** that implements a high‑precision concentrated‑liquidity AMM. Multiple **licensees** (which you can think of as white‑labeled AMMs with their own revenue models and frontends) all share this same Core.

The objectives are:

* **One implementation, many brands.** Different teams can run their own “instance” of Ekubo as an extension or licensee, but all orders and liquidity ultimately settle in the same Core.
* **Shared tooling and integrations.** Indexers, risk engines, analytics, and aggregators only need to integrate once.
* **Gas efficiency across licensees.** When everything settles in one Core, you can avoid ERC‑20 transfers even when moving value between different licensees.

## 2. The AMM Encoded in Core

Ekubo Core encodes a concrete AMM design:

* A constant‑product (`x * y = k`) AMM.
* With **concentrated liquidity** over price ranges, similar in spirit to Uniswap v3‑style positions.
* At a **very fine tick size** of 1/100th of a basis point, enabling precise market‑maker control.

Core also supports additional pool configurations, such as stableswap‑style curves and full‑range pools. Across all of these configurations, the key point is that:

* The **curve logic and pool mechanics live in Core itself.**
* Licensees do **not** ship their own AMM math; they all rely on this shared implementation.

This keeps the “hard part” – the AMM's correctness and efficiency – in one place that is audited for correctness, while still allowing many different products to be built on top of it.

## 3. Engineering for Gas Efficiency

Ekubo Core is engineered under the assumption that **gas is the scarcest resource**. A central design objective was to make it extremely difficult to build a meaningfully more gas‑efficient AMM without sacrificing safety, features or developer experience.

At a high level:

* Critical state is **bit‑packed into as few storage slots as possible**, reducing both reads and writes.
* Hot paths are implemented using carefully written low‑level arithmetic, while respecting clear invariants.
* Rounding is always chosen to favor the pool, preserving solvency even in edge cases.

One concrete example is the way Core represents price:

* Instead of using a fixed‑point type, Core defines a **custom floating‑point‑like representation with a 2‑bit exponent**, tailored specifically to AMM needs.
* This encoding allows **liquidity and the square‑root price to be packed together into a single storage word**, dramatically reducing the cost of updating pool state.
* The representation and arithmetic are designed so that any rounding error is biased toward the pool, ensuring that the system never pays out more than it should.

Taken together, these choices mean that:

* The marginal gas cost of a swap or liquidity update is dominated by **economic necessities** (price discovery, fee accounting), not by avoidable overhead.
* Competing designs that simply “re‑implement the same thing” are very likely to be more expensive on‑chain, because all of the possible optimizations have already been exhausted in Ekubo Core.

## 4. Licensees and White‑Labeled AMMs

Under the Ekubo DAO Shared Revenue License, multiple independent teams can become **licensees** of the Core implementation. Each licensee can:

* Operate its own frontend(s) and branding.
* Choose its own revenue model and fee recipients.
* Curate asset lists, default pools, and guardrails for their users.

On‑chain, a licensee may interact with Core:

* Directly from its frontend or contracts, calling the Core interface.
* Via thin helper contracts that batch or wrap user flows.
* Optionally, by opting into shared **extensions** that add features on top of the base AMM.

In practice, many licensees will deploy the **standard positions contract** that ships with Ekubo V3:

* It manages user liquidity positions on top of Core.
* It exposes a configurable “protocol fee” parameter that lets a licensee take a share of LP fees without changing Core itself.
* It can be deployed as‑is with different fee settings, giving each licensee its own revenue model on the same underlying AMM.

All licensees:

* Share the **same** pools, ticks, and positions inside Core.
* Share the **same** token custody.
* Share the **same** global state that integrators and tools observe.

From a user's point of view:

* Different licensees can feel like different “venues” or “frontends” with their own economic models and features.
* At a low level, they are all trading against one shared set of pools in the same Core contract, which directly results in better pricing.

## 5. Extensions: Shared Protocol Features

Extensions are **separate contracts** that integrate with Core to add reusable features. They are not new AMMs and they are not tied one‑to‑one with licensees. Instead, they provide functionality that **any** licensee can use.

In the reference implementation of this version of the protocol, three extensions are shipped alongside Core (additional extensions can be added over time):

* **Oracle** – efficiently records and exposes on‑chain price history for any token pair, perfect for bootstrapping new lending markets.
* **TWAMM (Time‑Weighted AMM)** – lets users place orders that execute gradually over time, smoothing execution and reducing market impact.
* **MEV Capture** – charges additional fees on swaps that move price significantly, directing that value back to liquidity providers.

Licensees can:

* Use these canonical extensions as‑is.
* Combine them in different ways in their own products.
* Optionally write additional extensions.

Conceptually:

* **Core** is the shared AMM engine and custody layer.
* **Extensions** are shared feature modules that any licensee can call.
* **Licensees** are off‑chain entities and/or contracts that choose how to assemble Core and extensions into a user‑facing product.

## 6. Network Effects From a Singleton Core

Having many licensees share a single Core contract creates several reinforcing network effects.

### 6.1 Tooling and Analytics

Because Core is the canonical place where all swaps and liquidity changes happen:

* Indexers, explorers, and analytics platforms only need to understand Core's event stream.
* Risk and monitoring tools can be written once and reused across every licensee.
* New licensees can launch without waiting for custom integrations; they inherit the existing ecosystem “for free”.

This is similar to the way a common L2 or common DEX becomes a focal point for tooling: once the infrastructure exists, new frontends and business models are cheap to add.

### 6.2 Integrations and Routing

Aggregators, market makers, and other protocols only need to target Core's interface:

* A single integration immediately supports all current and future licensees.
* Routing strategies can reason about one pool per pair (per configuration), not a forest of forks with small differences of behavior.

This reduces both engineering and operational complexity, and makes Ekubo a more attractive target for sophisticated routing logic.

### 6.3 Gas Efficiency Across Licensees

When users create trades that execute swaps on multiple AMM protocols, moving tokens between them requires at least one additional transfer, but often in practice incurs multiple additional token transfers:

* An ERC‑20 transfer out of AMM A to some intermediary router contract.
* A separate ERC‑20 transfer from that intermediary into AMM B.

Even though this happens in one transaction, those token transfers cost a lot of gas.

When two Ekubo Protocol licensees use the same Core contract:

* Tokens never need to leave Core just to move from “licensee A” logic to “licensee B” logic.
* Different licensees can even point at the **exact same pool**, but set up different revenue models externally (for example, by configuring different protocol‑fee parameters on their positions contracts).
* Licensee‑specific behavior can execute via direct Core calls or shared extensions, while balances stay in one place.

From the protocol's point of view, that means **one swap instead of two**: a trader routed through multiple licensees still interacts with a single Core pool, and each licensee settles its own economics off the back of that shared swap. This is the key network effect on gas: once tokens are in Core, all licensees can work with them without additional transfers between one another or redundant AMM hops.

## 7. Flash Accounting as a Supporting Feature

Ekubo Core also uses **flash accounting**: instead of transferring tokens in and out for every action, it keeps track of what each caller owes or is owed, and settles based on the net result.

This idea is not new in DeFi, but it is a good fit for a singleton AMM:

* Users can sequence multiple actions (e.g., swaps, liquidity changes) and only handle ERC‑20 transfers once.
* Power users and extensions can **save balances** in Core for later, reusing them across many operations.

Flash accounting is therefore best understood as **one of several mechanisms** that make the shared‑Core vision practical:

* It complements the singleton design by minimizing ERC‑20 calls.
* It makes it easier for licensees to compose complex flows without burdening users with many approvals and transfers.

It is important, but not the central conceptual novelty; the more fundamental idea is that many licensees share a single AMM implementation and liquidity layer.

## 8. Permissionless, Ownerless, and Fee‑Externalized

Ekubo Core is designed to be:

* **Permissionless to deploy:** anyone can take the contracts in this repository and deploy them to any chain, at the same addresses, using the provided deploy scripts. Bringing Ekubo to a new chain does not require coordination with the Ekubo team or DAO.
* **Permissionless to build on:** anyone can integrate Core, extensions, and the positions contract into their own product, subject only to the terms of the Ekubo license.
* **Ownerless on‑chain:** there is no privileged actor that can confiscate funds or reroute global protocol fees at the Core level.

Legally, the only global requirement is **revenue sharing** as defined in the Ekubo DAO Shared Revenue License:

* Licensees that collect protocol revenue (for example, by setting a non‑zero protocol‑fee share on the positions contract) share a portion of that revenue with Ekubo DAO.
* This revenue‑sharing arrangement can be negotiated with the Ekubo DAO when necessary.
* If a licensee chooses **not** to collect any protocol revenue (e.g., allows LPs to keep 100% of fees), then there is no protocol revenue to share with Ekubo DAO.
* Core does not enforce that a user's positions must pay any protocol fees; there can be completely free deployments of Ekubo Protocol, but the user must depend on the tooling offered by such deployment

Crucially, the notion of a **“protocol fee”** is **externalized** from Core itself:

* Core does not force a single global fee recipient or tax.
* Each licensee (through its chosen contracts and frontends, possibly via extensions) can define its own fee model and revenue split—for example, by configuring the protocol‑fee parameter on the shared positions contract it deploys.
* Communities can choose or fork the licensee logic that matches their values.

This separation lets Core focus on being a neutral, efficient, and durable AMM implementation, while economic policies live at the edges and are governed by license terms rather than on‑chain privileges. In this sense, the Core contracts function as **public infrastructure** for AMMs: a shared, well‑engineered base layer that anyone can deploy, integrate, and build on, so long as they respect the simple revenue‑sharing rules of the license.

## 9. How It Feels to Use Ekubo

### 9.1 For Traders

* You interact with a frontend (often tied to a specific licensee) and trade as usual.
* Under the hood, your trades settle against the same shared pools inside Core that other licensees use.
* You benefit from deeper liquidity and, over time, lower gas overhead per unit of volume as more activity concentrates in the singleton.

### 9.2 For Liquidity Providers

* You provide liquidity once into a Core pool.
* That liquidity can serve order flow from many different licensees.
* Your capital is not fragmented across multiple forks of the same AMM.

As more licensees launch on Ekubo, the same positions can see more order flow, without any extra management overhead from LPs.

### 9.3 For Licensees and Builders

* You focus on product, UX, and economics rather than re‑implementing AMM internals.
* You inherit existing liquidity, tooling, and integrations by plugging into Core.
* You can differentiate on fees, governance, curation, and user experience, while sharing a common, battle‑tested AMM engine.

## 10. Summary

Ekubo Core is a **singleton AMM implementation** that multiple licensees share—a public good for concentrated‑liquidity markets:

* The AMM itself is a high‑precision, concentrated‑liquidity constant‑product design encoded directly in Core.
* Licensees act as white‑labeled AMMs on top, with their own frontends and revenue models, but common liquidity and custody.
* Shared state produces strong network effects for tooling, integrations, and gas efficiency—even when value flows between different licensees.
* Flash accounting and saved balances support this vision by reducing ERC‑20 transfers and simplifying multi‑step flows, without being the main conceptual innovation.

The long‑term picture is a DeFi ecosystem where many brands and business models can coexist on top of a single, efficient, permissionless, and ownerless AMM Core, rather than a patchwork of incompatible forks all reinventing the same mechanics.


# Overview

What you can do with Ekubo, and the infrastructure behind it

Ekubo is more than an AMM contract. It is a set of products built around one shared liquidity layer — for traders, liquidity providers, ecosystems bootstrapping their own markets, and developers who need the underlying data.

### [Trading](/products/trading)

Swap across every Ekubo pool with routing that accounts for extension behavior. Quotes come from a public routing service that returns block-pinned split routes, which execute through a gas-optimized router in a single transaction.

### [Providing liquidity](/products/liquidity)

Supply concentrated, stableswap, or full-range liquidity in the same Core contract. Extensions add behavior on top — automatic order execution, MEV capture, externally funded fee boosts — without fragmenting liquidity into separate protocols.

### [Ve33 and STONX](/products/ve33)

A token-governed liquidity marketplace anyone can deploy around their own token. Holders lock the stake token, direct emissions to pools, set those pools' fees, and earn the fees of the pools they support. **STONX** on Robinhood Chain is the first ecosystem deployment, coordinating liquidity across stock-token markets.

### [Rewards and incentives](/products/rewards)

Liquidity incentive campaigns that measure real, useful liquidity and distribute rewards through periodic on-chain drops. Claims are merkle-based and permissionless, and anyone can fund a drop.

### [Indexer](/products/indexer)

The open source indexer that turns Ekubo's on-chain events into a queryable Postgres database — the same code behind the public API. Nightly database dumps let you bootstrap a node in minutes instead of days.

### [MCP server](/products/mcp-server)

A public Model Context Protocol server at `mcp.ekubo.org` that lets AI agents quote swaps, read pools and positions, and prepare unsigned execution plans — non-custodially, with signing left to the user's wallet.

### [Governance](/products/governance)

The EKUBO token, the Staker and Governor contracts that control the protocol's upgradeable deployments, how protocol revenue flows back to the DAO, and Ekubo, Inc.'s defined role within it.


# Trading

How trading works on Ekubo: routing across every pool, quotes that account for extensions, and single-transaction execution

A trade on Ekubo goes through three stages: finding a route, quoting it, and executing it. Each stage is available as public infrastructure you can use directly.

## Routing and quoting

Ekubo's liquidity is spread across pool types and [extensions](/concepts/extensions) — concentrated, stableswap, full-range, TWAMM, MEV capture, Ve33 — all inside the same Core contract. A good route often splits a trade across several of them.

The [Quoter API](/reference/quoter-api) does this for you. Given a chain, an amount, and a token pair, it returns **block-pinned split routes** for exact-input or exact-output swaps:

```
GET https://prod-api-quoter.ekubo.org/{chainId}/{amount}/{specifiedToken}/{otherToken}
```

A negative amount requests an exact-output quote. Token addresses and decimals resolve through the [Ekubo API](/reference/ekubo-api) token list.

Because quotes are pinned to a block, they reflect exact pool state at that block — including extension behavior, which is the part hand-rolled integrations most often get wrong.

## Simulation

If you need to compute quotes yourself — running your own solver, backtesting, or simulating without a network round-trip — the [SDKs](/integration-guides/sdks) implement the same math the contracts do.

The Rust SDK ([`ekubo_sdk`](https://crates.io/crates/ekubo_sdk)) is the most complete: its quoting module implements every pool type and extension, so simulated quotes match on-chain execution rather than approximating it. It is `no_std`-compatible, so it runs in constrained environments as well as on a server.

For pool math in TypeScript — tick and price conversions, liquidity sizing, swap steps — use [`@ekubo/sdk`](https://www.npmjs.com/package/@ekubo/sdk).

## Execution

On EVM chains, swaps execute through the [Yul Router](/integration-guides/yul-router), deployed at the same address on every supported chain. Routes are encoded with [`@ekubo/yul-router-sdk`](https://www.npmjs.com/package/@ekubo/yul-router-sdk) and sent as raw calldata — there is no ABI selector, because the calldata *is* the route.

The router executes every split of a trade under a **single Core lock**, aggregates the amounts, applies **one slippage check** against the total, and settles token transfers once. This is what makes multi-pool routing on Ekubo cheap: [flash accounting](/concepts/architecture) means intermediate hops never touch the token contracts.

Trading from your own contract on either chain follows the same lock-and-callback flow — see [Swapping](/integration-guides/swapping).

## Trade types beyond spot

* [**DCA orders**](/user-guides/dollar-cost-average-orders) — sell a token gradually over a period, netted against opposing orders, using the TWAMM extension
* **Limit orders** (Starknet) — narrow positions one tick spacing wide (128 ticks) that execute at a chosen price and are pulled automatically once filled
* [**Signed exclusive swaps**](/integration-guides/signed-exclusive-swaps) — RFQ-style pools where a market maker signs each quote off-chain with its own fee and bounds

## MEV capture

The MEV capture extension charges an additional fee on swaps that move a pool's price significantly, and directs that value back to the pool's liquidity providers rather than to searchers. Pools using it are routed and quoted like any other pool.


# Providing liquidity

Pool types, fees, and the extensions that change what providing liquidity on Ekubo looks like

Liquidity on Ekubo lives in one Core contract, but the shape it takes is configurable. A pool's type, its fee, and its [extension](/concepts/extensions) are all part of its identity, chosen when the pool is created.

For the step-by-step version in the app, see [Add liquidity](/user-guides/add-liquidity).

## Pool types

| Type             | Liquidity shape                                                                     | Suits                                               |
| ---------------- | ----------------------------------------------------------------------------------- | --------------------------------------------------- |
| **Concentrated** | Provider-chosen price range, with tick spacing controlling how narrow ranges can be | Most pairs; precise control over where capital sits |
| **Stableswap**   | Concentrated around a configurable center price, with an amplification factor       | Correlated assets that trade near a fixed ratio     |
| **Full range**   | The entire price range                                                              | Long-tail pairs and the simplest possible position  |

All three settle in the same Core contract and are quoted and routed together, so choosing one does not fragment your liquidity away from the others.

Ekubo's ticks are 1/100th of a basis point — 100x finer than most concentrated-liquidity AMMs — so a range can be drawn exactly where you want it. See [Pool math](/reference/pool-math) for how positions and prices are represented.

## Fees

The pool fee is what swappers pay to trade against your liquidity, and it is chosen when the pool is created rather than from a fixed set of tiers. A protocol fee is applied to the swap fees you collect (10% on EVM, 20% on Starknet, both applied by the Positions contract) and funds the DAO through [revenue buybacks](/products/governance#revenue-buybacks). The canonical Positions deployment charges **no fee on your principal** when you withdraw — the contract supports a withdrawal fee, but it is configured to zero.

Because any licensee can deploy their own Positions contract with its own fee settings, always check the deployment you are actually using.

Core itself charges nothing — see [Protocol architecture](/concepts/architecture).

## Extensions that change the LP experience

Extensions attach behavior to a pool without moving liquidity out of Core:

* **TWAMM** — pools that back [DCA orders](/user-guides/dollar-cost-average-orders). Providing liquidity here earns fees from order flow that executes against the pool when buy and sell orders are imbalanced, in addition to ordinary swap volume.
* **MEV capture** — charges extra on price-moving swaps and returns that value to LPs instead of searchers.
* **Boosted fees** — lets anyone fund additional fee rewards for a pool's LPs, on top of swap fees. Used for targeted liquidity campaigns.
* [**Ve33**](/products/ve33) — replaces swap-fee income with emissions of a stake token, with voters directing where those emissions go and setting each pool's fee. LPs earn emissions; voters earn the swap fees.
* **Oracle** — records price history for a token against native ETH on EVM (a configured quote token on Starknet). Oracle pools are full-range and fee-free by design.

## Positions are NFTs

A liquidity position is an ERC-721 token minted by the Positions contract, so it can be held, transferred, or used as collateral like any other NFT. Its `tokenURI` resolves to the [Ekubo API](/reference/ekubo-api), which serves the position's metadata and a rendered SVG image.

{% hint style="warning" %}
Never sell the NFT representing a position or an order — it *is* the claim on the underlying capital.
{% endhint %}

## Rewards beyond swap fees

Liquidity can also earn from [incentive campaigns](/products/rewards), which measure the depth your position provides near the market price and distribute reward tokens through periodic drops.

## Risk

Providing liquidity is not a yield product. A position earns fees from the volume that trades against it, and simultaneously accrues divergence loss as the price moves — ending up holding more of whichever asset fell in relative value. Narrowing a range concentrates both effects at once: more fees per dollar while the price is in range, and faster conversion into the losing asset when it moves. Whether a position is profitable depends on the volume it captures relative to that divergence, which is a property of the market, not of the protocol.


# Ve33 and STONX

A token-governed liquidity marketplace anyone can deploy on Ekubo Core, and STONX, the first ecosystem deployment

Ecosystems — chains, protocols, launchpads — often need liquidity across a set of related markets. **Ve33** turns that into a marketplace: holders lock a stake token, direct emissions to the pools they want liquidity in, set those pools' fees, and earn the trading fees the pools generate. Because voters are paid from the fees of the pools they support, they have a direct financial stake in allocating well.

Ve33 is an [extension](/concepts/extensions) on Ekubo Core. Anyone can deploy an instance around a token of their choice, including an existing token. It is permissionless and ownerless: no admin, no treasury cut, no weekly epoch, and emission policy lives outside the base extension.

The design descends from Curve's veCRV and Solidly's ve(3,3) — later refined by Velodrome and Aerodrome — with one significant addition: **voters set the pool fee**, not just the emission share. Background and economics: [Ve33: a token-governed liquidity marketplace](https://hackmd.io/@moodysalem/r1VnZQr4fg).

## Tokenomics

**Locking.** Holders lock the stake token for up to four years. Voting power is linear in both size and remaining time:

```
voting power = stake amount × (unlock time − now) / 4 years
```

A lock is represented by a transferable ERC-721 through the `VeToken` wrapper, whose metadata and SVG image are rendered entirely on-chain. Stakes can be increased, extended, split into several positions, or merged.

**Voting.** A stake assigns its full voting power to one pool — split a stake to support several. Each vote carries two decisions:

1. **Emissions** — the pool's share of the global emission stream
2. **Fee** — the swap fee that pool charges

The active pool fee is the voting-power-weighted average of all fee votes on that pool. A pool with no active vote weight charges a zero extension fee.

**Who earns what.** This is the part that differs most from an ordinary AMM:

| Participant             | Earns                                                                                                 |
| ----------------------- | ----------------------------------------------------------------------------------------------------- |
| **Voters**              | The full swap fees of the pools they vote for, proportional to vote weight — no treasury share        |
| **Liquidity providers** | Emissions of the stake token, by pool vote weight and their active liquidity — **not** Core swap fees |

LP rewards are range-aware: concentrated positions earn while in range, using the same inside/outside accounting Core uses for fees. Stableswap positions use global pool reward growth.

**Continuous accounting.** Unlike weekly-epoch ve(3,3) systems, fees and emissions accrue through on-chain growth indices updated whenever a pool or position is touched. There is no weekly distribution transaction. Vote weights, by contrast, are *sampled* — stored pool weights change only when a vote is cast or refreshed, so stake owners should renew votes as locks approach expiry.

**Emissions are modular.** Anyone can fund an emission schedule over a chosen interval by calling `scheduleEmissions(startTime, endTime, rate)` through the Ve33 periphery. Emission policy therefore lives outside the extension: a community can place the funding source — a mint authority, a treasury, or anything else — under whatever governance it prefers.

Because Ve33 pools use Ekubo Core underneath, they inherit singleton settlement, flash accounting, high-precision concentrated liquidity, stableswap support, and the shared routing surface. Swaps must be forwarded through a Ve33-aware router — direct Core swaps revert, since the voter-selected fee is applied by the extension.

## STONX on Robinhood Chain

**STONX** is the first ecosystem deployment of Ve33: a stake and reward token for bootstrapping capital-efficient **stock token liquidity** on [Robinhood Chain](https://robinhood.com/us/en/newsroom/robinhood-chain-launches-public-testnet).

{% hint style="info" %}
STONX is a [DAO proposal](https://hackmd.io/@moodysalem/stonx-on-rhc) under discussion. Details are subject to change and it will not proceed without Ekubo DAO approval on a specific version.
{% endhint %}

As proposed, the launch mints **999,999 STONX** in three equal parts:

| Allocation    | Amount        | Purpose                                                               |
| ------------- | ------------- | --------------------------------------------------------------------- |
| Ekubo, Inc.   | 333,333 STONX | Max-staked to direct initial emissions and fund continued development |
| DAO liquidity | 333,333 STONX | Paired with 333,333 USDG as DAO-owned STONX/USDG liquidity            |
| Emissions     | 333,333 STONX | 100 days of bootstrap emissions (about 3,333 STONX/day)               |

The DAO would pair its allocation with 333,333 USDG in a STONX/USDG Ve33 pool at an initial price of $1.00 per STONX, with the DAO's position spanning the full usable tick range, funded by selling existing DAO liquidity positions on Ethereum.

**Emission policy.** The initial 333,333 STONX is scheduled directly through the Ve33 periphery over **100 days**, an average of about **3,333 STONX/day**, to bootstrap liquidity. Because a funded schedule is committed on-chain, emissions already scheduled cannot be withdrawn or reduced; further emissions require funding a new schedule.

**What the DAO controls.** STONX mint authority is transferred to governance through the owner proxy on Robinhood Chain. This is the DAO's *only* form of ownership over the system: it can approve additional mints — to fund further emission schedules, partnerships, or distributions — by proposal. It cannot control votes, fees, or existing liquidity, and cannot claw back emissions already scheduled.

Ekubo, Inc. would max-stake its allocation and direct initial votes toward stock-token markets, with the resulting fee income funding development and maintenance.

The open questions are the ones voters answer continuously: which stock-token markets deserve emissions, what fee gives traders competitive execution while producing useful voter revenue, and whether the volume that follows each unit of emissions justifies the issuance.

## For developers

* [Ve33 user guide](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/docs/ve33-user-guide.md) — operational detail, including vote clearing, reward claiming, and expired stakes
* [Architecture notes](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/docs/ve-integrated-extension.md)
* [`Ve33.sol`](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/extensions/Ve33.sol), [`VeToken.sol`](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/VeToken.sol), [`Ve33Periphery.sol`](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/Ve33Periphery.sol)

The Ve33 accounting is covered by unit, fuzz, and stateful invariant tests for stake backing, voter-fee solvency, emission solvency, vote consistency, and range-aware LP rewards — see [Audits](/reference/audits).


# Rewards and incentives

Liquidity incentive campaigns, how rewards are measured, and the on-chain drop infrastructure used to distribute them

Ekubo runs liquidity incentive campaigns that pay reward tokens to liquidity providers. Rather than paying for deposited value alone, campaigns measure the **depth a position actually provides near the market price**, so rewards track useful liquidity rather than parked capital.

Rewards accrue continuously and are distributed through periodic on-chain drops that you claim yourself.

## Campaigns

A campaign defines what is being rewarded and for how long: a chain, a reward token, a start and end time, the set of pairs included, and which lockers and extensions qualify. Campaigns are listed publicly:

```
GET https://prod-api.ekubo.org/campaigns?chainId={chainId}
```

Each campaign reports, per pair, the depth currently measured and the amounts `scheduled` and already `distributed`, along with a campaign-level `nextDropTime` — when the next drop is scheduled to land.

To see what a specific position has earned over a period:

```
GET https://prod-api.ekubo.org/rewards/{chainId}/{locker}/{salt}
```

Rewards are computed from the same on-chain event data served by the open source [indexer](/products/indexer), so the measurement is reproducible from public data.

## Drops and claiming

Accrued rewards are published periodically as a **merkle drop**. Each drop commits to a merkle root covering every recipient and amount; claiming means presenting a proof against that root.

To find what an address can claim right now:

```
GET https://prod-api.ekubo.org/claims/{address}?chainId={chainId}
```

The [app](https://ekubo.org/rewards) turns these into one-click claims, but the claim is an ordinary on-chain call — nothing gates it.

### The Incentives contract

On EVM chains, drops are handled by a single [`Incentives`](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/Incentives.sol) contract — a singleton that hosts many independent airdrops at the deterministic address `0xC52D2656cb8C634263E6A15469588beB9C3Bb738` on every chain.

A drop is identified by its **drop key**: the owner, the reward token, and the merkle root. That means a drop is fully described by what it pays and who it pays — no registry, no per-drop deployment.

| Function                      | Who can call it           | What it does                                                                |
| ----------------------------- | ------------------------- | --------------------------------------------------------------------------- |
| `fund(key, minimum)`          | Anyone                    | Tops the drop up to a minimum funded amount, pulling tokens from the caller |
| `claim(key, claimKey, proof)` | Anyone with a valid proof | Claims a recipient's allocation; each claim can only be made once           |
| `refund(key)`                 | The drop owner            | Returns whatever remains unclaimed                                          |

Funding is permissionless: anyone can fund a drop, including third parties who want to add to a campaign. Claims are tracked in a bitmap so a claim cannot be replayed, and the contract is multicallable, so several claims can be batched into one transaction.

On Starknet, the equivalent role is played by the [`Airdrop`](https://github.com/EkuboProtocol/governance) contract in the governance repository, which uses the same merkle-proof approach and supports batched claims. It is what distributed the original [EKUBO token](/user-guides/ekubo-token) airdrop, and it has no claim deadline.

## Running your own campaign

Because both the campaign measurement data and the drop contract are public, incentive programs are not limited to the DAO:

* Compute allocations from indexed liquidity data — your own instance of the [indexer](/products/indexer) is enough
* Publish a merkle root and fund the drop through `Incentives.fund(...)`
* Recipients claim against it permissionlessly; you can reclaim whatever goes unclaimed

For incentives that pay continuously rather than in periodic drops, the **boosted fees** extension streams externally funded rewards directly to a pool's LPs — see [Providing liquidity](/products/liquidity), or [Ve33](/products/ve33) if you want token holders to decide where those rewards go.


# Indexer

The open source service that turns Ekubo's on-chain events into a queryable Postgres database — and how to bootstrap one from a nightly dump

The [indexer](https://github.com/EkuboProtocol/indexer) is the open-source service that ingests Ekubo events into a Postgres database. It is the same code that powers the public [Ekubo API](/reference/ekubo-api), so anything the API can answer, your own instance can answer too.

Its design goal is an **always-consistent realtime view**: events are cataloged rather than transformed, and the schema is reorg-safe. Analysis happens on top, in materialized views and queries, so the raw record stays faithful to the chain.

Run your own instance when you need lower latency than the public API, higher request volume than its rate limits allow, or direct SQL access for analytics.

## Bootstrapping from a database dump

Syncing a fresh database across all networks takes **days**. You almost never want to do that. Instead, start from a published dump.

A nightly workflow (`.github/workflows/pg-dump.yaml`) runs `pg_dump -Fc` against the production database and uploads the result as a GitHub Actions artifact:

1. Open the [Actions tab](https://github.com/EkuboProtocol/indexer/actions) of the indexer repository and select the most recent **pg-dump** run.
2. Download the artifact — named `db-backup-<run_id>`, containing `db-backup-<timestamp>.dump`.
3. Restore it into your Postgres instance:

```bash
pg_restore --clean --if-exists --no-owner \
  --dbname postgres://user:pass@host:5432/dbname \
  db-backup-20240101T000000Z.dump
```

{% hint style="info" %}
Artifacts are retained for **7 days**, so grab a recent run. During restore you may see warnings about the DigitalOcean `doadmin` role or the `pg_cron` extension — those are expected and safe to ignore if your target database lacks those privileges or extensions.
{% endhint %}

Once restored, start the indexer and it will catch up from the dump's head to the current chain tip.

## Running it

A prebuilt image is published to the GitHub Container Registry for every commit to `main`:

```bash
docker pull ghcr.io/ekuboprotocol/indexer:<git-sha>
```

Bun executes the TypeScript sources directly, so there is no build step. Run the entrypoint for the chain family you want, with `NETWORK` selecting the specific network:

```bash
docker run --rm -e NETWORK=mainnet ekubo-indexer bun src/starknet.ts   # Starknet
docker run --rm -e NETWORK=mainnet ekubo-indexer bun src/evm.ts        # EVM chains
```

Point it at Postgres with `PG_CONNECTION_STRING`, and apply the schema first:

```bash
docker run --rm --env-file .env ekubo-indexer bun scripts/migrate.ts
```

Migrations live under `migrations/` and run in order. Apply them **before** rolling out new workers.

The repository also includes a DigitalOcean App Platform spec (`.do/app.yaml`) describing the full production stack — a worker per network, managed Postgres, a pre-deploy migration job, and the price-sync process. Use it as a template for reproducing the setup elsewhere.

## What's in the database

Tables mirror Ekubo's on-chain events and derived state: pool initializations and swaps, position updates, per-tick liquidity, pool TVL, TWAMM sale rates and orders, oracle snapshots, Ve33 staking and voting, and ERC-20 token metadata with USD price history. Aggregations such as hourly volume and 24-hour pool statistics are maintained as views.

The repository keeps a **breaking changelog** documenting every schema change and any deployment that needs manual intervention. Read it before upgrading a running instance — some entries change columns that downstream consumers depend on.

{% hint style="info" %}
Token metadata is not written by the indexer. It is owned by the [`default-tokens`](https://github.com/EkuboProtocol/default-tokens) repository, which syncs metadata into the database separately.
{% endhint %}

Need help running one? Ask in the `#devs` channel of the [Discord](https://discord.ekubo.org).


# MCP server

Connect an AI agent to Ekubo: quotes, pool and position data, and unsigned execution plans over the Model Context Protocol

Ekubo runs a public [Model Context Protocol](https://modelcontextprotocol.io/) server at **`https://mcp.ekubo.org/mcp`**. It gives AI agents first-class access to Ekubo — resolving tokens, quoting swaps, reading pools and positions, and building transactions — without scraping a web interface.

The server is **non-custodial and read-only with respect to keys**. It never holds funds, never signs, and never submits. Tools that produce a transaction return a reference to an unsigned *execution plan*; signing and submission happen in the user's own wallet tooling.

## Connecting

The server speaks Streamable HTTP and requires no authentication. Add it to any MCP-capable client:

```json
{
  "mcpServers": {
    "ekubo": {
      "type": "http",
      "url": "https://mcp.ekubo.org/mcp"
    }
  }
}
```

Most clients also accept it from the command line — for example, `claude mcp add --transport http ekubo https://mcp.ekubo.org/mcp`.

## What it can do

Roughly fifty tools, grouped by what you're trying to accomplish:

| Area                                        | Capabilities                                                                                                                                                                                                     |
| ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Tokens**                                  | Search the canonical token list by symbol, or look up exact chain/address pairs in batches of up to 1,000. Ordered by visibility priority so the preferred token wins ambiguous symbol matches.                  |
| **Swaps**                                   | One call returns every available quote — Ekubo and 0x for same-chain swaps, Across for cross-chain — each option already carrying the execution plan that executes it, so there is no separate preparation step. |
| **Pools**                                   | Read pool state and liquidity, list pool keys, derive pool IDs, decode pool configs, initialize pools, and correct a mispriced pool.                                                                             |
| **Liquidity positions**                     | List positions by owner, inspect a position, find candidate pools for a position, and prepare deposits, withdrawals, earnings claims, and transfers.                                                             |
| **DCA / TWAMM**                             | Place, collect, and stop orders, and execute virtual orders.                                                                                                                                                     |
| [**Ve33**](/products/ve33)                  | Stake, vote, reallocate, extend, split, merge, increase, withdraw, reinvest, and claim fees — plus current allocations and a STONX allocation recommendation.                                                    |
| [**Auctions**](/reference/contracts/evm-v3) | Create an auction, complete it, and collect creator proceeds.                                                                                                                                                    |
| **Rewards**                                 | List claimable [rewards](/products/rewards) for an owner and prepare claims, including recovery fund claims; surface boosted-fee, incentive, and projected ve(3,3) opportunities ranked by APR.                  |
| **Utilities**                               | Revoke approvals, wrap and unwrap tokens, boost a pool manually, expand oracle capacity, unwrap old gEKUBO, and trigger revenue buybacks.                                                                        |

It also publishes **resources** that document its own conventions — the canonical agent workflow, the LP position workflow, the Ve33 workflow, quote semantics across providers, the execution-plan handoff, the data API's OpenAPI spec, and a chain-indexed directory of deployed contract addresses. Agents can read these directly rather than guessing at usage.

## The execution plan boundary

This is the part worth understanding before building on it.

Preparation tools never return "a transaction to send" loosely — and they do not return the plan inline either. Every executable preparation returns an `execution_plan_reference`: an artifact-reference envelope naming where the server stored the plan body, an integrity block (the keccak256 of the exact stored bytes plus their byte count), and a summary carrying the plan's `chain_id`, sender, and step count for sanity checks. The stored body — a signer-neutral, ordered sequence of unsigned calls — is the canonical boundary between the MCP server and whatever signs.

The rules that keep the boundary safe:

* **Relay the reference, not the body.** The agent passes the envelope unchanged to the wallet, which fetches the body over HTTPS, recomputes the integrity digest, checks the byte count, and refuses a mismatch. The agent in between never fetches, restates, or reconstructs the plan itself.
* **No timestamps travel in the envelope.** A plan's validity is expressed by the deadline inside its calldata and enforced by the wallet's simulation against current chain state. An expired reference simply 404s on fetch, and the fix is to re-run the preparation for fresh state and calldata.
* **Bind the sender first.** Choose the signing account before preparing, and pass that exact address. After preparation, the envelope summary's `chain_id` and sender must match the wallet's observed chain and account — a mismatch invalidates the plan rather than being silently rewritten.
* **Let the wallet own execution.** The wallet simulates the exact plan, presents the simulated result, collects authorization, and submits — preserving step order (a plan may require atomic batching), following the plan's simulation-failure policy on reverts, and applying each step's bundled custom-error ABI when decoding failures. An agent should not add a second confirmation on top.

Prepared on-chain reads travel the same way, as `read_calls_reference` envelopes whose stored body is an exact batch-call argument object. The full contract is published as the server's `ekubo://docs/execution-plan` resource.

## A typical swap

1. Resolve the tokens by symbol or address, and show the user the chains and addresses chosen.
2. Convert the user's amount to base units without floating-point arithmetic.
3. Once the user has decided to swap, request quotes in a single call with the sender and slippage tolerance. Every available source is returned — the server does not pick one — and each option already carries the execution plan that executes it, so the quote the user compared is the quote that executes. Individual provider failures are reported separately without invalidating the quotes that succeeded.
4. Choose an option and hand its `execution_plan_reference` to the wallet unchanged. The wallet fetches and verifies the plan body, simulates it, presents the result, and submits after authorization.
5. Re-quote only after an expiry, a revert, or a change to the request — never to "refresh" a plan already in hand, which would replace the quote the user approved.

For "swap my entire balance" requests there is an extra step: read the exact on-chain balance first, rather than trusting a displayed number. And for a purely indicative "what would I get" comparison, omit the sender and slippage — quotes come back with no calldata attached.

## Rate limits

The server is public and unauthenticated, so there is no API key to raise a quota against. Limits apply per caller, where a caller is one IPv4 address or one IPv6 /64. No fixed quota is guaranteed and the thresholds are not published, so an agent should react to what the server tells it rather than pace itself against a constant it has memorized.

The part worth designing around is that requests are not all counted the same. Four budgets run at once, and a rejection names the one you hit:

| Scope               | What it counts                                                                         |
| ------------------- | -------------------------------------------------------------------------------------- |
| `burst`             | Requests over a few seconds, across every endpoint                                     |
| `sustained`         | Requests over a minute, across every endpoint                                          |
| `tool_units`        | The weighted cost of tool calls over a minute                                          |
| `metered_providers` | Calls over a minute to the tools that buy quotes or recommendations from a third party |

`tool_units` is the one that catches integrators out. A tool call is charged by what it costs the server to answer, not as one request: `ekubo_derive_pool_id` hashes a struct locally and costs nothing at all, an ordinary token or pool read is the unit of measure, and a call that fans out across several upstream requests or buys a firm quote costs several times that. Two clients making an identical number of calls per minute can get very different answers, and the cheapest way to stay inside the budget is usually to ask for more per call rather than to call more often.

`metered_providers` is deliberately a separate and much smaller budget rather than a share of the same one, because those calls spend real money with third parties. Sitting comfortably inside `tool_units` does not buy you headroom for quotes.

### When you are limited

A rejection is an HTTP `429` carrying a `Retry-After` header in seconds. Honor that header rather than retrying on a fixed interval.

When the rejected request was a single identified JSON-RPC call, the body is a JSON-RPC error as well, so a client can attach the refusal to the call that caused it rather than to the connection:

```json
{
  "jsonrpc": "2.0",
  "id": "your-request-id",
  "error": {
    "code": -32029,
    "message": "Tool budget for this minute is exhausted...",
    "data": {
      "reason": "rate_limited",
      "scope": "tool_units",
      "retry_after_seconds": 60
    }
  }
}
```

`data.scope` is worth branching on, because the four scopes do not all mean the same thing. `burst` and `sustained` say you are asking too fast, and waiting is a complete fix. `tool_units` and `metered_providers` say the work you asked for was expensive, and waiting alone will not help if you resume the same pattern afterwards.

Enforcement is approximate, and it is measured close to where your request lands rather than globally. A batch of simultaneous requests can slip through where the same requests sent one after another would not, so treat a `200` as permission for that call rather than as evidence that you are inside the budget.

### Staying inside the budget

Batch instead of iterating. `ekubo_get_tokens` resolves up to 1,000 chain and address pairs in a single call and is charged once; the same thousand lookups issued individually are charged a thousand times. Where a tool offers a search or a filter, narrowing is far cheaper than paging the whole catalog and filtering client-side.

Reuse the quote you already hold. `ekubo_get_quotes_with_plans` buys firm quotes from providers, and each option it returns already carries the execution plan that executes it. Re-quoting to refresh a plan you already have spends the metered budget a second time and replaces the quote the user approved. Re-quote after an expiry, a revert, or a change to the request, not on a timer.

Respect the freshness windows. Protocol data is cached upstream, and pool state, pool keys, and tick liquidity do not change on every request. The server publishes the specific intervals as `polling_guidance` alongside the rate limit contract; polling faster than those windows spends budget without producing fresher data.

### Request size

Independently of the budgets, a single request may not exceed:

| Limit                          | Value         | Rejection |
| ------------------------------ | ------------- | --------- |
| Body size                      | 262,144 bytes | `413`     |
| JSON-RPC messages per request  | 20            | `400`     |
| Combined tool cost per request | 40 units      | `400`     |

The cost ceiling refuses rather than trims: an over-budget request is rejected outright instead of being quietly served at a discount. No single tool call reaches the ceiling on its own, so in practice it only ever asks you to split a batch — and JSON-RPC batching was removed from the protocol in revision 2025-06-18 in any case.

### Reading the contract at runtime

Rather than hard-coding any of this, a client can read it. `GET https://mcp.ekubo.org/` returns the current contract under `operational_semantics.rate_limit_contract`, covering every scope name, the response format, and the request-size limits, and `https://mcp.ekubo.org/llms.txt` carries the same summary in prose. Those are authoritative if this page ever drifts from them.

## Notes

* Supported on EVM chains, including Robinhood Chain (chain ID **4663** — the L2, not the brokerage).
* The server is versioned and advertises `listChanged` for both tools and resources, so clients are notified when its surface changes.
* Underlying protocol data comes from the same public [Ekubo API](/reference/ekubo-api) and [Quoter API](/reference/quoter-api) documented here, so results agree with the interface and with your own integrations.

Questions or problems? Ask in the [Discord](https://discord.ekubo.org).


# Governance

How the Ekubo DAO is structured, what it controls, how protocol revenue flows back to it, and Ekubo, Inc.'s role within it

Ekubo Protocol is governed by holders of the [EKUBO token](/user-guides/ekubo-token). Governance is deliberately narrow in scope: the EVM V3 Core contracts are **ownerless and immutable**, so there is no privileged actor who can change how the AMM works or seize funds. What governance does control is the protocol's upgradeable deployments, its treasury, and the parameters of the periphery.

The contracts are open source in the [governance repository](https://github.com/EkuboProtocol/governance) and are themselves ownerless and non-upgradeable, apart from the Governor's ability to upgrade itself by proposal.

## The three contracts

**EKUBO** is an ERC-20 on Ethereum, bridged to Starknet. It is the unit of voting weight.

**Staker** holds staked tokens and tracks delegation. Staking is not vote-escrow: there is no lockup, no decay, and no penalty for withdrawing. You stake to a delegate — often yourself — and can withdraw at any time.

Voting weight is not simply your staked balance. The Staker records delegation over time, and weight is the **average amount delegated to you** over a smoothing window ending when voting opens. This makes weight expensive to manufacture immediately before a vote.

**Governor** runs the proposal lifecycle and executes approved calls itself, so a passed proposal can make arbitrary calls — including `send_message_to_l1`, which is how it drives the owner proxies on other chains. (It also implements the account interface, but only so that proposals can be simulated off-chain.)

## Proposal lifecycle

```
propose → voting delay → voting period → execution delay → execution window → executed
```

A proposal commits to a set of calls by hash; those exact calls must be supplied again at execution. It passes only if it reaches quorum **and** receives strictly more `yea` than `nay` votes — a tie fails.

Current configuration:

| Parameter                        | Value           |
| -------------------------------- | --------------- |
| Voting start delay               | 1 hour          |
| Voting period                    | 4 days          |
| Voting weight smoothing duration | 1 day           |
| Quorum                           | 3,250,000 EKUBO |
| Proposal creation threshold      | 100,000 EKUBO   |
| Execution delay                  | 1 hour          |
| Execution window                 | 30 days         |

These are themselves governance-configurable, and each proposal is versioned against the configuration in effect when it was created — so a proposal created before a reconfiguration still runs under the old parameters. Read the current values directly from the Governor's `get_config` entrypoint.

Additional rules worth knowing: a proposer may have only one active proposal at a time, and a proposal can be cancelled only by its proposer and only before voting opens — the delay period exists so mistakes can be corrected. Execution is atomic and happens once; if a call reverts, the whole proposal can be retried within the execution window.

For the practical steps, see [Participate in governance](/user-guides/governance).

## What governance controls

* **Starknet contracts** — Core, Positions, and the extensions are upgradeable in place. The extensions are owned directly by the Governor; Core and Positions are held by the RevenueBuybacks contracts, which the Governor owns and can reclaim from by proposal
* **The treasury** — assets held by the DAO, disbursed by proposal (including streamed payments)
* **Cross-chain deployments** — owner proxies on Ethereum, Arbitrum, Optimism, Base, and Robinhood Chain let a Starknet proposal control contracts on other chains
* **Periphery ownership** — the owner role on Positions and other periphery contracts, which withdraws accumulated protocol fees to the DAO. The fee *rate* itself is not a governance parameter: it is immutable on EVM and a compile-time constant on Starknet, so changing it requires a new deployment

Notably *not* controlled: the EVM V3 Core contract, which has no owner at all.

## Revenue buybacks

Protocol fees are collected at the periphery — a share of the swap fees liquidity providers collect — and flow back to the DAO through the RevenueBuybacks contract.

The mechanism is permissionless: anyone can trigger it. It withdraws accumulated protocol fees and places a [TWAMM order](/user-guides/dollar-cost-average-orders) selling them for EKUBO gradually over a configured window, rather than in a single market-moving trade. Proceeds are collected to the Governor. Order timing and duration bounds, and the pool fee used, are set by governance per token.

## Ekubo, Inc.'s role

Ekubo, Inc. is the Delaware C corporation that built the initial version of Ekubo Protocol, along with the [indexer](/products/indexer), the interface, the governance contracts, and the API. It was founded by [Moody Salem](https://x.com/sendmoodz), and bootstrapped the Ekubo DAO in May 2024, distributing two thirds of total supply — one third by airdrop and one third sold by the DAO (see [EKUBO token](/user-guides/ekubo-token)) — and governing actively from the start. The DAO received the largest [Starknet Catalyst Program grant](https://www.starknet.io/blog/announcing-the-catalyst-program-igniting-transformative-change/) in recognition of that work.

In July 2024 the DAO approved a proposal defining the company's role in exchange for a one-time grant of roughly $1.5M — intended to be the only grant the company ever requests. Under it, Ekubo, Inc. committed to:

* Develop the core contracts for the benefit of the DAO, and make source code available at the DAO's direction
* Design and implement a framework for returning protocol revenue to stakers — delivered as the revenue buybacks above
* Develop and host the interface, free to swap on, with a public feature prioritization process
* Maintain this documentation, provide developer support in the [Discord](https://discord.ekubo.org), and help delegates create proposals
* Operate the public API and open source the governance tooling

The company holds one third of the total EKUBO supply and has committed to **never sell** those tokens for as long as it exists, keeping it permanently aligned with the protocol.

### Where the ecosystem can contribute

Ekubo, Inc. deliberately does not cover everything. Areas that benefit from independent teams include liquidity provider tooling and automated liquidity management, advanced delegate and governance tooling, market analytics, aggregator and routing integrations, marketing and community management, and exchange listings. If you want to build in one of these areas, start a conversation in the [Discord](https://discord.ekubo.org).


# Key concepts

The concepts behind Ekubo Protocol, and the platforms it runs on

## Protocol concepts

### Automated Market Maker (AMM)

A decentralized finance protocol that lets users trade against pooled liquidity at algorithmically determined prices, allowing liquidity providers to automatically "buy low, sell high." AMMs differ primarily in the amount of customization available to liquidity providers. Depositing into an AMM is a bet that trading fees will outweigh the loss due to price divergence of the two assets in a pair.

### Constant product

The `x*y=k` formula that forms the basis of most AMMs, where `x` is the amount of one token (`token0`), `y` is the amount of the other (`token1`), and `k` is held constant as users trade with the pool. This formula is how trades are computed on Ekubo within regions of constant liquidity — see the [pool math](/reference/pool-math).

### Concentrated liquidity

The main feature that allows Ekubo to provide better pricing than other AMMs. In simple constant-product AMMs, deposited capital backs trading at every possible price — including unrealistic ones — so most of it sits unused. Concentrated liquidity lets you choose the price range each position trades in. If you think ETH will only trade between 1,800 and 2,200 USDC, you can provide liquidity in just that range and deploy the rest of your capital elsewhere — or concentrate more capital into the range. Concentration cuts both ways: it raises fees earned per dollar while the price is in range, and equally amplifies divergence loss when the price moves. It does not make providing liquidity more profitable on its own.

**Capital efficiency** measures this concentration: how much more capital a full-range position would need to match a concentrated position's depth at the current price. It describes how the same exposure is expressed with less principal — not a higher expected return.

### Ticks

Ticks are the discrete price points that can serve as position boundaries. Ekubo divides the price range logarithmically: tick `i` corresponds to the price `1.000001^i`, so each tick is 1/100th of a basis point — finer precision than most centralized limit order books. One-tick positions behave like limit orders, which makes Ekubo suitable as an on-chain order book.

**Tick spacing** is the minimum distance between the ticks a pool's positions may use, and is set per pool. Smaller tick spacing allows tighter ranges; larger tick spacing makes swaps cheaper to compute. Volatile pairs, where tiny price differences don't matter, are better served by a larger tick spacing.

### Flash accounting

All token balance accounting happens inside Ekubo before any tokens are transferred: you can trade with many pools and update many positions, then settle only the net difference at the end. This is what the ["till" pattern](/concepts/architecture) enables, and it is why multi-pool routing on Ekubo is cheap.

## Platform concepts

### Ethereum

[Ethereum](https://ethereum.org/en/) is a blockchain that supports "smart contracts": autonomous programs that anyone can deploy and use. Ekubo Protocol V3 is deployed to Ethereum mainnet and many EVM-compatible networks — at the same contract addresses on every chain.

### Layer 2

A network that uses Ethereum to provide a more scalable platform without sacrificing decentralization or security. Sites such as [L2BEAT](https://l2beat.com/) track L2s and score them along multiple dimensions. Several of Ekubo's deployment chains (Arbitrum, Base, Optimism, Robinhood Chain, Starknet) are Ethereum L2s.

### Starknet

Starknet is a Layer 2 that scales Ethereum using STARK [zero-knowledge proofs](https://en.wikipedia.org/wiki/Zero-knowledge_proof), which let Ethereum verify the correctness of Starknet state transitions without re-executing every transaction. Starknet contracts are written in [Cairo](https://www.cairo-lang.org/), so ordinary Ethereum wallets are not compatible; wallets such as [Ready](https://www.ready.co/) and [Braavos](https://braavos.app/) work with Starknet. Ekubo's original deployment is on Starknet.


# Protocol architecture

How Ekubo is put together: one singleton Core contract, deferred settlement via the "till" pattern, and a thin periphery

Ekubo is built as a **singleton**: a single Core contract holds every pool, every position, and all token balances. Rather than a separate contract per pool or per version, all liquidity lives in one place. This is what makes shared liquidity, cheap multi-pool routing, and a single integration surface possible — see the [V3 whitepaper](/about-ekubo/v3-whitepaper) for the reasoning.

## The "till" pattern

Every interaction that moves tokens starts with a call to `lock`. Core calls back into your contract, you perform any number of operations (swap, add or remove liquidity, collect fees), and only the **net** token amounts are settled at the end. A few operations that move no funds — notably pool initialization and extension registration — are callable directly, without a lock. Payments are deferred until you have finished — like a shop till that is reconciled once, rather than per item.

This is the "till" pattern, [publicly introduced](https://www.youtube.com/watch?v=xFp8RlRq0qU) at EthCC\[5] and described in more detail [here](https://github.com/OpenZeppelin/openzeppelin-contracts/issues/4361#issuecomment-1595095135).

```
your contract          Core
     │  lock() ────────▶ │
     │  ◀──── locked()   │   ← callback: you are now inside the lock
     │  swap() ────────▶ │
     │  swap() ────────▶ │   ← any number of operations, no transfers yet
     │  withdraw()/pay() │   ← settle the net difference
     │  ◀──────────────  │   ← lock ends; Core asserts all balances are settled
```

Core tracks what you owe and are owed as **deltas** during the lock, and requires every delta to be zero before the lock can close. Until then, no tokens move.

{% hint style="info" %}
Even though the entrypoint for all methods is named `lock`, Ekubo supports reentrancy. Locks can be nested, so any contract you call from inside a lock can itself interact with Ekubo.
{% endhint %}

## Flash accounting

Deferred settlement is what makes Ekubo cheap for anything more complex than a single swap. Trading across many pools, or opening several positions, requires only the minimum number of token transfers — the net difference — instead of one transfer per operation.

Two consequences worth knowing:

* **Saved balances.** Rather than withdrawing tokens at the end of a lock, you can leave them inside Ekubo for later use, avoiding token transfers entirely across repeated interactions.
* **Free flash loans.** Because balances are only checked when the lock closes, you can `withdraw` tokens and repay them within the same transaction at no cost.

## Pools

A pool is identified by its **pool key**: the two tokens (sorted, so `token0 < token1`), the fee, the pool type parameters, and the [extension](/concepts/extensions) address. Pool state is deliberately compact — on EVM, the current price, tick, and liquidity pack into a single storage word (see [Price representation](/reference/price-representation)) so that swaps touch as little storage as possible.

Ekubo V3 supports several pool types in the same Core contract: concentrated liquidity, stableswap (liquidity concentrated around a center price with an amplification factor), and full range.

## Extensions

[Extensions](/concepts/extensions) are separate contracts that Core calls at defined points in a pool's lifecycle — before and after initialization, swaps, position updates, and fee collection. They let developers add behavior (oracles, order types, custom fee logic) without reimplementing the AMM, and without fragmenting liquidity into a separate protocol.

## Periphery

Core charges no protocol fee, and on EVM it is ownerless — there is no privileged account at all. (The Starknet Core is owner-upgradeable by governance, but likewise charges no protocol fee.) Everything user-facing lives in periphery contracts that hold locks on your behalf:

* **Positions** wraps liquidity positions as NFTs, and is where protocol fees are applied. It supports a fee on collected swap fees and, separately, one on withdrawn principal — the canonical deployment sets the latter to zero (see [Providing liquidity](/products/liquidity#fees))
* **Orders** manages TWAMM ([DCA](/user-guides/dollar-cost-average-orders)) orders
* **Routers** execute swap routes, including the gas-optimized [Yul Router](/integration-guides/yul-router) used in production on EVM
* **Lens contracts** provide read-only helpers for prices, quotes, and pool state

This separation is deliberate: Core stays neutral and durable, while fee models and user experience live at the edges. See [Contract addresses](/reference/contracts) for what is deployed where.


# Extensions

Customize pool behavior by writing extension contracts

Extensions let you insert custom logic at defined points in a pool's lifecycle. Like any other contract, an extension can also re-enter Core to place its own swaps or position updates before or after the calling contract interacts with a pool. This lets third-party developers build on Ekubo's efficient, audited AMM — concentrated liquidity included — without reimplementing any of the math. From this small interface you can build oracles, new order types, trading strategies, and privacy solutions.

{% hint style="info" %}
Each pool has its own separate state, meaning the capital deposited into each pool with an extension is isolated from all other pools, including other pools with the same extension.
{% endhint %}

### Rationale

Liquidity fragmentation is inevitable. In the absence of extensions, others will make small improvements to the Ekubo design or add new features, and deploy a variant of the core AMM. As a result, users must split their trades across many different sources of liquidity.

Ekubo aims to solve this problem by reducing the cost of fragmentation to near-zero. This is the purpose of the [singleton design and the till pattern](/concepts/architecture). Ekubo is a platform for an ecosystem of different types of pools that are all aggregated with every aggregator and arbitrageur, so markets can operate as efficiently as possible. This ecosystem of different kinds of liquidity also has the benefit of providing traders the best possible execution.

### What belongs in an extension

Extensibility is for creating new *pool functionality*: behavior that plugs into the [flash accounting system](/concepts/architecture#flash-accounting) and works as if it had been part of the original design. A TWAMM pool, an oracle pool, a vote-governed ve(3,3) pool — each changes what a pool *is*, and every router, aggregator, and arbitrageur settles against it through the same lock as any other pool.

What an extension is not is a place to add third-party dependencies. An extension is an immutable part of its pools' keys and sits in the hot path of every interaction with them, so coupling one to an external protocol welds that protocol's risks, upgrades, and failure modes into the pool itself — permanently, for every LP and trader in it.

The line is easy to draw in practice. Take a product someone might plausibly want to build. A position spanning a few basis points around the current price offers the same depth *at* that price as a far larger wide-range position, so the capital the wide position would have tied up is not needed in the pool at all. The product keeps a narrow position centered on the market, parks the capital it frees in a money market, and moves funds between the two as the price drifts — drawing on the money market to re-center or top up the position, returning what the position no longer needs. Nothing in that changes how the pool works. Swaps quote, fill, and settle exactly as before, and from the pool's point of view the whole arrangement is indistinguishable from an LP who mints and burns positions by hand. What it decides is *where capital sits and when it moves*: that is a **strategy**, and it belongs on top of the AMM, not inside it.

On top is also where an idea like this can be judged and adjusted. A narrower range concentrates fee income and divergence loss in equal measure, the price leaves it more often, and every re-centering pays gas and swap fees — so whether the arrangement beats simply holding a wider position depends on volatility, gas prices, and money-market rates, and the answer moves as they do. A strategy on top can be re-tuned, or abandoned, in the next block. An extension cannot: it is fixed in the pool key, so every revision is a new pool that must attract liquidity from scratch, and until then one particular money market sits in the hot path of every swap for everyone in the pool, LPs and traders alike, whether they want that exposure or not.

There are many ways to run a strategy on top. Vaults in the style of Yearn run them autonomously as contract code, at the cost of a newly audited contract per strategy. Hedge funds run them with people, who take a share of the profits and make mistakes. The most forward-looking way is to hand the strategy to an AI agent and let it run autonomously — which is precisely what the [MCP server](/products/mcp-server) is for: an agent gets the same tokens, quotes, position data, and execution plans the interface uses, so the strategy lives in the agent's instructions, where rewriting it costs a sentence, rather than in anyone's pool.

### Flexibility

You may want a different trading algorithm entirely — a different curve. You can approximate almost any curve by overlapping several `x*y=k` positions, so Ekubo's core components serve a wide range of AMM designs. At the extreme, where you want to quote every trade individually, Ekubo's very small ticks let you use it as an order book: place one-tick orders at whatever prices your extension decides, whether from an oracle or as a function of time.

Because an extension can re-enter the core Ekubo contract to perform its own actions within these lifecycle events, the simple interface allows for a huge amount of customization of pool behavior. For example, on the before-swap call point you could add your own liquidity ahead of the trade, improving the price the swapper gets; or you could record the pool's price *before* the trade moves it, which is exactly how the Oracle extension builds its history — it snapshots on before-swap and before-position-update, so every observation is a pre-trade price.

### Immutability

Extensions are specified as part of the pool key. The specified extension is an immutable configuration of a pool. Before a pool can be initialized with an extension, the extension must be registered with Core along with the set of pool lifecycle events ("call points") at which it should be called.

{% hint style="info" %}
Declare every hook you might need up front. How call points are declared — and how permanent they are — differs by deployment. On EVM (V3) they are encoded in the top byte of the extension's own address (addresses are mined so the address itself declares the hooks), and Core rejects a second registration, so they are genuinely immutable. On Starknet the extension registers them with Core via `set_call_points`, which can be called again; a change applies to all of that extension's existing pools, so treat them as immutable by convention.
{% endhint %}

The full list of call points is:

* Before pool initialization
* After pool initialization
* Before position update
* After position update
* Before collect fees
* After collect fees
* Before a swap
* After a swap

Because an extension holds its own state like any other contract, you can use that state to implement new kinds of orders. Limit orders, for example, work like this:

* Expose functions that let users create limit orders through your extension
* Immediately add liquidity to the pool for each new order
* After each swap, remove any orders that were fully executed

{% hint style="info" %}
An extension is immutable for a given pool, so either make the extension itself upgradeable or keep it simple enough that it never needs upgrading. We recommend immutable extensions, deploying a new version when one is needed.
{% endhint %}

## Available extensions

These extensions are already built and deployed. Each one is a pool type you can trade against or build on — see [Contract addresses](/reference/contracts) for deployments.

| Extension                                                                | Chains   | What it does                                                                                                                           |
| ------------------------------------------------------------------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| **Oracle**                                                               | Both     | Records on-chain price history for a token against a designated quote asset — native ETH on EVM, a configured oracle token on Starknet |
| **TWAMM**                                                                | Both     | Executes orders gradually over time, powering [DCA orders](/user-guides/dollar-cost-average-orders)                                    |
| **Limit orders**                                                         | Starknet | Narrow positions exactly one tick spacing wide (128 ticks, about 1.28 bps) that are pulled automatically once fully executed           |
| **MEV capture**                                                          | EVM      | Charges additional fees on swaps that move the price significantly within a block, directing that value back to liquidity providers    |
| **Boosted fees**                                                         | EVM      | Streams externally funded fee rewards to a pool's liquidity providers                                                                  |
| [**Ve33**](/products/ve33)                                               | EVM      | Token-governed liquidity: stakers vote to direct emissions and set pool fees, and earn the fees of the pools they support              |
| [**Signed exclusive swaps**](/integration-guides/signed-exclusive-swaps) | EVM      | RFQ-style pools where a controller signs each swap off-chain with its own fee and bounds                                               |

Source code: [EVM extensions](https://github.com/EkuboProtocol/evm-contracts/tree/v3.2.0/src/extensions) and [Starknet extensions](https://github.com/EkuboProtocol/starknet-contracts/tree/v5.0.3/src/extensions).

For how this design compares to Uniswap v4 hooks — and why the `forward` primitive replaces v4's custom-accounting machinery — see [Extensions vs. Uniswap v4 hooks](/concepts/extensions/extensions-vs-v4-hooks).


# Compared to Uniswap v4 hooks

How Ekubo V3 extensions compare to Uniswap v4 hooks, and why one primitive — forward — replaces an entire category of hook machinery

Ekubo V3 and Uniswap v4 look similar from a distance: both are singleton AMMs with flash accounting, and both let a pool bind custom logic — an [extension](/concepts/extensions) or a hook — as an immutable part of its pool key, with the set of lifecycle callbacks encoded in the logic contract's mined address. The designs diverge in how that custom logic is allowed to act, and the difference is most visible when you try to build behavior that is *outside* the scope of a concentrated liquidity AMM: new order types, reward schedules, RFQ fills, staking and emissions, wrappers.

## Lifecycle callbacks: notifications, not plumbing

Ekubo has eight [call points](/concepts/extensions#immutability): before and after pool initialization, swaps, position updates, and fee collection. Every one of them is a plain notification — extension callbacks return nothing. An extension that wants to move funds doesn't hand deltas back to Core through its return value; it re-enters Core inside the same lock, as an ordinary participant, and the [flash accounting](/concepts/architecture#the-till-pattern) guarantees everything nets to zero before the lock closes.

Uniswap v4 has ten hook callbacks governed by **fourteen** permission flags, because four of them come in an extra "returns delta" variant. A [v4 hook](https://github.com/Uniswap/v4-core/blob/v4.0.0/src/interfaces/IHooks.sol) communicates through its return values: each callback returns its own selector as a validity check, `beforeSwap` additionally returns a `BeforeSwapDelta` (with sign conventions over "specified" and "unspecified" currencies) plus an LP fee override (a `uint24` that is only honored when the pool is dynamic-fee and a particular bit is set), and every `PoolManager` operation carries a `hookData` bytes parameter so callers can smuggle arguments through to the hook. All of that plumbing sits in the hot path of every swap, whether a pool uses it or not.

Ekubo needs none of it, because custom behavior doesn't flow through callback return values at all.

## `forward`: custom behavior as a first-class citizen

Ekubo's answer to "I want my pool to do something the AMM doesn't do" is a single primitive on the flash accountant:

```solidity
function forward(address to) external;
```

Any locker can forward its lock context to any contract. For the duration of the call, the forwardee *becomes* the locker: it can swap, update positions, save balances, accumulate fees — any Core operation — all charged against the original lock and settled once at the end. Extra calldata is passed through raw, return data comes back raw, and reverts bubble up. The whole mechanism is [about thirty lines of assembly](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/base/FlashAccountant.sol); the receiving side is a [one-function abstract contract](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/base/BaseForwardee.sol). A forwardee defines its own ABI — nothing has to be squeezed through `hookData` bytes or exposed as separate external functions sitting outside the pool's accounting — and because the forwardee inherits the caller's lock, "the user pays for it" falls out of the accounting for free: whatever the forwardee spends becomes a debt the original locker must settle.

## What's been built with it

Every extension below is deployed and live. Together they exercise the full design space, and each one is the best illustration of a specific capability.

### Oracle — pure observation, call points alone

The [Oracle](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/extensions/Oracle.sol) never uses `forward` at all. `beforeInitializePool` validates the pool's shape (paired against native ETH, zero fee, full range), and `beforeSwap` / `beforeUpdatePosition` insert a time-weighted snapshot just before the price or liquidity changes. Void notifications are all an oracle needs — there is no delta to return, so none of v4's return-value plumbing would have been exercised.

### TWAMM — a new order type with its own ABI

[TWAMM](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/extensions/TWAMM.sol) uses both halves of the design. Its call points make time-execution invisible to everyone else: before any swap, position update, or fee collection it executes pending virtual orders (opening its own nested lock to do so), so every interaction sees a pool state as if DCA orders had been trading continuously. Placing or modifying an order, and collecting its proceeds, happens through `forward`: the [Orders periphery](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/Orders.sol) forwards an order key and sale-rate delta in TWAMM's own encoding, and the tokens the order sells are simply debts on the forwarding lock. Neither the extension nor the periphery ever holds a balance: placement moves tokens once, straight into Core, where unsold amounts and accrued proceeds sit as [saved balances](/concepts/architecture#flash-accounting) under Core's settlement invariant until the owner collects them — one transfer in, one transfer out, over the entire life of an order. Executing virtual orders moves no tokens at all; it is internal accounting against the pool's liquidity.

Uniswap built [a TWAMM hook of its own](https://github.com/Uniswap/v4-periphery/blob/bb589f1340a2e60ffc476d05cb0794edaa5b0810/contracts/hooks/examples/TWAMM.sol) (example code from the v4 announcement era, since removed from the periphery repo), which makes the comparison concrete. Order placement there is not disguised as a swap — `submitOrder`, `updateOrder`, and `claimTokens` are ordinary external functions on the hook — but because those entry points sit outside the `PoolManager`'s accounting, the hook must become a custodian. `submitOrder` pulls the entire sale amount into the hook with `safeTransferFrom`; every order's unsold tokens and earned proceeds are commingled in the hook's own ERC-20 balance until `claimTokens` transfers them out; and each virtual-order execution that moves the price has the hook lock the `PoolManager`, swap the net amount, and settle it from that balance — a transfer in and a transfer out of the `PoolManager` per execution, on top of the deposit and claim transfers, with funds parked in the hook contract the rest of the time. That custody is backed by nothing but the hook's own bookkeeping: `claimTokens` caps every claim at whatever the contract happens to hold, commented "to catch precision errors". One contract ends up being the extension, the order book, and the vault at once — not because the design wants a vault, but because a hook's own entry points cannot participate in flash accounting. `forward` is exactly the primitive that closes that gap.

### Boosted fees — scheduling rewards through the lock

[BoostedFees](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/extensions/BoostedFees.sol) shows that `forward` is not only for trading. Anyone — an incentives program, a token team — forwards `(poolKey, startTime, endTime, rate0, rate1)` to schedule a TWAMM-style stream of extra fee rewards into a pool. The extension computes the total cost and books it against the scheduler's lock as saved balances; the funding settles with everything else when the lock closes. Its call points then drip the accrued boost into the pool as fees before every swap, position update, and fee collection. One forwarded call both *prices* and *collects payment for* a scheduled future action — no separate deposit flow, no token approvals to the extension.

### MEV capture — owning the swap path

[MEVCapture](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/extensions/MEVCapture.sol) rejects direct swaps: `beforeSwap` reverts with `SwapMustHappenThroughForward`. Swappers forward to the extension, which performs `CORE.swap` itself and then charges an additional fee proportional to how many tick spacings the price has moved within the current block — captured value that is paid back to the pool's liquidity providers via `accumulateAsFees`. This is a dynamic fee model expressed as ordinary Solidity around a swap call, with no dynamic-fee pool flag, no fee-override bit encoding, and no delta sign conventions.

### Ve33 — an entire protocol behind one forwardee

[Ve33](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/extensions/Ve33.sol) is the strongest stress test of the model: a complete [ve(3,3) system](/products/ve33) — staking, vote-directed emissions, voter-set swap fees, reward claims — lives behind a single `handleForwardData` that multiplexes six call types: swap (with the vote-chosen fee accounted to voters), stake, unstake, claim LP rewards, claim voter pool fees, and schedule emissions. The forwarded `original` locker doubles as the authenticated owner for staking and claiming, so authorization comes from the lock context itself. Call points do the AMM-shaped bookkeeping: validating pool configuration, snapshotting range-aware reward accounting before liquidity changes, and rejecting direct swaps so fees can't be bypassed. In v4 this would be several hooks, external manager contracts, and custom accounting; here it is one extension.

### Signed exclusive swaps — RFQ fills as a pool

[SignedExclusiveSwap](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/extensions/SignedExclusiveSwap.sol) turns a pool into an [RFQ venue](/integration-guides/signed-exclusive-swaps): pools carry zero base fee, and every swap must arrive through `forward` carrying a controller-signed payload that sets that specific swap's fee, bounds, deadline, and nonce. The extension verifies the signature and executes against Core. Off-AMM, quote-driven liquidity — the very thing v4's custom accounting exists for — implemented as signature checks around a swap call.

### Wrappers — `forward` without being an extension

[TokenWrapper](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/TokenWrapper.sol) and [SavedBalancesWrapper](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/SavedBalancesWrapper.sol) attach to no pool, register no call points, and are not extensions at all — they simply receive forwarded lock contexts to wrap tokens or tokenize saved balances mid-lock. `forward` is a general composition primitive of the settlement layer, not a pool feature; in v4 there is no analogue, because hooks exist only as pool configuration.

Routing over all of this stays uniform. In the production [Yul router](/integration-guides/yul-router), each hop in a route's calldata begins with a caller-specified hop type followed by hop-type-specific data: a direct `Core.swap`, a swap forwarded to the pool's extension (MEVCapture, Ve33), a controller-signed swap on a SignedExclusiveSwap pool, or a wrap/unwrap through a token wrapper. Whatever the mix, the hops compose inside one lock with one aggregate slippage check and one net settlement.

## Case study: "aggregator hooks"

A category of v4 hook worth examining is the *aggregator hook*: a hook that fills swaps against external liquidity — other pools, other venues, off-chain inventory — so that a v4 pool behaves like an aggregator, pitched as giving integrators one pool that routes everywhere.

This is machinery in the wrong layer:

* **Aggregation is already a solved router problem.** Finding the best split across venues is off-chain computation; executing it is batching calls — precisely what a router contract does. Many aggregators do exactly this in production today, and do it more gas-efficiently than a hook can: the router calls each venue directly, with no `PoolManager` dispatch, no hook permission checks, and no custom-accounting delta translation wrapped around every fill.
* **What remains is rent collection.** Strip away the routing (done better off-chain) and the execution (done cheaper in a router), and the hook's remaining function is to insert a toll for the hook's beneficiaries — in Uniswap's case, a fee path for its tokenholders. But a fee is the one thing that genuinely is trivial to implement in a router contract, so the hook code is superfluous even for that purpose.
* **There is no distribution advantage.** A custom-accounting hook changes a pool's quoting and settlement semantics, so aggregators and solvers must still integrate that specific hook, one by one — the same per-venue effort as integrating any new protocol. Deploying as "a v4 pool" does not make existing integrations pick it up automatically; it just makes the integration target more complicated and the execution more expensive.

Ekubo draws the layer boundary the other way. Core is ownerless and charges no protocol fee; extensions change what a *pool* is, and aggregation across pools and venues lives where it is cheap — in [router contracts](/integration-guides/yul-router) and [aggregator integrations](/integration-guides/aggregators). Where off-AMM liquidity is genuinely useful, it appears as a pool type built on `forward` — [signed exclusive swaps](/integration-guides/signed-exclusive-swaps) give RFQ market makers a venue that every router and aggregator can fill against like any other Ekubo pool.

## Summary

|                                | Ekubo V3 extensions                                 | Uniswap v4 hooks                                                                                                    |
| ------------------------------ | --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| Lifecycle callbacks            | 8 call points, all void notifications               | 10 callbacks, 14 permission flags, selector + delta returns                                                         |
| Moving funds                   | Re-enter Core inside the lock, like any participant | Return `BeforeSwapDelta` / `BalanceDelta` from callbacks                                                            |
| Custom behavior beyond the AMM | `forward` — take over the lock, define your own ABI | Hook's own external functions, custodying tokens in the hook — or custom accounting through an intercepted `swap()` |
| Passing arguments              | Raw calldata through `forward`                      | `hookData` bytes threaded through every `PoolManager` call                                                          |
| Paying for extension actions   | Debts on the forwarding lock, settled once          | ERC-20 transfers into and out of the hook contract, arranged per hook                                               |
| Non-pool integrations          | Any contract can be a forwardee, no registration    | N/A — hooks exist only as pool configuration                                                                        |
| Protocol fee                   | None; Core is ownerless                             | Governance-set protocol fee in Core, up to 0.1% per direction                                                       |


# Add liquidity

How to become a liquidity provider on Ekubo

To add liquidity to Ekubo and start earning fees from swaps, [create a position](https://ekubo.org/positions/new).

{% hint style="info" %}
Bookmark the app and beware of signing transactions from other websites claiming to be Ekubo that are not hosted on ekubo.org. [Read more](https://en.wikipedia.org/wiki/Phishing) about phishing scams.
{% endhint %}

Adding liquidity takes three steps:

* Selecting a pool
* Selecting a price range
* Specifying an amount

### Selecting a pool

Every position is tied to a specific pool, and a pool is a combination of token pair, fee, tick spacing, and extension.

The two tokens you select — the "pair" — are the market you want to make. To market-make ETH against dollars, for example, choose ETH as the base token and USDC or DAI as the quote token. Here, the quote token is the numerator of the prices shown on the following pages. Swapping which token is which does not change the pair; it only changes how prices are displayed.

The fee you select is how much swappers are charged to trade against your liquidity. A fixed protocol fee is deducted from the swap fees you collect (currently 20% on Starknet and 10% on EVM chains, applied by the Positions contract when fees are collected). The canonical deployments take no fee on your principal when you withdraw.

Tick spacing should typically be about twice the fee. It determines how narrow your price range can be, and therefore how much leverage you can take.

### Selecting a price range

Once you've selected a pool, you must select the range of prices in which you would like to market make. If the price leaves this selected price range, your position will become "out of range," meaning it is no longer actively earning fees. If the market price reaches the upper boundary, your position will hold entirely the quote token; at the lower boundary, entirely the base token.

You should choose your price range to maximize capital efficiency: a narrower range earns more fees per dollar of principal while the price stays inside it, but requires more active management.

### Specifying an amount

Once you choose the parameters of your position, all that is left is to decide how much capital you wish to deposit. Enter an amount within your balance and confirm to create the position.

You are now an Ekubo liquidity provider.


# Dollar-cost average orders

Also known as TWAMM. Place orders that execute over time to enter and exit positions more efficiently or even programmatically

TWAMM, or "time-weighted average market maker," is an Ekubo extension that powers the "DCA orders" and "DCA-enabled pools" features in the [interface](https://ekubo.org). DCA orders are orders to sell a token at a specified rate between a start and end time. The mechanism involved in executing Ekubo's DCA orders is best described by [Paradigm](https://www.paradigm.xyz/2021/07/twamm).

DCA-enabled pools are liquidity pools that use the extension to support these orders by providing liquidity for the buy/sell side when DCA orders are imbalanced. In practice, orders are split into per-second pieces and netted against each other; whenever the ratio of buy to sell orders does not exactly match the current pool price, the difference is swapped against the pool. That ratio rarely matches the pool price exactly, which is why this liquidity is necessary for DCA orders to price well.

{% hint style="info" %}
In practice, virtual orders execute at most once per block. DCA orders are therefore best suited to trades that play out over longer periods than the chain's block time. The shorter the block time, the more pieces an order is split into, so the same liquidity supports larger or shorter-duration orders.
{% endhint %}

Orders on both sides of a DCA-enabled pool are netted against each other, and the difference is swapped on the pool to compute the resulting price for the orders.

Ekubo's TWAMM implementation is integrated into the core protocol via [extensions](/concepts/extensions). There are two features in the user interface:

* **DCA-enabled pools:** liquidity pools that use the TWAMM extension
  * Provide liquidity to these pools to earn fees from DCA orders as well as regular swap volume
  * Concentrated liquidity is not supported for these pools
* **DCA orders:** orders that sell a token over a specified period for a specific pool
  * Because the price is not known at the time of the swap, "exact output" swaps are not supported
  * There is no price protection, but you may stop an order at any time
  * Arbitrage keeps pricing efficient

Because TWAMM pools are cheaply aggregated with other pools in Ekubo, and opposite orders are netted against each other, DCA orders provide efficient pricing and lower fees for large swaps, i.e. you are likely to receive the time-weighted average price of the pair on your order.

Consider using TWAMM in the following cases:

* When moving large amounts of capital into and out of positions where the order books are thin
* When converting tokens programmatically
* When you want to buy/sell a token but don't want to time the entry/exit

Note that the price you receive on your DCA orders is heavily dependent on the liquidity in the pools and other orders on the pool over the period that the order executes. If you want to ensure good execution, it's best to supply liquidity to the pool on which you want to place the order to limit the price impact of each swap.

### Creating DCA orders

You can place DCA orders in the [interface](https://ekubo.org/dca).

DCA orders pay fees when the orders are imbalanced: you pay swap fees to liquidity providers of the pool on which you placed the order to swap your tokens, depending on how one-sided the orders are. Volume that nets directly against opposing orders pays no swap fee. Stopping or decreasing an order is free.

{% hint style="info" %}
An order's start and end times must each be a multiple of a step size that grows the further in the future they are. The smallest step is 256 seconds on EVM chains (16 on Starknet), so the shortest possible order is 256 seconds; the longest is just under `2**32` seconds.
{% endhint %}

### Adding liquidity to DCA pools

Creating a DCA-enabled pool provides the backstop liquidity that orders on it execute against, and that liquidity has exclusive rights to that pool's TWAMM volume. Orders cannot be placed between two tokens until at least one direct DCA-enabled pool exists.

Splitting an order into per-second pieces minimizes its price impact. You choose the DCA-enabled pool the order runs against and specify an amount; the order is then netted against volume flowing the other way, and any volume that never reaches the backstop pool pays no fee at all. Placing an order mints an NFT representing your ownership of it.

{% hint style="warning" %}
As with the NFT you receive when you create a position, you should **never sell this NFT** — selling it gives up the right to the capital behind the order.
{% endhint %}

### Fees

All fees from the DCA orders are directed towards liquidity providers of the pool on which the order is placed.

DCA orders incur *fees* only when there is no volume on the other side of the trade and the order must be executed against the liquidity pool. The fees paid to liquidity providers when orders are executed against the pool are like regular swap fees. Order volume that nets against opposing orders pays no fee, and stopping an order early is free.


# EKUBO token

Information about the EKUBO token

The EKUBO token is an [L1 Ethereum token](https://etherscan.io/token/0x04C46E830Bb56ce22735d5d8Fc9CB90309317d0f) bridged to [Starknet](https://voyager.online/token/0x075afe6402ad5a5c20dd25e10ec3b3986acaa647b77e4ae24b0cbc9a54a27a87), developed for the purpose of decentralizing ownership of Ekubo Protocol.

## Initial distribution

The total supply of the EKUBO token is 10 million (`10,000,000`), split into three equal parts of `3,333,333` tokens. The three distribution categories at the token generation event were:

* **Airdrop**: one third of the total supply was distributed to users via airdrop
* **Team**: one third of the total supply is held by Ekubo, Inc.
* **Sale**: one third of the total supply was sold by the DAO for ETH, USDC, and STRK using Ekubo's DCA orders

### Airdrop

Each user's allocation was calculated from their share of total leaderboard points, with two adjustments: points were raised to an exponent (`p^x`) that depended on the user's role in the ecosystem, and accounts with fewer than 1,000 points were excluded. As a result, users who concentrated their activity in fewer accounts received slightly more EKUBO.

* Moderators had a boost of `1.01`
* Active translators received a boost of `1.001`
* Everyone else received a boost of `1.0001`

The airdrop contract is deployed at the address `0x04bfacd0fcf70f444815de9150008fd12b5fb6721562707e502ce71ccb327d88` ([Starkscan](https://starkscan.co/contract/0x04bfacd0fcf70f444815de9150008fd12b5fb6721562707e502ce71ccb327d88), [Voyager](https://voyager.online/contract/0x04bfacd0fcf70f444815de9150008fd12b5fb6721562707e502ce71ccb327d88)). It uses the open-source airdrop contract found [here](https://github.com/EkuboProtocol/governance/releases/tag/v2.2.1). There is no deadline to claim the airdrop.

An airdrop can be claimed by using a block explorer to submit a transaction using data found in this spreadsheet:

{% file src="/files/hCgV59lMc6LD0OQ2lp48" %}
A CSV containing the entirety of the merkle tree for the EKUBO airdrop
{% endfile %}

#### Claiming your airdrop

You may claim your airdrop from the [app](https://ekubo.org/rewards), or use this guide to claim it directly from a block explorer.

{% stepper %}
{% step %}

#### Download the airdrop data CSV file

This CSV contains a list of all the accounts and allocation amounts that were included in the airdrop. Each row corrresponds to a single address's claim in the airdrop.
{% endstep %}

{% step %}

#### Find your address in the CSV file

Extract the `.csv` from the `.zip` file, then open it in any text editor. Search for the line containing your address. If you cannot find it, remove any leading zeroes from your address and search again — if your address is `0x0000abcd`, search for `0xabcd`. If you still cannot find it, you did not receive an airdrop.

{% hint style="warning" %}
Do not use Excel or Numbers to open the spreadsheet. They will round the numbers in the columns causing you to get "Invalid proof" when you try to submit your transaction.
{% endhint %}
{% endstep %}

{% step %}

#### Copy the row into Voyager block explorer

The columns in the CSV are claimee, id, amount and proof from left to right. For the row containing your address, copy each column value into the [block explorer](https://voyager.online/contract/0x04bfacd0fcf70f444815de9150008fd12b5fb6721562707e502ce71ccb327d88#writeContract). Remove the quotes and curly braces from the `proof` column data. Then connect your wallet and it should look something like this.

![](/files/ZyJLdZKnUlHY8PZ42jd6)
{% endstep %}

{% step %}

#### Click transact to claim!

When you click transact, the transaction should simulate successfully, and if you are transacting from the `claimee` address you should see an EKUBO balance increase from the simulation.
{% endstep %}
{% endstepper %}

### Sale

The DCA orders executed over 2 months, starting 5/24/24, 2:48 AM UTC and ending 7/23/24, 7:09 PM. The proceeds of the DCA order were owned by the [Governor ](https://voyager.online/contract/0x053499f7aa2706395060fe72d00388803fb2dcc111429891ad7b2d9dcea29acd), a.k.a. the DAO.

The following pools were used: [EKUBO/ETH 5%](https://ekubo.org/positions/new?poolType=twamm\&quoteCurrency=ETH\&baseCurrency=EKUBO\&fee=17014118346046923173168730371588410570\&poolOnly=true), [EKUBO/STRK 5%](https://ekubo.org/positions/new?poolType=twamm\&quoteCurrency=STRK\&baseCurrency=EKUBO\&fee=17014118346046923173168730371588410570\&poolOnly=true), and [EKUBO/USDC 5%](https://ekubo.org/positions/new?poolType=twamm\&quoteCurrency=USDC\&baseCurrency=EKUBO\&fee=17014118346046923173168730371588410570\&poolOnly=true).

Approximately `3,269,920` EKUBO was sold for `343.675` ETH, `1,204,770` USDC, and `1,549,920` STRK.

### Team

The company Ekubo, Inc., a service provider to the DAO, holds one-third of the total supply. There is no vesting schedule for these tokens. The team has come to an agreement with the DAO to hold these tokens indefinitely via [governance proposal](https://ekubo.org/governance/proposals/0x1bfc2ccdd2f9a718c45a9aa3a88770435f5272fbfeeb38ca2b3ad54c51c81e9) to fund the company's provision of services to the DAO.

## Value accrual

As of August 2025, the DAO currently holds total ownership of Ekubo Protocol smart contracts, meaning it can upgrade the contracts on Starknet, and collect revenue on EVM and Starknet. Note there is no upgrade mechanism for EVM contracts.

It is the right and responsibility of the DAO to decide what to do with the revenue. The DAO may direct protocol revenue towards EKUBO buybacks, and then may further direct that EKUBO to active participants in the protocol, such as governance stakers or liquidity providers.

As of August 2025, the position of Ekubo, Inc. is that the revenue should be directed towards growth of the protocol, and we will vote accordingly until the protocol achieves sustainable revenue, i.e. the revenue earned by the protocol exceeds the total costs to keep the protocol running.

The team allocation of EKUBO tokens is entirely held by Ekubo, Inc., and the company is committed to maintaining its share of Ekubo Protocol. The end-goal is for Ekubo, Inc. to be sustained by protocol revenue that is directed towards token holders. This aligns the equity holders with the interests of the protocol. The vision is further described in the [alignment proposal](https://ekubo.org/governance/proposals/0x1bfc2ccdd2f9a718c45a9aa3a88770435f5272fbfeeb38ca2b3ad54c51c81e9).

## Governance contracts

The governance contract addresses can be found [here](/reference/contracts/evm-v2#governance-contracts).

You can learn more about how the governance contracts work [here](https://github.com/EkuboProtocol/docs/tree/main/user-guides/README.md).

## Disclaimer

The EKUBO token serves only to decentralize the ownership role of the on-chain instance of the core Ekubo Protocol.

Ekubo, Inc., is not in any way obligated to provide service, maintenance, or development for Ekubo Protocol and related tools or services except as explicitly agreed via governance proposal.

The governance infrastructure is necessary security infrastructure: it protects users of Ekubo Protocol from malicious upgrades by requiring a decentralized and interested majority of stakeholders to come to consensus on the validity and safety of the changes.


# Participate in governance

How to stake, delegate, propose, and vote in Ekubo governance

This guide covers the practical steps. For how the system is designed — what governance controls, the contracts involved, and Ekubo, Inc.'s role — see [Governance](/products/governance).

## 1. Stake and delegate

Voting power comes from staked [EKUBO](/user-guides/ekubo-token), delegated to an address. Delegate to yourself to vote directly, or to someone else to have them vote on your behalf.

Use the [Delegate page](https://ekubo.org/governance/simple-delegate) in the app, or call the Staker contract directly:

| Function                                                                         | Effect                                          |
| -------------------------------------------------------------------------------- | ----------------------------------------------- |
| `stake(delegate)`                                                                | Stakes your full approved balance to a delegate |
| `stake_amount(delegate, amount)`                                                 | Stakes a specific amount                        |
| `withdraw(delegate, recipient)` / `withdraw_amount(delegate, recipient, amount)` | Withdraws staked tokens                         |

There is **no lockup and no penalty** for withdrawing at any time.

{% hint style="info" %}
Your voting weight is not your staked balance. It is the *average* amount delegated to you over the voting weight smoothing duration ending when voting opens — currently 1 day. Because the voting start delay is only 1 hour, stake well before a proposal you care about is created, not after.
{% endhint %}

## 2. Create a proposal

You need voting weight at or above the proposal creation threshold (currently 100,000 EKUBO) and no other active proposal of your own.

Create one from the [proposal page](https://ekubo.org/governance/create-proposal), or on-chain:

* `propose(calls)` — submit the calls the proposal will execute
* `describe(proposalId, description)` — attach a description
* `propose_and_describe(calls, description)` — both at once

A proposal commits to its calls by hash. The **same calls must be supplied again at execution**, so keep them.

Voting opens after the voting start delay (currently 1 hour). Discuss proposals in the [Discord](https://discord.ekubo.org) before submitting — the delay exists so problems can be caught early.

## 3. Vote

Call `vote(proposalId, yea)` with `true` for yea or `false` for nay, or vote in the app. One vote per address, only during the voting period (currently 4 days).

A proposal passes if it reaches quorum (currently 3,250,000 EKUBO in yea votes) **and** receives strictly more yea than nay. A tie fails.

## 4. Cancel, if needed

`cancel(proposalId)` — available to the proposer only, and only before voting opens. This is the correction window for a mistake in a submitted proposal.

## 5. Execute

After a proposal passes and the execution delay elapses (currently 1 hour), anyone can call `execute(proposalId, calls)` with the original calls. Execution must happen within the execution window (currently 30 days) or the proposal expires.

Execution is atomic: all calls succeed or none do. A proposal that reverts can be retried within the window.

***

Current parameter values and what governance controls are documented in [Governance](/products/governance#proposal-lifecycle). Contract addresses are in the [governance contracts reference](/reference/contracts/governance).


# Integrating Ekubo

The different ways to integrate Ekubo Protocol, and production integrations you can use as reference implementations

There are several ways to integrate Ekubo, depending on what you're building.

### 1. Swap on-chain from a smart contract

Every interaction with Ekubo goes through the Core singleton's [`lock` callback pattern](/concepts/architecture): call `lock`, receive a callback, perform swaps and settle net token amounts at the end. See [Swapping](/integration-guides/swapping) for the flow. On EVM, production swaps go through the gas-optimized [Yul Router](https://github.com/EkuboProtocol/yul-router) with routes encoded by [`@ekubo/yul-router-sdk`](https://www.npmjs.com/package/@ekubo/yul-router-sdk); the Router contracts ([Starknet](https://github.com/EkuboProtocol/starknet-contracts/blob/v5.0.3/src/router.cairo), [EVM](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/Router.sol)) are reference implementations for building your own.

### 2. Quote Ekubo liquidity off-chain (aggregators, solvers)

To route trades through Ekubo pools you need to simulate swaps off-chain. The easiest path is the [Quoter API](/reference/quoter-api), which returns block-pinned split routes ready to execute. To compute quotes yourself, use the [SDKs](/integration-guides/sdks) — the Rust SDK implements every pool type and extension — or see [Price representation](/reference/price-representation) and [Pool math](/reference/pool-math) to implement the math directly. Remember that pools with [extensions](/concepts/extensions) can modify swap behavior; see the [Aggregators guide](/integration-guides/aggregators) for how to handle them safely.

### 3. Index Ekubo data

The open source [indexer](https://github.com/EkuboProtocol/indexer) ingests Ekubo events on any supported chain into Postgres — it is the same code that powers the [Ekubo API](/reference/ekubo-api). Run your own instance for low-latency or high-volume needs.

### 4. Connect an AI agent

The public [MCP server](/products/mcp-server) at `mcp.ekubo.org` exposes quoting, pool and position data, and unsigned execution plans to any MCP-capable agent.

### 5. Provide exclusive, quoted liquidity

Market makers can run RFQ-style pools where each swap is signed off-chain with its own fee and bounds, while still settling in Ekubo Core — see [Signed exclusive swaps](/integration-guides/signed-exclusive-swaps).

## SDKs

[`ekubo_sdk`](https://crates.io/crates/ekubo_sdk) (Rust) computes quotes across every pool type; [`@ekubo/sdk`](https://www.npmjs.com/package/@ekubo/sdk) provides pool math in TypeScript; [`@ekubo/yul-router-sdk`](https://www.npmjs.com/package/@ekubo/yul-router-sdk) encodes swap routes for EVM execution. See [SDKs](/integration-guides/sdks).

## Reference integrations

Production integrations of Ekubo you can use as working examples:

| Integration                                                             | Language   | What it shows                                                       | Ekubo code                                                                                                                                                                                                                                                                                     |
| ----------------------------------------------------------------------- | ---------- | ------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [KyberSwap dex-lib](https://github.com/EkuboProtocol/kyberswap-dex-lib) | Go         | Aggregator pool discovery, state tracking, and swap simulation      | [`pkg/liquidity-source/ekubo`](https://github.com/EkuboProtocol/kyberswap-dex-lib/tree/main/pkg/liquidity-source/ekubo)                                                                                                                                                                        |
| [ParaSwap dex-lib](https://github.com/EkuboProtocol/paraswap-dex-lib)   | TypeScript | Aggregator integration with on-chain quoting and event-based state  | [`src/dex/ekubo`](https://github.com/EkuboProtocol/paraswap-dex-lib/tree/master/src/dex/ekubo)                                                                                                                                                                                                 |
| [Tycho](https://github.com/EkuboProtocol/tycho)                         | Rust       | Substreams-based indexing and off-chain swap simulation of Ekubo V3 | [`protocols/substreams/ethereum-ekubo-v3`](https://github.com/EkuboProtocol/tycho/tree/main/protocols/substreams/ethereum-ekubo-v3), [`crates/tycho-simulation/src/evm/protocol/ekubo_v3`](https://github.com/EkuboProtocol/tycho/tree/main/crates/tycho-simulation/src/evm/protocol/ekubo_v3) |

Questions about an integration? Ask in the [Ekubo Discord](https://discord.ekubo.org).


# SDKs

Libraries for computing Ekubo quotes, working with pool math, and encoding swap routes

Ekubo publishes three libraries. All of them implement the same protocol math, so quotes computed off-chain match on-chain execution exactly.

| Package                                                                        | Language   | Use it for                                                   |
| ------------------------------------------------------------------------------ | ---------- | ------------------------------------------------------------ |
| [`ekubo_sdk`](https://crates.io/crates/ekubo_sdk)                              | Rust       | Computing quotes and simulating swaps across every pool type |
| [`@ekubo/sdk`](https://www.npmjs.com/package/@ekubo/sdk)                       | TypeScript | Pool math, tick and price conversions, pool key encoding     |
| [`@ekubo/yul-router-sdk`](https://www.npmjs.com/package/@ekubo/yul-router-sdk) | TypeScript | Encoding swap routes for execution on EVM chains             |

All three cover both Starknet and EVM deployments where the underlying math is shared.

## Rust: `ekubo_sdk`

The [Rust SDK](https://github.com/EkuboProtocol/rust-sdk) is the most complete implementation — it is what powers Ekubo's own routing. It exists primarily to **compute quotes** from Ekubo pools, plus the supporting protocol math.

```toml
[dependencies]
ekubo_sdk = { version = "3.2", features = ["evm"] }
```

The crate is `no_std`-compatible, which makes it usable in constrained environments. Select what you need with feature flags:

| Feature                    | Effect                                                                      |
| -------------------------- | --------------------------------------------------------------------------- |
| `std` (default) / `no_std` | Standard library or `libm`-backed math; exactly one must be enabled         |
| `evm`                      | EVM types via `alloy-primitives` (v1 by default; `evm-alloy-0_6` pins v0.6) |
| `starknet`                 | Starknet felt types via `starknet-types-core`                               |
| `serde`                    | Serialization for the protocol types                                        |

Three modules:

* **`math`** — tick and sqrt-ratio conversions, swap step computation, deltas, fixed-point helpers, and TWAMM math
* **`quoting`** — the quoting engine: a `Pool` trait with a `quote` method, `QuoteParams` / `Quote` / `TokenAmount` types, and `PoolKey` / `PoolConfig` / `Tick` definitions
* **`chain`** — the EVM and Starknet type mappings

The `quoting::pools` module implements every pool type and extension so that off-chain quotes account for extension behavior: `concentrated`, `full_range`, `stableswap`, `limit_order`, `mev_capture`, `twamm`, `oracle`, `ve33`, `spline`, and `boosted_fees` variants. This is the practical reason to prefer the Rust SDK for routing — replicating extension behavior by hand is where most integrations go wrong (see [Aggregators](/integration-guides/aggregators)).

## TypeScript: `@ekubo/sdk`

```sh
npm install @ekubo/sdk
```

Shared math and protocol encoding utilities, with **no runtime dependencies**. It covers the pieces most integrations need in a browser or Node environment:

* **Tick and price math** — conversions between ticks, sqrt ratios, and prices for both chains (see [Price representation](/reference/price-representation))
* **Liquidity math** — `maxLiquidityForTokenAmounts`, `liquidityToAmountBase` / `liquidityToAmountQuote`, `amountsFromSpecifiedAmount`, and the `amount0Delta` / `amount1Delta` primitives for sizing positions
* **Swap math** — `computeStep`, `computeFee`, `amountBeforeFee`, `nextSqrtRatioFromAmount0` / `nextSqrtRatioFromAmount1`
* **TWAMM math** — `calculateNextSqrtRatio` for [DCA](/user-guides/dollar-cost-average-orders) pools
* **Pool key and protocol encoding** for EVM
* **Chain constants** — `EVM_MIN_TICK` / `EVM_MAX_TICK` (±88,722,835), `EVM_MAX_TICK_SPACING`, `EVM_MIN_SQRT_RATIO` / `EVM_MAX_SQRT_RATIO`, and the Starknet equivalents (±88,722,883)

{% hint style="info" %}
`@ekubo/sdk` is published as an alpha. Pin an exact version, and expect the surface to change before a stable release.
{% endhint %}

## TypeScript: `@ekubo/yul-router-sdk`

```sh
npm install @ekubo/yul-router-sdk
```

Encodes calldata for the [Yul Router](/integration-guides/yul-router), the router used for Ekubo swaps on EVM chains. Use it to turn a route — typically one returned by the [Quoter API](/reference/quoter-api) — into a transaction. See the [Yul Router guide](/integration-guides/yul-router) for the full surface and worked examples.


# Swapping

Integrate Ekubo protocol to provide better prices to swappers or to perform arbitrage

### Routing

It is your responsibility to find the best list of pools for executing a trade. This is equivalent to finding the best route for arbitrage. The easiest way to get a route is the [Quoter API](/reference/quoter-api), which returns block-pinned split routes ready to execute.

### Executing swaps on-chain

Every interaction that moves tokens starts with `ICore#lock` (see the ["till" pattern](/concepts/architecture)). Core then calls back into your contract: on Starknet via `ILocker#locked(id, data)`, and on EVM via the zero function selector `0x00000000`, which the reference implementations expose as `locked_6416899205(uint256)` — the numeric suffix is mined so that the selector is zero. Inside the callback you execute the swap(s), pay the input, and withdraw the output tokens, in any order. You typically call `swap` several times in a single `locked` callback so that you only settle the net differences.

### Swapping on EVM chains

Production swaps go through the [Yul Router](/integration-guides/yul-router) — a gas-optimized router deployed deterministically at the same address on each chain it has been released to (currently Ethereum, Base, Arbitrum, and Robinhood Chain, plus their testnets), with routes encoded by [`@ekubo/yul-router-sdk`](https://www.npmjs.com/package/@ekubo/yul-router-sdk). Core is deployed to more chains than the router; on those, use the reference `Router` below. See the [Yul Router guide](/integration-guides/yul-router) for the calldata model, hop types, and SDK usage.

### Reference routers

To execute swaps from your own contract, see the reference Router implementations on [Starknet](https://github.com/EkuboProtocol/starknet-contracts/blob/v5.0.3/src/router.cairo) and [EVM](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/Router.sol).


# Yul Router

The gas-optimized router used for Ekubo swaps on EVM chains, and the TypeScript SDK that encodes routes for it

The [Yul Router](https://github.com/EkuboProtocol/yul-router) is a gas-focused router written in Yul that executes Ekubo swaps on EVM chains. It is how swaps are executed in production today: the [interface](https://ekubo.org) encodes routes from [Quoter API](/reference/quoter-api) results and sends them to the router.

The router is deployed deterministically at the same address on every supported network (currently Ethereum, Base, Arbitrum, and Robinhood Chain, plus their testnets):

```
0x00000000D542a1Afa7A01ECB16254F7A0F8ceB61
```

The address is also exported by the SDK as `YUL_ROUTER_ADDRESS`.

## Design

The router deliberately carries everything it needs — token addresses, pool configs, extension forwardee addresses, and token wrapper addresses — **in calldata**. There are no token or extension jump tables and no stored routes. There is also no public ABI selector: any call that does not come from Ekubo Core is interpreted directly as packed route data. Calls from Core are reserved for the lock callback (selector `0x00000000`); the forward callback (selector `0x00000001`) always reverts.

A single transaction can contain many multi-hop routes. The router executes all of them under **one Core lock**, aggregates the specified and calculated amounts, applies **one slippage check** against the aggregate, and settles token transfers once.

Supported hop types:

| Hop type              | Executes                                                                                                                      |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `core`                | A direct `Core.swap` against the pool key                                                                                     |
| `forwarded`           | `Core.forward(forwardee, ...)` for forward-only swap extensions such as MEVCapture and [Ve33](/products/ve33)                 |
| `signedExclusiveSwap` | A controller-signed swap on a SignedExclusiveSwap pool (pool key, params, signed meta, minimum balance update, and signature) |
| `wrapper`             | Wrapping or unwrapping through an Ekubo token wrapper                                                                         |

Excluded by design, as a security posture:

* No delegatecall routing (the router checks an immutable self address and rejects delegatecall execution)
* No routing through `Core.forward(router, ...)` — the forward callback reverts
* No protocol or integration fee collection and no fee claiming

The router has been [audited](https://github.com/EkuboProtocol/yul-router/blob/v0.6.0/audits/codex-audit-2026-07-06.md), and CI continuously verifies it against production: live mainnet quotes from the Quoter API are converted to calldata with the SDK and executed against canonical Core on a mainnet fork, covering ETH↔ERC20, ERC20↔ERC20, and exact-output swaps.

## The SDK: `@ekubo/yul-router-sdk`

Routes are encoded with [`@ekubo/yul-router-sdk`](https://www.npmjs.com/package/@ekubo/yul-router-sdk) (published with npm provenance from the repository's release workflow):

```sh
npm install @ekubo/yul-router-sdk
```

### Encoding a swap

`encodeRoutes(...)` is the primary surface. Each entry in `multiHops` is an independent path from `specifiedToken` to `calculatedToken` with its own `specifiedAmount`; the router aggregates them all under one lock and one slippage check. Splitting a trade across multiple multi-hops is how split routes execute atomically.

```typescript
import { encodeRoutes, YUL_ROUTER_ADDRESS } from "@ekubo/yul-router-sdk";

const calldata = encodeRoutes({
  // positive specifiedAmount = exact-in; negative = exact-out
  specifiedToken: WETH,
  calculatedToken: USDC,
  // slippage protection: minimum output (exact-in) or maximum input
  // (exact-out). Required — pass `false` only to explicitly opt into an
  // unbounded threshold.
  calculatedAmountThreshold: minUsdcOut,
  recipient, // optional; defaults to the sender
  multiHops: [
    { specifiedAmount: 10n ** 18n, hops: [{ type: "core", poolKey }] },
    // e.g. a second split through a MEVCapture pool:
    // { specifiedAmount: ..., hops: [{ type: "forwarded", forwardee: MEV_CAPTURE, poolKey: otherPoolKey }] },
  ],
});

// send directly to the router — the calldata IS the route
await wallet.sendTransaction({ to: YUL_ROUTER_ADDRESS, data: calldata });
```

Notes:

* Native ETH is `address(0)` (always `token0`); attach `value` to the transaction when the input is native ETH.
* All multi-hops in one call must agree on direction — mixing exact-in and exact-out throws.
* `encodeRoute(...)` is a convenience wrapper for a single path; `generateCalldata(...)` is an alias of `encodeRoutes(...)`.
* Limits: up to 256 multi-hops per call and 256 hops per multi-hop.

### Signed exclusive swaps

For `signedExclusiveSwap` hops, `encodeSignedSwapMeta({ deadline, fee, nonce, authorizedLocker })` packs the signed metadata word. `deadline` and `fee` are `uint32` numbers; `nonce` must be a `bigint` (a JavaScript `number` is rejected so `uint64` nonces cannot lose precision). `encodePoolBalanceUpdate(delta0, delta1)` packs the signed minimum balance update.

### Other exports

* `YUL_ROUTER_ADDRESS` — the deterministic router address
* `MIN_SQRT_RATIO` / `MAX_SQRT_RATIO` — bounds for `sqrtRatioLimit` on hops (see [Price representation](/reference/price-representation))
* `PoolKey`, `Hop`, `MultiHop`, and parameter types for TypeScript consumers
* `calldataSize(data)` — helper for estimating calldata cost

## Typical flow

1. Fetch a quote from the [Quoter API](/reference/quoter-api) — it returns block-pinned split routes in exactly the shape the SDK consumes.
2. Convert each split and hop into `multiHops` entries and call `encodeRoutes(...)` with your slippage threshold.
3. Send the calldata to `YUL_ROUTER_ADDRESS` promptly (quotes are pinned to a block).

To deploy the router to a new chain, use the repository's Foundry deploy script — it deploys through the canonical deterministic deployer against the canonical Core address, so the router lands at the same address everywhere.


# Aggregators

Take advantage of liquidity in Ekubo pools to provide better pricing for users

{% hint style="info" %}
The code samples on this page are written in Cairo for the Starknet deployment. The same lock/callback flow applies on EVM chains — see [Swapping](/integration-guides/swapping) and the [EVM contracts repository](https://github.com/EkuboProtocol/evm-contracts) for Solidity equivalents.
{% endhint %}

### Summary

Ekubo is a singleton AMM that utilizes the "till" pattern. The till pattern was publicly introduced at EthCC\[5] and is also described [here](https://github.com/OpenZeppelin/openzeppelin-contracts/issues/4361#issuecomment-1595095135).

Every interaction with Ekubo starts with `ICore#lock`. In order to interact with Ekubo, you must first implement the `ILocker` interface in your calling contract:

```rust
#[starknet::interface]
trait ICore<TStorage> {
    // ...

    // Main entrypoint for any actions, which must be called before any other pool functions can be called.
    // Other functions must be called within the callback to lock. The ILocker#locked function is called with the input data,
    // and the returned array is passed through to the caller.
    fn lock(ref self: TStorage, data: Array<felt252>) -> Array<felt252>;

    // Make a swap against a pool.
    // You must call this within a lock callback.
    fn swap(ref self: TStorage, pool_key: PoolKey, params: SwapParameters) -> Delta;

    // ...
}

// This interface must be implemented by any contract that intends to call ICore#lock
#[starknet::interface]
trait ILocker<TStorage> {
    // This function is called on the caller of lock, i.e. a callback
    // The input is the data passed to ICore#lock, the output is passed back through as the return value of #lock
    fn locked(ref self: TStorage, id: u32, data: Array<felt252>) -> Array<felt252>;
}
```

You must then perform your swaps within the callback. To know which swaps you need to do, encode your parameters, such as the pools against which you'd like to swap, into the data argument

An example locker might look like this:

```rust
// ILocksCoreExample interface and imports are implied here

#[starknet::contract]
mod Example {
  #[storage]
  struct Storage {
    ekubo: ContractAddress,
  }

  #[derive(Copy, Drop, Serde)]
  struct SwapData {
     // the list of pools that you'd like to swap against, etc.
  }

  #[derive(Copy, Drop, Serde)]
  struct SwapResult {
     // the result of the swap
  }

  #[external(v0)]
  impl LocksCoreExample of ILocksCoreExample<ContractState> {
    fn swap(ref self: ContractState, swap_data: SwapData) -> SwapResult {
      let mut arr: Array<felt252> = ArrayTrait::new();
      Serde::<SwapData>::serialize(@swap_data, ref arr);

      let result = ICoreDispatcher { contract_address: self.ekubo.read() }.lock(arr);

      let mut result_data = result.span();
      let mut result: SwapResult = Serde::<SwapResult>::deserialize(
          ref result_data
      )
          .expect('DESERIALIZE_RESULT_FAILED');

      result
    }

    fn locked(ref self: ContractState, id: u32, data: Array<felt252>) -> Array<felt252> {
      let caller = get_caller_address();
      let ekubo = self.ekubo.read();
      // Only allow Ekubo's core contract to call this method.
      assert(caller == ekubo, 'UNAUTHORIZED_CALLBACK');

      let mut swap_data_span = data.span();
      let mut swap_data: SwapData = Serde::<SwapData>::deserialize(ref swap_data_span)
          .expect('DESERIALIZE_FAILED');

      // Do your swaps here! e.g.:
      // let delta = ICoreDispatcher { contract_address: ekubo }.swap(pool_key, params);

      // Each swap generates a "delta", but does not trigger any token transfers.
      // A negative delta indicates you are owed tokens. A positive delta indicates core owes you tokens.
      // To take a negative delta out of core, do (assuming token0):
      // ICoreDispatcher { contract_address: ekubo }.withdraw(token, recipient, delta.amount0.mag);
      // To pay tokens you owe, do (assuming token1):
      // IERC20Dispatcher {
      //   contract_address: token
      // }.transfer(ekubo, u256 { low: delta.mag, high: 0 });
      // assert(
      //   ICoreDispatcher { contract_address: ekubo }.deposit(token) == delta.amount1.mag,
      //   'DEPOSIT_FAILED'
      // );


      let mut arr: Array<felt252> = ArrayTrait::new();
      Serde::<SwapResult>::serialize(@result, ref arr);
      arr
    }
  }
}
```

{% hint style="info" %}
Instead of withdrawing a delta, you can also save it for use later using `#save` or load it using `#load`.
{% endhint %}

### Locker utility method

You may wish to use this shared code to call core with some calldata and automagically deserialize the result:

```rust
use serde::Serde;
use starknet::{call_contract_syscall, ContractAddress, SyscallResultTrait};
use ekubo::interfaces::core::{ICoreDispatcher, ICoreDispatcherTrait};
use array::{ArrayTrait};
use option::{OptionTrait};

fn call_core_with_callback<
    TInput, impl TSerdeInput: Serde<TInput>, TOutput, impl TSerdeOutput: Serde<TOutput>,
>(
    core: ICoreDispatcher, input: @TInput
) -> TOutput {
    let mut input_data: Array<felt252> = ArrayTrait::new();
    Serde::serialize(input, ref input_data);

    let mut output_span = core.lock(input_data).span();

    Serde::deserialize(ref output_span).expect('DESERIALIZE_RESULT_FAILED')
}
```

### Note on extensions

Extensions are third-party code that can change the result of swapping against a pool, usually by updating liquidity positions before the swap — though an extension may also front-run a swap with one of its own.

There are two ways to handle extensions:

* read the code for the extension and support it by off-chain simulation
* use the quoter to simulate swaps across pools, which always includes extension behavior

With the latter approach you remain exposed to per-block changes in an extension's behavior, but this is no different from exposure to any other front-runner. Always check the resulting output amount against an expected slippage tolerance, protecting users from a misbehaving extension exactly as you would from front-running.


# Signed exclusive swaps

RFQ-style exclusive liquidity on Ekubo: an extension where a controller signs each swap off-chain with its own fee and bounds

`SignedExclusiveSwap` is an [extension](/concepts/extensions) that makes a pool tradeable **only** through off-chain signed quotes. A designated *controller* signs each swap, setting a per-swap fee and bounds on the result. This gives market makers and aggregators an RFQ-style venue that still settles in Ekubo Core, reusing its liquidity math, routing, and [flash accounting](/concepts/architecture).

The extension is forward-only: direct swaps against the pool revert. Every swap must arrive through `Core.forward(...)` carrying a valid signature.

Source: [`SignedExclusiveSwap.sol`](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/extensions/SignedExclusiveSwap.sol) · [integration guide](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/signed-exclusive-swap-extension.md) · [scoped review](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/audits/SignedExclusiveSwap-Extension-Audit.md)

## What the extension enforces

* Direct swaps are blocked; swaps must be forwarded with a signed payload
* Signatures are one-time-use, via a nonce bitmap — with one reserved exception (see [Replay protection](#replay-protection-and-nonce-management))
* A signature can optionally restrict which locker may use it
* The pool's own fee must be zero — all fee logic lives in the signature
* Pools must be initialized through the extension's owner-only `initializePool(...)`
* Signed fees are collected by the extension and donated to LPs on the next block touch

## The signed payload

A forward call decodes five values: the `poolKey`, the swap `params`, a `meta` word, a `minBalanceUpdate` word, and the `signature`.

`SignedSwapMeta` packs four fields into one 256-bit word:

| Field                    | Width    | Meaning                                                      |
| ------------------------ | -------- | ------------------------------------------------------------ |
| `deadline`               | 32 bits  | Signature expiry                                             |
| `fee`                    | 32 bits  | Q32 fee rate applied to this swap                            |
| `nonce`                  | 64 bits  | One-time-use replay protection                               |
| `authorizedLockerLow128` | 128 bits | Lower 128 bits of the permitted locker; `0` means any locker |

The signature is EIP-712 over:

```
SignedSwap(bytes32 poolId,uint256 meta,bytes32 minBalanceUpdate)
```

where `poolId = keccak256(abi.encode(poolKey.token0, poolKey.token1, poolKey.config))`. The domain is `Ekubo SignedExclusiveSwap`, version `1`, bound to the chain ID and the extension address.

With [`@ekubo/yul-router-sdk`](/integration-guides/yul-router), `encodeSignedSwapMeta({ deadline, fee, nonce, authorizedLocker })` packs the meta word and `encodePoolBalanceUpdate(delta0, delta1)` packs the bounds.

## Swap flow

1. The caller holds a Core lock and forwards to the extension.
2. The extension validates the deadline (which must also be no further than 30 days out), the locker authorization, and the signature against the pool's stored controller.
3. If this is the pool's first touch at the current block timestamp, previously collected extension fees are donated to LPs.
4. The extension calls `Core.swap(...)`.
5. The balance update returned by Core is checked component-wise against `minBalanceUpdate`. **This check happens before the fee is applied**, so the bound constrains the raw swap result, not the amount the swapper finally receives.
6. Only now is the nonce consumed — deliberately after the bounds check, so a swap that fails its bounds costs less gas and does not burn the nonce.
7. The signed `fee` is applied — charged on the output for exact-in swaps, on the required input for exact-out — and the fee-adjusted balance update is returned to the caller.
8. The charged fee is credited to the **extension contract's own** saved balance in Core, salted by the pool ID, from which it is later donated to that pool's liquidity providers.

## Why `minBalanceUpdate` matters

`minBalanceUpdate` is a signed lower bound on both components of the final balance update, and it is part of the signed payload. One field gives the signer four protections at once:

* **Direction enforcement** — requiring the expected leg to be positive or negative prevents a fill that moves value the wrong way
* **Slippage tolerance** — the signer can allow a range ("at least X output") rather than an exact result
* **Maximum magnitude** — bounds cap how large a trade can execute under one signature
* **Best-price cap** — because both components are bounded, the signer can also cap how *favorable* a fill may be, avoiding overfilling beyond inventory or risk limits

## Fee donation timing

Collected fees are not donated to LPs immediately. On a pool's first touch at a new block *timestamp* — a swap, a position update, or a fee collection — the extension donates previously collected fees into LP accounting and records the timestamp. This prevents liquidity from being added purely to capture fees earned earlier. Note the gate is the block timestamp, not the block number, so on chains that can produce more than one block per second donation happens at most once per second.

## Replay protection and nonce management

Each signed quote carries a nonce that can be consumed only once, tracked in a bitmap. Managing the lifecycle is the controller's responsibility off-chain: track which nonces were consumed on-chain, which were issued but expired unfilled, and only recycle a nonce when it is safe to do so. The owner can reset nonce state through admin bitmap management when controlled recycling is needed.

{% hint style="warning" %}
The nonce `type(uint64).max` is a **reserved, reusable sentinel**: it is never consumed, so a signature carrying it can be replayed without limit until its deadline passes. Issue it only when unlimited reuse within the deadline is exactly what you intend.
{% endhint %}

## Quote selection risk

Off-chain signed quotes carry selective execution risk: a counterparty can request many quote variants, wait for the market to move, and execute only the profitable ones while letting the rest expire. Common mitigations:

* Use short deadlines so stale quotes lose value quickly
* Bind quotes to an authorized locker or session
* Gate quote access so only qualified users receive exclusive-liquidity quotes
* Apply stricter issuance policies — rate limits, narrower bounds, per-user controls — to higher-risk flow

## Controller management

The extension is `Ownable`. The owner initializes each pool with a controller address via `initializePool(poolKey, tick, controller)`; direct initialization through Core is blocked. The controller for an already-initialized pool can be updated by the owner. Controller signatures work for both EOAs and ERC-1271 contract wallets, and which path is used is determined by **bit 159 of the controller address itself**, not by a separate flag: addresses below `2^159` are verified as EOAs via ECDSA, addresses at or above it via ERC-1271. Initialization and controller updates enforce that the address's code presence matches, so an EOA whose address happens to have bit 159 set cannot be used as a controller.


# Reading pool price

How to interpret pool data from the on-chain methods

How to read a pool's current price on-chain and convert it to a human-readable number. For how prices are encoded (fixed point on Starknet, the compact `SqrtRatio` float type on EVM), see [Price representation](/reference/price-representation).

## EVM (V3)

The simplest way to read a pool price on EVM chains is the `CoreDataFetcher` lens contract (deployed at the same address on every chain — see [EVM Contracts (V3)](/reference/contracts/evm-v3)):

```solidity
// returns the sqrt ratio expanded to 64.128 fixed point, plus the current tick
(uint256 sqrtRatioFixed, int32 tick) = coreDataFetcher.poolPrice(poolKey);
```

The returned value is a 64.128 fixed-point number — identical semantics to Starknet's `sqrt_ratio` below, so the conversion to a human-readable price is the same: divide by `2**128`, square, and adjust for token decimals. (If you read Core's packed pool state directly instead, the price is a 96-bit `SqrtRatio`; call `toFixed()` on it or see [Price representation](/reference/price-representation) for the bit layout.)

Note that on EVM, native ETH is represented as `address(0)`, so ETH is always `token0` of any pool it is in.

## Starknet

Let's say you wanted to determine the human-readable price of the ETH-USDC pool on Starknet mainnet.

These two tokens are:

* **ETH**: `0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7` ([Voyager](https://voyager.online/contract/0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7))
* **USDC**: `0x053c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8` ([Voyager](https://voyager.online/contract/0x053c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8))

First, let's determine which token is `token0` and which token is `token1`. We can do this by comparing the integer values of the addresses. In this case, `0x053c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8 > 0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7`, so USDC is `token1` and ETH is `token0`.

Next, we need to decide which pool to read. Let's use the pool with the 0.05% fee and the 0.1% tick spacing, since it's the most popular ETH/USDC pool. The easiest way to get the fee and tick spacing parameters is to [read the URL](https://ekubo.org/positions/new?baseCurrency=ETH\&quoteCurrency=USDC\&fee=170141183460469235273462165868118016\&tickSpacing=1000\&step=1\&tickLower=-20104000\&tickUpper=-20064000\&initialTick=-20083671) from the website when you add liquidity to this pool:

```
https://ekubo.org/positions/new?baseCurrency=ETH&quoteCurrency=USDC&fee=170141183460469235273462165868118016&tickSpacing=1000
```

The second easiest way is to compute the value. Fee is a 0.128 fixed point number, so to compute the fee, we can do [`floor(0.05% * 2**128)`](https://www.wolframalpha.com/input?i=floor%280.05%25*2**128%29). The result is `170141183460469235273462165868118016`. The tick spacing of `0.01%` is represented as an exponent of `1.000001`, so it can be computed as [`log base 1.000001 of 1.001`](https://www.wolframalpha.com/input?i=log+base+1.000001+of+1.001), which is roughly equal to `1000`. The extension is `0` because it is not used for this pool.

Input the values into the [Core](/reference/contracts/evm-v2) contract on Voyager to read the pool price. If you're following along, you'll get a value that looks like this:

```json
{
    "sqrt_ratio": "0x029895c9cbfca44f2c46e6e9b5459b",
    "tick": {
        "mag": "0x0135566d",
        "sign": "0x01"
    },
    "call_points": {
        "after_initialize_pool": "0x00",
        "before_swap": "0x00",
        "after_swap": "0x00",
        "before_update_position": "0x00",
        "after_update_position": "0x00"
    }
}
```

Let's compute the price from this result. The value `sqrt_ratio` is a `64.128` fixed point number. To convert it to a price, first divide it by `2**128`, then square it to get the price. Since USDC is `token1`, this value is the price of the pool in USDC/ETH. `(0x029895c9cbfca44f2c46e6e9b5459b / 2**128)**2 == 1.56914... ×10^-9`. To adjust for display, we have to account for the decimal difference between the USDC and ETH tokens. Because USDC has 6 decimals and ETH has 18 decimals, we need to scale it up by `10**(18-6)` to be human readable. `1.56914e-9 * 1e12 == 1.56914e3 == 1569.14 USDC/ETH`.


# Pool math

Where to learn the concentrated-liquidity math Ekubo uses, and the parameters that are specific to Ekubo

Ekubo's concentrated liquidity pools use the same underlying math as other concentrated-liquidity AMMs: a constant-product curve where each position is active only within a price range, pool state tracked as a square-root price and a liquidity value, and swaps executed piecewise across regions of constant liquidity.

That math is well documented elsewhere, and the derivations are identical, so this page does not restate them. What it does cover is **the parameters that are specific to Ekubo** — the numbers you need to get right when implementing against it.

## Learn the math

| Reference                                                                                                  | Best for                                                                                                        |
| ---------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| [Uniswap v3 whitepaper](https://app.uniswap.org/whitepaper-v3.pdf)                                         | The canonical statement of the model — sections 6.1–6.3 cover ticks, swapping within a tick, and crossing ticks |
| [Liquidity Math in Uniswap v3](https://atiselsts.github.io/pdfs/uniswap-v3-liquidity-math.pdf), Atis Elsts | The clearest derivation of the position and amount formulas, worked step by step                                |
| [Uniswap v3 Development Book](https://uniswapv3book.com/)                                                  | A build-it-yourself walkthrough, if you learn best from implementation                                          |
| [Concentrated liquidity](https://docs.uniswap.org/concepts/protocol/concentrated-liquidity)                | A short conceptual introduction                                                                                 |

For the concepts in plain language without the formulas, see [Key concepts](/concepts/key-concepts).

## What Ekubo does differently

### Tick base

Ekubo's tick base is **`1.000001`**, not `1.0001`. Tick `i` corresponds to the price `1.000001^i`, so one tick is 1/100th of a basis point — 100 times finer than the more common convention. Every tick-to-price conversion you take from an external reference must use this base.

The tick range is correspondingly wider:

|                  | EVM (V3)    | Starknet    |
| ---------------- | ----------- | ----------- |
| Min / max tick   | ±88,722,835 | ±88,722,883 |
| Max tick spacing | 698,605     | 354,892     |

### Price and sqrt-ratio range

Ekubo supports prices from `2^-128` to `2^128`, so the square root of the price ranges from `2^-64` to `2^64`.

### Representation

The square-root price is stored differently on each deployment: a 128-bit fixed-point number on Starknet, and a compact 96-bit floating-point-style type on EVM. Fees are encoded as binary fractions, with a different denominator on each chain. Both are documented in [Price representation](/reference/price-representation) — read that before implementing any conversion.

### Token decimals

Ekubo performs no decimal adjustment anywhere in its math. If `token0` and `token1` have different decimals, a raw price of `1` corresponds to a human-readable price of `10**token1_decimals / 10**token0_decimals`. See [Reading pool price](/integration-guides/reading-pool-price) for a worked conversion.

### Other pool types

Concentrated liquidity is one of three pool types. **Stableswap** pools concentrate liquidity around a configurable center tick with an amplification factor, and **full-range** pools span the entire price range — the cheapest configuration, and equivalent to a constant-product AMM. See [Providing liquidity](/products/liquidity).

## Reference implementations

Rather than reimplementing the conversions, use an SDK — both handle Starknet and EVM, and both produce exactly the values the contracts use:

* [`ekubo_sdk`](https://crates.io/crates/ekubo_sdk) (Rust) — full quoting across every pool type and extension
* [`@ekubo/sdk`](https://www.npmjs.com/package/@ekubo/sdk) (TypeScript) — tick, price, liquidity, and swap math

See [SDKs](/integration-guides/sdks).


# Price representation

How Ekubo represents pool prices: sqrt ratios, ticks, and the compact floating-point SqrtRatio type used by Ekubo V3 on EVM chains

Every Ekubo pool tracks its current price as a **square root ratio**: the square root of the price of `token1` in terms of `token0`.

$$
sqrt\_ratio = \sqrt{token1/token0}
$$

Working with the square root of the price (rather than the price itself) makes the concentrated liquidity math cheaper and more precise — see the [Math 1-pager](/reference/pool-math) for why. Prices are supported over the enormous range `[2^-128, 2^128]`, so the sqrt ratio always lies in `[2^-64, 2^64]`.

Ticks are the same on every deployment: tick `i` corresponds to the sqrt ratio `sqrt(1.000001^i)`, i.e. each tick is **1/100th of a basis point** of price. The valid tick range is roughly ±88.72 million (`±88,722,835` on EVM, `±88,722,883` on Starknet — the EVM range is very slightly narrower).

How the sqrt ratio is *encoded* differs between the Starknet and EVM deployments.

## Starknet: 64.128 fixed point

On Starknet, `sqrt_ratio` is a `u256` interpreted as a binary fixed-point number with **128 fractional bits**. A sqrt ratio of `1.0` (price of 1) is `2^128`. Since the sqrt ratio never exceeds `2^64`, the value always fits in 192 bits, which lets Core pack the pool price and tick into a single `felt252` storage slot.

* `MIN_SQRT_RATIO = 18446748437148339061` (≈ `2^-64 · 2^128`)
* `MAX_SQRT_RATIO = 6277100250585753475930931601400621808602321654880405518632` (≈ `2^64 · 2^128`)

To convert to a price: divide by `2^128`, then square. See [Reading pool price](/integration-guides/reading-pool-price) for a worked example.

## EVM (V3): the floating-point `SqrtRatio` type

Ekubo V3 on EVM chains introduces a purpose-built **96-bit dynamic fixed-point type** — a compact floating-point-style encoding invented for Ekubo:

```solidity
type SqrtRatio is uint96;
```

### Why a float?

A full-precision 64.128 sqrt ratio needs 192 bits, which doesn't leave room for anything else in a 256-bit storage word. The AMM doesn't actually need 192 bits of precision at every magnitude — it needs *relative* precision. By encoding the sqrt ratio in 96 bits, the entire pool state packs into a **single storage slot**:

```
poolState (bytes32) = sqrtRatio (96 bits) | tick (32 bits) | liquidity (128 bits)
```

This makes updating pool state during a swap dramatically cheaper. The arithmetic on the type is designed so that any rounding error is biased in favor of the pool.

### Bit layout

The 96 bits consist of a **2-bit exponent selector** (bits 95–94) and a **94-bit mantissa** (bits 93–0). The exponent selects how the mantissa is interpreted as a binary fixed-point number:

| Exponent (bits 95–94) | Mantissa interpretation  | Value range covered       |
| --------------------- | ------------------------ | ------------------------- |
| `00`                  | 0.126 (mantissa / 2^126) | sqrt ratios below `2^-32` |
| `01`                  | 0.94 (mantissa / 2^94)   | `2^-32` up to `1`         |
| `10`                  | 32.62 (mantissa / 2^62)  | `1` up to `2^32`          |
| `11`                  | 64.30 (mantissa / 2^30)  | `2^32` up to `2^64`       |

There is no implicit leading bit and no bias — each exponent value simply shifts the radix point by 32 bits. A sqrt ratio of exactly `1.0` is encoded with exponent `10` and mantissa `2^62`:

```solidity
SqrtRatio constant ONE = SqrtRatio.wrap(uint96((1 << 95) + (1 << 62)));
```

Key properties:

* **Monotonic**: a larger raw `uint96` always means a larger sqrt ratio, so comparisons are plain integer comparisons — no decoding required.
* **Normalized**: a valid value always has at least 63 significant mantissa bits (`isValid` requires the mantissa to be ≥ `2^62`, so bit 62 is set, and the raw value to be within the min/max bounds). Effective precision therefore ranges from 63 to 94 significant bits depending on where in the range the value falls — far more than enough for 1/100th-basis-point ticks.
* **Bounded**: `MIN_SQRT_RATIO` (raw `4611797791050542631`) through `MAX_SQRT_RATIO` (raw `79227682466138141934206691491`), covering approximately the same `[2^-64, 2^64]` sqrt ratio range as Starknet — the EVM bounds are very slightly tighter, matching its slightly narrower tick range.

### Converting to and from 64.128

The canonical "expanded" form on EVM is the same 64.128 fixed point used on Starknet. Conversion is a single shift, chosen by the exponent:

```solidity
// SqrtRatio -> 64.128 fixed point (uint256)
function toFixed(SqrtRatio sqrtRatio) pure returns (uint256);
// shift left by 2, 34, 66 or 98 bits for exponents 00, 01, 10, 11

// 64.128 fixed point -> SqrtRatio (rounding toward or away from zero)
function toSqrtRatio(uint256 sqrtRatioFixed, bool roundUp) pure returns (SqrtRatio);
```

Both directions round-trip losslessly for any valid `SqrtRatio`. To compute a human-readable price from the 64.128 value: divide by `2^128`, square, and adjust for the two tokens' decimals (worked example in [Reading pool price](/integration-guides/reading-pool-price)).

### Where you'll encounter it

* **Swap parameters**: `sqrtRatioLimit` in swap params and router `RouteNode`s is a `SqrtRatio` (a value of `0` means "no limit").
* **Pool state and events**: `PoolInitialized` emits the initial `SqrtRatio`; extension hooks (`afterSwap`, `afterUpdatePosition`, ...) receive the packed pool state containing it.
* **Reading prices**: `CoreDataFetcher.poolPrice()` returns the price already expanded to 64.128 via `toFixed()`, so most integrators never need to decode the packed form manually. Its `poolState()` returns the packed `SqrtRatio` undecoded.
* **SDKs**: [`@ekubo/sdk`](https://www.npmjs.com/package/@ekubo/sdk) (TypeScript) and the [Rust SDK](https://github.com/EkuboProtocol/rust-sdk) implement the exact tick and sqrt-ratio conversions for both Starknet and EVM.

## Fee encoding differs too

While ticks are chain-agnostic, the **pool fee** encoding differs between deployments:

| Deployment | Fee type                   | Denominator | Example: 0.3%                           |
| ---------- | -------------------------- | ----------- | --------------------------------------- |
| Starknet   | `u128`, 0.128 fixed point  | `2^128`     | `1020847100762815411640772995208708096` |
| EVM (V3)   | `uint64`, 0.64 fixed point | `2^64`      | `55340232221128654`                     |

In both cases the fee is a binary fraction of the full width — `fee / 2^128` (Starknet) or `fee / 2^64` (EVM) — applied to the swap input amount, rounded up in favor of the pool.


# Contract addresses

Addresses of all deployed Ekubo Protocol contracts

Deployed contract addresses by deployment:

* [EVM (V3)](/reference/contracts/evm-v3) — the current EVM deployment: same deterministic addresses on every supported chain
* [Starknet](/reference/contracts/starknet) — the original Ekubo deployment on Starknet
* [Governance](/reference/contracts/governance) — the EKUBO token, Staker, and Governor contracts across Ethereum and Starknet
* [EVM (V2, deprecated)](/reference/contracts/evm-v2) — the deprecated Ethereum-only V2 deployment


# EVM (V3)

Architecture and deployed contracts for Ekubo Protocol V3 on EVM chains

Ekubo Protocol V3 is the open-source EVM deployment of Ekubo. The source code lives at [EkuboProtocol/evm-contracts](https://github.com/EkuboProtocol/evm-contracts) and is licensed under the [Ekubo DAO Shared Revenue License](https://ekubo-license-v1.eth.link/). The authoritative deployment table and ABIs are published with each [GitHub release](https://github.com/EkuboProtocol/evm-contracts/releases/latest) — the tables below reflect release `v3.2.0`.

## Architecture

* **Core** is an ownerless, permissionless singleton holding all pools and tokens, using [flash accounting](/concepts/key-concepts#flash-accounting) and the ["till" pattern](/concepts/architecture). All interactions start with `lock()`; Core calls back into your contract, which performs swaps and position updates and settles net balances at the end. Native ETH is supported directly as `address(0)` (so ETH is always `token0`).
* **Pool types** — a pool's configuration is packed into a single word (`PoolConfig`: extension, fee, and pool-type parameters):
  * **Concentrated liquidity** — tick-spacing-parameterized, as on Starknet
  * **Stableswap** — liquidity concentrated around a configurable center tick with an amplification factor
  * **Full range** — a zero-amplification stableswap pool; the cheapest option
* **Fees** are a `uint64` binary fraction of `2^64` (e.g. 0.3% = `55340232221128654`). See [Price representation](/reference/price-representation) for fee and price encodings.
* **Extensions** customize pool behavior at eight lifecycle call points. On EVM, an extension's call points are **encoded in the top byte of its own address** (extensions are deployed by mining an address with the right bits).
* **Periphery** (Positions, Orders, Router, Incentives, ...) is where protocol fees are applied — Core itself takes no fees. The canonical Positions deployment applies a 10% protocol fee to collected swap fees (for the Ekubo DAO) and no withdrawal fee.

For the design rationale, read the [V3 whitepaper](/about-ekubo/v3-whitepaper).

## Shared deterministic deployments

These contracts are deployed at the **same address on every supported chain** (Ethereum, Base, Arbitrum, MegaETH, Monad, Optimism, Ink, Robinhood Chain, Unichain, Polygon, and BNB Smart Chain). Anyone can deploy them to another compatible network with the [DeployAll script](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/script/DeployAll.s.sol). Note: chains must support [EIP-7939](https://eips.ethereum.org/EIPS/eip-7939) (`CLZ`) for the contracts to function.

### Core and extensions

| Contract                                                                                                                | Address (every chain)                        | Description                                                                                |
| ----------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------ |
| [Core](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/Core.sol)                                         | `0x00000000000014aA86C5d3c41765bb24e11bd701` | The singleton AMM: all pools, positions, and token custody                                 |
| [MEVCapture](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/extensions/MEVCapture.sol)                  | `0x5555fF9Ff2757500BF4EE020DcfD0210CFfa41Be` | Extension charging extra fees on price-moving swaps, directing that value to LPs           |
| [Oracle](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/extensions/Oracle.sol)                          | `0x517E506700271AEa091b02f42756F5E174Af5230` | Extension recording on-chain price history for token pairs (vs native ETH)                 |
| [TWAMM](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/extensions/TWAMM.sol)                            | `0xd47f1B1eDCfEaBb08F6eBd8FC337c27E636C75BA` | Time-weighted AMM extension powering [DCA orders](/user-guides/dollar-cost-average-orders) |
| [BoostedFees (concentrated)](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/extensions/BoostedFees.sol) | `0xd4B54d0ca6979Da05F25895E6e269E678ba00f9e` | Extension streaming boosted fee rewards to concentrated-liquidity pools                    |
| [BoostedFees (stableswap)](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/extensions/BoostedFees.sol)   | `0x948b9C2C99718034954110cB61a6e08e107745f9` | Same, for stableswap pools                                                                 |
| [ManualPoolBooster](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/ManualPoolBooster.sol)               | `0xddb1758118F65e13a91497015B8cB26801402761` | Permissionless funding of boosted-fee campaigns for specific pools                         |
| [Incentives](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/Incentives.sol)                             | `0xC52D2656cb8C634263E6A15469588beB9C3Bb738` | Distributes liquidity incentive campaigns                                                  |
| [TokenWrapperFactory](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/TokenWrapperFactory.sol)           | `0xAA166592922C4020cEfA23448054AD070211790a` | Wraps non-standard tokens for safe use with Ekubo                                          |
| [MEVCaptureRouter (legacy)](https://github.com/EkuboProtocol/evm-contracts/blob/v3.1.1/src/MEVCaptureRouter.sol)        | `0xd26f20001a72a18C002b00e6710000d68700ce00` | Legacy `v3.1.1` router for MEVCapture pools (superseded by the configurable Router)        |

### Lens (read-only data fetchers)

| Contract                                                                                                                 | Address (every chain)                        | Description                                                                             |
| ------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------- | --------------------------------------------------------------------------------------- |
| [CoreDataFetcher](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/lens/CoreDataFetcher.sol)               | `0xF68F25CA6C817733b7B15a42191AE72A34d56a2B` | Pool state, [prices](/integration-guides/reading-pool-price), positions, saved balances |
| [QuoteDataFetcher](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/lens/QuoteDataFetcher.sol)             | `0x5a3F0F1dA4Ac0c4b937d5685f330704c8e8303f1` | Batched data for computing swap quotes                                                  |
| [TWAMMDataFetcher](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/lens/TWAMMDataFetcher.sol)             | `0xDEFe25E56a7891CC4c0E1401879f3dC81F1Cc4A6` | DCA order and TWAMM state                                                               |
| [IncentivesDataFetcher](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/lens/IncentivesDataFetcher.sol)   | `0x69F9eCfa84CF0C41bE9F68b557b07b6b89d71eD0` | Incentive campaign state                                                                |
| [PriceFetcher](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/lens/PriceFetcher.sol)                     | `0xFE0Aa09c1CC2bA299b3AaFA52716bE00f40F1D6d` | Oracle price reads for many tokens at once                                              |
| [TokenDataFetcher](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/lens/TokenDataFetcher.sol)             | `0x305Cf9A34dCb265522780D1D64544d3f7C450407` | Token metadata and balances                                                             |
| [BoostedFeesDataFetcher](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/lens/BoostedFeesDataFetcher.sol) | `0x50DabB063ab0B62a33abf49f1357f0981cec241F` | Boosted-fees campaign state                                                             |

### Position and order managers

Deployed on Ethereum, Base, Arbitrum, MegaETH, and Monad. The Positions deployment charges a 10% swap protocol fee for the Ekubo DAO (and no withdrawal fee).

| Contract                                                                                  | Address                                      | Description                                                                                                     |
| ----------------------------------------------------------------------------------------- | -------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| [Positions](https://github.com/EkuboProtocol/evm-contracts/blob/v3.1.1/src/Positions.sol) | `0x02D9876A21AF7545f8632C3af76eC90b5ad4b66D` | ERC-721 manager for [liquidity positions](/user-guides/add-liquidity) (`v3.1.1` deployment — remains canonical) |
| [Orders](https://github.com/EkuboProtocol/evm-contracts/blob/v3.1.1/src/Orders.sol)       | `0x3325428adB409c239E88ca472F50b0efe00E98B4` | ERC-721 manager for [DCA orders](/user-guides/dollar-cost-average-orders) (`v3.1.1` deployment)                 |

### Deployment-specific contracts

Contracts added or reworked in `v3.2.0` are configuration-specific and have no universal address — their addresses are per deployment:

* [Ve33](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/extensions/Ve33.sol), [VeToken](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/VeToken.sol), [Ve33Positions](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/Ve33Positions.sol), Ve33Periphery, Ve33EmissionRateScheduler, and [Ve33DataFetcher](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/lens/Ve33DataFetcher.sol) — the [Ve33](/products/ve33) token-governed liquidity system (each instance is deployed around its own stake token, e.g. STONX on Robinhood Chain)
* [SignedExclusiveSwap](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/extensions/SignedExclusiveSwap.sol) — extension for controller-signed EIP-712 swaps ([integration guide](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/signed-exclusive-swap-extension.md))
* [Router](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/Router.sol) — configurable router executing Core swaps and forwarding to MEVCapture / Ve33 pools
* [PoolKeyIndex](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/PoolKeyIndex.sol) — optional registry for discovering initialized pool keys by pool ID, token, or extension
* [Auctions](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/Auctions.sol) — on-chain auctions ([whitepaper](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/auctions-whitepaper.md))
* [MintableERC20](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/src/MintableERC20.sol) — owner-mintable token used by deployments that need one (e.g. a Ve33 stake token)

Audit reports for the EVM contracts are linked on the [Audits](/reference/audits) page.


# Starknet

Addresses of the deployed contracts on Starknet

## Starknet

The source code for the smart contracts is available [here](https://github.com/EkuboProtocol/starknet-contracts) and it can be used as a scarb package.

### Upgradeable contracts

The addresses of these contracts do not change. The contracts are updated to new versions in-place. These contracts are owned by the governance contracts.

<table><thead><tr><th width="197.33333333333331">Contract / Network</th><th>Sepolia</th><th>Mainnet</th><th data-hidden>Goerli (Deprecated)</th></tr></thead><tbody><tr><td>Core</td><td><code>0x0444a09d96389aa7148f1aada508e30b71299ffe650d9c97fdaae38cb9a23384</code><br><a href="https://sepolia.voyager.online/contract/0x0444a09d96389aa7148f1aada508e30b71299ffe650d9c97fdaae38cb9a23384">Voyager</a> <a href="https://sepolia.starkscan.co/contract/0x0444a09d96389aa7148f1aada508e30b71299ffe650d9c97fdaae38cb9a23384">Starkscan</a></td><td><code>0x00000005dd3D2F4429AF886cD1a3b08289DBcEa99A294197E9eB43b0e0325b4b</code><br><a href="https://voyager.online/contract/0x00000005dd3d2f4429af886cd1a3b08289dbcea99a294197e9eb43b0e0325b4b">Voyager</a> <a href="https://starkscan.co/contract/0x00000005dd3D2F4429AF886cD1a3b08289DBcEa99A294197E9eB43b0e0325b4b">Starkscan</a></td><td><code>0x031e8a7ab6a6a556548ac85cbb8b5f56e8905696e9f13e9a858142b8ee0cc221</code><br><a href="https://goerli.voyager.online/contract/0x031e8a7ab6a6a556548ac85cbb8b5f56e8905696e9f13e9a858142b8ee0cc221">Voyager</a> <a href="https://testnet.starkscan.co/contract/0x031e8a7ab6a6a556548ac85cbb8b5f56e8905696e9f13e9a858142b8ee0cc221">Starkscan</a></td></tr><tr><td>Positions</td><td><code>0x06a2aee84bb0ed5dded4384ddd0e40e9c1372b818668375ab8e3ec08807417e5</code><br><a href="https://sepolia.voyager.online/contract/0x06a2aee84bb0ed5dded4384ddd0e40e9c1372b818668375ab8e3ec08807417e5">Voyager</a> <a href="https://sepolia.starkscan.co/contract/0x06a2aee84bb0ed5dded4384ddd0e40e9c1372b818668375ab8e3ec08807417e5">Starkscan</a></td><td><code>0x02e0af29598b407c8716b17f6d2795eca1b471413fa03fb145a5e33722184067</code><br><a href="https://voyager.online/contract/0x02e0af29598b407c8716b17f6d2795eca1b471413fa03fb145a5e33722184067">Voyager</a> <a href="https://starkscan.co/contract/0x02e0af29598b407c8716b17f6d2795eca1b471413fa03fb145a5e33722184067">Starkscan</a></td><td><code>0x073fa8432bf59f8ed535f29acfd89a7020758bda7be509e00dfed8a9fde12ddc</code><br><a href="https://goerli.voyager.online/contract/0x073fa8432bf59f8ed535f29acfd89a7020758bda7be509e00dfed8a9fde12ddc">Voyager</a> <a href="https://testnet.starkscan.co/contract/0x073fa8432bf59f8ed535f29acfd89a7020758bda7be509e00dfed8a9fde12ddc">Starkscan</a></td></tr><tr><td>Positions NFT</td><td><code>0x04afc78d6fec3b122fc1f60276f074e557749df1a77a93416451be72c435120f</code><br><a href="https://sepolia.voyager.online/contract/0x04afc78d6fec3b122fc1f60276f074e557749df1a77a93416451be72c435120f">Voyager</a> <a href="https://sepolia.starkscan.co/contract/0x04afc78d6fec3b122fc1f60276f074e557749df1a77a93416451be72c435120f">Starkscan</a></td><td><code>0x07b696af58c967c1b14c9dde0ace001720635a660a8e90c565ea459345318b30</code><br><a href="https://voyager.online/contract/0x07b696af58c967c1b14c9dde0ace001720635a660a8e90c565ea459345318b30">Voyager</a> <a href="https://starkscan.co/contract/0x07b696af58c967c1b14c9dde0ace001720635a660a8e90c565ea459345318b30">Starkscan</a></td><td><code>0x01090e3cfd9990c396f246cd1d5c7fb091905cba9f99739653db1f2960a3311f</code><br><a href="https://goerli.voyager.online/contract/0x01090e3cfd9990c396f246cd1d5c7fb091905cba9f99739653db1f2960a3311f">Voyager</a> <a href="https://testnet.starkscan.co/contract/0x01090e3cfd9990c396f246cd1d5c7fb091905cba9f99739653db1f2960a3311f">Starkscan</a></td></tr><tr><td>TWAMM Extension</td><td><code>0x073ec792c33b52d5f96940c2860d512b3884f2127d25e023eb9d44a678e4b971</code><br><a href="https://sepolia.voyager.online/contract/0x073ec792c33b52d5f96940c2860d512b3884f2127d25e023eb9d44a678e4b971">Voyager</a> <a href="https://sepolia.starkscan.co/contract/0x073ec792c33b52d5f96940c2860d512b3884f2127d25e023eb9d44a678e4b971">Starkscan</a></td><td><code>0x043e4f09c32d13d43a880e85f69f7de93ceda62d6cf2581a582c6db635548fdc</code><br><a href="https://voyager.online/contract/0x043e4f09c32d13d43a880e85f69f7de93ceda62d6cf2581a582c6db635548fdc">Voyager</a> <a href="https://starkscan.co/contract/0x043e4f09c32d13d43a880e85f69f7de93ceda62d6cf2581a582c6db635548fdc">Starkscan</a></td><td></td></tr><tr><td>Oracle Extension</td><td><code>0x003ccf3ee24638dd5f1a51ceb783e120695f53893f6fd947cc2dcabb3f86dc65</code><br><a href="https://sepolia.voyager.online/contract/0x003ccf3ee24638dd5f1a51ceb783e120695f53893f6fd947cc2dcabb3f86dc65">Voyager</a> <a href="https://sepolia.starkscan.co/contract/0x003ccf3ee24638dd5f1a51ceb783e120695f53893f6fd947cc2dcabb3f86dc65">Starkscan</a></td><td><code>0x005e470ff654d834983a46b8f29dfa99963d5044b993cb7b9c92243a69dab38f</code><br><a href="https://voyager.online/contract/0x005e470ff654d834983a46b8f29dfa99963d5044b993cb7b9c92243a69dab38f">Voyager</a> <a href="https://starkscan.co/contract/0x005e470ff654d834983a46b8f29dfa99963d5044b993cb7b9c92243a69dab38f">Starkscan</a></td><td></td></tr><tr><td>Limit Orders Extension</td><td><code>0x00c4c863f6de467b91ce974be48cc17ad7209d0d600926e82845a43a7848b822</code><br><a href="https://sepolia.voyager.online/contract/0x00c4c863f6de467b91ce974be48cc17ad7209d0d600926e82845a43a7848b822">Voyager</a> <a href="https://sepolia.starkscan.co/contract/0x00c4c863f6de467b91ce974be48cc17ad7209d0d600926e82845a43a7848b822">Starkscan</a></td><td><code>0x050ed6ab03aef492cd062e25facf40ceef63294c53d12b514226f8fb4753266e</code><br><a href="https://voyager.online/contract/0x050ed6ab03aef492cd062e25facf40ceef63294c53d12b514226f8fb4753266e">Voyager</a> <a href="https://starkscan.co/contract/0x050ed6ab03aef492cd062e25facf40ceef63294c53d12b514226f8fb4753266e">Starkscan</a></td><td></td></tr></tbody></table>

### Immutable contracts

These are the addresses of the latest versions of the contracts. They may change, but the old versions will remain. We do not upgrade these contracts because it is often the case that old versions still work fine with core. Prefer to use the latest version whenever possible.

<table><thead><tr><th width="190">Contract / Network</th><th>Sepolia</th><th>Mainnet</th><th data-hidden>Goerli (Deprecated)</th></tr></thead><tbody><tr><td>Router V3.0.13</td><td><code>0x0045f933adf0607292468ad1c1dedaa74d5ad166392590e72676a34d01d7b763</code><br><a href="https://sepolia.voyager.online/contract/0x0045f933adf0607292468ad1c1dedaa74d5ad166392590e72676a34d01d7b763">Voyager</a> <a href="https://sepolia.starkscan.co/contract/0x0045f933adf0607292468ad1c1dedaa74d5ad166392590e72676a34d01d7b763">Starkscan</a></td><td><code>0x0199741822c2dc722f6f605204f35e56dbc23bceed54818168c4c49e4fb8737e</code><br><a href="https://voyager.online/contract/0x0199741822c2dc722f6f605204f35e56dbc23bceed54818168c4c49e4fb8737e">Voyager</a> <a href="https://starkscan.co/contract/0x0199741822c2dc722f6f605204f35e56dbc23bceed54818168c4c49e4fb8737e">Starkscan</a></td><td></td></tr><tr><td>Router V3.0.3</td><td><code>0x050d4da9f66589eadaa1d5e31cf73b08ac1a67c8b4dcd88e6fd4fe501c628af2</code><br><a href="https://sepolia.voyager.online/contract/0x050d4da9f66589eadaa1d5e31cf73b08ac1a67c8b4dcd88e6fd4fe501c628af2">Voyager</a> <a href="https://sepolia.starkscan.co/contract/0x050d4da9f66589eadaa1d5e31cf73b08ac1a67c8b4dcd88e6fd4fe501c628af2">Starkscan</a></td><td><code>0x04505a9f06f2bd639b6601f37a4dc0908bb70e8e0e0c34b1220827d64f4fc066</code><br><a href="https://voyager.online/contract/0x04505a9f06f2bd639b6601f37a4dc0908bb70e8e0e0c34b1220827d64f4fc066">Voyager</a> <a href="https://starkscan.co/contract/0x04505a9f06f2bd639b6601f37a4dc0908bb70e8e0e0c34b1220827d64f4fc066">Starkscan</a></td><td><code>0x014d923c16ce183496053eba0b33062f0c60a793080e4f3ca90e11638ad149df</code><br><a href="https://goerli.voyager.online/contract/0x014d923c16ce183496053eba0b33062f0c60a793080e4f3ca90e11638ad149df">Voyager</a> <a href="https://testnet.starkscan.co/contract/0x014d923c16ce183496053eba0b33062f0c60a793080e4f3ca90e11638ad149df">Starkscan</a></td></tr><tr><td>Router V2.0.1</td><td><code>0x02bcf6ff92070beb7ed9a022744a659666e87830b591a8b375af4370eeadaa5b</code><br><a href="https://sepolia.voyager.online/contract/0x02bcf6ff92070beb7ed9a022744a659666e87830b591a8b375af4370eeadaa5b">Voyager</a> <a href="https://sepolia.starkscan.co/contract/0x02bcf6ff92070beb7ed9a022744a659666e87830b591a8b375af4370eeadaa5b">Starkscan</a></td><td><code>0x03266fe47923e1500aec0fa973df8093b5850bbce8dcd0666d3f47298b4b806e</code><br><a href="https://voyager.online/contract/0x03266fe47923e1500aec0fa973df8093b5850bbce8dcd0666d3f47298b4b806e">Voyager</a> <a href="https://starkscan.co/contract/0x03266fe47923e1500aec0fa973df8093b5850bbce8dcd0666d3f47298b4b806e">Starkscan</a></td><td><code>0x037d4ba7ea2cd87993f2c0e38080ed442c740c536ae2861a61ac6ff8228964aa</code><br><a href="https://goerli.voyager.online/contract/0x037d4ba7ea2cd87993f2c0e38080ed442c740c536ae2861a61ac6ff8228964aa">Voyager</a> <a href="https://testnet.starkscan.co/contract/0x037d4ba7ea2cd87993f2c0e38080ed442c740c536ae2861a61ac6ff8228964aa">Starkscan</a></td></tr><tr><td>Router V2</td><td><code>0x04576498a328e5b44f88da12204b6138028643ee4c8c0146cfe3c3de7a22f783</code><br><a href="https://sepolia.voyager.online/contract/0x04576498a328e5b44f88da12204b6138028643ee4c8c0146cfe3c3de7a22f783?lang=en-US&#x26;theme=light">Voyager</a> <a href="https://sepolia.starkscan.co/contract/0x04576498a328e5b44f88da12204b6138028643ee4c8c0146cfe3c3de7a22f783">Starkscan</a></td><td><code>0x010c7eb57cbfeb18bde525912c1b6e9a7ebb4f692e0576af1ba7be8b3b9a70f6</code><br><a href="https://voyager.online/contract/0x010c7eb57cbfeb18bde525912c1b6e9a7ebb4f692e0576af1ba7be8b3b9a70f6#code">Voyager</a> <a href="https://starkscan.co/contract/0x010c7eb57cbfeb18bde525912c1b6e9a7ebb4f692e0576af1ba7be8b3b9a70f6">Starkscan</a></td><td><code>0x0384211022228b84eda3a07336b68e83618ee658ad92ff7b176a9e4958b1ae51</code><br><a href="https://goerli.voyager.online/contract/0x0384211022228b84eda3a07336b68e83618ee658ad92ff7b176a9e4958b1ae51#code">Voyager</a> <a href="https://testnet.starkscan.co/contract/0x0384211022228b84eda3a07336b68e83618ee658ad92ff7b176a9e4958b1ae51">Starkscan</a></td></tr><tr><td>Router</td><td><code>0x02a3328b7f0f83c3ca23149623b6cee9e7fddb5591d36d52c77d2a9302d221d0</code><br><a href="https://sepolia.voyager.online/contract/0x02a3328b7f0f83c3ca23149623b6cee9e7fddb5591d36d52c77d2a9302d221d0">Voyager</a> <a href="https://sepolia.starkscan.co/contract/0x02a3328b7f0f83c3ca23149623b6cee9e7fddb5591d36d52c77d2a9302d221d0">Starkscan</a></td><td><code>0x01b6f560def289b32e2a7b0920909615531a4d9d5636ca509045843559dc23d5</code><br><a href="https://voyager.online/contract/0x01b6f560def289b32e2a7b0920909615531a4d9d5636ca509045843559dc23d5#writeContract">Voyager</a> <a href="https://starkscan.co/contract/0x01b6f560def289b32e2a7b0920909615531a4d9d5636ca509045843559dc23d5">Starkscan</a></td><td><code>0x04c95177eb2aee798d901c34d825715136d5dd33c5cbeff1930e52f8b74ce3c3</code><br><a href="https://goerli.voyager.online/contract/0x04c95177eb2aee798d901c34d825715136d5dd33c5cbeff1930e52f8b74ce3c3">Voyager</a> <a href="https://testnet.starkscan.co/contract/0x04c95177eb2aee798d901c34d825715136d5dd33c5cbeff1930e52f8b74ce3c3">Starkscan</a></td></tr><tr><td>Token Registry V3 (supports ByteArray)</td><td><code>0x04484f91f0d2482bad844471ca8dc8e846d3a0211792322e72f21f0f44be63e5</code><br><a href="https://sepolia.voyager.online/contract/0x04484f91f0d2482bad844471ca8dc8e846d3a0211792322e72f21f0f44be63e5">Voyager</a> <a href="https://sepolia.starkscan.co/contract/0x04484f91f0d2482bad844471ca8dc8e846d3a0211792322e72f21f0f44be63e5">Starkscan</a></td><td><code>0x064bdb4094881140bc39340146c5fcc5a187a98aec5a53f448ac702e5de5067e</code><br><a href="https://voyager.online/contract/0x064bdb4094881140bc39340146c5fcc5a187a98aec5a53f448ac702e5de5067e">Voyager</a> <a href="https://starkscan.co/contract/0x064bdb4094881140bc39340146c5fcc5a187a98aec5a53f448ac702e5de5067e">Starkscan</a></td><td></td></tr><tr><td>Token Registry V2</td><td><code>0x0293c0d738eff5aa65619069d437eba2bcb320fb8003f37c9708afcdbe8739c8</code><br><a href="https://sepolia.voyager.online/contract/0x0293c0d738eff5aa65619069d437eba2bcb320fb8003f37c9708afcdbe8739c8">Voyager</a> <a href="https://sepolia.starkscan.co/contract/0x0293c0d738eff5aa65619069d437eba2bcb320fb8003f37c9708afcdbe8739c8">Starkscan</a></td><td><code>0x0013e25867b6eef62703735aa4cfa7754e72f4e94a56c9d3d9ad8ebe86cee4aa</code><br><a href="https://voyager.online/contract/0x0013e25867b6eef62703735aa4cfa7754e72f4e94a56c9d3d9ad8ebe86cee4aa">Voyager</a> <a href="https://starkscan.co/contract/0x0013e25867b6eef62703735aa4cfa7754e72f4e94a56c9d3d9ad8ebe86cee4aa">Starkscan</a></td><td><code>0x0205b51a3ca54e718dac5f8424c7e67b6d169af5255316ff65be388f0a7e2eaf</code><br><a href="https://goerli.voyager.online/contract/0x0205b51a3ca54e718dac5f8424c7e67b6d169af5255316ff65be388f0a7e2eaf">Voyager</a> <a href="https://testnet.starkscan.co/contract/0x0205b51a3ca54e718dac5f8424c7e67b6d169af5255316ff65be388f0a7e2eaf">Starkscan</a></td></tr><tr><td>Token Registry V1 (Legacy)</td><td>N/A</td><td><code>0x006f55e718ae592b22117c3e3b557b6b2b5f827ddcd7e6fdebd1a4ce7462c93e</code><br><a href="https://voyager.online/contract/0x006f55e718ae592b22117c3e3b557b6b2b5f827ddcd7e6fdebd1a4ce7462c93e">Voyager</a> <a href="https://starkscan.co/contract/0x006f55e718ae592b22117c3e3b557b6b2b5f827ddcd7e6fdebd1a4ce7462c93e">Starkscan</a></td><td><code>0x0016da7891c07b6338d1126cb9181f36376775c57029c424063b4fc403bc720a</code><br><a href="https://goerli.voyager.online/contract/0x0016da7891c07b6338d1126cb9181f36376775c57029c424063b4fc403bc720a">Voyager</a> <a href="https://testnet.starkscan.co/contract/0x0016da7891c07b6338d1126cb9181f36376775c57029c424063b4fc403bc720a">Starkscan</a></td></tr><tr><td>Revenue buybacks (v5.0.3, current)</td><td>N/A</td><td><code>0x03d921193ccdc888785892d0677cf0f8eb98f4a87abcb60eee61e2874e687541</code><br><a href="https://voyager.online/contract/0x03d921193ccdc888785892d0677cf0f8eb98f4a87abcb60eee61e2874e687541">Voyager</a> <a href="https://starkscan.co/contract/0x03d921193ccdc888785892d0677cf0f8eb98f4a87abcb60eee61e2874e687541">Starkscan</a><br>Sources protocol fees from Positions (fixed 20% of collected position fees) and sells them for EKUBO via TWAMM.</td><td></td></tr><tr><td>Revenue buybacks v1.0.0 (legacy)</td><td><code>0x03e65ebb45d47e0874fba9a41af0ba5df625bb7e67952ce7e567f40da52c509c</code><br><a href="https://sepolia.voyager.online/contract/0x03e65ebb45d47e0874fba9a41af0ba5df625bb7e67952ce7e567f40da52c509c">Voyager</a> <a href="https://sepolia.starkscan.co/contract/0x03e65ebb45d47e0874fba9a41af0ba5df625bb7e67952ce7e567f40da52c509c">Starkscan</a></td><td><code>0x00f2e9a400ba65b13255ef2792612b45d5a20a7a7cf211ffb3f485445022ef72</code><br><a href="https://voyager.online/contract/0x00f2e9a400ba65b13255ef2792612b45d5a20a7a7cf211ffb3f485445022ef72">Voyager</a> <a href="https://starkscan.co/contract/0x00f2e9a400ba65b13255ef2792612b45d5a20a7a7cf211ffb3f485445022ef72">Starkscan</a></td><td></td></tr><tr><td>Price Fetcher</td><td><code>0x04613bee55d8a37adfa249b24c6b13451dedf7cf4f02d01de859579119de3add</code><br><a href="https://sepolia.voyager.online/contract/0x04613bee55d8a37adfa249b24c6b13451dedf7cf4f02d01de859579119de3add">Voyager</a> <a href="https://sepolia.starkscan.co/contract/0x04613bee55d8a37adfa249b24c6b13451dedf7cf4f02d01de859579119de3add">Starkscan</a></td><td><code>0x04946fb4ad5237d97bbb1256eba2080c4fe1de156da6a7f83e3b4823bb6d7da1</code><br><a href="https://voyager.online/contract/0x04946fb4ad5237d97bbb1256eba2080c4fe1de156da6a7f83e3b4823bb6d7da1">Voyager</a> <a href="https://starkscan.co/contract/0x04946fb4ad5237d97bbb1256eba2080c4fe1de156da6a7f83e3b4823bb6d7da1">Starkscan</a></td><td></td></tr><tr><td></td><td></td><td></td><td></td></tr></tbody></table>


# Governance

Addresses of the governance contracts across Starknet and Ethereum

### Governance contracts

These contracts are responsible for the management of the core contracts. The source code is [here](https://github.com/EkuboProtocol/governance). Note the EKUBO token used in governance is deployed to Ethereum and bridged to Starknet via Starkgate. The code for the EKUBO token is [here](https://github.com/EkuboProtocol/token).

<table><thead><tr><th width="199">Contract / Network</th><th>Sepolia</th><th>Mainnet</th></tr></thead><tbody><tr><td>EKUBO (Ethereum)</td><td><code>0x618C25b11a5e9B5Ad60B04bb64FcBdfBad7621d1</code><br><a href="https://sepolia.etherscan.io/address/0x618C25b11a5e9B5Ad60B04bb64FcBdfBad7621d1">Etherscan</a></td><td><code>0x04C46E830Bb56ce22735d5d8Fc9CB90309317d0f</code><br><a href="https://etherscan.io/address/0x04c46e830bb56ce22735d5d8fc9cb90309317d0f">Etherscan</a></td></tr><tr><td>EKUBO</td><td><code>0x01fad7c03b2ea7fbef306764e20977f8d4eae6191b3a54e4514cc5fc9d19e569</code><br><a href="https://sepolia.voyager.online/contract/0x01fad7c03b2ea7fbef306764e20977f8d4eae6191b3a54e4514cc5fc9d19e569">Voyager</a> <a href="https://sepolia.starkscan.co/contract/0x01fad7c03b2ea7fbef306764e20977f8d4eae6191b3a54e4514cc5fc9d19e569">Starkscan</a></td><td><code>0x075afe6402ad5a5c20dd25e10ec3b3986acaa647b77e4ae24b0cbc9a54a27a87</code><br><a href="https://voyager.online/contract/0x075afe6402ad5a5c20dd25e10ec3b3986acaa647b77e4ae24b0cbc9a54a27a87">Voyager</a> <a href="https://starkscan.co/contract/0x075afe6402ad5a5c20dd25e10ec3b3986acaa647b77e4ae24b0cbc9a54a27a87">Starkscan</a></td></tr><tr><td>Staker</td><td><code>0x0359c9a32c758a162a6c59a9cff93f09a2eb2dbe82cba9ecbdd64aa4314f8687</code><br><a href="https://sepolia.voyager.online/contract/0x0359c9a32c758a162a6c59a9cff93f09a2eb2dbe82cba9ecbdd64aa4314f8687">Voyager</a> <a href="https://sepolia.starkscan.co/contract/0x0359c9a32c758a162a6c59a9cff93f09a2eb2dbe82cba9ecbdd64aa4314f8687">Starkscan</a></td><td><code>0x02a3ed03046e1042e193651e3da6d3c973e3d45c624442be936a374380a78bb5</code><br><a href="https://voyager.online/contract/0x02a3ed03046e1042e193651e3da6d3c973e3d45c624442be936a374380a78bb5">Voyager</a> <a href="https://starkscan.co/contract/0x02a3ed03046e1042e193651e3da6d3c973e3d45c624442be936a374380a78bb5">Starkscan</a></td></tr><tr><td>Governor</td><td><code>0x048bb83134ce6a312d1b41b0b3deccc4ce9a9d280e6c68c0eb1c517259c89d74</code><br><a href="https://sepolia.voyager.online/contract/0x048bb83134ce6a312d1b41b0b3deccc4ce9a9d280e6c68c0eb1c517259c89d74">Voyager</a> <a href="https://sepolia.starkscan.co/contract/0x048bb83134ce6a312d1b41b0b3deccc4ce9a9d280e6c68c0eb1c517259c89d74">Starkscan</a></td><td><code>0x053499f7aa2706395060fe72d00388803fb2dcc111429891ad7b2d9dcea29acd</code><br><a href="https://voyager.online/contract/0x053499f7aa2706395060fe72d00388803fb2dcc111429891ad7b2d9dcea29acd">Voyager</a> <a href="https://starkscan.co/contract/0x053499f7aa2706395060fe72d00388803fb2dcc111429891ad7b2d9dcea29acd">Starkscan</a></td></tr><tr><td>Streamed Payments</td><td><code>0x038898fc956075394eaecefe7c51e35de9e4d43f7f7fd8c7ea8ad6316663c33e</code><br><a href="https://sepolia.voyager.online/contract/0x038898fc956075394eaecefe7c51e35de9e4d43f7f7fd8c7ea8ad6316663c33e">Voyager</a> <a href="https://sepolia.starkscan.co/contract/0x038898fc956075394eaecefe7c51e35de9e4d43f7f7fd8c7ea8ad6316663c33e">Starkscan</a></td><td><code>0x006ce8b1f418f7e28e85ea355a069e71ad33a6da791fe3550a94a84f693755a4</code><br><a href="https://voyager.online/contract/0x006ce8b1f418f7e28e85ea355a069e71ad33a6da791fe3550a94a84f693755a4">Voyager</a> <a href="https://starkscan.co/contract/0x006ce8b1f418f7e28e85ea355a069e71ad33a6da791fe3550a94a84f693755a4">Starkscan</a></td></tr><tr><td>L1 Starknet Proxy</td><td><code>0x36e3FDC259A4a8b0775D25b3f9396e0Ea6E110a5</code><br><a href="https://sepolia.etherscan.io/address/0x36e3fdc259a4a8b0775d25b3f9396e0ea6e110a5#readContract">Etherscan</a></td><td><code>0x1E0EF4162e42C9bF820c307218c4E41cCcA6E9CC</code><br><a href="https://etherscan.io/address/0x1e0ef4162e42c9bf820c307218c4e41ccca6e9cc#readContract">Etherscan</a></td></tr></tbody></table>


# EVM (V2, deprecated)

Addresses of the deployed contracts on Ethereum (V2)

{% hint style="info" %}
The EVM contracts are now deprecated.
{% endhint %}

All smart contracts on Ethereum are **immutable**. The `owner` role in the contract can *only* withdraw protocol fees, and is set to the L1 StarknetOwnerProxy ([Etherscan](https://etherscan.io/address/0x1e0ef4162e42c9bf820c307218c4e41ccca6e9cc)) contract, representing the governor on Starknet.

There are two kinds of contracts in Ekubo Protocol, stateful and stateless. Stateless contracts may be redeployed at any time for the use in our backend and interface. Stateful contracts are *never redeployed*, i.e. their addresses will never change.

The addresses of all Ethereum contracts are the same on Sepolia and Mainnet. The contracts are not deployed to any other EVM chain.

<table><thead><tr><th width="200.234375">Contract</th><th>Address</th></tr></thead><tbody><tr><td>Core</td><td><code>0xe0e0e08A6A4b9Dc7bD67BCB7aadE5cF48157d444</code><br><a href="https://etherscan.io/address/0xe0e0e08A6A4b9Dc7bD67BCB7aadE5cF48157d444#readContract">Mainnet</a>, <a href="https://sepolia.etherscan.io/address/0xe0e0e08A6A4b9Dc7bD67BCB7aadE5cF48157d444#readContract">Sepolia</a></td></tr><tr><td>Positions</td><td><code>0xA37cc341634AFD9E0919D334606E676dbAb63E17</code><br><a href="https://etherscan.io/address/0xA37cc341634AFD9E0919D334606E676dbAb63E17#readContract">Mainnet</a>, <a href="https://sepolia.etherscan.io/address/0xA37cc341634AFD9E0919D334606E676dbAb63E17#readContract">Sepolia</a></td></tr><tr><td>Oracle</td><td><code>0x51d02A5948496a67827242EaBc5725531342527C</code><br><a href="https://etherscan.io/address/0x51d02A5948496a67827242EaBc5725531342527C#readContract">Mainnet</a>, <a href="https://sepolia.etherscan.io/address/0x51d02A5948496a67827242EaBc5725531342527C#readContract">Sepolia</a></td></tr><tr><td>TWAMM</td><td><code>0xd4279c050da1f5c5b2830558c7a08e57e12b54ec</code><br><a href="https://etherscan.io/address/0xD4279c050DA1F5c5B2830558C7A08E57e12b54eC">Mainnet</a>, <a href="https://sepolia.etherscan.io/address/0xd4279c050da1f5c5b2830558c7a08e57e12b54ec">Sepolia</a></td></tr><tr><td>Orders</td><td><code>0xae1430e3e089794beacba260657fcd0f0967c18a</code><br><a href="https://etherscan.io/address/0xae1430e3e089794beacba260657fcd0f0967c18a">Mainnet</a>, <a href="https://sepolia.etherscan.io/address/0xae1430e3e089794beacba260657fcd0f0967c18a">Sepolia</a></td></tr><tr><td>Router (Stateless)</td><td><code>0x9995855C00494d039aB6792f18e368e530DFf931</code><br><a href="https://etherscan.io/address/0x9995855C00494d039aB6792f18e368e530DFf931#readContract">Mainnet</a>, <a href="https://sepolia.etherscan.io/address/0x9995855C00494d039aB6792f18e368e530DFf931#readContract">Sepolia</a></td></tr><tr><td>MEVResistRouter (Stateless)</td><td><code>0x0c95eA31e4501B3b879Cae2232087E478D44aEAB</code><br><a href="https://etherscan.io/address/0x0c95eA31e4501B3b879Cae2232087E478D44aEAB#code">Mainnet</a>, <a href="https://sepolia.etherscan.io/address/0x0c95eA31e4501B3b879Cae2232087E478D44aEAB#code">Sepolia</a></td></tr><tr><td>MEVResist</td><td><code>0x553a2EFc570c9e104942cEC6aC1c18118e54C091</code><br><a href="https://etherscan.io/address/0x553a2efc570c9e104942cec6ac1c18118e54c091#internaltx">Mainnet</a>, <a href="https://sepolia.etherscan.io/address/0x553a2efc570c9e104942cec6ac1c18118e54c091#internaltx">Sepolia</a></td></tr><tr><td>PriceFetcher (Stateless)</td><td><code>0x5a9093673cdc9f7cae8b0d177797040b758205be</code></td></tr><tr><td>CoreDataFetcher (Stateless)</td><td><code>0x208bb00c6b142351e4a431f6dd323691ebb7c285</code><br></td></tr><tr><td>QuoteDataFetcher (Stateless)</td><td><code>0x91cB8a896cAF5e60b1F7C4818730543f849B408c</code></td></tr><tr><td>TokenWrapperFactory (Stateless)</td><td><code>0x2b8d80d891C1E20aca70fF8a85714aa1900Ab120</code><br><a href="https://etherscan.io/address/0x2b8d80d891c1e20aca70ff8a85714aa1900ab120">Mainnet</a>, <a href="https://sepolia.etherscan.io/address/0x2b8d80d891c1e20aca70ff8a85714aa1900ab120">Sepolia</a></td></tr><tr><td>TokenWrapperPeriphery (Stateless)</td><td><code>0x2992e49d73Cc97FbC69c5498F332984C9a89f5d6</code><br><a href="https://etherscan.io/address/0x2992e49d73cc97fbc69c5498f332984c9a89f5d6">Mainnet</a>, <a href="https://sepolia.etherscan.io/address/0x2992e49d73cc97fbc69c5498f332984c9a89f5d6">Sepolia</a></td></tr><tr><td>RevenueBuybacks</td><td>TBD</td></tr></tbody></table>


# Ekubo API

The API that powers Ekubo's websites

{% hint style="warning" %}
Our API is available to support our website and may undergo breaking changes without notice. [Join the Discord](https://discord.ekubo.org) to ask questions or get support.
{% endhint %}

The API URL is hosted at the following URL:

`https://prod-api.ekubo.org`

It contains data for all the chains that we are deployed to and index. It also hosts an [OpenAPI 3.1 document](https://prod-api.ekubo.org/openapi.json) that self-describes all the endpoints.

### API Architecture

Our API functionality is based entirely on querying the Postgres schema kept up-to-date by the open-source indexer repository ([GitHub](https://github.com/EkuboProtocol/indexer)). You can replicate all of the API functionality by simply running your own instance of the Indexer and querying the your own Postgres database. Much of the API functionality is conveniently contained in views and scheduled jobs allowing you to easily replicate all the functionalities of our API.

#### Caching

There are multiple layers of caching with varying TTL between the database and the client. If you have specific latency requirements or need to make a large number of requests per second, it's best to run your own indexer.

#### Rate limiting

We have a rate limiting web application firewall (WAF) in front of the API. If you would like to make calls in excess of the limit, please reach out on [Discord](https://discord.ekubo.org/) to find a solution. The rate limit is not fixed and we can change it as necessary to control costs or ensure fair access to all users. Generally we will not create exceptions for you to exceed our public API rate limits.

### Endpoints

Every endpoint is browsable and testable on the [Endpoints](/reference/ekubo-api/endpoints) page, which renders the OpenAPI specification interactively — including sending requests to the live API.

The endpoints are documented via OpenAPI 3.1 at the `/openapi.json` endpoint. The URL is <https://prod-api.ekubo.org/openapi.json>, which can also be imported into REST clients like Postman.


# Endpoints

All the endpoints available in the Ekubo API, generated from the OpenAPI 3.1 specification

The canonical, always-current reference is the OpenAPI 3.1 document served by the API itself: <https://prod-api.ekubo.org/openapi.json>, which can also be imported into REST clients like Postman.

All endpoints take a `chainId` (path or query parameter) identifying the network — the API serves every chain Ekubo is deployed to and indexes.

Each operation below is rendered from the OpenAPI specification — expand one for its full request/response schemas, or use **Test it** to send a request to the live API directly from this page.

### Tokens

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/tokens" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/tokens/batch" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/tokens/{chainId}/{tokenAddress}" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/tokens/{chainId}/{tokenAddress}/price-history" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/tokens/{chainId}/{tokenA}/{tokenB}/events" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/tokens/{chainId}/{tokenA}/{tokenB}/liquidity" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

### Prices

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/price/{chainId}/{baseToken}/{quoteToken}/history" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

### Pools

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/pools/{chainId}/{coreAddress}/{poolId}/key" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/pools/{chainId}/{coreAddress}/{poolId}/liquidity" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/pools/{chainId}/{coreAddress}/{poolId}/positions" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/pools/{chainId}/{coreAddress}/{poolId}/price/history" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

### Pairs

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/pair/{chainId}/{tokenA}/{tokenB}/pools" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/pair/{chainId}/{tokenA}/{tokenB}/positions" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/pair/{chainId}/{tokenA}/{tokenB}/tvl" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/pair/{chainId}/{tokenA}/{tokenB}/volume" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

### Positions

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/positions/batch" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/positions/{address}" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/positions/{chainId}/events" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/positions/{chainId}/{lockerAddress}/{id}/history" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/positions/{chainId}/{nftAddress}/{id}" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/positions/{chainId}/{nftAddress}/{id}/image.svg" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

### Protocol overview

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/overview/boosted-fees-pools" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/overview/pairs" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/overview/revenue" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/overview/tvl" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/overview/volume" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

### Blocks

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/blocks/{chainId}/closest" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/blocks/{chainId}/{blockTag}" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

### DCA orders

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/orders/{chainId}/{nftAddress}/{id}" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/orders/{chainId}/{nftAddress}/{id}/image.svg" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

### Limit orders

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/limit-orders/orders/{address}" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

### Auctions

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/auctions" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/auctions/{chainId}/{nftAddress}/{id}" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/auctions/{chainId}/{nftAddress}/{id}/image.svg" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/auctions/{chainId}/{nftAddress}/{id}/state" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

### NFT metadata

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/nft/{chainId}/{nftAddress}/{id}" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/nft/{chainId}/{nftAddress}/{id}/image.svg" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

### Governance

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/governance/{chainId}/delegates" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/governance/{chainId}/delegates/{address}" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/governance/{chainId}/proposals" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/governance/{chainId}/proposals/{proposalId}/voters" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/governance/{chainId}/proposals/{proposalId}/votes" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

### Campaigns & rewards

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/campaigns" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/claims/{address}" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/rewards/{chainId}/{locker}/{salt}" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

### Misc

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/country" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/twap/orders/batch" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/twap/orders/{address}" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/twap/pair/{chainId}/{tokenA}/{tokenB}" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/twap/pools/{chainId}/{coreAddress}/{poolId}" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/twap/pools/{chainId}/{coreAddress}/{tokenA}/{tokenB}/{fee}" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/ve33/{ve33Address}/pools" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api.ekubo.org/openapi.json>" path="/ve33/{veTokenAddress}/{address}" method="get" %}
<https://prod-api.ekubo.org/openapi.json>
{% endopenapi %}


# Quoter API

The routing API that powers swaps on Ekubo's websites

{% hint style="warning" %}
Like the [Ekubo API](/reference/ekubo-api), the quoter is operated to support our website and may undergo breaking changes without notice. [Join the Discord](https://discord.ekubo.org) to ask questions or get support.
{% endhint %}

The Quoter API is hosted at:

```
https://prod-api-quoter.ekubo.org
```

It returns **block-pinned split routes** for exact-input and exact-output Ekubo Protocol swaps — the same routes the [interface](https://ekubo.org) executes through the [Yul Router](/integration-guides/swapping#evm-the-yul-router). Because quotes simulate actual pool state (including [extension](/concepts/extensions) behavior), the quoter is the easiest way for aggregators and integrators to price Ekubo liquidity without implementing the pool math.

The API is self-described by an OpenAPI 3.1 document at <https://prod-api-quoter.ekubo.org/openapi.json>, usable with any REST explorer.

### Endpoints

Each operation below is rendered from the OpenAPI specification — expand one for its full request/response schemas, or use **Test it** to send a request to the live API directly from this page. (`GET /openapi.json` serves the specification itself.)

{% openapi src="<https://prod-api-quoter.ekubo.org/openapi.json>" path="/{chainId}/{amount}/{specifiedToken}/{otherToken}" method="get" %}
<https://prod-api-quoter.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api-quoter.ekubo.org/openapi.json>" path="/{chainId}/health" method="get" %}
<https://prod-api-quoter.ekubo.org/openapi.json>
{% endopenapi %}

{% openapi src="<https://prod-api-quoter.ekubo.org/openapi.json>" path="/" method="get" %}
<https://prod-api-quoter.ekubo.org/openapi.json>
{% endopenapi %}

Notes:

* All token amounts are base-unit decimal strings; a negative `amount` requests an exact-output quote.
* Resolve token addresses and decimals through the [Ekubo API](/reference/ekubo-api) token list before requesting a quote.
* Quotes are pinned to a block, so route calldata should be encoded and submitted promptly (see [Swapping](/integration-guides/swapping) for executing the returned route).


# Audits

Audit reports for the Ekubo Protocol contracts

## Ekubo V3 (EVM)

The current EVM contracts are immutable and have been reviewed by multiple independent auditors and a public competitive audit. All reports live in the [audits directory](https://github.com/EkuboProtocol/evm-contracts/tree/v3.2.0/audits) of the source repository.

| Report                                                                                                                                                                                                                                             | Scope                                                                                                               |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| [Code4rena competitive audit](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/audits/Code4rena%20x%20Ekubo%20audit%20report%202025-11.pdf) (November 2025)                                                                              | The V3 protocol, reviewed publicly by a competitive audit field                                                     |
| [Riley Holterhus audit](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/audits/Ekubo-Riley-Holterhus-Audit.pdf)                                                                                                                         | Core protocol                                                                                                       |
| [Riley Holterhus update](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/audits/Ekubo-Feb-2026-Update-Riley-Holterhus-Audit.pdf) (February 2026)                                                                                        | Changes since the original review                                                                                   |
| [Auctions audit](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/audits/Ekubo-Auctions-Riley-Holterhus-Audit.pdf) — Riley Holterhus                                                                                                     | The Auctions contract                                                                                               |
| [SignedExclusiveSwap review](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/audits/SignedExclusiveSwap-Extension-Audit.md)                                                                                                             | The [signed exclusive swap](/integration-guides/signed-exclusive-swaps) extension                                   |
| [Ve33 invariants](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/audits/ve33-audit-invariants.md) and [invariant verification](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/audits/ve33-audit-invariant-verification.md) | [Ve33](/products/ve33) stake backing, voter-fee and emission solvency, vote consistency, and range-aware LP rewards |
| [AI audit scan](https://github.com/EkuboProtocol/evm-contracts/blob/v3.2.0/audits/EkuboProtocol%20Audit%20Scan%20-%20AI%20Scan.pdf)                                                                                                                | Automated review pass                                                                                               |

## Starknet

All Starknet contracts were most recently audited by Plainshift, concluding February 14th, 2025.

{% file src="/files/ZteKYv1B5p8xBTLk0WHk" %}
Plainshift audit report — all Starknet contracts
{% endfile %}

The core contracts were audited for 15 engineer-weeks by Nethermind Security.

{% file src="/files/kQq4oTK63nPKgDEoamvv" %}
Nethermind Security — core contracts (partially redacted)
{% endfile %}

The TWAMM extension, which powers [DCA-enabled pools and DCA orders](/user-guides/dollar-cost-average-orders), was audited separately.

{% file src="/files/fsvA0biViUjqOugzWCpp" %}
Nethermind Security — TWAMM extension
{% endfile %}

The first version of the revenue buybacks contract was also audited.

{% file src="/files/P90TVJaqE2nSbQK8gGIA" %}
Nethermind Security — revenue buybacks
{% endfile %}

## Governance

The Starknet L1 proxy, which lets Starknet governance control contracts on Ethereum, was audited by Cairo Security Clan. The report is [on GitHub](https://github.com/EkuboProtocol/governance/blob/v2.8.0/l1_proxy/Ekubo_Governance_L1_Proxy.pdf).

## Legacy deployments

<details>

<summary>Audits of the deprecated EVM V2 deployment</summary>

These reports cover the [EVM V2 contracts](/reference/contracts/evm-v2), which are deprecated and superseded by V3. They are retained for reference only.

{% file src="/files/1042dfOFiBvLsynSekwP" %}
Plainshift — V2 deployment
{% endfile %}

{% file src="/files/O3hmZNmik6ILJhHTC8IH" %}
ABDK — V2 core
{% endfile %}

{% file src="/files/eyli1nRLQbAA8hrc4qod" %}
ABDK — V2 TWAMM
{% endfile %}

{% file src="/files/Y6XPrMUh6XwAnboHo23g" %}
ABDK — V2 TWAMM invariant analysis
{% endfile %}

</details>


