position source realm
View source
Position
NFT-based liquidity position management for concentrated liquidity.
Overview
Each liquidity position is a unique GRC721 NFT containing pool identifier, price range, liquidity amount, accumulated fees, and token balances.
Configuration
- Withdrawal Fee: 1% on collected fees
- Max Position Size: No limit
- Transfer Restrictions: Non-transferable NFTs
Core Functions
Mint
Creates new position NFT with initial liquidity.
- Validates tick range alignment
- Calculates optimal token ratio
- Returns actual amounts used
IncreaseLiquidity
Adds liquidity to existing position.
- Maintains same price range
- Pro-rata token amounts
DecreaseLiquidity
Removes liquidity while keeping NFT.
- Two-step: decrease then collect
- Calculates owed tokens
CollectFee
Claims accumulated swap fees.
- No liquidity removal required
- 1% protocol fees applied
Reposition
Updates an existing position's price range.
- Requires position to be cleared first (zero liquidity/tokens owed)
- Reuses the same position ID and NFT
- Adds new liquidity to the updated range
Technical Details
Tick Alignment
Ticks must align with pool's tick spacing:
0.01% fee: every 1 tick
0.05% fee: every 10 ticks
0.3% fee: every 60 ticks
1% fee: every 200 ticks
Optimal Range Width
Stable Pairs (USDC/USDT):
- Narrow: ±0.05% (max efficiency)
- Medium: ±0.1% (balanced)
- Wide: ±0.5% (safety)
Correlated Pairs (WETH/stETH):
- Narrow: ±0.5%
- Medium: ±1%
- Wide: ±2%
Volatile Pairs (WETH/USDC):
- Narrow: ±5%
- Medium: ±10%
- Wide: ±25%
Capital Efficiency
Concentration factor vs infinite range:
Range ±0.1% → 2000x efficient
Range ±1% → 200x efficient
Range ±10% → 20x efficient
Range ±50% → 4x efficient
Token Calculations
Below range (token1 only):
amount1 = L * (sqrtUpper - sqrtLower)
amount0 = 0
Above range (token0 only):
amount0 = L * (sqrtUpper - sqrtLower) / (sqrtUpper * sqrtLower)
amount1 = 0
In range (both tokens):
amount0 = L * (sqrtUpper - sqrtCurrent) / (sqrtUpper * sqrtCurrent)
amount1 = L * (sqrtCurrent - sqrtLower)
Usage
1// Mint new position through the proxy realm
2tokenId, liquidity, amount0, amount1 := position.Mint(
3 cross,
4 "gno.land/r/onbloc/weth", // token0
5 "gno.land/r/gnoswap/test_token/test_usdc", // token1
6 3000, // fee
7 -887220, // tickLower
8 887220, // tickUpper
9 "1000000", // amount0Desired
10 "2000000000", // amount1Desired
11 "950000", // amount0Min
12 "1900000000", // amount1Min
13 deadline,
14 recipient, // mintTo
15 "", // referrer
16)
17
18// Add liquidity
19positionId, liquidity, amount0, amount1, poolPath := position.IncreaseLiquidity(
20 cross,
21 tokenId,
22 "500000",
23 "1000000000",
24 "475000",
25 "950000000",
26 deadline,
27)
28
29// Collect fees
30positionId, fee0, fee1, poolPath, token0Path, token1Path := position.CollectFee(
31 cross,
32 tokenId,
33)
34
35// Reposition to new range (requires cleared position)
36positionId, liquidity, tickLower, tickUpper, amount0, amount1 := position.Reposition(
37 cross,
38 tokenId,
39 -443610, // new tickLower
40 443610, // new tickUpper
41 "1000000", // amount0Desired
42 "2000000000", // amount1Desired
43 "950000", // amount0Min
44 "1900000000", // amount1Min
45 deadline,
46)
Security
- Tick range validation prevents invalid positions
- Slippage protection on all operations
- Deadline prevents stale transactions
- Position NFTs are non-transferable
- Only owner can manage their positions
2
32
func CollectFee
crossing ActionCollectFee collects accumulated fees from a position.
Parameters:
- positionId: ID of the position
Returns:
- uint64: position ID
- string: amount of token0 collected
- string: amount of token1 collected
- string: pool path
- string: token0 path
- string: token1 path
func DecreaseLiquidity
crossing ActionDecreaseLiquidity removes liquidity from a position.
Parameters:
- positionId: ID of the position
- liquidityStr: amount of liquidity to remove
- amount0MinStr: minimum amount of token0
- amount1MinStr: minimum amount of token1
- deadline: transaction deadline
Returns:
- uint64: position ID
- string: removed liquidity amount
- string: amount of token0 removed
- string: amount of token1 removed
- string: pool path
- string: net amount of token0 after fees
- string: net amount of token1 after fees
func GetImplementationPackagePath
ActionGetImplementationPackagePath returns the package path of the currently active implementation.
func GetPositionCount
ActionGetPositionCount returns the total number of positions.
func GetPositionFeeGrowthInside0LastX128
ActionGetPositionFeeGrowthInside0LastX128 returns the last recorded fee growth inside for token0.
func GetPositionFeeGrowthInside1LastX128
ActionGetPositionFeeGrowthInside1LastX128 returns the last recorded fee growth inside for token1.
func GetPositionFeeGrowthInsideLastX128
ActionGetPositionFeeGrowthInsideLastX128 returns the last recorded fee growth inside for both tokens.
func GetPositionIDs
ActionGetPositionIDs returns a paginated list of position IDs.
func GetPositionLiquidity
ActionGetPositionLiquidity returns the liquidity amount of a position.
func GetPositionPoolKey
ActionGetPositionPoolKey returns the pool key of a position.
func GetPositionTickLower
ActionGetPositionTickLower returns the lower tick of a position.
func GetPositionTickUpper
ActionGetPositionTickUpper returns the upper tick of a position.
func GetPositionTicks
ActionGetPositionTicks returns the lower and upper ticks of a position.
func GetPositionToken0Balance
Actionfunc GetPositionToken1Balance
Actionfunc GetPositionTokenBalances
ActionGetPositionTokenBalances returns the token0 balance of a position.
func GetPositionTokensOwed
ActionGetPositionTokensOwed returns the amount of tokens owed to a position.
func GetPositionTokensOwed0
ActionGetPositionTokensOwed0 returns the amount of token0 owed to a position.
func GetPositionTokensOwed1
ActionGetPositionTokensOwed1 returns the amount of token1 owed to a position.
func GetUnclaimedFee
ActionGetUnclaimedFee returns the unclaimed fees for both tokens of a position.
func IncreaseLiquidity
crossing ActionIncreaseLiquidity adds liquidity to an existing position.
Parameters:
- positionId: ID of the position
- amount0DesiredStr: desired amount of token0
- amount1DesiredStr: desired amount of token1
- amount0MinStr: minimum amount of token0
- amount1MinStr: minimum amount of token1
- deadline: transaction deadline
Returns:
- uint64: position ID
- string: new liquidity amount
- string: amount of token0 added
- string: amount of token1 added
- string: pool path
func IsBurned
ActionIsBurned returns whether a position has been burned.
func IsInRange
ActionIsInRange returns whether a position's ticks are within the current price range.
func Mint
crossing ActionMint creates a new liquidity position NFT.
Parameters:
- token0: path of the first token
- token1: path of the second token
- fee: pool fee tier
- tickLower: lower tick boundary
- tickUpper: upper tick boundary
- amount0Desired: desired amount of token0
- amount1Desired: desired amount of token1
- amount0Min: minimum amount of token0
- amount1Min: minimum amount of token1
- deadline: transaction deadline
- mintTo: recipient of the position NFT
- referrer: referrer address for reward tracking
Returns:
- uint64: position ID
- string: liquidity amount
- string: amount of token0 added
- string: amount of token1 added
func NewPositionsTree
ActionNewPositionsTree allocates the positions BP-tree under /r/gnoswap/position's realm context (the realm that declares Position).
func RegisterInitializer
crossing Action1func RegisterInitializer(cur realm, initializer func(_ int, rlm realm, positionStore IPositionStore) IPosition)RegisterInitializer registers a new position implementation version. This function is called by each version (v1, v2, etc.) during initialization to register their implementation with the proxy system.
The initializer function creates a new instance of the implementation using the provided positionStore interface.
Security: Only contracts within the domain path can register initializers. Each package path can only register once to prevent duplicate registrations.
func Reposition
crossing ActionReposition changes the tick range of a position.
Parameters:
- positionId: ID of the position
- tickLower: new lower tick boundary
- tickUpper: new upper tick boundary
- amount0DesiredStr: desired amount of token0
- amount1DesiredStr: desired amount of token1
- amount0MinStr: minimum amount of token0
- amount1MinStr: minimum amount of token1
- deadline: transaction deadline
Returns:
- uint64: position ID
- string: new liquidity amount
- int32: new lower tick
- int32: new upper tick
- string: amount of token0 used
- string: amount of token1 used
func SetPositionOperator
crossing ActionSetPositionOperator sets an operator for a position.
Parameters:
- positionId: ID of the position
- operator: address of the operator
func UpgradeImpl
crossing ActionUpgradeImpl switches the active position implementation to a different version. This function allows seamless upgrades from one version to another without data migration or downtime.
Security: Only admin or governance can perform upgrades. The new implementation must have been previously registered via RegisterInitializer.
func NewPositionStore
ActionNewPositionStore creates a new protocol fee store instance with the provided KV store. This function is used by the upgrade system to create storage instances for each implementation.
func GetPosition
ActionGetPosition returns the position data for a given position ID.
func NewPosition
Action6
type IPosition
interfacetype IPositionGetter
interface 1type IPositionGetter interface {
2 GetPositionCount() int
3 GetPositionIDs(offset, count int) []uint64
4 GetPosition(positionId uint64) (Position, bool)
5 IsBurned(positionId uint64) bool
6 IsInRange(positionId uint64) bool
7 GetPositionOperator(positionId uint64) address
8 GetPositionPoolKey(positionId uint64) string
9 GetPositionTickLower(positionId uint64) int32
10 GetPositionTickUpper(positionId uint64) int32
11 GetPositionLiquidity(positionId uint64) string
12 GetPositionTokenBalances(positionId uint64) (int64, int64)
13 GetPositionFeeGrowthInside0LastX128(positionId uint64) string
14 GetPositionFeeGrowthInside1LastX128(positionId uint64) string
15 GetPositionTokensOwed0(positionId uint64) int64
16 GetPositionTokensOwed1(positionId uint64) int64
17 GetUnclaimedFee(positionId uint64) (*u256.Uint, *u256.Uint)
18 GetPositionOwner(positionId uint64) address
19}type IPositionManager
interface 1type IPositionManager interface {
2 Mint(
3 _ int,
4 rlm realm,
5 token0 string,
6 token1 string,
7 fee uint32,
8 tickLower int32,
9 tickUpper int32,
10 amount0Desired string,
11 amount1Desired string,
12 amount0Min string,
13 amount1Min string,
14 deadline int64,
15 mintTo address,
16 referrer string,
17 ) (uint64, string, string, string)
18
19 IncreaseLiquidity(
20 _ int,
21 rlm realm,
22 positionId uint64,
23 amount0DesiredStr string,
24 amount1DesiredStr string,
25 amount0MinStr string,
26 amount1MinStr string,
27 deadline int64,
28 ) (uint64, string, string, string, string)
29
30 DecreaseLiquidity(
31 _ int,
32 rlm realm,
33 positionId uint64,
34 liquidityStr string,
35 amount0MinStr string,
36 amount1MinStr string,
37 deadline int64,
38 ) (uint64, string, string, string, string, string, string)
39
40 Reposition(
41 _ int,
42 rlm realm,
43 positionId uint64,
44 tickLower int32,
45 tickUpper int32,
46 amount0DesiredStr string,
47 amount1DesiredStr string,
48 amount0MinStr string,
49 amount1MinStr string,
50 deadline int64,
51 ) (uint64, string, int32, int32, string, string)
52
53 CollectFee(
54 _ int,
55 rlm realm,
56 positionId uint64,
57 ) (uint64, string, string, string, string, string)
58
59 SetPositionOperator(
60 _ int,
61 rlm realm,
62 positionId uint64,
63 operator address,
64 )
65}type IPositionStore
interface 1type IPositionStore interface {
2 HasPositionsStoreKey() bool
3 GetPositions() *bptree.BPTree
4 SetPositions(_ int, rlm realm, positions *bptree.BPTree) error
5
6 HasPositionNextIDStoreKey() bool
7 GetPositionNextID() uint64
8 SetPositionNextID(_ int, rlm realm, nextID uint64) error
9
10 HasPosition(positionId uint64) bool
11 GetPosition(positionId uint64) (Position, bool)
12 SetPosition(_ int, rlm realm, positionId uint64, position Position) error
13 RemovePosition(_ int, rlm realm, positionId uint64) error
14}type Position
struct 1type Position struct {
2 operator address // address that is approved for spending this token
3 poolKey string // poolPath of the pool which this has lp token
4 tickLower int32 // the lower tick of the position, bounds are included
5 tickUpper int32 // the upper tick of the position
6 liquidity string // liquidity of the position
7
8 // fee growth of the aggregate position as of the last action on the individual position
9 feeGrowthInside0LastX128 string
10 feeGrowthInside1LastX128 string
11
12 // how many uncollected tokens are owed to the position, as of the last computation
13 tokensOwed0 int64
14 tokensOwed1 int64
15
16 burned bool // whether the position has been burned (we don't burn the NFT, just mark as burned)
17}Position represents a liquidity position in a pool. Each position tracks the amount of liquidity, fee growth, and tokens owed to the position owner. All uint256 fields are stored as decimal strings to reduce realm object overhead.
Methods on Position
func Burned
method on Positionfunc FeeGrowthInside0LastX128
method on Positionfunc FeeGrowthInside1LastX128
method on Positionfunc IsClear
method on PositionisClear reports whether the position is empty