Open Position

DLMM Base Function

The DLMM contract only provides two primitive methods: open_position (which generates an OpenPositionCert that must be repaid) and repay_open_position (which burns the OpenPositionCert).

/// Opens a new liquidity position in the pool.
///
/// This function creates a new position and adds liquidity to the specified bins.
/// It handles composition fees for the active bin and tracks all liquidity changes.
///
/// ## Type Parameters
/// - `CoinTypeA`: First token type in the pool
/// - `CoinTypeB`: Second token type in the pool
///
/// ## Parameters
/// - `pool`: Mutable reference to the pool
/// - `bins`: Vector of bin IDs for the position
/// - `amounts_a`: Vector of token A amounts for each bin
/// - `amounts_b`: Vector of token B amounts for each bin
/// - `config`: Global configuration
/// - `versioned`: Versioned object for compatibility check
/// - `clk`: Clock for timestamp tracking
/// - `ctx`: Transaction context
///
/// ## Returns
/// - `(Position, AddLiquidityCert<CoinTypeA, CoinTypeB>)`: New position and receipt
///
/// ## Errors
/// - `EInvalidAmountsOrBinsLength`: If amounts and bins have different lengths
/// - `EInvalidBins`: If bins are not consecutive or exceed maximum
/// - `EPositionLengthOverMax`: If position length exceeds maximum
public fun open_position<CoinTypeA, CoinTypeB>(
    pool: &mut Pool<CoinTypeA, CoinTypeB>,
    bins: vector<u32>,
    amounts_a: vector<u64>,
    amounts_b: vector<u64>,
    config: &GlobalConfig,
    versioned: &Versioned,
    clk: &Clock,
    ctx: &mut TxContext,
): (Position, OpenPositionCert<CoinTypeA, CoinTypeB>) {
    ...
}

/// Repays an open position.
///
/// ## Parameters
/// - `pool`: Mutable reference to the pool
/// - `position`: Mutable reference to the position
/// - `cert`: Open position certificate
/// - `balance_a`: Balance of token A
/// - `balance_b`: Balance of token B
/// - `versioned`: Versioned object for compatibility check
///
/// ## Events Emitted
/// - `AddLiquidityEvent`: Contains position and liquidity delta information
public fun repay_open_position<CoinTypeA, CoinTypeB>(
    pool: &mut Pool<CoinTypeA, CoinTypeB>,
    position: &mut Position,
    cert: OpenPositionCert<CoinTypeA, CoinTypeB>,
    balance_a: Balance<CoinTypeA>,
    balance_b: Balance<CoinTypeB>,
    versioned: &Versioned,
) {
    ...
}

Open Position in DLMM Router

You can either use the router contract's open position method directly, or reference the DLMM router contract implementation to develop your own custom open position method tailored to your specific requirements.

Contract Code

How to calculate bin id by price?

Formula: bin_id = log(price) / log(1 + bin_step/10000)

Explanation: The bin ID represents which price level (bin) a given price falls into within a geometric sequence. Each bin is (1 + bin_step/10000) times larger than the previous one, where bin_step is measured in basis points (1/10000). The logarithm calculates how many multiplicative steps are needed to reach the target price from the base price of 1.

Example: If bin_step = 20 (0.2%) and price = 1.5, then bin_id = log(1.5) / log(1.002) ≈ 203, meaning the price 1.5 is at the 203rd bin in the geometric price scale.

public fun open_position<CoinTypeA, CoinTypeB>(
    pool: &mut Pool<CoinTypeA, CoinTypeB>,
    coin_a: &mut Coin<CoinTypeA>,
    coin_b: &mut Coin<CoinTypeB>,
    bins: vector<u32>,
    amounts_a: vector<u64>,
    amounts_b: vector<u64>,
    config: &GlobalConfig,
    versioned: &Versioned,
    clk: &Clock,
    ctx: &mut TxContext,
) {
    let (mut position, open_position_cert) = pool::open_position(
        pool,
        bins,
        amounts_a,
        amounts_b,
        config,
        versioned,
        clk,
        ctx,
    );
    let (amount_a, amount_b) = open_position_cert.open_cert_amounts();

    assert!(coin_a.value() >= amount_a, EAmountInNotEnough);
    assert!(coin_b.value() >= amount_b, EAmountInNotEnough);
    let (balance_a, balance_b) = (
        coin_a.split(amount_a, ctx).into_balance(),
        coin_b.split(amount_b, ctx).into_balance(),
    );
    pool::repay_open_position(
        pool,
        &mut position,
        open_position_cert,
        balance_a,
        balance_b,
        versioned,
    );
    transfer::public_transfer(position, ctx.sender());
}

Last updated