Skip to main content
The Full Sail SDK provides developers with a simple and efficient interface for interacting with the Full Sail protocol. It enables seamless integration of swaps, liquidity management, and governance interactions into external applications, analytics tools, or automated strategies.

Installation

Install the SDK using npm:
$ npm i @fullsailfinance/sdk
Install the SDK using yarn:
$ yarn add @fullsailfinance/sdk

Configuration

import { initFullSailSDK } from '@fullsailfinance/sdk'

const fullSailSDK = initFullSailSDK({
  network: 'mainnet-production',
  // You can also provide your own full node URL and simulation account. It's optional.
  fullNodeUrl: 'https://...',
  simulationAccount: '0x...',
})
After you have connected the wallet, you should set senderAddress to your sdk instance
fullSailSDK.senderAddress = '0x...'
// or
fullSailSDK.setSenderAddress('0x...')

Key Concepts

Gauge

A smart contract that manages staked positions rewards and oSAIL token distribution. Core functions:
  • Position Staking Management: Tracks staked positions, calculates rewards, and enables withdrawal
  • Reward & oSAIL Distribution: Distributes rewards and oSAIL tokens to staked positions. New oSAIL tokens are issued weekly for each epoch
  • Reward Calculation: Calculates earned oSAIL rewards based on staked liquidity and time, and processes reward claims to position owners
  • Fee Collection: Collects and manages fees generated by the associated liquidity pool

Ticks

Discrete price points that define the boundaries of liquidity ranges. Important considerations:
  • Each tick represents a specific price ratio
  • Ticks are spaced at constant intervals determined by the pool’s tick spacing

Pool

Initially created without a gauge. It can be added later to enable reward distribution. There are two pool entities available through the SDK: Chain Pool (Pool.getByIdFromChain()): Contains real-time data directly from the blockchain. Data from this entity is always relevant. Some core/unique properties:
  • currentTickIndex: The tick corresponding to the current price of the pool (determines which liquidity positions are currently active)
  • rewardCoinsInfo: List of reward coins available for this pool
  • currentSqrtPrice: Current sqrt price of the pool
Pool (Pool.getById()): Contains calculated data and metadata from the backend which can be convinient for frontend. It can contain same fields as ChainPool but they may be outdated by a few minutes, especially those that change frequently. Some core/unique properties:
  • gauge_id: Unique identifier for gauge contract related to this pool
Use backend pool for stable metadata and gauge_id, use chain pool for relevant current price and reward data.

Position

Represents a range of prices where you provide liquidity to a pool. If gauge exists for this pool, position should be staked, otherwise it will not receive rewards. Some core properties:
  • tick_lower: The lower bound of your price range (minimum price where your liquidity is active)
  • tick_upper: The upper bound of your price range (maximum price where your liquidity is active)
  • liquidity: The amount of liquidity provided in this price range
  • stake_info: Information about the position stake object within the gauge. Can be undefined if position not staked or no gauge exists for this pool.

Position Rewards

Different position states receive different types of rewards:
  • Position in pool without gauge: Receives pool fees and pool rewards
  • Unstaked position in pool with gauge: Receives nothing
  • Staked position in pool with gauge: Receives oSAIL tokens and pool rewards

oSAIL

oSAIL is the emissions token distributed to staked positions. Each epoch has its own oSAIL token with a specific expiration date - 5 weeks from the start of the epoch. It offers two possible paths: lock it into veSAIL to participate in governance and earn trading fees, or redeem it for liquid SAIL at 50% of the current spot price at time of redemption, paid in USDC. Expired oSAIL can be only locked into veSAIL.

Lock (veSAIL)

Represents voting power and used only for voting. Minted by locking SAIL or oSAIL for a fixed duration. Lock amount multiplied by lock duration determines voting power. isPermanent option commits to the longest available lock period for maximum voting power. Some core properties:
  • permanent: whether lock is permanent or not
  • voting_power: current voting power provided by this lock. Reduces slowly over time if lock is not permanent
  • is_voting_onchain: whether lock is used for voting at current epoch
  • amount: amount of SAIL locked
Locks are transferable, supports merging, splitting, increasing amount/duration. Liquid SAIL unlocks at lock expiration.

Epoch

7-day cycle during which veSAIL holders vote and predict trading volumes for pools.

Voting

veSAIL holders can vote and predict trading volumes for pools for the next epoch. Only pools with gauge can be voted. Voters earn from two sources:
  • Trading fees from voted pools, distributed by prediction accuracy and allocated voting power.
  • oSAIL Rebase from oSAIL redemptions, distributed proportionally to allocated voting power.

Usage examples

If method name ends with ...Transaction, it returns a transaction that should be signed and executed using your sui client or wallet kit.
const poolId = '0x0...'

const amountA = 1000000n
const fixedAmountA = true
const roundUp = true
// 1%
const slippage = Percentage.fromNumber(1)

const pool = await fullSailSDK.Pool.getById(poolId)
const chainPool = await fullSailSDK.Pool.getByIdFromChain(poolId)

// get the nearest tick indexes based on current price
const tickLower = TickMath.getPrevInitializableTickIndex(chainPool.currentTickIndex, chainPool.tickSpacing)
const tickUpper = TickMath.getNextInitializableTickIndex(chainPool.currentTickIndex, chainPool.tickSpacing)

// Calculate the opposite coin amount based on the first coin amount and tick range
// This ensures proper liquidity ratio within the specified price range, including slippage
const { maxAmountB } = ClmmPoolUtil.estLiquidityAndCoinAmountFromOneAmounts(
  tickLower,
  tickUpper,
  amountA, // change to amountB if you want to calculate maxAmountA based on amountB
  fixedAmountA, // change to false if you want to calculate maxAmountA based on amountB
  roundUp,
  slippage.toCoefficient(),
  chainPool.currentSqrtPrice,
)

const transaction = await fullSailSDK.Position.openTransaction({
  coinTypeA: chainPool.coinTypeA,
  coinTypeB: chainPool.coinTypeB,
  poolId,
  tickLower,
  tickUpper,
  amountA,
  amountB: maxAmountB,
  slippage,
  fixedAmountA,
  currentSqrtPrice: chainPool.currentSqrtPrice,
  // if gaugeId provided, position will be staked automatically
  gaugeId: pool.gauge_id,
})
Only oSAIL claimed automatically within this transaction. Fees and pool rewards are not.
const poolId = '0x0...'
const positionId = '0x0...'

const amountA = 1000000n
const fixedAmountA = true
const roundUp = true
// 1%
const slippage = Percentage.fromNumber(1)

const pool = await fullSailSDK.Pool.getById(poolId)
const chainPool = await fullSailSDK.Pool.getByIdFromChain(poolId)
const position = await fullSailSDK.Position.getById(positionId)
const currentEpochOSail = await fullSailSDK.Coin.getCurrentEpochOSail()

const { maxAmountB } = ClmmPoolUtil.estLiquidityAndCoinAmountFromOneAmounts(
  position.tick_lower,
  position.tick_upper,
  amountA,
  fixedAmountA,
  roundUp,
  slippage.toCoefficient(),
  chainPool.currentSqrtPrice,
)

const transaction = await sdk.Position.addLiquidityTransaction({
  coinTypeA: chainPool.coinTypeA,
  coinTypeB: chainPool.coinTypeB,
  poolId,
  positionId,
  tickLower: position.tick_lower,
  tickUpper: position.tick_upper,
  amountA,
  amountB: maxAmountB,
  slippage,
  fixedAmountA,
  currentSqrtPrice: chainPool.currentSqrtPrice,
  // params below are required only if position is staked
  gaugeId: pool.gauge_id,
  oSailCoinType: currentEpochOSail.address,
  positionStakeId: position.stake_info?.id,
})
This method can be used only if position is created before gauge is added to the pool or if gaugeId was not provided when creating the position via Position.openTransaction(). Unstaked positions within a pool with gauge will not receive rewards. Only fees claimed automatically within this transaction. Pool rewards are not.
const poolId = '0x0...'
const positionId = '0x0...'

const position = await fullSailSDK.Position.getById(positionId)
const pool = await fullSailSDK.Pool.getById(poolId)

const transaction = await sdk.Position.stakeTransaction({
  coinTypeA: pool.token_a.address,
  coinTypeB: pool.token_b.address,
  poolId: pool.address,
  positionId: position.id,
  gaugeId: pool.gauge_id,
})
Only oSAIL claimed automatically within this transaction. Fees and pool rewards are not.
const poolId = '0x0...'
const positionId = '0x0...'

const liquidity = 1000000n
// 1%
const slippage = Percentage.fromNumber(1)

const pool = await fullSailSDK.Pool.getById(poolId)
const chainPool = await fullSailSDK.Pool.getByIdFromChain(poolId)
const position = await fullSailSDK.Position.getById(positionId)
const currentEpochOSail = await fullSailSDK.Coin.getCurrentEpochOSail()

const transaction = await sdk.Position.removeLiquidityTransaction({
  coinTypeA: chainPool.coinTypeA,
  coinTypeB: chainPool.coinTypeB,
  poolId,
  positionId,
  tickLower: position.tick_lower,
  tickUpper: position.tick_upper,
  currentSqrtPrice: chainPool.currentSqrtPrice,
  liquidity,
  slippage,
  // params below are required only if position is staked
  gaugeId: pool.gauge_id,
  oSailCoinType: currentEpochOSail.address,
  positionStakeId: position.stake_info?.id,
})
All rewards claimed automatically within this transaction.
const poolId = '0x0...'
const positionId = '0x0...'

const liquidity = 1000000n
// 1%
const slippage = Percentage.fromNumber(1)

const pool = await fullSailSDK.Pool.getById(poolId)
const chainPool = await fullSailSDK.Pool.getByIdFromChain(poolId)
const position = await fullSailSDK.Position.getById(positionId)
const currentEpochOSail = await fullSailSDK.Coin.getCurrentEpochOSail()

const transaction = await sdk.Position.closeTransaction({
  coinTypeA: chainPool.coinTypeA,
  coinTypeB: chainPool.coinTypeB,
  poolId,
  positionId,
  tickLower: position.tick_lower,
  tickUpper: position.tick_upper,
  currentSqrtPrice: chainPool.currentSqrtPrice,
  liquidity,
  slippage,
  // You must provide the exact reward coin list to claim all available rewards.
  rewardCoinTypes: chainPool.rewardCoinsInfo.map(({ coinType }) => coinType),
  // params below are required only if position is staked
  gaugeId: pool.gauge_id,
  oSailCoinType: currentEpochOSail.address,
  positionStakeId: position.stake_info?.id,
})
This method can be used only if position is not staked.
const poolId = '0x0...'
const positionId = '0x0...'

const pool = await fullSailSDK.Pool.getById(poolId)

const transaction = await sdk.Position.claimFeeTransaction({
  coinTypeA: pool.token_a.address,
  coinTypeB: pool.token_b.address,
  poolId,
  positionId,
})
This method can be used only if position is not staked.
const poolId = '0x0...'
const positionId = '0x0...'

const chainPool = await fullSailSDK.Pool.getByIdFromChain(poolId)

const transaction = await fullSailSDK.Position.claimUnstakedPoolRewardsTransaction({
  coinTypeA: chainPool.coinTypeA,
  coinTypeB: chainPool.coinTypeB,
  poolId,
  positionId,
  rewardCoinTypes: chainPool.rewardCoinsInfo.map(({ coinType }) => coinType),
})
This method claims both fee and pool rewards for unstaked position.
const poolId = '0x0...'
const positionId = '0x0...'

const chainPool = await fullSailSDK.Pool.getByIdFromChain(poolId)

const transaction = await sdk.Position.claimFeeAndUnstakedPoolRewardsTransaction({
  coinTypeA: chainPool.coinTypeA,
  coinTypeB: chainPool.coinTypeB,
  poolId,
  positionId,
  rewardCoinTypes: chainPool.rewardCoinsInfo.map(({ coinType }) => coinType),
})
This method can be used only if position is staked.
const poolId = '0x0...'
const positionId = '0x0...'

const pool = await fullSailSDK.Pool.getById(poolId)
const position = await fullSailSDK.Position.getById(positionId)
const currentEpochOSail = await fullSailSDK.Coin.getCurrentEpochOSail()

const transaction = await sdk.Position.claimOSailTransaction({
  coinTypeA: pool.token_a.address,
  coinTypeB: pool.token_b.address,
  poolId,
  positionStakeId: position.stake_info.id,
  oSailCoinType: currentEpochOSail.address,
  gaugeId: pool.gauge_id,
})
This method can be used only if position is staked.
const poolId = '0x0...'
const positionId = '0x0...'

const pool = await fullSailSDK.Pool.getById(poolId)
const chainPool = await fullSailSDK.Pool.getByIdFromChain(poolId)
const position = await fullSailSDK.Position.getById(positionId)

const transaction = await sdk.Position.claimStakedPoolRewardsTransaction({
  coinTypeA: chainPool.coinTypeA,
  coinTypeB: chainPool.coinTypeB,
  poolId,
  positionStakeId: position.stake_info.id,
  rewardCoinTypes: chainPool.rewardCoinsInfo.map(({ coinType }) => coinType),
  gaugeId: pool.gauge_id,
})
This method claims both oSAIL and pool rewards for staked position.
const poolId = '0x0...'
const positionId = '0x0...'

const pool = await fullSailSDK.Pool.getById(poolId)
const chainPool = await fullSailSDK.Pool.getByIdFromChain(poolId)
const position = await fullSailSDK.Position.getById(positionId)
const currentEpochOSail = await fullSailSDK.Coin.getCurrentEpochOSail()

const transaction = await sdk.Position.claimOSailAndStakedPoolRewardsTransaction({
  coinTypeA: chainPool.coinTypeA,
  coinTypeB: chainPool.coinTypeB,
  poolId,
  rewardCoinTypes: chainPool.rewardCoinsInfo.map(({ coinType }) => coinType),
  positionStakeId: position.stake_info.id,
  gaugeId: pool.gauge_id,
  oSailCoinType: currentEpochOSail?.address,
})
See Aftermath router docs for more details.
const coinInType = '0x0...'
const coinOutType = '0x0...'
const coinInAmount = 1000000n
const slippage = Percentage.fromNumber(1)

// getCompleteTradeRouteGivenAmountIn method from Aftermath router
const completeRoute = await fullSailSDK.Swap.getSwapRoute({
  coinInType,
  coinOutType,
  coinInAmount,
})

// getTransactionForCompleteTradeRoute method from Aftermath router
const transaction = await fullSailSDK.Swap.swapRouterTransaction({
  completeRoute,
  slippage: slippage.toCoefficient(),
})
Swaps coins directly through smart contract.
const poolId = '0x0...'
const coinInType = '0x0...'
const coinOutType = '0x0...'

const slippage = Percentage.fromNumber(1)
const amount = 1000000n
const byAmountIn = true

const chainPool = await fullSailSDK.Pool.getByIdFromChain(poolId)
const coinIn = await fullSailSDK.Coin.getByType(coinInType)
const coinOut = await fullSailSDK.Coin.getByType(coinOutType)

// checking swap direction
const isAtoB = chainPool.coinTypeA === coinInType

// calculates estimated amounts, price after swap, and returns some of method params as swap params for convinience
const presSwap = await fullSailSDK.Swap.preSwap({
  coinTypeA: isAtoB ? coinInType : coinOutType,
  coinTypeB: isAtoB ? coinOutType : coinInType,
  decimalsA: isAtoB ? coinIn.decimals : coinOut.decimals,
  decimalsB: isAtoB ? coinOut.decimals : coinIn.decimals,
  poolId,
  amount,
  byAmountIn,
  isAtoB,
  currentSqrtPrice: chainPool.currentSqrtPrice,
})

const transaction = await fullSailSDK.Swap.swapTransaction({
  amount,
  amountLimit: byAmountIn ? presSwap.estimatedAmountOut : presSwap.estimatedAmountIn,
  slippage,
  ...presSwap.swapParams,
})
Creates lock using SAIL.
// 1 SAIL
const amount = 1000000n
// 4 years
const durationDays = 365 * 4

const transaction = await fullSailSDK.Lock.createLockTransaction({
  amount,
  isPermanent: true,
  durationDays,
})
Creates lock using oSAIL.
const oSailCoinType = '0x0...'

// 1 oSAIL
const amount = 1000000n
// 4 years
const durationDays = 365 * 4

const transaction = await fullSailSDK.Lock.createLockFromOSailTransaction({
  amount,
  oSailCoinType,
  durationDays,
  isPermanent: true,
})
Combines Position.claimOSailTransaction() and Lock.createLockFromOSailTransaction() for convinience.
const poolId = '0x0...'
const positionId = '0x0...'

const pool = await fullSailSDK.Pool.getById(poolId)
const position = await fullSailSDK.Position.getById(positionId)
const currentEpochOSail = await fullSailSDK.Coin.getCurrentEpochOSail()

// 4 years
const durationDays = 365 * 4

const transaction = await fullSailSDK.Lock.claimOSailAndCreateLockTransaction({
  coinTypeA: pool.token_a.address,
  coinTypeB: pool.token_b.address,
  poolId,
  gaugeId: pool.gauge_id,
  positionStakeId: position.stake_info.id,
  oSailCoinType: currentEpochOSail.address,
  durationDays,
  isPermanent: true,
})
Increases lock by provided SAIL amount.
const lockId = '0x0...'

// 1 SAIL
const amount = 1000000n

const transaction = await fullSailSDK.Lock.increaseAmountTransaction({
  lockId,
  amount,
})
Merges two locks into one. If one of the locks is used for voting, votes from this lock will be reset, and user will need to vote again.
const fromLockId = '0x0...'
const toLockId = '0x0...'

const transaction = await fullSailSDK.Lock.mergeTransaction({
  fromLockId,
  toLockId,
  isFromLockVoted: true,
  isToLockVoted: false,
})
Splits lock into two locks by SAIL amount. If lock is used for voting, votes from this lock will be reset, and user will need to vote again.
const lockId = '0x0...'

// 1 SAIL
const amount = 1000000n

const transaction = await fullSailSDK.Lock.splitTransaction({
  lockId,
  amount,
  isVoted: true,
})
Transfers lock to another wallet.
const lockId = '0x0...'
const receiver = '0x0...'

const transaction = await fullSailSDK.Lock.transferTransaction({
  lockId,
  receiver,
})
const lockId = '0x0...'
// 4 years
const durationDays = 365 * 4

const transaction = await fullSailSDK.Lock.increaseDurationTransaction({
  lockId,
  durationDays,
})
Enables isPermanent flag for lock.
const lockId = '0x0...'

const transaction = await fullSailSDK.Lock.enablePermanentTransaction({
  lockId,
})
Disables isPermanent flag for lock.
const lockId = '0x0...'

const transaction = await fullSailSDK.Lock.disablePermanentTransaction({
  lockId,
})
Votes using all locks.weight is in abstract units to calculate voting power ratio. It sum can be any value and will be used as 100%. For example you can provide weights [20, 80] or [1, 1] and it will work fine. In case of [20, 80] total weight is 100 (similarly to 100%). First pool will receve 20/100 (20%) of voting power and second pool will receive 80/100 (80%) of voting power. In case of [1, 1] total weight is 2. First pool will receve 1/2 (50%) of voting power and second pool will receive 1/2 (50%) of voting power.volume is predicted volume of pool for next epoch in USD with decimals 6.
const lockId1 = '0x0...'
const lockId2 = '0x0...'
const lockId3 = '0x0...'

const poolId1 = '0x0...'
const poolId2 = '0x0...'

const weight1 = 20n
const weight2 = 80n

// 1 USD
const volume1 = 1000000n
// 2 USD
const volume2 = 2000000n

const transaction = await fullSailSDK.Lock.batchVoteTransaction({
  // locks will be used for voting
  lockIds: [lockId1, lockId2, lockId3],
  // each array item is a vote for pool
  votes: [
    { poolId: poolId1, weight: weight1, volume: volume1 },
    { poolId: poolId2, weight: weight2, volume: volume2 },
  ],
})