Remove Liquidity
Withdraw tokens from a liquidity position in Ferra pools.
Quick Start
const tx = await sdk.Position.removeLiquidityTransactionPayload({
pool_id: '0x...',
pos_id: '0x...',
coinTypeA: '0x2::sui::SUI',
coinTypeB: '0x...::usdc::USDC',
delta_liquidity: '500000',
min_amount_a: '490000000',
min_amount_b: '490000'
collect_fee: true
rewarder_coin_types: [],
})
Parameters
Parameter
Type
Description
pool_id
string
The object id about which pool you want to operation.
pos_id
string
Position object ID
coinTypeA
string
First token type
coinTypeB
string
Second token type
delta_liquidity
string
Liquidity amount to remove
min_amount_a
string
Minimum token A to receive
min_amount_b
string
Minimum token B to receive
collect_fee
boolean
Indicates whether to collect fees during the removal.
rewarder_coin_types
string[]
Coin types associated with rewarder contracts.
Calculate Token Amounts
async function estimateRemovePosition(
pool,
position,
amountABigint,
amountBBigint
) {
const slippage = new Percentage(new BN(5), new BN(1000));
const { curSqrtPrice, lowerSqrtPrice, upperSqrtPrice } = getCoinsRate(
pool,
position
);
const tokenAmounts = adjustForCoinSlippage(
{
coinA: new BN(amountABigint.toFixed(0)),
coinB: new BN(amountBBigint.toFixed(0)),
},
slippage,
false
);
const liquidity = ClmmPoolUtil.estimateLiquidityFromcoinAmounts(
curSqrtPrice,
Number(position.tick_lower_index),
Number(position.tick_upper_index),
{
coinA: new BN(Math.ceil(amountABigint)),
coinB: new BN(Math.ceil(amountBBigint)),
}
);
return {
tokenMaxA: tokenAmounts.tokenMaxA,
tokenMaxB: tokenAmounts.tokenMaxB,
lowerTick: lowerSqrtPrice,
upperTick: upperSqrtPrice,
curSqrtPrice,
liquidity,
};
}
function getCoinsRate(pool, position) {
const curSqrtPrice = new BN(pool.current_sqrt_price);
const lowerSqrtPrice = TickMath.tickIndexToSqrtPriceX64(
position.tick_lower_index
);
const upperSqrtPrice = TickMath.tickIndexToSqrtPriceX64(
position.tick_upper_index
);
const amounts = ClmmPoolUtil.getCoinAmountFromLiquidity(
new BN(position.liquidity),
curSqrtPrice,
lowerSqrtPrice,
upperSqrtPrice,
false
);
const coinAValue = fromDecimalsAmount(
amounts.coinA.toNumber(),
pool.coinA?.decimals ?? 1
);
const coinBValue = fromDecimalsAmount(
amounts.coinB.toNumber(),
pool.coinB?.decimals ?? 1
);
const total = coinAValue + coinBValue;
const percent = {
ratioA: coinAValue / total,
ratioB: coinBValue / total,
};
return {
percent,
curSqrtPrice,
lowerSqrtPrice,
upperSqrtPrice,
};
}
Complete Example
const position = await sdk.Position.getPositionById(positionId)
const pool = await sdk.Pool.getPool(position.pool)
const amountA = 100000000
const amountB = 2000000000
const percent = 0.2
const { tokenMaxA, tokenMaxB, liquidity } = await estimateRemovePosition(poolOnchain, position, amountA, amountB);
const rewards = await ferraSDK.Rewarder.fetchPositionRewarders(
poolOnchain,
position.pos_object_id
);
const rewardCoinTypes = rewards
.filter((item) => Number(item.amount_owed) > 0)
.map((item) => item.coin_address);
const removeLiquidityPayloadParams: RemoveLiquidityParams = {
coinTypeA: poolOnchain.coinTypeA,
coinTypeB: poolOnchain.coinTypeB,
pool_id: poolOnchain.poolAddress,
min_amount_a: tokenMaxA.toString(),
min_amount_b: tokenMaxB.toString(),
collect_fee: true,
rewarder_coin_types: rewardCoinTypes,
pos_id: position.pos_object_id,
delta_liquidity: new BN(position.liquidity).lte(new BN(liquidity))
? position.liquidity.toString()
: liquidity.toString(),
};
const tx = await sdk.Position.removeLiquidityTransactionPayload(removeLiquidityPayloadParams);
const result = await sdk.fullClient.signAndExecuteTransaction({
transaction: tx,
signer: keypair
})
Error Handling
try {
const tx = await sdk.Position.removeLiquidityTransactionPayload(params)
} catch (error) {
if (error.message.includes('Slippage exceeded')) {
console.log('Price moved too much, increase slippage')
}
if (error.message.includes('Insufficient liquidity')) {
console.log('Not enough liquidity in position')
}
}
Important Notes
SDK automatically collects fees before removing liquidity
Tokens are sent directly to sender address
Cannot remove more liquidity than position contains
Position NFT remains after removing all liqui
Last updated