npm package: @fullsailfinance/sdk
Description: SDK for FullSail ve(4,4) dex
Author: LFBuild
Latest version: 9.0.0

# @fullsailfinance/sdk

- [Installation](#installation)
- [Configuration](#configuration)
- [Key Concepts](#key-concepts)
- [Usage examples](#usage-examples)

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

## Installation

##### npm:

    $ npm i @fullsailfinance/sdk

##### yarn:

    $ yarn add @fullsailfinance/sdk

## Configuration

```typescript
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

```typescript
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 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
- `gauge_killed`: can be `true` if 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. If gauge exists for this pool, position should be staked, otherwise it will only receive pool rewards. 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 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 only pool rewards
- **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. 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:

1. `"vesail"` - lock it into veSAIL to participate in governance and earn trading fees.
2. `"sail"` - redeem it for liquid SAIL in a 2 to 1 ratio (100 oSAIL will be redeemed for 50 SAIL).
3. `"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 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 **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.

- [Positions](#positions)
- [Swap (directly and via router)](#swap)
- [Locks](#locks)
- [Vote](#vote)

### Positions

- [Create position](#create-position)
- [Add liquidity to position](#add-liquidity-to-position)
- [Stake position](#stake-position)
- [Unstake position](#unstake-position)
- [Remove Liquidity from position](#remove-liquidity-from-position)
- [Close position](#close-position)
- [Claim fee](#claim-fee)
- [Claim pool rewards for unstaked position](#claim-pool-rewards-for-unstaked-position)
- [Claim all unstaked position rewards](#claim-all-unstaked-position-rewards)
- [Claim oSAIL](#claim-osail)
- [Claim pool rewards for staked position](#claim-pool-rewards-for-staked-position)
- [Claim all staked position rewards](#claim-all-staked-position-rewards)
- [Claim all rewards for multiple positions](#claim-all-rewards-for-multiple-positions)

#### Create position

```typescript
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 gaugeId provided, position will be staked automatically.
  gaugeId: pool.gauge_id,
})
```

#### Add liquidity to position

Only oSAIL claimed automatically within this transaction. Fees and pool rewards are not.

```typescript
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 && !pool.gauge_killed) {
  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,
  // params below are required only if position is staked
  gaugeId: pool.gauge_id,
  positionStakeId: position.stake_info?.id,
  oSailCoinType: currentEpochOSail?.address,
  oSailAmount: oSailReward,
  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,
})
```

#### Stake position

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 receive only pool rewards.
Only fees claimed automatically within this transaction. Pool rewards are not.

```typescript
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

Should be used if gauge contract for the pool was killed (`pool.gauge_killed` is `true`) and 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.

```typescript
const poolId = '0x0...'
const positionId = '0x0...'

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 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,
})
```

#### Remove liquidity from position

Only oSAIL claimed automatically within this transaction. Fees and pool rewards are not.

```typescript
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 && !pool.gauge_killed) {
  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,
  // 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.

```typescript
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 && !pool.gauge_killed) {
  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,
  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.

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

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

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

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

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

```typescript
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 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,
  oSailCoinType: currentEpochOSail?.address,
})
```

#### Claim all rewards for multiple positions

The combined method for claiming all posible rewards from multiple positions.

```typescript
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 && !pool.gauge_killed) {
  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,
    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,
  },
])
```

### Swap

Uses Cetus Aggregator to swap coins.
See [Cetus Aggregator docs](https://cetus-1.gitbook.io/cetus-developer-docs/developer/cetus-aggregator) for more details.

```typescript
import { Percentage } from '@fullsailfinance/sdk'

const from = '0x0...'
const target = '0x0...'
const amount = 1000000n

// 1%
const slippage = Percentage.fromNumber(1)

// findRouters method from Cetus Aggregator
const router = await fullSailSDK.Swap.getSwapRoute({
  from,
  target,
  amount,
})

// routerSwap method from Cetus Aggregator
const transaction = await fullSailSDK.Swap.swapRouterTransaction({
  router,
  slippage: slippage.toCoefficient(),
})
```

Swaps coins directly through smart contract.

```typescript
import { Percentage } from '@fullsailfinance/sdk'

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,
})
```

### Locks

- [Create lock (using SAIL or oSAIL)](#create-lock)
- [Claim oSAIL and create lock](#claim-osail-and-create-lock)
- [Increase lock amount](#increase-lock-amount)
- [Merge locks](#merge-locks)
- [Split lock](#split-lock)
- [Increase lock duration](#increase-lock-duration)
- [Toggle isPermanent](#toggle-ispermanent)

#### Create lock

Creates lock using SAIL.

```typescript
import { MAX_LOCK_DURATION_DAYS } from '@fullsailfinance/sdk'

// 1 SAIL
const amount = 1000000n

const transaction = await fullSailSDK.Lock.createLockTransaction({
  amount,
  isPermanent: true,
  durationDays: MAX_LOCK_DURATION_DAYS,
})
```

Creates lock using oSAIL.

```typescript
import { MAX_LOCK_DURATION_DAYS } from '@fullsailfinance/sdk'

const oSailCoinType = '0x0...'

// 1 oSAIL
const amount = 1000000n

const transaction = await fullSailSDK.Lock.createLockFromOSailTransaction({
  amount,
  oSailCoinType,
  isPermanent: true,
  durationDays: MAX_LOCK_DURATION_DAYS,
})
```

#### Claim oSAIL and create lock

Combines `Position.claimOSailTransaction()` and `Lock.createLockFromOSailTransaction()` for convinience.

```typescript
import { MAX_LOCK_DURATION_DAYS } from '@fullsailfinance/sdk'

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.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: MAX_LOCK_DURATION_DAYS,
  isPermanent: true,
})
```

#### Increase lock amount

Increases lock by provided SAIL amount.

```typescript
const lockId = '0x0...'

// 1 SAIL
const amount = 1000000n

const transaction = await fullSailSDK.Lock.increaseAmountTransaction({
  lockId,
  amount,
})
```

#### Merge locks

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.

```typescript
const fromLockId = '0x0...'
const toLockId = '0x0...'

const transaction = await fullSailSDK.Lock.mergeTransaction({
  fromLockId,
  toLockId,
  isFromLockVoted: true,
  isToLockVoted: false,
})
```

#### Split lock

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.

```typescript
const lockId = '0x0...'

// 1 SAIL
const amount = 1000000n

const transaction = await fullSailSDK.Lock.splitTransaction({
  lockId,
  amount,
  isVoted: true,
})
```

#### Transfer lock

Transfers lock to another wallet.

```typescript
const lockId = '0x0...'
const receiver = '0x0...'

const transaction = await fullSailSDK.Lock.transferTransaction({
  lockId,
  receiver,
})
```

#### Increase lock duration

```typescript
const lockId = '0x0...'
// 4 years
const durationDays = 365 * 4

const transaction = await fullSailSDK.Lock.increaseDurationTransaction({
  lockId,
  durationDays,
})
```

#### Toggle isPermanent

Enables `isPermanent` flag for lock.

```typescript
const lockId = '0x0...'

const transaction = await fullSailSDK.Lock.enablePermanentTransaction({
  lockId,
})
```

Disables `isPermanent` flag for lock.

```typescript
const lockId = '0x0...'

const transaction = await fullSailSDK.Lock.disablePermanentTransaction({
  lockId,
})
```

### Vote

Votes using all locks.

`weight` is in abstract units to calculate voting power ratio. One `weight` cannot be more than 10000. Sum of `weights` 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.

```typescript
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 },
  ],
})
```