Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

v1 source realm

package v1 manages liquidity positions as NFTs in GnoSwap pools.

Readme View source

Position

NFT-based liquidity position management for concentrated liquidity.

Overview

Each liquidity position is a unique GRC721 NFT. Stored state includes the pool key, price range, liquidity, fee-growth checkpoints, tokens owed, burned marker, and operator. Current token balances are derived from the current pool price, range, and liquidity; they are not permanently stored balances.

The pool accounting key encodes only the lower/upper tick pair and is scoped by pool. NFTs with the same range in one pool share the pool-level accounting entry.

Configuration

  • Withdrawal Fee: 1% by default on fee-bearing swap-fee collection
  • Max Position Size: No separate position-level cap; pool tick limits apply
  • Transfers: Unstaked NFTs follow GRC721 owner/approval/operator rules; staked NFTs are locked to staker-mediated transfers

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 an existing position.

  • Maintains the existing price range
  • Uses the current-price token ratio
  • Can clear a burned marker when the position is used again

DecreaseLiquidity

Removes liquidity while keeping the NFT.

  • One atomic public operation: internally collects swap fees, burns liquidity, then collects principal through the pool's fee-free Collect path
  • Returns fee amounts net of the withdrawal fee and collected principal
  • Amount-minimum checks apply to the principal actually collected

CollectFee

Claims accumulated swap fees without removing liquidity.

  • No liquidity removal required
  • Returns net collected amounts plus the raw pre-withdrawal-fee amounts
  • The configured withdrawal fee applies only to this fee-bearing path

Reposition

Updates an existing position's price range.

  • Requires the position to be clear first (zero liquidity and tokens owed)
  • Reuses the same position ID and NFT
  • Adds new liquidity to the updated range and clears the burned marker

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

For liquidity L and square-root prices sqrtLower, sqrtCurrent, and sqrtUpper:

Below range (current < lower, token0 only):

amount0 = L * (sqrtUpper - sqrtLower) / (sqrtUpper * sqrtLower)
amount1 = 0

In range (lower <= current < upper, both tokens):

amount0 = L * (sqrtUpper - sqrtCurrent) / (sqrtUpper * sqrtCurrent)
amount1 = L * (sqrtCurrent - sqrtLower)

Above range (current >= upper, token1 only):

amount0 = 0
amount1 = L * (sqrtUpper - sqrtLower)

Approval and Transfer Requirements

Mint, IncreaseLiquidity, and Reposition pull token0 and token1 from the caller inside the pool realm, so the approved spender is the pool realm address, not the position realm.

  • Approve the pool realm for both token contracts before calling a liquidity-adding function.
  • Approving the position realm alone is not sufficient; the position realm never holds or pulls the pair tokens itself.
  • Approve at least amount0Desired / amount1Desired. Any desired amount the pool does not consume stays with the caller.
  • DecreaseLiquidity and CollectFee pay out to the caller and require no approval.
1// Approve the pool realm for both pair tokens before minting
2poolAddress := access.MustGetAddress(prabc.ROLE_POOL.String())
3weth.Approve(cross(cur), poolAddress, 1000000)
4usdc.Approve(cross(cur), poolAddress, 2000000000)

Usage

These snippets call the public domain proxy from a realm function with a current cur token. Import the proxy package and qualify its function names in integrating code.

 1// Mint a new position through the domain proxy
 2tokenId, liquidity, amount0, amount1 := Mint(
 3    cross(cur),
 4    "gno.land/r/gnoland/wugnot.wugnot", // token0
 5    "gno.land/r/gnoswap/gns.GNS",   // 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 := IncreaseLiquidity(
20    cross(cur),
21    tokenId,
22    "500000",
23    "1000000000",
24    "475000",
25    "950000000",
26    deadline,
27)
28
29// Collect swap fees
30positionId, collected0, collected1, poolPath, rawAmount0, rawAmount1 := CollectFee(
31    cross(cur),
32    tokenId,
33)
34
35// Reposition to a new range (requires a clear position)
36positionId, liquidity, tickLower, tickUpper, amount0, amount1 := Reposition(
37    cross(cur),
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)

Lifecycle

A full decrease that leaves zero liquidity and zero tokens owed sets the burned marker but does not destroy the NFT. IncreaseLiquidity and Reposition clear the marker when the position is used again; the marker does not by itself block an increase.

Security

  • Tick range validation prevents invalid positions
  • Slippage protection applies to liquidity-changing operations; fee collection has no amount-minimum parameter
  • Deadlines prevent stale liquidity-changing transactions
  • Unstaked NFTs follow standard GRC721 transfer authorization; staked NFTs can move only through staker-mediated flows
  • Liquidity changes and repositioning require the owner; fee collection also permits the position's approved operator where applicable

Overview

package v1 manages liquidity positions as NFTs in GnoSwap pools.

Each position is represented as a GRC721 NFT with concentrated liquidity within a specific tick range. Unstaked NFTs follow GRC721 owner/approval/ operator transfer rules; staked NFTs are locked to staker-mediated transfers. The package handles minting, liquidity adjustments, fee collection, and repositioning.

Constants 1

const MAX_UINT256

1const MAX_UINT256 string = "115792089237316195423570985008687907853269984665640564039457584007913129639935"
source

Functions 1

func NewPositionV1

Action
1func NewPositionV1(positionStore position.IPositionStore, accessor NFTAccessor) position.IPosition
source

NewPositionV1 constructs a position implementation backed by the supplied position store and NFT accessor.

Parameters:

  • positionStore: storage interface used to read and update position records
  • accessor: NFT interface used for position token minting, burning, approval, and ownership

Returns:

  • position: IPosition implementation connected to positionStore and accessor

Types 9

type AddLiquidityParams

struct
 1type AddLiquidityParams struct {
 2	poolKey        string     // poolPath of the pool which has the position
 3	tickLower      int32      // lower end of the tick range for the position
 4	tickUpper      int32      // upper end of the tick range for the position
 5	amount0Desired *u256.Uint // desired amount of token0 to be minted
 6	amount1Desired *u256.Uint // desired amount of token1 to be minted
 7	amount0Min     *u256.Uint // minimum amount of token0 to be minted
 8	amount1Min     *u256.Uint // minimum amount of token1 to be minted
 9	caller         address    // address to call the function
10}
source

type DecreaseLiquidityParams

struct
1type DecreaseLiquidityParams struct {
2	positionId uint64     // positionId of the position to decrease liquidity
3	liquidity  string     // amount of liquidity to decrease
4	amount0Min *u256.Uint // minimum amount of token0 to be minted
5	amount1Min *u256.Uint // minimum amount of token1 to be minted
6	deadline   int64      // time by which the transaction must be included to effect the change
7	caller     address    // address to call the function
8}
source

type FeeGrowthInside

struct
1type FeeGrowthInside struct {
2	feeGrowthInside0LastX128 *u256.Uint
3	feeGrowthInside1LastX128 *u256.Uint
4}
source

FeeGrowthInside represents fee growth inside ticks

type IncreaseLiquidityParams

struct
1type IncreaseLiquidityParams struct {
2	positionId     uint64     // positionId of the position to increase liquidity
3	amount0Desired *u256.Uint // desired amount of token0 to be minted
4	amount1Desired *u256.Uint // desired amount of token1 to be minted
5	amount0Min     *u256.Uint // minimum amount of token0 to be minted
6	amount1Min     *u256.Uint // minimum amount of token1 to be minted
7	deadline       int64      // time by which the transaction must be included to effect the change
8	caller         address    // address to call the function
9}
source

type MintInput

struct
 1type MintInput struct {
 2	token0         string
 3	token1         string
 4	fee            uint32
 5	tickLower      int32
 6	tickUpper      int32
 7	amount0Desired string
 8	amount1Desired string
 9	amount0Min     string
10	amount1Min     string
11	deadline       int64
12	mintTo         address
13	caller         address
14}
source

type MintParams

struct
 1type MintParams struct {
 2	token0         string     // token0 path for a specific pool
 3	token1         string     // token1 path for a specific pool
 4	fee            uint32     // fee for a specific pool
 5	tickLower      int32      // lower end of the tick range for the position
 6	tickUpper      int32      // upper end of the tick range for the position
 7	amount0Desired *u256.Uint // desired amount of token0 to be minted
 8	amount1Desired *u256.Uint // desired amount of token1 to be minted
 9	amount0Min     *u256.Uint // minimum amount of token0 to be minted
10	amount1Min     *u256.Uint // minimum amount of token1 to be minted
11	deadline       int64      // time by which the transaction must be included to effect the change
12	mintTo         address    // address to mint lpToken
13	caller         address    // address to call the function
14}
source

type NFTAccessor

interface
 1type NFTAccessor interface {
 2	// Approve forwards an operator approval for a position NFT.
 3	//
 4	// Parameters:
 5	//   - _: leading realm-call discriminator for the forwarded NFT operation; callers pass 0
 6	//   - rlm: propagated realm context validated by the concrete accessor before crossing into the NFT realm
 7	//   - approved: address to approve for the specified position NFT; the empty address revokes approval
 8	//   - tid: position NFT token ID whose operator approval is changed
 9	//
10	// Returns:
11	//   - err: nil when approval succeeds; otherwise the NFT accessor or realm-validation error
12	Approve(_ int, rlm realm, approved address, tid grc721.TokenID) error
13	// Mint creates a position NFT for an address and token ID.
14	//
15	// Parameters:
16	//   - _: leading realm-call discriminator for the forwarded NFT operation; callers pass 0
17	//   - rlm: propagated realm context used to cross into the NFT realm
18	//   - to: address that receives the newly minted position NFT
19	//   - tid: requested position NFT token ID
20	//
21	// Returns:
22	//   - mintedTid: token ID returned by the NFT realm after minting
23	Mint(_ int, rlm realm, to address, tid grc721.TokenID) grc721.TokenID
24	// Burn removes a position NFT from the NFT ledger.
25	//
26	// Parameters:
27	//   - _: leading realm-call discriminator for the forwarded NFT operation; callers pass 0
28	//   - rlm: propagated realm context used to cross into the NFT realm
29	//   - tid: position NFT token ID to burn
30	Burn(_ int, rlm realm, tid grc721.TokenID)
31	// TotalSupply returns the number of NFTs currently recorded by the ledger.
32	//
33	// Returns:
34	//   - totalSupply: current number of position NFTs in the ledger
35	TotalSupply() int64
36	// Exists reports whether the NFT ledger contains a token ID.
37	//
38	// Parameters:
39	//   - tid: position NFT token ID to test
40	//
41	// Returns:
42	//   - exists: true when the NFT ledger contains tid
43	Exists(tid grc721.TokenID) bool
44	// OwnerOf returns the current owner of a position NFT.
45	//
46	// Parameters:
47	//   - tid: position NFT token ID whose owner is requested
48	//
49	// Returns:
50	//   - owner: address recorded as the token owner
51	//   - err: nil when the token exists; otherwise the NFT ledger's ownership lookup error
52	OwnerOf(tid grc721.TokenID) (address, error)
53}
source

type ProcessedMintInput

struct
 1type ProcessedMintInput struct {
 2	tokenPair      TokenPair
 3	amount0Desired *u256.Uint
 4	amount1Desired *u256.Uint
 5	amount0Min     *u256.Uint
 6	amount1Min     *u256.Uint
 7	tickLower      int32
 8	tickUpper      int32
 9	poolPath       string
10}
source

type TokenPair

struct
1type TokenPair struct {
2	token0 string
3	token1 string
4}
source

Imports 24

Source Files 17