Close Position
Remove all liquidity and close a position permanently.
Quick Start
const tx = await sdk.Position.closePositionTransactionPayload({
pool_id: '0x...',
pos_id: '0x...',
coinTypeA: '0x2::sui::SUI',
coinTypeB: '0x...::usdc::USDC',
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
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.
Close with Slippage Protection
async function estimateClosePosition(
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 closeLiquidityPayloadParams: ClosePositionParams = {
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,
};
const tx = await sdk.Position.closePositionTransactionPayload(closeLiquidityPayloadParams);
const result = await sdk.fullClient.signAndExecuteTransaction({
transaction: tx,
signer: keypair
})
Close vs Remove Liquidity
Action
Effect
Position NFT
Remove Liquidity
Withdraws tokens
Remains (empty)
Close Position
Withdraws all + burns NFT
Destroyed
Error Handling
try {
const tx = await sdk.Position.closePositionTransactionPayload(params)
} catch (error) {
if (error.message.includes('Slippage exceeded')) {
console.log('Price moved, increase slippage')
}
if (error.message.includes('Position not found')) {
console.log('Position does not exist')
}
}
Important Notes
Closing automatically collects all fees and rewards
Position NFT is burned and cannot be recovered
All liquidity must be removed (SDK handles this)
Cannot close position with active orders
Last updated