Shared TypeScript math and protocol encoding utilities for Ekubo Protocol, supporting EVM and Starknet.
npm install @ekubo/sdk
# or
bun add @ekubo/sdkAll functions are pure and dependency-free. Exact protocol integers use native
bigint values.
import {
toSqrtRatio,
computeStep,
maxLiquidityForTokenAmounts,
} from "@ekubo/sdk";
// Convert a tick to a sqrt ratio for Starknet
const sqrtRatio = toSqrtRatio(0, "starknet");
// Compute a single swap step
const result = computeStep({
sqrtRatio,
liquidity: 1000000n,
sqrtRatioLimit: toSqrtRatio(100, "starknet"),
amount: 500n,
isToken1: false,
fee: 0n,
});
// result: { consumedAmount, calculatedAmount, sqrtRatioNext, feeAmount }
// Compute the maximum liquidity for a position given token amounts
const liquidity = maxLiquidityForTokenAmounts({
sqrtPrice: sqrtRatio,
sqrtPriceLower: toSqrtRatio(-100, "starknet"),
sqrtPriceUpper: toSqrtRatio(100, "starknet"),
amountBase: 1000n,
amountQuote: 1000n,
});type Chain = "evm" | "starknet";Selects chain-specific tick and sqrt ratio bounds.
| Name | Description |
|---|---|
MAX_U128 |
Maximum value of a 128-bit unsigned integer |
MAX_U256 |
Maximum value of a 256-bit unsigned integer |
EVM_MIN_TICK |
Minimum tick for EVM |
EVM_MAX_TICK |
Maximum tick for EVM |
EVM_MIN_SQRT_RATIO |
Minimum sqrt ratio for EVM |
EVM_MAX_SQRT_RATIO |
Maximum sqrt ratio for EVM |
EVM_MAX_TICK_SPACING |
Maximum tick spacing for EVM |
STARKNET_MIN_TICK |
Minimum tick for Starknet |
STARKNET_MAX_TICK |
Maximum tick for Starknet |
STARKNET_MIN_SQRT_RATIO |
Minimum sqrt ratio for Starknet |
STARKNET_MAX_SQRT_RATIO |
Maximum sqrt ratio for Starknet |
STARKNET_MAX_TICK_SPACING |
Maximum tick spacing for Starknet |
Converts a tick index to a fixed-point sqrt ratio for the given chain.
const sqrtRatio = toSqrtRatio(1000, "evm");Converts a compact 96-bit float sqrt ratio (as stored on-chain on Starknet mainnet) to the fixed-point representation used in math functions.
Converts a fixed-point sqrt ratio to the compact float format expected on Starknet mainnet.
Computes the amount of token0 (base token) between two sqrt ratios for a given liquidity.
Computes the amount of token1 (quote token) between two sqrt ratios for a given liquidity.
Calculates the next sqrt ratio after trading a given amount of token0. Returns null if the result is out of range.
Calculates the next sqrt ratio after trading a given amount of token1. Returns null if the result is out of range.
Computes a single step of a swap within a tick range.
sqrtRatio– current sqrt ratioliquidity– active liquiditysqrtRatioLimit– sqrt ratio at which the swap stops (price limit)amount– signed amount to swap; negative means exact outputisToken1–trueif the specified amount is in token1fee– fee as a fraction of2^128
Returns:
{
consumedAmount: bigint; // amount of the specified token consumed
calculatedAmount: bigint; // amount of the other token
sqrtRatioNext: bigint; // sqrt ratio after the step
feeAmount: bigint; // fee collected
}Returns the maximum liquidity achievable from a given amount of base token (token0) over a price range.
Returns the maximum liquidity achievable from a given amount of quote token (token1) over a price range.
maxLiquidityForTokenAmounts({ sqrtPrice, sqrtPriceLower, sqrtPriceUpper, amountBase, amountQuote }): bigint
Returns the maximum liquidity achievable given both token amounts and the current price.
Returns the maximum liquidity for either a specified base or quote amount.
// Specify base amount
const liq = maxLiquidityForSpecifiedAmount({
sqrtPrice,
sqrtPriceLower,
sqrtPriceUpper,
amount: { base: 1000n },
});
// Specify quote amount
const liq2 = maxLiquidityForSpecifiedAmount({
sqrtPrice,
sqrtPriceLower,
sqrtPriceUpper,
amount: { quote: 1000n },
});amountsFromSpecifiedAmount({ sqrtPrice, sqrtPriceLower, sqrtPriceUpper, amount }): { base, quote, maxLiquidity }
Given either a base or quote token amount, returns both token amounts and the maximum liquidity for the position.
Converts a liquidity value to the required base token amount for a price range.
Converts a liquidity value to the required quote token amount for a price range.
calculateNextSqrtRatio(sqrtRatio, liquidity, token0SaleRate, token1SaleRate, timeElapsed, fee): bigint
Computes the next sqrt ratio for a TWAMM (Time-Weighted Average Market Maker) position after a given elapsed time.
Returns the position of the most significant bit of x.
encodeEvmConcentratedPoolConfig and encodeEvmStableswapPoolConfig pack the
32-byte v3 config used by Core. decodeEvmPoolConfig returns the extension,
exact bigint Q64 fee, pool discriminator, and type-specific parameters.
encodeEvmPoolKey returns the exact 96-byte ABI encoding hashed by Core.
deriveEvmPoolId accepts a Keccak-256 function supplied by the consumer, which
keeps the SDK dependency-free:
import { deriveEvmPoolId, encodeEvmConcentratedPoolConfig } from "@ekubo/sdk";
import { keccak256 } from "viem";
const config = encodeEvmConcentratedPoolConfig({
fee: 2n ** 64n / 10_000n,
tickSpacing: 200,
extension: "0x0000000000000000000000000000000000000000",
});
const poolId = deriveEvmPoolId({ token0, token1, config }, keccak256);Legacy position readers can use encodeEvmV2ConcentratedPoolConfig and
encodeEvmV2StableswapPoolConfig; v2 configs deliberately omit the v3 pool
type discriminator.
The SDK also owns the exact protocol encodings shared by applications and transaction preparers:
encodeEvmAuctionConfig/decodeEvmAuctionConfigencodeEvmTwammOrderConfig/decodeEvmTwammOrderConfigcalculateEvmTwammMaxSaleRatederiveEvmAuctionTokenId,deriveEvmTwammOrderTokenId, andderiveEvmVeTokenIdderiveEvmIndexedSalt
Hashing functions accept a caller-supplied Keccak-256 implementation, keeping the SDK dependency-free while using the same ABI preimages everywhere.
MevCaptureV21MainPool quotes a concentrated pool on the current EVM Core
behind the MEVCapture v2.1 main extension. It reproduces every CORE.swap
call the extension makes: the toward call, charged away calls cut one step
short of the next rate boundary or initialized tick, bridges onto initialized
ticks, and the per-call surcharge passed to accumulateAsFees (taken from the
output on exact input, added to the input on exact output). The first-touch
anchor update and post-swap refresh run in simulateSwap and quote.
import { MevCaptureV21MainPool } from "@ekubo/sdk";
const pool = new MevCaptureV21MainPool({
poolFee, // the pool key's Q0.64 fee
tickSpacing,
config, // the extension's getConfig tuple
state: { core: { sqrtRatio, tick, liquidity }, anchor }, // anchor: getAnchor
sortedTicks, // every initialized tick the swap can reach
});
const quote = pool.quote({
isToken1: false,
amount: 10n ** 18n,
now: blockTimestamp,
});Quotes are exact only for a complete initialized-tick snapshot, because the
extension cuts calls at Core's bitmap tick events. The replay in
test/mevCaptureV21Main.test.ts checks all 21 contract vectors (266 Core
calls) with no tolerance.
This project uses Bun.
# Install dependencies
bun install
# Run tests
bun test
# Build
bun run build