Turbos CLMM SDK

v0.1.0

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...

0· 117·0 current·0 all-time
bylyiyun@lyiyun1024

Install

OpenClaw Prompt Flow

Install with OpenClaw

Best for remote or guided setup. Copy the exact prompt, then paste it into OpenClaw for lyiyun1024/turbos.

Previewing Install & Setup.
Prompt PreviewInstall & Setup
Install the skill "Turbos CLMM SDK" (lyiyun1024/turbos) from ClawHub.
Skill page: https://clawhub.ai/lyiyun1024/turbos
Keep the work scoped to this skill only.
After install, inspect the skill metadata and help me finish setup.
Use only the metadata you can verify from ClawHub; do not invent missing requirements.
Ask before making any broader environment changes.

Command Line

CLI Commands

Use the direct CLI path if you want to install manually and keep every step visible.

OpenClaw CLI

Bare skill slug

openclaw skills install turbos

ClawHub CLI

Package manager switcher

npx clawhub@latest install turbos
Security Scan
VirusTotalVirusTotal
Benign
View report →
OpenClawOpenClaw
Benign
high confidence
Purpose & Capability
The name/description (Turbos CLMM SDK integration for Sui) matches the SKILL.md content: code examples, API references, and guidance for using the turbos-clmm-sdk and SuiClient. It does not request unrelated services, binaries, or credentials.
Instruction Scope
SKILL.md contains usage examples, API reference, and initialization code only. It does not instruct the agent to read arbitrary files, harvest environment variables, or transmit data to unexpected endpoints. Placeholders such as YOUR_RPC_URL and YOUR_SUI_ADDRESS are present and expected for RPC and wallet parameters.
Install Mechanism
There is no install spec in the skill bundle (instruction-only). The guide recommends installing the npm package (pnpm add turbos-clmm-sdk @mysten/sui) which will fetch code from npm — a normal step for SDK use. Users should verify the npm package and its provenance before installing.
Credentials
The skill requests no environment variables, credentials, or config paths. The examples require user-supplied RPC URLs and Sui addresses (expected for blockchain SDK usage) but do not attempt to access secrets on their own.
Persistence & Privilege
always is false and the skill is user-invocable. As an instruction-only skill it does not persist files or modify other skills. Autonomous invocation is enabled by default but not combined with elevated privileges or broad credential access.
Assessment
This skill is an instruction-only integration guide and does not request secrets or install anything on its own. Before you install or use the referenced npm package: verify the package name and version on npm/github, inspect the package source or use a trusted lockfile, avoid pasting private keys or full wallet mnemonics into examples (use ephemeral/read-only RPC endpoints for testing), and prefer connecting via a wallet/provider that you control. If you plan to run the code locally, review the turbos-clmm-sdk code and its dependencies for any unexpected network calls or post-install scripts.

Like a lobster shell, security has layers — review code before you run it.

latestvk97bwy3szc1yghjfbz55jvdhnn83hssy
117downloads
0stars
1versions
Updated 1mo ago
v0.1.0
MIT-0

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.


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

Comments

Loading comments...