Skip to main content

1. Introduction to Positions in Pools

A position in a liquidity pool represents your contribution of liquidity, which allows traders to swap between tokens while you earn a share of the trading fees. When you open a position, you decide how much liquidity to add, and this liquidity can later be adjusted or removed.
  • Splash Pools: Provide liquidity without specifying a price range. Ideal for those seeking a simple way to start providing liquidity.
  • Concentrated Liquidity Pools: Allow you to provide liquidity within a specified price range, enabling higher capital efficiency but requiring more advanced management.
Upon creation of the position, an NFT will be minted to represent ownership of the position. This NFT is used by the program to verify your ownership when adjusting liquidity, harvesting rewards, or closing the position. For more information, refer to Tokenized Positions.
⚠️ Risk of Divergence loss: The ratio of Token A to Token B that you deposit as liquidity is determined by several factors, including the current price. As trades occur against the pool, the amounts of Token A and Token B in the pool — and in your position — will change, which affects the price of the tokens relative to each other. This can work to your advantage, but it may also result in the combined value of your tokens (including any earned fees and rewards) being lower than when you initially provided liquidity.

2. Getting Started Guide

The usual action of opening a position consists of two instruction calls
  • initializeTickArray to initialize the tick arrays that would host your desired ticks for your position if they do not exist yet.
  • Whirlpool.openPosition or Whirlpool.openPositionWithMetadata to mint the position and define the tick range
  • increaseLiquidity to transfer tokens from your wallet into a position.
The Whirlpool.openPosition function now supports both traditional and Token2022-based position NFTs. To utilize Token2022, provide the Token2022 ProgramId as the tokenProgramId parameter when calling openPosition. This will mint the NFT using Token2022, which leverages the MetadataPointer and TokenMetadata extensions, eliminating the need for Metaplex metadata accounts.

Opening Position with Metadata

By using Whirlpool.openPositionWithMetadata, users have the option of appending Metaplex metadata onto the Token Program position NFT. Doing so will allow the token to be identifiable in tracking websites or wallets as a Whirlpool NFT. The drawback is it will require more compute-budget and will incurr Metaplex fees of 0.01 SOL.

Initialize Tick Array accounts if needed

For liquidity to exist in the Whirlpool, the tick-array that contains that particular tick must be initialized. Calculate the start_index of the required tick array and use the initialize_tick_array instruction to initialize it. More often than not, tick-arrays are already created. But if you want your code to be defensive, you should do a check prior to invoking open_position. To understand more on how Tick-Arrays work in Whirlpools, read here.
TypeScript

Open a Concentrated Liqudity Position with WhirlpoolClient

WhirlpoolClient’s openPosition method bundles the open and increase liquidity instructions into a single transaction for you. Below is a code sample to create a position for the SOL/devUSDC pool in the tick range of -138176 and -22976, with the intention to deposit 1 SOL into the position.
TypeScript

Open a Splash Pool Position with WhirlpoolClient

Opening a position in a splash pool is almost identical to opening a concentrated liquidity position. The only difference is where the lower and upper bounds are set. For splash pools, defined the position’s range to the global boundaries using MIN_TICK_INDEX and MAX_TICK_INDEX.
TypeScript

The Manual way

Follow the instructions below if you would like to have more control over your instruction building process. Note that open_position does not add liquidity to a position. Follow the next article “Modify Liquidity” to add liquidity.

Determine position parameters

To open a position against a Whirlpool, you must first define certain parameters of your position to invoke the open_position instruction.
  • WhirlpoolKey - The public key for the Whirlpool that the position will host liquidity in.
  • tickLowerIndex, tickUpperIndex - The tick index bounds for the position. Must be an initializable index.
  • positionMintAddress - A generated empty Keypair that will be initialized to a token mint.
  • positionPda - Derived address of the position account via getPositionPda
  • positionTokenAccountAddress - This is the account that will hold the minted position token. It is the associated token address of the position-mint.
TypeScript

3. Usage examples

Opening a Position in a Splash Pool

Suppose you want to provide 1,000,000 tokens of Token A at a price of 0.0001 SOL. You will also need to provide 100 SOL as Token B to match the price. By using the SDK to open full range positions, you ensure that your liquidity is spread evenly across all price levels. This approach is ideal if you are launching a new token and want to facilitate easy swaps for traders.

Opening a Position in a Concentrated Liquidity Pool

If you want to maximize capital efficiency, you can open a position in a Concentrated Liquidity Pool. For example, if the current price is at 0.01 and you want to maximize profitability, you could use the SDK to deposit liquidity between the price range of 0.009 and 0.011. This approach allows you to focus your liquidity in a narrow range, making it more effective and potentially more profitable.

Autonomous capital allocation

An LP management agent can use openConcentratedPosition as its execution step after deciding on a strategy. For example: query the pool’s current price via Monitor Pools, compute an optimal price range based on recent volatility, calculate the deposit amount using tokenA or tokenB parameters, and open the position. The returned positionMint uniquely identifies the position for all subsequent monitoring, harvesting, and closing operations.

Next Steps

After opening a position, you can:
  • Add or Remove Liquidity: Adjust the amount of liquidity in your position based on market conditions.
  • Harvest Rewards: Collect rewards and fees without closing the position.
  • Monitor Performance: Track your position’s performance and earned fees.
  • Close Position: When you decide to exit, close the position and withdraw the provided tokens along with any earned fees.

Common Errors

  • InvalidTickIndex (0x177a)
    • tickLowerIndex is higher than upper tickUpperIndex
    • Some tick indices is not an initializable index (not a multiple of tickSpacing). Use TickUtil.getInitializableTickIndex to get the closest initializable tick to your index.
    • Some tick indices is out of bounds
  • NotRentExempt (0x0)
    • Usually, the TickArray that houses your tickLowerIndex or tickUpperIndex has not been initialized. Use the WhirlpoolClient.initTickArrayForTicks or WhirlpoolIx.initTickArrayIx to initialize the array at the derived startTickIndex.
    • Alternatively, if this failure is from init_tick_array, the tick array has already been initialized.