PathrockNetwork Gno Explorer
HomeBlocksTransactionsRealmsPackagesValidatorsAnalytics

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/r/gnoswap/position/v1

Realm
Open in gnoweb ↗

Overview

Kind
Realm (renderable)
Name
v1
Namespace
gnoswap / position
Files
17 (README)(gnomod.toml)
Exported functions
1
Module
gno.land/r/gnoswap/position/v1
gno
0.9

Files (17)

  • README.mdmarkdown
  • gnomod.tomltoml
  • assert.gnogno
  • burn.gnogno
  • doc.gnogno
  • errors.gnogno
  • getter.gnogno
  • init.gnogno
  • instance.gnogno
  • liquidity_management.gnogno
  • manager.gnogno
  • mint.gnogno
  • position.gnogno
  • render.gnogno
  • reposition.gnogno
  • type.gnogno
  • utils.gnogno
  • README.mdPreviewRaw
    # 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.
    
    ```go
    // Approve the pool realm for both pair tokens before minting
    poolAddress := access.MustGetAddress(prabc.ROLE_POOL.String())
    weth.Approve(cross(cur), poolAddress, 1000000)
    usdc.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.
    
    ```go
    // Mint a new position through the domain proxy
    tokenId, liquidity, amount0, amount1 := Mint(
        cross(cur),
        "gno.land/r/gnoland/wugnot.wugnot", // token0
        "gno.land/r/gnoswap/gns.GNS",   // token1
        3000,                      // fee
        -887220,                   // tickLower
        887220,                    // tickUpper
        "1000000",                 // amount0Desired
        "2000000000",              // amount1Desired
        "950000",                  // amount0Min
        "1900000000",              // amount1Min
        deadline,
        recipient,                 // mintTo
        "",                        // referrer
    )
    
    // Add liquidity
    positionId, liquidity, amount0, amount1, poolPath := IncreaseLiquidity(
        cross(cur),
        tokenId,
        "500000",
        "1000000000",
        "475000",
        "950000000",
        deadline,
    )
    
    // Collect swap fees
    positionId, collected0, collected1, poolPath, rawAmount0, rawAmount1 := CollectFee(
        cross(cur),
        tokenId,
    )
    
    // Reposition to a new range (requires a clear position)
    positionId, liquidity, tickLower, tickUpper, amount0, amount1 := Reposition(
        cross(cur),
        tokenId,
        -443610,                   // new tickLower
        443610,                    // new tickUpper
        "1000000",                 // amount0Desired
        "2000000000",              // amount1Desired
        "950000",                  // amount0Min
        "1900000000",              // amount1Min
        deadline,
    )
    ```
    
    ## 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
    

    Functions

    • NewPositionV1(positionStore interface {GetPosition func(uint64) (gno.land/r/gnoswap/position.Position, bool); GetPositionNextID func() uint64; GetPositions func() *gno.land/p/nt/bptree/v0.BPTree; HasPosition func(uint64) bool; HasPositionNextIDStoreKey func() bool; HasPositionsStoreKey func() bool; RemovePosition func(int, .uverse.realm, uint64) .uverse.error; SetPosition func(int, .uverse.realm, uint64, gno.land/r/gnoswap/position.Position) .uverse.error; SetPositionNextID func(int, .uverse.realm, uint64) .uverse.error; SetPositions func(int, .uverse.realm, *gno.land/p/nt/bptree/v0.BPTree) .uverse.error}, accessor interface {Approve func(int, .uverse.realm, .uverse.address, gno.land/p/nt/grc721/v0.TokenID) .uverse.error; Burn func(int, .uverse.realm, gno.land/p/nt/grc721/v0.TokenID); Exists func(gno.land/p/nt/grc721/v0.TokenID) bool; Mint func(int, .uverse.realm, .uverse.address, gno.land/p/nt/grc721/v0.TokenID) gno.land/p/nt/grc721/v0.TokenID; OwnerOf func(gno.land/p/nt/grc721/v0.TokenID) (.uverse.address, .uverse.error); TotalSupply func() int64}) interface {CollectFee func(int, .uverse.realm, uint64) (uint64, string, string, string, string, string); DecreaseLiquidity func(int, .uverse.realm, uint64, string, string, string, int64) (uint64, string, string, string, string, string, string); GetPositionFeeGrowthInside0LastX128 func(uint64) (string, .uverse.error); GetPositionFeeGrowthInside1LastX128 func(uint64) (string, .uverse.error); GetPositionFeeGrowthInsideLastX128 func(uint64) (string, string, .uverse.error); GetPositionLiquidity func(uint64) (string, .uverse.error); GetPositionOperator func(uint64) (.uverse.address, .uverse.error); GetPositionOwner func(uint64) (.uverse.address, .uverse.error); GetPositionPoolKey func(uint64) (string, .uverse.error); GetPositionTickLower func(uint64) (int32, .uverse.error); GetPositionTickUpper func(uint64) (int32, .uverse.error); GetPositionTicks func(uint64) (int32, int32, .uverse.error); GetPositionTokenBalances func(uint64) (int64, int64, .uverse.error); GetPositionTokensOwed func(uint64) (int64, int64, .uverse.error); GetPositionTokensOwed0 func(uint64) (int64, .uverse.error); GetPositionTokensOwed1 func(uint64) (int64, .uverse.error); GetPositions func() *gno.land/p/nt/bptree/rotree/v0.ReadOnlyTree; GetUnclaimedFee func(uint64) (*gno.land/p/gnoswap/uint256/v1.Uint, *gno.land/p/gnoswap/uint256/v1.Uint, .uverse.error); IncreaseLiquidity func(int, .uverse.realm, uint64, string, string, string, string, int64) (uint64, string, string, string, string); IsBurned func(uint64) (bool, .uverse.error); IsInRange func(uint64) (bool, .uverse.error); Mint func(int, .uverse.realm, string, string, uint32, int32, int32, string, string, string, string, int64, .uverse.address, string) (uint64, string, string, string); Render func(string) string; Reposition func(int, .uverse.realm, uint64, int32, int32, string, string, string, string, int64) (uint64, string, int32, int32, string, string); SetPositionOperator func(int, .uverse.realm, uint64, .uverse.address)}

    Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.

    Rendered

    RenderedRawgnoweb ↗
    This realm does not provide renderable output (vm/qrender → NoRenderDeclError). View on gnoweb ↗

    vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.