Adding & Removing Liquidity
This section explains how to modify a liquidity position by adding or removing liquidity on the Storyhunt V3 protocol.
Introduction
This section explains how to modify a liquidity position by adding or removing liquidity on the Storyhunt V3 protocol. Liquidity positions are represented as non-fungible tokens. This guide demonstrates adding and removing liquidity for the USDC - DAI pair using the NonfungiblePositionManager
class. The inputs include the tokens, pool fee, and fractions to adjust the position's liquidity.
Adding Liquidity to a Position
To add liquidity, construct a modified position by calculating the amount to increase based on the current position:
const fractionToAdd: number = ...;
const amount0Increased: JSBI = fromReadableAmount(
readableAmount0 * fractionToAdd,
token0.decimals
);
const amount1Increased: JSBI = fromReadableAmount(
readableAmount1 * fractionToAdd,
token1.decimals
);
const positionToIncreaseBy = constructPosition(
amount0Increased,
amount1Increased
);
Here, fromReadableAmount()
converts the token amount to its smallest unit (e.g., 1 WIP = 1e18 Wei).
Create the constructPosition
function to build a Position
object:
import { Pool, Position } from '@storyhunt/v3-sdk';
import JSBI from 'jsbi';
function constructPosition(
amount0: JSBI,
amount1: JSBI
): Position {
const pool = new Pool(...);
return Position.fromAmounts({
pool,
tickLower:
nearestUsableTick(pool.tickCurrent, pool.tickSpacing) -
pool.tickSpacing * 2,
tickUpper:
nearestUsableTick(pool.tickCurrent, pool.tickSpacing) +
pool.tickSpacing * 2,
amount0,
amount1,
useFullPrecision: true,
});
}
Next, create an options object of type AddLiquidityOptions
:
import { AddLiquidityOptions } from '@storyhunt/v3-sdk';
const addLiquidityOptions: AddLiquidityOptions = {
deadline: Math.floor(Date.now() / 1000) + 60 * 20,
slippageTolerance: new Percent(50, 10_000),
tokenId,
};
Use the NonfungiblePositionManager
to get the calldata and value for the transaction:
import { NonfungiblePositionManager } from '@storyhunt/v3-sdk';
const { calldata, value } = NonfungiblePositionManager.addCallParameters(
positionToIncreaseBy,
addLiquidityOptions
);
Construct and execute the transaction:
import { ethers } from 'ethers';
const transaction = {
data: calldata,
to: NONFUNGIBLE_POSITION_MANAGER_CONTRACT_ADDRESS,
value: value,
from: address,
maxFeePerGas: MAX_FEE_PER_GAS,
maxPriorityFeePerGas: MAX_PRIORITY_FEE_PER_GAS,
};
const wallet = new ethers.Wallet(privateKey, provider);
const txRes = await wallet.sendTransaction(transaction);
Removing Liquidity from a Position
To remove liquidity, start by constructing a position identical to the minted one:
const amount0: JSBI = fromReadableAmount(
readableAmount0 * fractionToRemove,
token0.decimals
);
const amount1: JSBI = fromReadableAmount(
readableAmount1 * fractionToRemove,
token1.decimals
);
const currentPosition = constructPosition(
amount0,
amount1
);
Create a RemoveLiquidityOptions
object:
import { RemoveLiquidityOptions } from '@storyhunt/v3-sdk';
import { Percent } from '@storyhunt/sdk-core';
const removeLiquidityOptions: RemoveLiquidityOptions = {
deadline: Math.floor(Date.now() / 1000) + 60 * 20,
slippageTolerance: new Percent(50, 10_000),
tokenId: positionId,
liquidityPercentage: new Percent(0.5),
collectOptions,
};
The collectOptions
parameter allows optional fee collection:
import { CurrencyAmount } from '@storyhunt/sdk-core';
import { CollectOptions } from '@storyhunt/v3-sdk';
const collectOptions: Omit<CollectOptions, 'tokenId'> = {
expectedCurrencyOwed0: CurrencyAmount.fromRawAmount(
token0,
0
),
expectedCurrencyOwed1: CurrencyAmount.fromRawAmount(
token1,
0
),
recipient: address,
};
Use removeCallParameters
to get the calldata and value for the transaction:
const { calldata, value } = NonfungiblePositionManager.removeCallParameters(
currentPosition,
removeLiquidityOptions
);
Construct and execute the transaction:
const transaction = {
data: calldata,
to: NONFUNGIBLE_POSITION_MANAGER_CONTRACT_ADDRESS,
value: value,
from: address,
maxFeePerGas: MAX_FEE_PER_GAS,
maxPriorityFeePerGas: MAX_PRIORITY_FEE_PER_GAS,
};
const txRes = await wallet.sendTransaction(transaction);
Next Steps
Now that you can mint and modify a position, check out how to collect fees from the position!