Position Module

Manages liquidity position NFTs and their associated metadata. Each position is represented as a transferable NFT that tracks liquidity ownership, accumulated fees, rewards.

Overview

The Position module provides:

  • Position NFT creation and management

  • Liquidity tracking within tick ranges

  • Fee accumulation and collection

  • Reward and points tracking

  • Position locking mechanism

  • Batch position queries

Data Structures

Position

The main NFT struct representing position ownership:

struct Position has key, store {
    id: UID,                    // Unique identifier
    pool: ID,                   // Associated pool ID
    index: u64,                 // Position index number
    coin_type_a: TypeName,      // First token type
    coin_type_b: TypeName,      // Second token type
    name: String,               // Display name
    description: String,        // Description
    url: String,                // Icon URL
    tick_lower_index: I32,      // Lower tick bound
    tick_upper_index: I32,      // Upper tick bound
    liquidity: u128,            // Current liquidity amount
    lock_until: u64,            // Lock expiration timestamp (0 = unlocked)
}

PositionInfo

The computational data for position calculations:

PositionManager

Central registry managing all positions within a pool:

PositionReward

Individual reward token tracking:

Core Functions

Position Creation

open_position

Creates a new liquidity position NFT.

Parameters:

Parameter
Description

manager

Mutable reference to PositionManager

pool_id

ID of the target pool

pool_index

Pool's index number

icon_url

URL for position icon

tick_lower_index

Lower bound of liquidity range

tick_upper_index

Upper bound of liquidity range

ctx

Transaction context

Returns: New Position NFT

Example:

Liquidity Management

increase_liquidity

Adds liquidity to an existing position.

Parameters:

Parameter
Description

manager

Mutable reference to PositionManager

position

Mutable reference to Position NFT

delta_liquidity

Amount of liquidity to add

fee_growth_inside_a

Current fee growth for token A

fee_growth_inside_b

Current fee growth for token B

points_growth_inside

Current points growth

rewards_growth_inside

Vector of current reward growth rates

Returns: New total liquidity amount

decrease_liquidity

Removes liquidity from a position.

Additional Checks:

  • Validates position is not locked

  • Ensures sufficient liquidity exists

Fee Management

update_fee

Updates accumulated fees for a position.

reset_fee

Collects accumulated fees and resets counters.

update_and_reset_fee

Updates and collects fees in one operation.

Points Management

update_points

Updates accumulated points for a position.

Reward Management

update_rewards

Updates accumulated rewards for a position.

update_and_reset_rewards

Updates and collects specific reward token.

reset_rewarder

Resets reward counter for specific token.

Position Locking

lock_position

Locks a position until a specific timestamp.

get_lock_until

Gets the lock expiration timestamp.

Position Closure

close_position

Closes an empty position and burns the NFT.

Requirements:

  • Position must have zero liquidity

  • No accumulated fees

  • No pending rewards

Query Functions

Position Information

PositionInfo Queries

Manager Queries

Validation Functions

is_empty

Checks if position can be closed:

  • Zero liquidity

  • No accumulated fees

  • No pending rewards

Position Lifecycle

1. Creation

2. Liquidity Addition

3. Fee Collection

4. Liquidity Removal

5. Position Closure

Fee and Reward Calculation

Fee Calculation

Fees are calculated using the formula:

The system tracks fee growth both globally and per position to calculate accumulated fees accurately.

Multi-token Rewards

The rewards system supports multiple reward tokens simultaneously:

Reward Distribution

Rewards are distributed proportionally based on:

  • Position liquidity amount

  • Time in range

  • Growth rates specific to the position's tick range

Position Locking

Lock Mechanism

Positions can be locked until a specific timestamp to prevent premature liquidity removal:

Lock Validation

When decreasing liquidity, the system validates:

Error Codes

Code
Constant
Description

601

E_MATH_OVERFLOW

Fee calculation overflow

602

E_REWARD_OVERFLOW

Reward calculation overflow

603

E_POINTS_OVERFLOW

Points calculation overflow

606

E_POSITION_NOT_FOUND

Position not found in registry

608

E_LIQUIDITY_OVERFLOW

Liquidity addition overflow

609

E_INSUFFICIENT_LIQUIDITY

Not enough liquidity to remove

610

E_REWARD_INDEX_OUT_OF_BOUNDS

Invalid reward index

611

E_POSITION_LOCKED

Position is currently locked

Examples

Complete Position Lifecycle

Batch Position Query

Locking Position for Vesting

Best Practices

Position Management

  1. Always validate tick ranges before creating positions

  2. Check lock status before attempting to decrease liquidity

  3. Update fees and rewards before liquidity changes

  4. Use batch queries for efficient data retrieval

Error Handling

  • 601: Fee overflow - reduce liquidity or collect fees more frequently

  • 602: Reward overflow - collect rewards more frequently

  • 603: Points overflow - rare, indicates very long-held position

  • 606: Position not found - verify position ID exists

  • 608: Liquidity overflow - reduce liquidity delta

  • 609: Insufficient liquidity - check position liquidity before removing

  • 610: Invalid reward index - verify rewarder is initialized

  • 611: Position locked - wait until lock expires

Gas Optimization

  • Use fetch_positions for batch operations

  • Combine fee updates with liquidity changes

  • Close empty positions to free storage

Security Considerations

  1. Position Ownership: Only the NFT holder can modify the position

  2. Tick Validation: All tick ranges are validated for correctness

  3. Overflow Protection: All arithmetic operations include overflow checks

  4. Lock Enforcement: Locked positions cannot have liquidity decreased

  5. Access Control: Critical functions are friend-only (callable only by pool module)

Last updated