← 返回
未分类 中文

Turbos CLMM SDK

Integrate the Turbos Finance CLMM SDK on Sui for pools, swaps, positions, fees, and liquidity workflows. Use when building or analyzing apps that interact wi...
在 Sui 上集成 Turbos Finance CLMM SDK,实现池、兑换、仓位、费用及流动性工作流。适用于构建或分析交互式应用。
lyiyun1024 lyiyun1024 来源
未分类 clawhub v0.1.0 1 版本 100000 Key: 无需
★ 0
Stars
📥 344
下载
💾 1
安装
1
版本
#latest

概述

Turbos Finance CLMM SDK - Integration Guide

Turbos Finance is a Concentrated Liquidity Market Maker (CLMM) DEX on Sui. This guide covers the turbos-clmm-sdk package for building DeFi applications.

  • Repository: https://github.com/turbos-finance/turbos-clmm-sdk
  • npm: turbos-clmm-sdk

Table of Contents

  1. Installation
  2. SDK Initialization
  3. Module: Contract
  4. Module: Pool
  5. Module: Trade
  6. Module: Position
  7. Module: Account
  8. Module: Math
  9. Key Types Reference

Installation

pnpm add turbos-clmm-sdk @mysten/sui

SDK Initialization

import { Network, TurbosSdk } from 'turbos-clmm-sdk';
import { SuiClient } from '@mysten/sui/client';

// Mainnet (default fullnode RPC)
const sdk = new TurbosSdk(Network.mainnet);

// Testnet
const sdk = new TurbosSdk(Network.testnet);

// Custom SuiClient
const client = new SuiClient({ url: 'YOUR_RPC_URL' });
const sdk = new TurbosSdk(Network.mainnet, client);

// Custom SuiClientOptions
const sdk = new TurbosSdk(Network.mainnet, { url: 'YOUR_RPC_URL' });

SDK Structure

The TurbosSdk instance exposes the following modules:

PropertyTypeDescription
-----------------------------
sdk.poolPoolPool creation, liquidity operations
sdk.contractContractProtocol configuration & fee tiers
sdk.tradeTradeSwap operations
sdk.positionPositionPosition/NFT queries (replaces sdk.nft)
sdk.mathMathUtilTick/price/sqrt math utilities
sdk.accountAccountKeypair & mnemonic helpers
sdk.coinCoinCoin metadata & selection
sdk.providerSuiClientUnderlying Sui RPC client

Module: Contract

Protocol configuration and fee tier management.

getConfig

import type { Contract } from 'turbos-clmm-sdk';

const config: Contract.Config = await sdk.contract.getConfig();
// Returns: { PackageId, PackageIdOriginal, PoolConfig, Positions, Versioned, PoolTableId, ... }

getFees

const fees: Contract.Fee[] = await sdk.contract.getFees();
// Each fee: { fee: number, objectId: string, type: string, tickSpacing: number }

getFee

// Get fee tier by tick spacing
const fee: Contract.Fee = await sdk.contract.getFee(60);

Contract.Config Interface

interface Config {
  PackageId: string;
  PackageIdOriginal: string;
  PoolConfig: string;
  Positions: string;
  PoolFactoryAdminCap: string;
  Versioned: string;
  PoolTableId: string;
  VaultOriginPackageId: string;
  VaultPackageId: string;
  VaultGlobalConfig: string;
  VaultRewarderManager: string;
  VaultUserTierConfig: string;
  AclConfig: string;
}

Module: Pool

Core pool operations: create, add/remove liquidity, collect fees/rewards.

getPools

// Get all unlocked pools
const pools: Pool.Pool[] = await sdk.pool.getPools();

// Include locked pools
const allPools: Pool.Pool[] = await sdk.pool.getPools(true);

getPool

const pool: Pool.Pool = await sdk.pool.getPool(poolId);

createPool

const fee = await sdk.contract.getFee(60); // or other tick spacing

const txb = await sdk.pool.createPool({
  fee,                          // Fee object from sdk.contract.getFees()
  coinTypeA: '0x2::sui::SUI',
  coinTypeB: '0x...::usdc::USDC',
  sqrtPrice: '79226673515401279992447579055',  // Initial sqrt price (X64)
  address: 'YOUR_SUI_ADDRESS',
  amountA: '1000000000',        // Amount of coin A (raw units)
  amountB: '1000000',           // Amount of coin B (raw units)
  tickLower: -100,
  tickUpper: 100,
  slippage: '5',                // Percentage [0, 100)
  deadline: 60000,              // Optional, default 60s
});

addLiquidity (Open New Position)

const txb = await sdk.pool.addLiquidity({
  pool: poolId,
  address: 'YOUR_SUI_ADDRESS',
  amountA: '1000000000',
  amountB: '1000000',
  tickLower: -100,
  tickUpper: 100,
  slippage: '5',                // Percentage [0, 100)
  deadline: 60000,              // Optional
  txb: existingTxb,             // Optional, append to existing transaction
});

increaseLiquidity (Add to Existing Position)

const txb = await sdk.pool.increaseLiquidity({
  pool: poolId,
  nft: positionNftId,           // Existing position NFT ID
  address: 'YOUR_SUI_ADDRESS',
  amountA: '500000000',
  amountB: '500000',
  slippage: '5',
  deadline: 60000,
});

decreaseLiquidity

const txb = await sdk.pool.decreaseLiquidity({
  pool: poolId,
  nft: positionNftId,
  address: 'YOUR_SUI_ADDRESS',
  amountA: '500000000',         // Expected amount A to receive
  amountB: '500000',            // Expected amount B to receive
  decreaseLiquidity: '1000000', // Liquidity units to remove
  slippage: '5',
});

decreaseLiquidityWithReturn

Returns coin objects instead of transferring, for composability in PTBs:

const { txb, coinA, coinB } = await sdk.pool.decreaseLiquidityWithReturn({
  pool: poolId,
  nft: positionNftId,
  address: 'YOUR_SUI_ADDRESS',
  amountA: '500000000',
  amountB: '500000',
  decreaseLiquidity: '1000000',
  slippage: '5',
});
// coinA, coinB are TransactionObjectArguments for further PTB composition

removeLiquidity (Full Removal + Collect + Burn)

Combines decreaseLiquidity + collectFee + collectReward + burn:

const txb = await sdk.pool.removeLiquidity({
  pool: poolId,
  nft: positionNftId,
  address: 'YOUR_SUI_ADDRESS',
  amountA: '500000000',
  amountB: '500000',
  decreaseLiquidity: '1000000',
  slippage: '5',
  collectAmountA: '1000',       // Fee amount A to collect
  collectAmountB: '1000',       // Fee amount B to collect
  rewardAmounts: ['500', '0', '0'],
});

collectFee

const txb = await sdk.pool.collectFee({
  pool: poolId,
  nft: positionNftId,
  address: 'YOUR_SUI_ADDRESS',
  collectAmountA: '1000',
  collectAmountB: '1000',
});

collectReward

const txb = await sdk.pool.collectReward({
  pool: poolId,
  nft: positionNftId,
  address: 'YOUR_SUI_ADDRESS',
  rewardAmounts: ['500', '0', '0'],  // Amount per reward slot
});

estimateAmountsFromOneAmount

Estimate the required token pair amounts given a single token amount:

const [amountA, amountB] = sdk.pool.estimateAmountsFromOneAmount({
  sqrtPrice: pool.sqrt_price,
  tickLower: -100,
  tickUpper: 100,
  amount: '1000000000',
  isAmountA: true,
});

getTokenAmountsFromLiquidity

Calculate token amounts from a liquidity value:

import BN from 'bn.js';

const [amountA, amountB] = sdk.pool.getTokenAmountsFromLiquidity({
  currentSqrtPrice: new BN(pool.sqrt_price),
  lowerSqrtPrice: sdk.math.tickIndexToSqrtPriceX64(tickLower),
  upperSqrtPrice: sdk.math.tickIndexToSqrtPriceX64(tickUpper),
  liquidity: new BN(position.liquidity),
  ceil: true,  // Optional, defaults true
});

getPoolTypeArguments

// Returns [coinTypeA, coinTypeB, feeType]
const types: Pool.Types = await sdk.pool.getPoolTypeArguments(poolId);

fetchTicks

Fetch all initialized ticks for a pool:

const ticks = await sdk.pool.fetchTicks(poolId);
// Each tick: { id, tick_index, liquidity_gross, liquidity_net, fee_growth_outside_a/b, ... }

Module: Trade

Swap operations with single-hop and multi-hop routing.

computeSwapResult

Simulate a swap to get expected output (single amount for all pools):

import type { Trade } from 'turbos-clmm-sdk';

const results: Trade.ComputedSwapResult[] = await sdk.trade.computeSwapResult({
  pools: [{ pool: poolId, a2b: true }],
  address: 'YOUR_SUI_ADDRESS',
  amountSpecified: '1000000000',
  amountSpecifiedIsInput: true,
  tickStep: 100,   // Optional, default 100
});

// Result fields: { a_to_b, amount_a, amount_b, fee_amount, protocol_fee,
//   sqrt_price, tick_current_index, tick_pre_index, pool, ... }

computeSwapResultV2

Simulate a swap with per-pool amounts (useful for multi-hop with different amounts):

const results = await sdk.trade.computeSwapResultV2({
  pools: [
    { pool: poolId1, a2b: true, amountSpecified: '500000000' },
    { pool: poolId2, a2b: false, amountSpecified: '500000000' },
  ],
  address: 'YOUR_SUI_ADDRESS',
  amountSpecifiedIsInput: true,
});

swap

Execute a swap (supports up to 2-hop routing):

const swapResult = await sdk.trade.computeSwapResult({ ... });

const txb = await sdk.trade.swap({
  routes: [{
    pool: poolId,
    a2b: true,
    nextTickIndex: sdk.math.bitsToNumber(swapResult[0].tick_current_index.bits),
  }],
  coinTypeA: '0x2::sui::SUI',
  coinTypeB: '0x...::usdc::USDC',
  address: 'YOUR_SUI_ADDRESS',
  amountA: swapResult[0].amount_a,
  amountB: swapResult[0].amount_b,
  amountSpecifiedIsInput: true,
  slippage: '1',   // Percentage
});

swapWithReturn

Swap and return coin objects for PTB composability:

const { txb, coinVecA, coinVecB } = await sdk.trade.swapWithReturn({
  poolId,
  coinType: coinTypeA,
  amountA: '1000000000',
  amountB: '500000',
  swapAmount: '1000000000',
  nextTickIndex: tickIndex,
  slippage: '1',
  amountSpecifiedIsInput: true,
  a2b: true,
  address: 'YOUR_SUI_ADDRESS',
});

swapWithPartner

Swap with a partner fee split:

const txb = await sdk.trade.swapWithPartner({
  poolId,
  swapAmount: '1000000000',
  amountSpecifiedIsInput: true,
  slippage: '1',
  a2b: true,
  address: 'YOUR_SUI_ADDRESS',
  partner: 'PARTNER_OBJECT_ID',
});

Module: Position

Position (NFT) management and analytics. sdk.position replaces the deprecated sdk.nft.

getPositionsByOwner (via SuiClient)

The SDK does not provide a built-in method to list all positions by owner address. Instead, use sdk.provider.getOwnedObjects to query TurbosPositionNFT objects, then batch-fetch position details. This is the pattern used by Turbos' own frontend:

import { unstable_getObjectFields, unstable_getObjectId, type NFT } from 'turbos-clmm-sdk';
import type { PaginatedObjectsResponse } from '@mysten/sui/client';

interface PositionInfo extends NFT.PositionField {
  nftId: string;
  positionId: string;
  poolId: string;
  tickLower: number;
  tickUpper: number;
  objectId: string;
}

async function getPositionsByOwner(
  sdk: TurbosSdk,
  ownerAddress: string,
): Promise<PositionInfo[]> {
  const contract = await sdk.contract.getConfig();

  // Step 1: Paginate through all TurbosPositionNFT objects owned by the address
  const nfts: { nftId: string; positionId: string; poolId: string }[] = [];
  let cursor: string | null | undefined;
  let page: PaginatedObjectsResponse;
  do {
    page = await sdk.provider.getOwnedObjects({
      owner: ownerAddress,
      options: { showContent: true, showOwner: true },
      cursor,
      filter: {
        StructType: `${contract.PackageIdOriginal}::position_nft::TurbosPositionNFT`,
      },
    });
    page.data.forEach((item) => {
      const nftId = unstable_getObjectId(item);
      const fields = unstable_getObjectFields(item) as {
        position_id: string;
        pool_id: string;
      };
      if (fields.position_id && fields.pool_id) {
        nfts.push({ nftId, positionId: fields.position_id, poolId: fields.pool_id });
      }
    });
    cursor = page.nextCursor;
  } while (page.hasNextPage);

  if (nfts.length === 0) return [];

  // Step 2: Batch fetch position details (max 50 per call)
  const batchSize = 50;
  const batches: Promise<any[]>[] = [];
  for (let i = 0; i < nfts.length; i += batchSize) {
    batches.push(
      sdk.provider.multiGetObjects({
        ids: nfts.slice(i, i + batchSize).map((n) => n.positionId),
        options: { showContent: true },
      }),
    );
  }

  const allResults = (await Promise.all(batches)).flat();

  // Step 3: Parse position fields
  return allResults.map((obj): PositionInfo => {
    const objectId = unstable_getObjectId(obj);
    const fields = unstable_getObjectFields(obj) as unknown as NFT.PositionField;
    const nft = nfts.find((n) => n.positionId === objectId)!;
    return {
      ...fields,
      ...nft,
      objectId,
      tickLower: sdk.math.bitsToNumber(fields.tick_lower_index.fields.bits),
      tickUpper: sdk.math.bitsToNumber(fields.tick_upper_index.fields.bits),
    };
  });
}

Key details:

  • NFT struct type filter: {PackageIdOriginal}::position_nft::TurbosPositionNFT
  • Each NFT has position_id and pool_id fields linking to the actual position data
  • Burned/locked positions use a different struct: {PackageId}::position_manager::TurbosPositionBurnNFT, with pool ID nested at position_nft.fields.pool_id
  • Use sdk.math.bitsToNumber(tick_index.fields.bits) to convert tick index from on-chain I32 format

getFields

const fields = await sdk.position.getFields(nftId);
// Returns: { description, id, img_url, name, pool_id, position_id }

getPositionFields

const position = await sdk.position.getPositionFields(nftId);
// Returns: { liquidity, tick_lower_index, tick_upper_index,
//   fee_growth_inside_a/b, tokens_owed_a/b, reward_infos }

getPositionFieldsByPositionId

const position = await sdk.position.getPositionFieldsByPositionId(positionId);

getPositionTick

const tick = await sdk.position.getPositionTick(poolId, position.tick_lower_index);
// Returns: { tickIndex, initialized, liquidityNet, liquidityGross,
//   feeGrowthOutsideA, feeGrowthOutsideB, rewardGrowthsOutside }

getPositionLiquidityUSD

const usdValue = await sdk.position.getPositionLiquidityUSD({
  poolId,
  position,                   // PositionField
  priceA: '1.00',            // USD price of coin A
  priceB: '50000',           // USD price of coin B
});

getUnclaimedFeesAndRewards

const result = await sdk.position.getUnclaimedFeesAndRewards({
  poolId,
  position,
  getPrice: async (coinType) => getPriceForCoin(coinType),
});
// Returns: { fees, rewards, total, fields: { feeOwedA, feeOwedB, collectRewards, ... } }

getUnclaimedFees

const fees = await sdk.position.getUnclaimedFees({
  pool,
  position,
  tickLowerDetail,
  tickUpperDetail,
  getPrice: async (coinType) => getPriceForCoin(coinType),
});
// Returns: { feeOwedA, feeOwedB, unclaimedFees, scaledFeeOwedA, scaledFeeOwedB }

getUnclaimedRewards

const rewards = await sdk.position.getUnclaimedRewards({
  pool,
  position,
  tickLowerDetail,
  tickUpperDetail,
  getPrice: async (coinType) => getPriceForCoin(coinType),
});
// Returns: { unclaimedRewards, collectRewards, scaledCollectRewards }

getPositionAPR

const apr = await sdk.position.getPositionAPR({
  poolId,
  tickLower: -100,
  tickUpper: 100,
  fees24h: '1000',
  getPrice: async (coinType) => getPriceForCoin(coinType),
  liquidity: '1000000',  // Optional, defaults to pool liquidity
});
// Returns: { fees: string, rewards: string, total: string }

burn

Burn a position NFT (must have 0 liquidity):

const txb = await sdk.position.burn({
  pool: poolId,
  nft: positionNftId,
});

Module: Account

Keypair and mnemonic utilities.

generateMnemonic

const mnemonic24 = sdk.account.generateMnemonic(24);   // 24 words (default)
const mnemonic12 = sdk.account.generateMnemonic(12);   // 12 words

getKeypairFromMnemonics

const keypair = sdk.account.getKeypairFromMnemonics(mnemonic, {
  accountIndex: 0,  // Optional, default 0
  isExternal: false, // Optional, default false
  addressIndex: 0,   // Optional, default 0
});
// Derive path: m/44'/784'/{accountIndex}'/{isExternal ? 1 : 0}'/{addressIndex}'

Module: Math

Tick, price, and sqrt math utilities accessed via sdk.math.

Key Methods

// Tick ↔ SqrtPrice
const sqrtPriceX64: BN = sdk.math.tickIndexToSqrtPriceX64(tickIndex);
const tickIndex: number = sdk.math.sqrtPriceX64ToTickIndex(sqrtPriceX64);

// Tick ↔ Price (human-readable)
const price: Decimal = sdk.math.tickIndexToPrice(tickIndex, decimalsA, decimalsB);
const sqrtPriceX64: BN = sdk.math.priceToSqrtPriceX64(price, decimalsA, decimalsB);

// Bits (I32/I128) ↔ Number
const num: number = sdk.math.bitsToNumber(bits);           // I32
const num: number = sdk.math.bitsToNumber(bits, 128);      // I128

// Scale conversions
const scaled: string = sdk.math.scaleDown(amount, decimals);

// X64 fixed-point conversions
const x64: Decimal = sdk.math.toX64_Decimal(value);
const fromX64: Decimal = sdk.math.fromX64_Decimal(value);

Key Types Reference

Pool.Pool

interface Pool {
  objectId: string;
  type: string;
  types: [coinTypeA: string, coinTypeB: string, feeType: string];
  coin_a: string;
  coin_b: string;
  fee: number;
  fee_protocol: number;
  liquidity: string;
  sqrt_price: string;
  tick_current_index: { type: string; fields: { bits: number } };
  tick_spacing: number;
  unlocked: boolean;
  max_liquidity_per_tick: string;
  fee_growth_global_a: string;
  fee_growth_global_b: string;
  protocol_fees_a: string;
  protocol_fees_b: string;
  reward_infos: {
    type: string;
    fields: {
      emissions_per_second: string;
      growth_global: string;
      manager: string;
      vault: string;
      vault_coin_type: string;
    };
  }[];
  deploy_time_ms: string;
  reward_last_updated_time_ms: string;
  tick_map: { type: string; fields: { id: { id: string }; size: string } };
}

Trade.ComputedSwapResult

interface ComputedSwapResult {
  a_to_b: boolean;
  amount_a: string;
  amount_b: string;
  fee_amount: string;
  is_exact_in: boolean;
  liquidity: string;
  pool: string;
  protocol_fee: string;
  recipient: string;
  sqrt_price: string;
  tick_current_index: { bits: number };
  tick_pre_index: { bits: number };
}

Network Enum

enum Network {
  mainnet = 'mainnet',
  testnet = 'testnet',
}

Common Patterns

Typical Swap Flow

import { Network, TurbosSdk } from 'turbos-clmm-sdk';

const sdk = new TurbosSdk(Network.mainnet);

// 1. Compute swap result
const swapResults = await sdk.trade.computeSwapResult({
  pools: [{ pool: poolId, a2b: true }],
  address: walletAddress,
  amountSpecified: '1000000000',
  amountSpecifiedIsInput: true,
});

// 2. Build swap transaction
const txb = await sdk.trade.swap({
  routes: [{
    pool: poolId,
    a2b: true,
    nextTickIndex: sdk.math.bitsToNumber(swapResults[0].tick_current_index.bits),
  }],
  coinTypeA,
  coinTypeB,
  address: walletAddress,
  amountA: swapResults[0].amount_a,
  amountB: swapResults[0].amount_b,
  amountSpecifiedIsInput: true,
  slippage: '1',
});

// 3. Sign and execute
await suiClient.signAndExecuteTransaction({ transaction: txb, signer: keypair });

Typical Liquidity Addition Flow

// 1. Get fee tier
const fee = await sdk.contract.getFee(60);

// 2. Estimate amounts from one side
const [amountA, amountB] = sdk.pool.estimateAmountsFromOneAmount({
  sqrtPrice: pool.sqrt_price,
  tickLower,
  tickUpper,
  amount: '1000000000',
  isAmountA: true,
});

// 3. Add liquidity (opens new position)
const txb = await sdk.pool.addLiquidity({
  pool: poolId,
  address: walletAddress,
  amountA,
  amountB,
  tickLower,
  tickUpper,
  slippage: '5',
});

// 4. Sign and execute
await suiClient.signAndExecuteTransaction({ transaction: txb, signer: keypair });

Package Reference

PackagenpmPurpose
-----------------------
CLMM SDKturbos-clmm-sdkConcentrated liquidity AMM (pools, positions, swaps)
Peer Dep@mysten/suiSui blockchain client

版本历史

共 1 个版本

  • v0.1.0 当前
    2026-03-31 10:16 安全 安全

安全检测

腾讯云安全 (Keen)

安全,无风险
查看报告

腾讯云安全 (Sanbu)

安全,无风险
查看报告

🔗 相关推荐

dev-programming

CodeConductor.ai

larsonreever
AI驱动平台,提供快速全栈开发、智能体、工作流自动化及低代码AI集成的可扩展产品创建。
★ 81 📥 182,959
dev-programming

YouTube

byungkyu
使用托管OAuth集成YouTube Data API,支持搜索视频、管理播放列表、获取频道数据及评论互动,适用于用户需要时使用此技能。
★ 142 📥 42,078
dev-programming

Mcporter

steipete
使用 mcporter CLI 直接列出、配置、认证及调用 MCP 服务器/工具(支持 HTTP 或 stdio),涵盖临时服务器、配置编辑及 CLI/类型生成功能。
★ 198 📥 68,186