How Positions Work
Every DAMM v2 LP position is an on-chain account paired with a position NFT. Whoever holds the NFT controls the position. This design enables positions to be:- Transferred between wallets (NFT transfer = position ownership transfer)
- Traded on secondary markets
- Used as collateral in future integrations
Position Account Structure
| Field | Type | Description |
|---|---|---|
pool | Pubkey | The pool this position belongs to |
nft_mint | Pubkey | The position NFT mint address |
unlocked_liquidity | u128 | Freely withdrawable liquidity |
vested_liquidity | u128 | Liquidity releasing on a vesting schedule |
permanent_locked_liquidity | u128 | Liquidity locked forever |
fee_a_pending | u64 | Unclaimed token A fees |
fee_b_pending | u64 | Unclaimed token B fees |
fee_a_per_token_checkpoint | [u8; 32] | Last fee-per-token snapshot (U256) |
fee_b_per_token_checkpoint | [u8; 32] | Last fee-per-token snapshot (U256) |
reward_infos | [UserRewardInfo; 2] | Per-position farming reward state |
inner_vesting | InnerVesting | Inline vesting schedule (no separate account needed) |
metrics | PositionMetrics | Cumulative claimed fee totals |
Creating a Position
A position is created when a pool is initialized viacreateCustomPool. The first position is created as part of pool setup. Additional positions can be created via addLiquidity / separate position creation instructions.
Required accounts:
payer— who pays rentcreator— who receives protocol creation fee creditpositionNft— new Keypair whose public key becomes the NFT minttokenAMint,tokenBMint— pool tokens- Pool config account
EvtCreatePosition, EvtInitializePool (on first creation), EvtLiquidityChange
Adding Liquidity
After a pool is live, any wallet can add liquidity to an existing position (if they own the position NFT) or create a new position.Calculating how much to deposit
UsegetDepositQuote on the SDK with the current pool state:
EvtLiquidityChange with change_type: 0
Removing Liquidity
To withdraw liquidity, pass the liquidity delta you want to remove. The program returns proportional token amounts from the pool’s current reserves.Calculating withdraw amounts
Only
unlocked_liquidity can be withdrawn. vested_liquidity and permanent_locked_liquidity cannot be removed (unless vesting releases them first).EvtLiquidityChange with change_type: 1
Claiming Fees
Fee claims are separate from liquidity removal. Accumulated fees sit infee_a_pending and fee_b_pending until claimed.
EvtClaimPositionFee with fee_a_claimed and fee_b_claimed amounts.
In
CollectFeeMode.OnlyB and CollectFeeMode.Compounding mode, fee_a_claimed is always 0.Splitting Positions
A position can be split into two positions usingsplitPosition. This is useful for:
- Separating vesting tranches
- Transferring a portion of a position to a different wallet
- LP token distribution events
SPLIT_POSITION_DENOMINATOR (10,000). E.g., split_numerator = 5000 splits the position 50/50.
The split proportionally divides:
unlocked_liquiditypermanent_locked_liquidityfee_a_pending,fee_b_pending- Reward pendings (both slots)
- Inner vesting schedule (cliff amounts and per-period amounts)
EvtSplitPosition3
Closing a Position
A position can be closed only when it is empty:get_total_liquidity() == 0(all liquidity removed)fee_a_pending == 0,fee_b_pending == 0(fees claimed)- All reward pendings == 0 (rewards claimed)
EvtClosePosition
