@fullsailfinance/sdk
v11.0.0
Published
SDK for FullSail ve(4,4) dex
Downloads
153
Keywords
Readme
@fullsailfinance/sdk
Overview
TypeScript toolkit for the FullSail ve(4,4) DEX on Sui. It centralizes payload assembly, liquidity management, and token-locking utilities to let applications interact with FullSail mechanics without hand-coding Move transactions.
Prerequisites
- Node.js ≥ 22
@mysten/suiv2 (≥ 2.20) — a peer dependency. Install it alongside the SDK.
Installation
npm:
$ npm i @fullsailfinance/sdk @mysten/suiyarn:
$ yarn add @fullsailfinance/sdk @mysten/suipnpm:
$ pnpm add @fullsailfinance/sdk @mysten/suiConfiguration
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 user has connected the wallet, you must 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 position staking and voting.
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 poolcurrentSqrtPrice: Current sqrt price of the pool
Pool (Pool.getById()): Contains calculated data and metadata from the backend which can be convenient 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 poolgauge_killed: can betrueif gauge contract for the pool has been killed. All positions in this pool should be unstaked manually to receive fees.
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.
Unstaked positions are the default and recommended way to provide liquidity to any pool.
They receive pool fees + pool rewards (regardless of whether the pool has a gauge or not).
When a pool has a gauge, you can choose to stake your position or keep it unstaked:
- Staked position receives pool rewards + oSAIL tokens.
- Unstaked position receives pool rewards + pool fees.
For reward abuse protection new positions always have a 20 minutes cooldown before any rewards can be claimed. You can remove liquidity from a position at any time, but if you do so before cooldown period you will not receive a 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 rangestake_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:
- Unstaked position (with or without a gauge): Receives pool fees and pool rewards
- Staked position (in pool with a gauge): Receives oSAIL tokens and pool rewards
- Staked position (in pool without a gauge): Receives only pool rewards (it's best to unstake)
Vault
A managed liquidity position created by depositing into a port — a protocol-controlled vault that handles position management automatically. Vaults are always staked and benefit from oSAIL token distributions.
The vault continuously adjusts its price range to keep the position in range at all times, so unlike a regular position it will never stop earning fees due to price moving outside the range.
Unlike regular positions, vaults have no 20-minute cooldown.
Some core properties:
port.position: The underlying pool position managed by the vault.port.rewards: List of port incentive rewards distributed to vault participants.port.vault_stopped: Whether the vault has been stopped by the protocol. New deposits are not allowed, but liquidity withdrawal is still available.port.is_paused: Whether the vault is fully paused. All operations including withdrawal are unavailable until unpaused.port_entry.ratio: The user's share of the vault's total liquidity. Used to calculate the user's proportional token amounts and withdrawable liquidity.port_entry.destroyed: Whether the vault position has been closed.
Vault Rewards
Since vault positions are always staked, they receive all three types of rewards:
- oSAIL tokens
- Pool rewards
- Port 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. In every method which includes claim rewards rewardChoice field is required to determine which path to take. It can be one of the following values:
"vesail"- lock it into veSAIL to participate in governance and earn trading fees."sail"- redeem it for liquid SAIL in a 2 to 1 ratio (100 oSAIL will be redeemed for 50 SAIL)."usd"- redeem it for USDC in a 50% of the current spot price at time of redemption.
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 notvoting_power: current voting power provided by this lock. Reduces slowly over time if lock is not permanentis_voting_onchain: whether lock is used for voting at current epochamount: amount of SAIL locked
Locks are transferable, supports merging, splitting, increasing amount/duration. These operations are only available while the lock is non-zero and not expired.
Once a lock expires, the locked SAIL becomes withdrawable and can be redeemed back as liquid SAIL.
After a merge, the source lock becomes nulled. After a split, the original lock becomes nulled and two new locks are created. Nulled locks should be cleaned up: the preferred way is to pass them via nulledLockIds in any of the claim methods so destruction happens in the same transaction. Alternatively, they can be destroyed explicitly via destroyNulledTransaction or destroyNulledListTransaction. In either case, ensure all rewards are claimed from the nulled lock before destroying it.
If a user has any locks with v1_version set, they must migrate all such locks before any other operations are allowed. Until migrated, locks can only be fetched (by ID or as a list) — no additional data should be fetched and no operations should be available.
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 Trading fees from the pools they vote for. Fees are distributed by prediction accuracy and 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.
It also accepts transaction as second parameter if you want to add this transaction to existing batch.
There are separate methods to create/add liquidity/remove liquidity/close for both kinds of positions (staked and unstaked) if you want to have more control or stronger types.
Pools
- Get pool list
- Get pools by coins
- Get pool by ID (offchain)
- Get pool by ID (onchain)
- Get pools with ports
- Get pools for voting
Get pool list
Returns a paginated list of pools with metadata.
const { full_pools, pagination } = await fullSailSDK.Pool.getList({
pagination: { page: 0, page_size: 20 },
...,
})Get pools by coins
Returns all pools matching the given coin pair and tick spacing.
const pools = await fullSailSDK.Pool.getListByCoins({
coinTypeA: '0x0...',
coinTypeB: '0x...',
tickSpacing: 40,
})Get pool by ID (offchain)
Returns backend pool data by pool ID.
const poolId = '0x0...'
const { pool } = await fullSailSDK.Pool.getById(poolId)Get pool by ID (onchain)
Returns real-time pool data directly from the blockchain by pool ID.
const poolId = '0x0...'
const chainPool = await fullSailSDK.Pool.getByIdFromChain(poolId)Get pools with ports
Returns all pools that have at least one port, along with their ports and the total cumulative yields in USD across all returned ports. Optionally accepts an account address – when provided, each pool's port_exists flag indicates whether the account has port entries for that pool.
const { pools, total_cumulative_yields_usd } = await fullSailSDK.Pool.getListWithPorts({
account_address: '0x0...', // optional
})
for (const { pool, ports } of pools) {
// pool — FullPool object with metadata
// ports — Port[] for this pool
}Get pools for voting
Returns a paginated list of pools available for voting, and other stats. Pass account_address to include the user's prediction and voting power ratio for each pool.
const { pools, pagination } = await fullSailSDK.Pool.getVotingList({
account_address: '0x0...', // optional: includes user_prediction and user_voting_power_ratio
with_votes: true, // optional: only pools that have received votes
page: 0,
page_size: 20,
})
for (const { pool } of pools) {
// pool — Pool object
// ...and other stats
}Positions
- Get position by ID
- Get positions by account
- Get positions by pool
- Get pool reward amounts for position
- Get pool reward amounts for multiple positions
- Get fee amounts for position
- Get fee amounts for multiple positions
- Get oSAIL rewards for position
- Get oSAIL rewards for multiple positions
- Create position
- Add liquidity to position
- Calculate zap-in deposit
- Create position (zap-in)
- Add liquidity to position (zap-in)
- Stake position
- Unstake position
- Remove Liquidity from position
- Close position
- Claim fee
- Claim pool rewards for unstaked position
- Claim all unstaked position rewards
- Claim oSAIL
- Claim pool rewards for staked position
- Claim all staked position rewards
- Claim all rewards for multiple positions
Get position by ID
Returns position data by position ID.
const positionId = '0x0...'
const position = await fullSailSDK.Position.getById(positionId)Get positions by account
Returns a list of pools along with the positions and vault positions (strategies) belonging to each pool for a given account address.
const accountAddress = '0x0...'
const { pools } = await fullSailSDK.Position.getListByAccountV2({
address: accountAddress,
})
for (const { pool, positions, strategies } of pools) {
// pool — Pool object
// positions — Position[] for this pool
// strategies — Strategy[] (vault positions) for this pool
}Get positions by pool
Returns the pool and a paginated list of positions for a given pool ID.
const poolId = '0x0...'
const { pool, positions, pagination } = await fullSailSDK.Position.getListByPool({
pool_id: poolId,
page: 0,
page_size: 20,
})
for (const { position } of positions) {
// position — Position for this pool
}Get pool reward amounts for position
Returns the pool reward amounts for a single position.
const poolId = '0x0...'
const positionId = '0x0...'
const { pool } = await fullSailSDK.Pool.getById(poolId)
const rewards = await fullSailSDK.Position.getRewardCoinsWithAmount({
coinTypeA: pool.token_a.address,
coinTypeB: pool.token_b.address,
poolId,
positionId,
rewardCoinTypes: pool.rewards?.map(({ token }) => token.address) ?? [],
})
for (const { coinType, amountOwned } of rewards) {
// coinType — reward coin type address
// amountOwned — claimable amount as bigint
}Get pool reward amounts for multiple positions
Batch version of getRewardCoinsWithAmount — fetches pool reward amounts for multiple positions in a single RPC call.
const poolId = '0x0...'
const { pool } = await fullSailSDK.Pool.getById(poolId)
const rewardsList = await fullSailSDK.Position.getRewardCoinsWithAmountList([
{
coinTypeA: pool.token_a.address,
coinTypeB: pool.token_b.address,
poolId,
positionId: '0x0...',
rewardCoinTypes: pool.rewards?.map(({ token }) => token.address) ?? [],
},
// ...more entries
])
// rewardsList[i] — PositionRewardAmountsResultItem[] for the i-th position
for (const positionRewards of rewardsList) {
for (const { coinType, amountOwned } of positionRewards) {
// coinType — reward coin type address
// amountOwned — claimable amount as bigint
}
}Get fee amounts for position
Returns the pool fee amounts for a single unstaked position.
const poolId = '0x0...'
const positionId = '0x0...'
const { pool } = await fullSailSDK.Pool.getById(poolId)
const { feeOwnedA, feeOwnedB } = await fullSailSDK.Position.getFeeAmounts({
coinTypeA: pool.token_a.address,
coinTypeB: pool.token_b.address,
poolId,
positionId,
})
// feeOwnedA — claimable fee amount for coin A as bigint
// feeOwnedB — claimable fee amount for coin B as bigintGet fee amounts for multiple positions
Batch version of getFeeAmounts — fetches pool fee amounts for multiple unstaked positions in a single RPC call.
const poolId = '0x0...'
const { pool } = await fullSailSDK.Pool.getById(poolId)
const feesList = await fullSailSDK.Position.getFeeAmountsList([
{
coinTypeA: pool.token_a.address,
coinTypeB: pool.token_b.address,
poolId,
positionId: '0x0...',
},
// ...more entries
])
// feesList[i] — PositionFeeAmountsResult for the i-th position
for (const { feeOwnedA, feeOwnedB } of feesList) {
// feeOwnedA — claimable fee amount for coin A as bigint
// feeOwnedB — claimable fee amount for coin B as bigint
}Get oSAIL rewards for position
Returns the claimable oSAIL reward amount for a single staked position.
const poolId = '0x0...'
const positionId = '0x0...'
const { pool } = await fullSailSDK.Pool.getById(poolId)
const currentEpochOSail = await fullSailSDK.Coin.getCurrentEpochOSail()
const amount = await fullSailSDK.Position.getOSailRewards({
coinTypeA: pool.token_a.address,
coinTypeB: pool.token_b.address,
poolId,
gaugeId: pool.gauge_id,
positionId,
oSailCoinType: currentEpochOSail.address,
})
// amount — claimable oSAIL amount as bigintGet oSAIL rewards for multiple positions
Batch version of getOSailRewards — fetches claimable oSAIL reward amounts for multiple staked positions in a single RPC call.
const poolId = '0x0...'
const { pool } = await fullSailSDK.Pool.getById(poolId)
const currentEpochOSail = await fullSailSDK.Coin.getCurrentEpochOSail()
const amounts = await fullSailSDK.Position.getOSailRewardsList([
{
coinTypeA: pool.token_a.address,
coinTypeB: pool.token_b.address,
poolId,
gaugeId: pool.gauge_id,
positionId: '0x0...',
oSailCoinType: currentEpochOSail.address,
},
// ...more entries
])
// amounts[i] — claimable oSAIL amount as bigint for the i-th positionCreate position
If the pool has a gauge, you can choose between unstaked and staked when creating a position.
To do this, you need to pass the gaugeId and ensure that pool.gauge_killed is not true.
import { ClmmPoolUtil, Percentage, TickMath } from '@fullsailfinance/sdk'
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 you want to create a staked position, pass the gaugeId
gaugeId: pool.gauge_killed ? undefined : pool.gauge_id,
})Add liquidity to position
All rewards claimed automatically within this transaction.
import { ClmmPoolUtil, MAX_LOCK_DURATION_DAYS, Percentage } from '@fullsailfinance/sdk'
const poolId = '0x0...'
const positionId = '0x0...'
// your existing permanent lock id
const permanentLockId = '0x0...'
// 'vesail' | 'sail' | 'usd'
const rewardChoice = 'vesail'
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)
let currentEpochOSail
let oSailReward
if (position.staked && pool.gauge_id) {
currentEpochOSail = await fullSailSDK.Coin.getCurrentEpochOSail()
oSailReward = await fullSailSDK.Position.getOSailRewards({
poolId,
coinTypeA: chainPool.coinTypeA,
coinTypeB: chainPool.coinTypeB,
positionId,
oSailCoinType: currentEpochOSail.address,
gaugeId: pool.gauge_id,
})
}
// 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(
position.tick_lower,
position.tick_upper,
amountA,
fixedAmountA,
roundUp,
slippage.toCoefficient(),
chainPool.currentSqrtPrice,
)
const transaction = await fullSailSDK.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,
rewardCoinTypes: chainPool.rewardCoinsInfo.map(({ coinType }) => coinType),
// params below are required only if position is staked
gaugeId: pool.gauge_id,
positionStakeId: position.stake_info?.id,
oSailCoinType: currentEpochOSail?.address,
oSailAmount: oSailReward,
rewardChoice,
positionUpdatedAt: position.updated_at,
// if rewardChoice is "vesail", you can provide your existing permanent lock id and oSAIL will be added to it
// otherwise new lock will be created every call
permanentLockId,
newLockDurationDays: MAX_LOCK_DURATION_DAYS,
newLockIsPermanent: true,
})Calculate zap-in deposit
Calculates how a single-coin deposit should be split between the part that is added to the position as-is (fixedAmountIn) and the part that is swapped into the other coin (swapAmountIn). The result is consumed by Create position (zap-in) and Add liquidity to position (zap-in).
Set fixedAmountA to true when the user provides coin A and the SDK should swap part of it into coin B, or false for the opposite direction. If the current price is outside the [tickLower, tickUpper] range, no swap is needed and the entire inputAmount is used as-is (the result has swapAmountIn: '0' and swapRoute: undefined).
import { Percentage } from '@fullsailfinance/sdk'
const poolId = '0x0...'
const inputAmount = 1000000n
const fixedAmountA = true
// 1%
const slippage = Percentage.fromNumber(1)
const { pool } = await fullSailSDK.Pool.getById(poolId)
const chainPool = await fullSailSDK.Pool.getByIdFromChain(poolId)
const tickLower = TickMath.getPrevInitializableTickIndex(chainPool.currentTickIndex, chainPool.tickSpacing)
const tickUpper = TickMath.getNextInitializableTickIndex(chainPool.currentTickIndex, chainPool.tickSpacing)
const calculateZapInDepositResult = await fullSailSDK.Position.calculateZapInDeposit({
coinTypeA: chainPool.coinTypeA,
coinTypeB: chainPool.coinTypeB,
coinDecimalsA: pool.token_a.decimals,
coinDecimalsB: pool.token_b.decimals,
tickLower,
tickUpper,
inputAmount,
fixedAmountA,
slippage,
currentSqrtPrice: chainPool.currentSqrtPrice,
})
// inputAmountIn — full input amount (as decimal string)
// fixedAmountIn — part of the input added to the position as-is
// swapAmountIn — part of the input that will be swapped into the other coin
// targetAmountOut — expected output of the swap
// swapRoute — resolved swap route (undefined when no swap is needed)
// fixedAmountA — fixedAmountA flag from params
// slippage — slippage from paramsCreate position (zap-in)
Creates a new position by providing a single coin — part of it is swapped into the other coin automatically before opening the position. Pass the result of calculateZapInDeposit as calculateZapInDepositResult.
If the pool has a gauge, you can choose between unstaked and staked when creating a position.
To do this, you need to pass the gaugeId and ensure that pool.gauge_killed is not true.
import { Percentage, TickMath } from '@fullsailfinance/sdk'
const poolId = '0x0...'
const inputAmount = 1000000n
const fixedAmountA = true
// 1%
const slippage = Percentage.fromNumber(1)
const { pool } = await fullSailSDK.Pool.getById(poolId)
const chainPool = await fullSailSDK.Pool.getByIdFromChain(poolId)
const tickLower = TickMath.getPrevInitializableTickIndex(chainPool.currentTickIndex, chainPool.tickSpacing)
const tickUpper = TickMath.getNextInitializableTickIndex(chainPool.currentTickIndex, chainPool.tickSpacing)
const calculateZapInDepositResult = await fullSailSDK.Position.calculateZapInDeposit({
coinTypeA: chainPool.coinTypeA,
coinTypeB: chainPool.coinTypeB,
coinDecimalsA: pool.token_a.decimals,
coinDecimalsB: pool.token_b.decimals,
tickLower,
tickUpper,
inputAmount,
fixedAmountA,
slippage,
currentSqrtPrice: chainPool.currentSqrtPrice,
})
const transaction = await fullSailSDK.Position.openZapInTransaction({
coinTypeA: chainPool.coinTypeA,
coinTypeB: chainPool.coinTypeB,
coinDecimalsA: pool.token_a.decimals,
coinDecimalsB: pool.token_b.decimals,
poolId,
tickLower,
tickUpper,
slippage,
currentSqrtPrice: chainPool.currentSqrtPrice,
calculateZapInDepositResult,
// pass gaugeId to open a staked position
gaugeId: pool.gauge_killed ? undefined : pool.gauge_id,
})Add liquidity to position (zap-in)
Adds liquidity to an existing position using a single coin — part of it is swapped into the other coin automatically. Pass the result of calculateZapInDeposit as calculateZapInDepositResult.
All rewards claimed automatically within this transaction.
import { MAX_LOCK_DURATION_DAYS, Percentage } from '@fullsailfinance/sdk'
const poolId = '0x0...'
const positionId = '0x0...'
// your existing permanent lock id
const permanentLockId = '0x0...'
// 'vesail' | 'sail' | 'usd'
const rewardChoice = 'vesail'
const inputAmount = 1000000n
const fixedAmountA = 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)
let currentEpochOSail
let oSailReward
if (position.staked && pool.gauge_id) {
currentEpochOSail = await fullSailSDK.Coin.getCurrentEpochOSail()
oSailReward = await fullSailSDK.Position.getOSailRewards({
poolId,
coinTypeA: chainPool.coinTypeA,
coinTypeB: chainPool.coinTypeB,
positionId,
oSailCoinType: currentEpochOSail.address,
gaugeId: pool.gauge_id,
})
}
const calculateZapInDepositResult = await fullSailSDK.Position.calculateZapInDeposit({
coinTypeA: chainPool.coinTypeA,
coinTypeB: chainPool.coinTypeB,
coinDecimalsA: pool.token_a.decimals,
coinDecimalsB: pool.token_b.decimals,
tickLower: position.tick_lower,
tickUpper: position.tick_upper,
inputAmount,
fixedAmountA,
slippage,
currentSqrtPrice: chainPool.currentSqrtPrice,
})
const transaction = await fullSailSDK.Position.addLiquidityZapInTransaction({
coinTypeA: chainPool.coinTypeA,
coinTypeB: chainPool.coinTypeB,
coinDecimalsA: pool.token_a.decimals,
coinDecimalsB: pool.token_b.decimals,
poolId,
positionId,
tickLower: position.tick_lower,
tickUpper: position.tick_upper,
slippage,
currentSqrtPrice: chainPool.currentSqrtPrice,
calculateZapInDepositResult,
rewardCoinTypes: chainPool.rewardCoinsInfo.map(({ coinType }) => coinType),
// params below are required only if position is staked
gaugeId: pool.gauge_id,
positionStakeId: position.stake_info?.id,
oSailCoinType: currentEpochOSail?.address,
oSailAmount: oSailReward,
rewardChoice,
positionUpdatedAt: position.updated_at,
// if rewardChoice is "vesail", you can provide your existing permanent lock id and oSAIL will be added to it
// otherwise new lock will be created every call
permanentLockId,
newLockDurationDays: MAX_LOCK_DURATION_DAYS,
newLockIsPermanent: true,
})Stake position
Can be used at any time for an unstaked position, provided the pool has a gauge (pool.gauge_id exists and pool.gauge_killed is not true).
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 fullSailSDK.Position.stakeTransaction({
coinTypeA: pool.token_a.address,
coinTypeB: pool.token_b.address,
poolId: pool.address,
positionId: position.id,
gaugeId: pool.gauge_id,
})Unstake position
Use this in the following cases:
- If you want to change the position type to "unstaked" even while the pool's gauge is active.
- If the gauge contract for the pool was killed (
pool.gauge_killedistrue) and the position is staked. Staked positions within a pool with killed gauge will receive only pool rewards.
Both oSAIL and pool rewards are claimed automatically within this transaction.
import { MAX_LOCK_DURATION_DAYS, Percentage } from '@fullsailfinance/sdk'
const poolId = '0x0...'
const positionId = '0x0...'
// your existing permanent lock id
const permanentLockId = '0x0...'
// 'vesail' | 'sail' | 'usd'
const rewardChoice = 'vesail'
// 1%
const slippage = Percentage.fromNumber(1)
const position = await fullSailSDK.Position.getById(positionId)
const { pool } = await fullSailSDK.Pool.getById(poolId)
const chainPool = await fullSailSDK.Pool.getByIdFromChain(poolId)
const currentEpochOSail = await fullSailSDK.Coin.getCurrentEpochOSail()
const oSailReward = await fullSailSDK.Position.getOSailRewards({
poolId,
coinTypeA: chainPool.coinTypeA,
coinTypeB: chainPool.coinTypeB,
positionId,
oSailCoinType: currentEpochOSail.address,
gaugeId: pool.gauge_id,
})
const transaction = await fullSailSDK.Position.unstakeTransaction({
coinTypeA: pool.token_a.address,
coinTypeB: pool.token_b.address,
poolId: pool.address,
positionStakeId: position.stake_info.id,
gaugeId: pool.gauge_id,
rewardCoinTypes: chainPool.rewardCoinsInfo.map(({ coinType }) => coinType),
oSailCoinType: currentEpochOSail.address,
rewardChoice,
oSailAmount: oSailReward,
positionUpdatedAt: position.updated_at,
slippage,
// if rewardChoice is "vesail", you can provide your existing permanent lock id and oSAIL will be added to it
// otherwise new lock will be created every call
permanentLockId,
newLockDurationDays: MAX_LOCK_DURATION_DAYS,
newLockIsPermanent: true,
})Remove liquidity from position
All rewards claimed automatically within this transaction.
import { MAX_LOCK_DURATION_DAYS, Percentage } from '@fullsailfinance/sdk'
const poolId = '0x0...'
const positionId = '0x0...'
// your existing permanent lock id
const permanentLockId = '0x0...'
// 'vesail' | 'sail' | 'usd'
const rewardChoice = 'vesail'
// liquidity amount to remove
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)
let currentEpochOSail
let oSailReward
if (position.staked && pool.gauge_id) {
currentEpochOSail = await fullSailSDK.Coin.getCurrentEpochOSail()
oSailReward = await fullSailSDK.Position.getOSailRewards({
poolId,
coinTypeA: chainPool.coinTypeA,
coinTypeB: chainPool.coinTypeB,
positionId,
oSailCoinType: currentEpochOSail.address,
gaugeId: pool.gauge_id,
})
}
const transaction = await fullSailSDK.Position.removeLiquidityTransaction({
coinTypeA: chainPool.coinTypeA,
coinTypeB: chainPool.coinTypeB,
poolId,
positionId,
tickLower: position.tick_lower,
tickUpper: position.tick_upper,
liquidity,
slippage,
currentSqrtPrice: chainPool.currentSqrtPrice,
rewardCoinTypes: chainPool.rewardCoinsInfo.map(({ coinType }) => coinType),
// params below are required only if position is staked
gaugeId: pool.gauge_id,
positionStakeId: position.stake_info?.id,
oSailCoinType: currentEpochOSail?.address,
oSailAmount: oSailReward,
positionUpdatedAt: position.updated_at,
rewardChoice,
// if rewardChoice is "vesail", you can provide your existing permanent lock id and oSAIL will be added to it
// otherwise new lock will be created every call
permanentLockId,
newLockDurationDays: MAX_LOCK_DURATION_DAYS,
newLockIsPermanent: true,
})Close position
All rewards claimed automatically within this transaction.
import { MAX_LOCK_DURATION_DAYS, Percentage } from '@fullsailfinance/sdk'
const poolId = '0x0...'
const positionId = '0x0...'
// your existing permanent lock id
const permanentLockId = '0x0...'
// 'vesail' | 'sail' | 'usd'
const rewardChoice = 'vesail'
// 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)
let currentEpochOSail
let oSailReward
if (position.staked && pool.gauge_id) {
currentEpochOSail = await fullSailSDK.Coin.getCurrentEpochOSail()
oSailReward = await fullSailSDK.Position.getOSailRewards({
poolId,
coinTypeA: chainPool.coinTypeA,
coinTypeB: chainPool.coinTypeB,
positionId,
oSailCoinType: currentEpochOSail.address,
gaugeId: pool.gauge_id,
})
}
const transaction = await fullSailSDK.Position.closeTransaction({
coinTypeA: chainPool.coinTypeA,
coinTypeB: chainPool.coinTypeB,
poolId,
positionId,
tickLower: position.tick_lower,
tickUpper: position.tick_upper,
currentSqrtPrice: chainPool.currentSqrtPrice,
liquidity: BigInt(position.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,
positionStakeId: position.stake_info?.id,
oSailCoinType: currentEpochOSail?.address,
oSailAmount: oSailReward,
positionUpdatedAt: position.updated_at,
rewardChoice,
// if rewardChoice is "vesail", you can provide your existing permanent lock id and oSAIL will be added to it
// otherwise new lock will be created every call
permanentLockId,
newLockDurationDays: MAX_LOCK_DURATION_DAYS,
newLockIsPermanent: true,
})Claim fee
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 fullSailSDK.Position.claimFeeTransaction({
coinTypeA: pool.token_a.address,
coinTypeB: pool.token_b.address,
poolId,
positionId,
})Claim pool rewards for unstaked position
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),
})Claim all unstaked position rewards
Can be used only if position is not staked. It claims both fee and pool rewards.
const poolId = '0x0...'
const positionId = '0x0...'
const chainPool = await fullSailSDK.Pool.getByIdFromChain(poolId)
const transaction = await fullSailSDK.Position.claimFeeAndUnstakedPoolRewardsTransaction({
coinTypeA: chainPool.coinTypeA,
coinTypeB: chainPool.coinTypeB,
poolId,
positionId,
rewardCoinTypes: chainPool.rewardCoinsInfo.map(({ coinType }) => coinType),
})Claim oSAIL
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 fullSailSDK.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,
})Claim pool rewards for staked position
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 fullSailSDK.Position.claimStakedPoolRewardsTransaction({
coinTypeA: chainPool.coinTypeA,
coinTypeB: chainPool.coinTypeB,
poolId,
positionStakeId: position.stake_info.id,
rewardCoinTypes: chainPool.rewardCoinsInfo.map(({ coinType }) => coinType),
gaugeId: pool.gauge_id,
})Claim all staked position rewards
Claims both oSAIL and pool rewards for staked position.
import { MAX_LOCK_DURATION_DAYS, Percentage } from '@fullsailfinance/sdk'
const poolId = '0x0...'
const positionId = '0x0...'
// your existing permanent lock id
const permanentLockId = '0x0...'
// 'vesail' | 'sail' | 'usd'
const rewardChoice = 'vesail'
// 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 oSailReward = await fullSailSDK.Position.getOSailRewards({
poolId,
coinTypeA: chainPool.coinTypeA,
coinTypeB: chainPool.coinTypeB,
positionId,
oSailCoinType: currentEpochOSail.address,
gaugeId: pool.gauge_id,
})
const transaction = await fullSailSDK.Position.claimOSailAndStakedPoolRewardsTransaction({
coinTypeA: chainPool.coinTypeA,
coinTypeB: chainPool.coinTypeB,
poolId,
rewardCoinTypes: chainPool.rewardCoinsInfo.map(({ coinType }) => coinType),
positionStakeId: position.stake_info.id,
gaugeId: pool.gauge_id,
slippage,
oSailCoinType: currentEpochOSail.address,
oSailAmount: oSailReward,
positionUpdatedAt: position.updated_at,
rewardChoice,
// if rewardChoice is "vesail", you can provide your existing permanent lock id and oSAIL will be added to it
// otherwise new lock will be created every call
permanentLockId,
newLockDurationDays: MAX_LOCK_DURATION_DAYS,
newLockIsPermanent: true,
})Claim all rewards for multiple positions
The combined method for claiming all possible rewards from multiple positions.
import { MAX_LOCK_DURATION_DAYS, Percentage } from '@fullsailfinance/sdk'
const poolId = '0x0...'
const positionId = '0x0...'
// your existing permanent lock id
const permanentLockId = '0x0...'
// 'vesail' | 'sail' | 'usd'
const rewardChoice = 'vesail'
// 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 sailCoin = await fullSailSDK.Coin.getByType(fullSailSDK.config.sailCoinType)
let currentEpochOSail
let oSailReward
if (position.staked && pool.gauge_id) {
currentEpochOSail = await fullSailSDK.Coin.getCurrentEpochOSail()
oSailReward = await fullSailSDK.Position.getOSailRewards({
poolId,
coinTypeA: chainPool.coinTypeA,
coinTypeB: chainPool.coinTypeB,
positionId,
oSailCoinType: currentEpochOSail.address,
gaugeId: pool.gauge_id,
})
}
const transaction = await fullSailSDK.Position.claimAllTransaction([
{
coinTypeA: chainPool.coinTypeA,
coinTypeB: chainPool.coinTypeB,
poolId,
positionId,
rewardCoinTypes: chainPool.rewardCoinsInfo.map(({ coinType }) => coinType),
// params below are required only if position is staked
gaugeId: pool.gauge_id,
positionStakeId: position.stake_info?.id,
oSailCoinType: currentEpochOSail?.address,
oSailAmount: oSailReward,
oSailDecimals: sailCoin.decimals,
sailPrice: sailCoin.current_price,
positionUpdatedAt: position.updated_at,
rewardChoice,
slippage,
// if rewardChoice is "vesail", you can provide your existing permanent lock id and oSAIL will be added to it
// otherwise new lock will be created every call
permanentLockId,
newLockDurationDays: MAX_LOCK_DURATION_DAYS,
newLockIsPermanent: true,
},
])Vaults
- Get vault by ID
- Get vault positions by account
- Get pool rewards
- Get pool rewards list
- Get port rewards
- Get port rewards list
- Get oSAIL types to claim
- Get oSAIL types to claim list
- Get oSAIL rewards
- Get oSAIL rewards list
- Create vault position
- Add liquidity to vault position
- Create vault position (zap-in)
- Add liquidity to vault position (zap-in)
- Remove liquidity from vault position
- Close vault position
- Claim all vault rewards
Get vault by ID
Returns vault (port entry) data along with its associated port by port entry ID.
const portEntryId = '0x0...'
const { port_entry, port } = await fullSailSDK.PortEntry.getById(portEntryId)
// port_entry — PortEntry object
// port — Port object associated with this vaultGet vault positions by account
Returns a list of pools along with the positions and vault positions (strategies) belonging to each pool for a given account address.
const accountAddress = '0x0...'
const { pools } = await fullSailSDK.Position.getListByAccountV2({
address: accountAddress,
})
for (const { pool, positions, strategies } of pools) {
// pool — Pool object
// positions — Position[] for this pool
// strategies — Strategy[] where each item has port_entry (PortEntry) and port (Port)
}Get pool rewards
Returns the claimable pool reward amount for each reward coin type for a given vault position.
const poolId = '0x0...'
const portEntryId = '0x0...'
const { pool } = await fullSailSDK.Pool.getById(poolId)
const { port_entry, port } = await fullSailSDK.PortEntry.getById(portEntryId)
const rewards = await fullSailSDK.PortEntry.getPoolRewards({
coinTypeA: pool.token_a.address,
coinTypeB: pool.token_b.address,
poolId,
gaugeId: pool.gauge_id,
portId: port.id,
portEntryId: port_entry.id,
poolRewardCoinTypes: pool.rewards?.map(({ token }) => token.address) ?? [],
})
for (const { coinType, amountOwned } of rewards) {
// coinType — reward coin type address
// amountOwned — claimable amount as bigint
}Get pool rewards list
Batch version of getPoolRewards — fetches claimable pool reward amounts for multiple vault positions in a single RPC call.
const poolId = '0x0...'
const portEntryId = '0x0...'
const { pool } = await fullSailSDK.Pool.getById(poolId)
const { port_entry, port } = await fullSailSDK.PortEntry.getById(portEntryId)
const rewards = await fullSailSDK.PortEntry.getPoolRewardsList([
{
coinTypeA: pool.token_a.address,
coinTypeB: pool.token_b.address,
poolId,
gaugeId: pool.gauge_id,
portId: port.id,
portEntryId: port_entry.id,
poolRewardCoinTypes: pool.rewards?.map(({ token }) => token.address) ?? [],
},
// ...more entries
])
// rewards[i] — PortEntryGetPoolRewardsResultItem[] for the i-th entry
for (const entryRewards of rewards) {
for (const { coinType, amountOwned } of entryRewards) {
// coinType — reward coin type address
// amountOwned — claimable amount as bigint
}
}Get port rewards
Returns the claimable port (vault incentive) reward amount for each reward coin type for a given vault position.
const portEntryId = '0x0...'
const { port_entry, port } = await fullSailSDK.PortEntry.getById(portEntryId)
const rewards = await fullSailSDK.PortEntry.getPortRewards({
portId: port.id,
portEntryId: port_entry.id,
portRewardCoinTypes: port.rewards.map(({ token }) => token.address),
})
for (const { coinType, amountOwned } of rewards) {
// coinType — reward coin type address
// amountOwned — claimable amount as bigint
}Get port rewards list
Batch version of getPortRewards — fetches claimable port reward amounts for multiple vault positions in a single RPC call.
const portEntryId = '0x0...'
const { port_entry, port } = await fullSailSDK.PortEntry.getById(portEntryId)
const rewards = await fullSailSDK.PortEntry.getPortRewardsList([
{
portId: port.id,
portEntryId: port_entry.id,
portRewardCoinTypes: port.rewards.map(({ token }) => token.address),
},
// ...more entries
])
// rewards[i] — PortEntryGetPortRewardsResultItem[] for the i-th entry
for (const entryRewards of rewards) {
for (const { coinType, amountOwned } of entryRewards) {
// coinType — reward coin type address
// amountOwned — claimable amount as bigint
}
}Get oSAIL types to claim
Returns a list of oSAIL coin types that are available to claim for a given vault position.
const poolId = '0x0...'
const portEntryId = '0x0...'
const { pool } = await fullSailSDK.Pool.getById(poolId)
const { port_entry, port } = await fullSailSDK.PortEntry.getById(portEntryId)
const currentEpochOSail = await fullSailSDK.Coin.getCurrentEpochOSail()
const oSailTypes = await fullSailSDK.PortEntry.getOSailTypesToClaim({
coinTypeA: pool.token_a.address,
coinTypeB: pool.token_b.address,
poolId,
gaugeId: pool.gauge_id,
currentOSailCoinType: currentEpochOSail.address,
portId: port.id,
portEntryId: port_entry.id,
})
// oSailTypes — string[] of oSAIL coin type addresses available to claimGet oSAIL types to claim list
Batch version of getOSailTypesToClaim — fetches claimable oSAIL coin types for multiple vault positions in a single RPC call.
const poolId = '0x0...'
const portEntryId = '0x0...'
const { pool } = await fullSailSDK.Pool.getById(poolId)
const { port_entry, port } = await fullSailSDK.PortEntry.getById(portEntryId)
const currentEpochOSail = await fullSailSDK.Coin.getCurrentEpochOSail()
const oSailTypesList = await fullSailSDK.PortEntry.getOSailTypesToClaimList([
{
coinTypeA: pool.token_a.address,
coinTypeB: pool.token_b.address,
poolId,
gaugeId: pool.gauge_id,
currentOSailCoinType: currentEpochOSail.address,
portId: port.id,
portEntryId: port_entry.id,
},
// ...more entries
])
// oSailTypesList[i] — string[] of oSAIL coin type addresses for the i-th entryGet oSAIL rewards
Returns the claimable oSAIL amount for each available oSAIL coin type for a given vault position.
const poolId = '0x0...'
const portEntryId = '0x0...'
const { pool } = await fullSailSDK.Pool.getById(poolId)
const { port_entry, port } = await fullSailSDK.PortEntry.getById(portEntryId)
const currentEpochOSail = await fullSailSDK.Coin.getCurrentEpochOSail()
const oSailRewards = await fullSailSDK.PortEntry.getOSailRewards({
coinTypeA: pool.token_a.address,
coinTypeB: pool.token_b.address,
poolId,
gaugeId: pool.gauge_id,
currentOSailCoinType: currentEpochOSail.address,
portId: port.id,
portEntryId: port_entry.id,
})
for (const { coinType, amount } of oSailRewards) {
// coinType — oSAIL coin type address
// amount — claimable amount as bigint
}Get oSAIL rewards list
Batch version of getOSailRewards — fetches claimable oSAIL amounts for multiple vault positions in a single RPC call.
const poolId = '0x0...'
const portEntryId = '0x0...'
const { pool } = await fullSailSDK.Pool.getById(poolId)
const { port_entry, port } = await fullSailSDK.PortEntry.getById(portEntryId)
const currentEpochOSail = await fullSailSDK.Coin.getCurrentEpochOSail()
const oSailRewardsList = await fullSailSDK.PortEntry.getOSailRewardsList([
{
coinTypeA: pool.token_a.address,
coinTypeB: pool.token_b.address,
poolId,
gaugeId: pool.gauge_id,
currentOSailCoinType: currentEpochOSail.address,
portId: port.id,
portEntryId: port_entry.id,
},
// ...more entries
])
// oSailRewardsList[i] — PortEntryGetOSailRewardsResultItem[] for the i-th entry
for (const entryRewards of oSailRewardsList) {
for (const { coinType, amount } of entryRewards) {
// coinType — oSAIL coin type address
// amount — claimable amount as bigint
}
}Create vault position
Creates a new vault position by depositing both coins into the vault.
const poolId = '0x0...'
const portId = '0x0...'
const amountA = 1000000n
const amountB = 1000000n
const { pool } = await fullSailSDK.Pool.getById(poolId)
const currentEpochOSail = await fullSailSDK.Coin.getCurrentEpochOSail()
const transaction = await fullSailSDK.PortEntry.createPortEntryTransaction({
coinTypeA: pool.token_a.address,
coinTypeB: pool.token_b.address,
amountA,
amountB,
poolId,
gaugeId: pool.gauge_id,
portId,
pythPriceIdA: pool.token_a.pyth_feed,
aggregatorPriceIdA: pool.token_a.switchboard_aggregator,
pythPriceIdB: pool.token_b.pyth_feed,
aggregatorPriceIdB: pool.token_b.switchboard_aggregator,
currentOSailCoinType: currentEpochOSail.address,
rewardCoinTypes: pool.rewards?.map(({ token }) => token.address) ?? [],
})Add liquidity to vault position
Adds liquidity to an existing vault position.
All rewards claimed automatically within this transaction.
import { MAX_LOCK_DURATION_DAYS, Percentage } from '@fullsailfinance/sdk'
const poolId = '0x0...'
const portEntryId = '0x0...'
// your existing permanent lock id
const permanentLockId = '0x0...'
// 'vesail' | 'sail' | 'usd'
const rewardChoice = 'vesail'
const amountA = 1000000n
const amountB = 1000000n
// 1%
const slippage = Percentage.fromNumber(1)
const { pool } = await fullSailSDK.Pool.getById(poolId)
const { port_entry, port } = await fullSailSDK.PortEntry.getById(portEntryId)
const currentEpochOSail = await fullSailSDK.Coin.getCurrentEpochOSail()
const sailCoin = await fullSailSDK.Coin.getByType(fullSailSDK.config.sailCoinType)
const oSailMap = await fullSailSDK.Coin.getOSailMap()
const oSailRewards = await fullSailSDK.PortEntry.getOSailRewards({
coinTypeA: pool.token_a.address,
coinTypeB: pool.token_b.address,
poolId,
gaugeId: pool.gauge_id,
currentOSailCoinType: currentEpochOSail.address,
portId: port.id,
portEntryId: port_entry.id,
})
const transaction = await fullSailSDK.PortEntry.addLiquidityTransaction({
coinTypeA: pool.token_a.address,
coinTypeB: pool.token_b.address,
amountA,
amountB,
poolId,
gaugeId: pool.gauge_id,
portId: port.id,
portEntryId: port_entry.id,
pythPriceIdA: pool.token_a.pyth_feed,
aggregatorPriceIdA: pool.token_a.switchboard_aggregator,
pythPriceIdB: pool.token_b.pyth_feed,
aggregatorPriceIdB: pool.token_b.switchboard_aggregator,
currentOSailCoinType: currentEpochOSail.address,
portRewardCoinTypes: port.rewards.map(({ token }) => token.address),
poolRewardCoinTypes: pool.rewards?.map(({ token }) => token.address) ?? [],
oSailRewards: oSailRewards.map((reward) => ({
...reward,
expired: oSailMap[reward.coinType]?.expired ?? true,
})),
rewardChoice,
slippage,
oSailDecimals: sailCoin.decimals,
sailPrice: sailCoin.current_price,
// if rewardChoice is 'vesail', you can provide your existing permanent lock id and oSAIL will be added to it
// otherwise a new lock will be created every call
permanentLockId,
newLockDurationDays: MAX_LOCK_DURATION_DAYS,
newLockIsPermanent: true,
})Create vault position (zap-in)
Creates a new vault position by providing a single coin — part of it is swapped into the other coin automatically before depositing. Pass the result of calculateZapInDeposit as calculateZapInDepositResult.
The vault's current tick range is read from port.position (loaded via Pool.getById) and passed into calculateZapInDeposit.
import { Percentage } from '@fullsailfinance/sdk'
const poolId = '0x0...'
const portId = '0x0...'
const inputAmount = 1000000n
const fixedAmountA = true
// 1%
const slippage = Percentage.fromNumber(1)
const { pool, ports } = await fullSailSDK.Pool.getById(poolId)
const chainPool = await fullSailSDK.Pool.getByIdFromChain(poolId)
const currentEpochOSail = await fullSailSDK.Coin.getCurrentEpochOSail()
const port = ports.find(({ id }) => id === portId)
if (!port?.position) {
throw new Error('Vault position not found')
}
const calculateZapInDepositResult = await fullSailSDK.Position.calculateZapInDeposit({
coinTypeA: pool.token_a.address,
coinTypeB: pool.token_b.address,
coinDecimalsA: pool.token_a.decimals,
coinDecimalsB: pool.token_b.decimals,
tickLower: port.position.tick_lower,
tickUpper: port.position.tick_upper,
inputAmount,
fixedAmountA,
slippage,
currentSqrtPrice: chainPool.currentSqrtPrice,
})
const transaction = await fullSailSDK.PortEntry.createZapInPortEntryTransaction({
coinTypeA: pool.token_a.address,
coinTypeB: pool.token_b.address,
coinDecimalsA: pool.token_a.decimals,
coinDecimalsB: pool.token_b.decimals,
calculateZapInDepositResult,
poolId,
gaugeId: pool.gauge_id,
portId,
pythPriceIdA: pool.token_a.pyth_feed,
aggregatorPriceIdA: pool.token_a.switchboard_aggregator,
pythPriceIdB: pool.token_b.pyth_feed,
aggregatorPriceIdB: pool.token_b.switchboard_aggregator,
currentOSailCoinType: currentEpochOSail.address,
rewardCoinTypes: pool.rewards?.map(({ token }) => token.address) ?? [],
})Add liquidity to vault position (zap-in)
Adds liquidity to an existing vault position using a single coin — part of it is swapped into the other coin automatically. Pass the result of calculateZapInDeposit as calculateZapInDepositResult.
All rewards claimed automatically within this transaction.
import { MAX_LOCK_DURATION_DAYS, Percentage } from '@fullsailfinance/sdk'
const poolId = '0x0...'
const portEntryId = '0x0...'
// your existing permanent lock id
const permanentLockId = '0x0...'
// 'vesail' | 'sail' | 'usd'
const rewardChoice = 'vesail'
const inputAmount = 1000000n
const fixedAmountA = true
// 1%
const slippage = Percentage.fromNumber(1)
const { pool } = await fullSailSDK.Pool.getById(poolId)
const chainPool = await fullSailSDK.Pool.getByIdFromChain(poolId)
const { port_entry, port } = await fullSailSDK.PortEntry.getById(portEntryId)
const currentEpochOSail = await fullSailSDK.Coin.getCurrentEpochOSail()
const sailCoin = await fullSailSDK.Coin.getByType(fullSailSDK.config.sailCoinType)
const oSailMap = await fullSailSDK.Coin.getOSailMap()
if (!port.position) {
throw new Error('Vault position not found')
}
const calculateZapInDepositResult = await fullSailSDK.Position.calculateZapInDeposit({
coinTypeA: pool.token_a.address,
coinTypeB: pool.token_b.address,
coinDecimalsA: pool.token_a.decimals,
coinDecimalsB: pool.token_b.decimals,
tickLower: port.position.tick_lower,
tickUpper: port.position.tick_upper,
inputAmount,
fixedAmountA,
slippage,
currentSqrtPrice: chainPool.currentSqrtPrice,
})
const oSailRewards = await fullSailSDK.PortEntry.getOSailRewards({
coinTypeA: pool.token_a.address,
coinTypeB: pool.token_b.address,
poolId,
gaugeId: pool.gauge_id,
currentOSailCoinType: currentEpochOSail.address,
portId: port.id,
portEntryId: port_entry.id,
})
const transaction = await fullSailSDK.PortEntry.addZapInLiquidityTransaction({
coinTypeA: pool.token_a.address,
coinTypeB: pool.token_b.address,
coinDecimalsA: pool.token_a.decimals,
coinDecimalsB: pool.token_b.decimals,
calculateZapInDepositResult,
poolId,
gaugeId: pool.gauge_id,
portId: port.id,
portEntryId: port_entry.id,
pythPriceIdA: pool.token_a.pyth_feed,
aggregatorPriceIdA: pool.token_a.switchboard_aggregator,
pythPriceIdB: pool.token_b.pyth_feed,
aggregatorPriceIdB: pool.token_b.switchboard_aggregator,
currentOSailCoinType: currentEpochOSail.address,
portRewardCoinTypes: port.rewards.map(({ token }) => token.address),
poolRewardCoinTypes: pool.rewards?.map(({ token }) => token.address) ?? [],
oSailRewards: oSailRewards.map((reward) => ({
...reward,
expired: oSailMap[reward.coinType]?.expired ?? true,
})),
rewardChoice,
slippage,
oSailDecimals: sailCoin.decimals,
sailPrice: sailCoin.current_price,
// if rewardChoice is 'vesail', you can provide your existing permanent lock id and oSAIL will be added to it
// otherwise a new lock will be created every call
permanentLockId,
newLockDurationDays: MAX_LOCK_DURATION_DAYS,
newLockIsPermanent: true,
})Remove liquidity from vault position
Removes liquidity from an existing vault position.
All rewards are claimed automatically within this transaction.
port_entry.ratio is the user's share of the vault position. Multiply the vault position's token amounts and liquidity by ratio to get the user's maximum withdrawable amounts, then scale by the desired withdrawal percentage (withdrawPercent must be greater than 0 and less than 100, not inclusive — to withdraw 100% use closeTransaction instead).
import Decimal from 'decimal.js'
import { MAX_LOCK_DURATION_DAYS, Percentage } from '@fullsailfinance/sdk'
const poolId = '0x0...'
const portEntryId = '0x0...'
// your existing permanent lock id
const permanentLockId = '0x0...'
// 'vesail' | 'sail' | 'usd'
const rewardChoice = 'vesail'
// percentage of the position to withdraw (1–100)
const withdrawPercent = 100
// 1%
const slippage = Percentage.fromNumber(1)
const { pool } = await fullSailSDK.Pool.getById(poolId)
const { port_entry, port } = await fullSailSDK.PortEntry.getById(portEntryId)
const chainPool = await fullSailSDK.Pool.getByIdFromChain(poolId)
const currentEpochOSail = await fullSailSDK.Coin.getCurrentEpochOSail()
const sailCoin = await fullSailSDK.Coin.getByType(fullSailSDK.config.sailCoinType)
const oSailMap = await fullSailSDK.Coin.getOSailMap()
const oSailRewards = await fullSailSDK.PortEntry.getOSailRewards({
coinTypeA: pool.token_a.address,
coinTypeB: pool.token_b.address,
poolId,
gaugeId: pool.gauge_id,
currentOSailCoinType: currentEpochOSail.address,
portId: port.id,
portEntryId: port_entry.id,
})
if (!port.position) {
throw new Error('Vault position not found')
}
const ratio = new Decimal(port_entry.ratio)
const amountA = BigInt(
new Decimal(port.position.amount_token_a).mul(ratio).mul(withdrawPercent).div(100).floor().toString(),
)
const amountB = BigInt(
new Decimal(port.position.amount_token_b).mul(ratio).mul(withdrawPercent).div(100).floor().toString(),
)
const liquidity = BigInt(
new Decimal(port.position.liquidity).mul(ratio).mul(withdrawPercent).div(100).floor().toString(),
)
const transaction = await fullSailSDK.PortEntry.removeLiquidityTransaction({
coinTypeA: pool.token_a.address,
coinTypeB: pool.token_b.address,
poolId,
gaugeId: pool.gauge_id,
portId: port.id,
portEntryId: port_entry.id,
volume: BigInt(port_entry.volume),
pythPriceIdA: pool.token_a.pyth_feed,
aggregatorPriceIdA: pool.token_a.switchboard_aggregator,
pythPriceIdB: pool.token_b.pyth_feed,
aggregatorPriceIdB: pool.token_b.switchboard_aggregator,
currentOSailCoinType: currentEpochOSail.address,
portRewardCoinTypes: port.rewards.map(({ token }) => token.address),
poolRewardCoinTypes: pool.rewards?.map(({ token }) => token.address) ?? [],
oSailRewards: oSailRewards.map((reward) => ({
...reward,
expired: oSailMap[reward.coinType]?.expired ?? true,
})),
rewardChoice,
slippage,
oSailDecimals: sailCoin.decimals,
sailPrice: sailCoin.current_price,
tickLower: port.position.tick_lower,
tickUpper: port.position.tick_upper,
liquidity,
currentSqrtPrice: chainPool.currentSqrtPrice,
amountA,
amountB,
// if rewardChoice is 'vesail', you can provide your existing permanent lock id and oSAIL will be added to it
// otherwise a new lock will be created every call
permanentLockId,
newLockDurationDays: MAX_LOCK_DURATION_DAYS,
newLockIsPermanent: true,
})Close vault position
Removes all liquidity and closes the vault position entirely. Use this instead of removeLiquidityTransaction when withdrawing 100%.
All rewards are claimed automatically within this transaction.
import Decimal from 'decimal.js'
import { MAX_LOCK_DURATION_DAYS, Percentage } from '@fullsailfinance/sdk'
const poolId = '0x0...'
const portEntryId = '0x0...'
// your existing permanent lock id
const permanentLockId = '0x0...'
// 'vesail' | 'sail' | 'usd'
const rewardChoice = 'vesail'
// 1%
const slippage = Percentage.fromNumber(1)
const { pool } = await fullSailSDK.Pool.getById(poolId)
const { port_entry, port } = await fullSailSDK.PortEntry.getById(portEntryId)
const chainPool = await fullSailSDK.Pool.getByIdFromChain(poolId)
const currentEpochOSail = await fullSailSDK.Coin.getCurrentEpochOSail()
const sailCoin = await fullSailSDK.Coin.getByType(fullSailSDK.config.sailCoinType)
const oSailMap = await fullSailSDK.Coin.getOSailMap()
const oSailRewards = await fullSailSDK.PortEntry.getOSailRewards({
coinTypeA: pool.token_a.address,
coinTypeB: pool.token_b.address,
poolId,
gaugeId: pool.gauge_id,
currentOSailCoinType: currentEpochOSail.address,
portId: port.id,
portEntryId: port_entry.id,
})
if (!port.position) {
throw new Error('Vault position not found')
}
const ratio = new Decimal(port_entry.ratio)
const amountA = BigInt(new Decimal(port.position.amount_token_a).mul(ratio).floor().toString())
const amountB = BigInt(new Decimal(port.position.amount_token_b).mul(ratio).floor().toString())
const liquidity = BigInt(new Decimal(port.position.liquidity).mul(ratio).floor().toString())
const transaction = await fullSailSDK.PortEntry.closeTransaction({
coinTypeA: pool.token_a.address,
coinTypeB: pool.token_b.address,
poolId,
gaugeId: pool.gauge_id,
portId: port.id,
portEntryId: port_entry.id,
volume: BigInt(port_entry.volume),
pythPriceIdA: pool.token_a.pyth_feed,
aggregatorPriceIdA: pool.token_a.switchboard_aggregator,
pythPriceIdB: pool.token_b.pyth_feed,
aggregatorPriceIdB: pool.token_b.switchboard_aggregator,
currentOSailCoinType: currentEpochOSail.address,
portRewardCoinTypes: port.rewards.map(({ token }) => token.address),
poolRewardCoinTypes: pool.rewards?.map(({ token }) => token.address) ?? [],
oSailRewards: oSailRewards.map((reward) => ({
...reward,
expired: oSailMap[reward.coinType]?.expired ?? true,
})),
rewardChoice,
slippage,
oSailDecimals: sailCoin.decimals,
sailPrice: sailCoin.current_price,
tickLower: port.position.tick_lower,
tickUpper: port.position.tick_upper,
liquidity,
currentSqrtPrice: chainPool.currentSqrtPrice,
amountA,
amountB,
// if rewardChoice is 'vesail', you can provide your existing permanent lock id and oSAIL will be added to it
// otherwise a new lock will be created every call
permanentLockId,
newLockDurationDays: MAX_LOCK_DURATION_DAYS,
newLockIsPermanent: true,
})Claim all vault rewards
Claims all available rewards (oSAIL, pool rewards, and port rewards) for one or more vault positions in a single transaction.
import { MAX_LOCK_DURATION_DAYS, Percentage } from '@fullsailfinance/sdk'
const poolId = '0x0...'
const portEntryId = '0x0...'
// your existing permanent lock id
const permanentLockId = '0x0...'
// 'vesail' | 'sail' | 'usd'
const rewardChoice = 'vesail'
// 1%
const slippage = Percentage.fromNumber(1)
const { pool } = await fullSailSDK.Pool.getById(poolId)
const { port_entry, port } = await fullSailSDK.PortEntry.getById(portEntryId)
const currentEpochOSail = await fullSailSDK.Coin.getCurrentE