# Welcome to Ferra

#### Welcome to **Ferra**, the dynamic liquidity layer built natively on the SUI network!

Ferra is the dynamic liquidity layer built natively on the SUI network, delivering deep, flexible, and capital-efficient liquidity for DeFi builders, liquidity providers, and token projects.\
At its core, Ferra combines three advanced AMM engines: **DLMM, CLMM, and DAMM** with a comprehensive toolkit that includes a DBC - The Dynamic Bonding Curve  for effortless token launches, an internal Pool & DEX Aggregator for best-price routing, and a fully documented API/SDK for seamless integration across the Sui ecosystem.

Our mission is to deliver a robust, modular liquidity platform that is capital-efficient, highly composable, and sustainable for the long haul. Whether you’re launching a blue-chip asset, spinning up a meme coin, or powering an institutional trading desk, Ferra equips you to deploy, manage, and grow liquidity better, faster, and more effectively than ever before.

{% hint style="warning" %}
**Before You Proceed**\
Please take a moment to review our [Legal Disclaimer ](/resources/legal-disclaimer)regarding risks, responsibilities, and limitations related to using Ferra and participating in DeFi activities on-chain.
{% endhint %}

### Jump right in

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-cover data-type="files"></th><th data-hidden></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>What's DLMM</strong></td><td>Learn about DLMM liquidity, an innovative and advantageous model on the Sui network</td><td><a href="/files/DFDAGtBCgAtl3IsCx4Oi">/files/DFDAGtBCgAtl3IsCx4Oi</a></td><td></td><td><a href="/pages/PxGSrkbi2I69IWIH4Tv9">/pages/PxGSrkbi2I69IWIH4Tv9</a></td></tr><tr><td><strong>Pool Comparison</strong></td><td>Compare the three types of Ferra liquidity pools and learn how to use each one effectively.</td><td><a href="/files/0CsOmTPg5VUP1Ihcsmt9">/files/0CsOmTPg5VUP1Ihcsmt9</a></td><td></td><td><a href="/pages/6cxKqyxLBR1G6uH3RGog">/pages/6cxKqyxLBR1G6uH3RGog</a></td></tr><tr><td><strong>Integration Guide</strong></td><td>Step-by-step guide to integrating with Ferra liquidity pools using either the TypeScript SDK or the smart contract interface.</td><td><a href="/files/Rxhkl7W5NgenJsQlKCGr">/files/Rxhkl7W5NgenJsQlKCGr</a></td><td></td><td><a href="/pages/rkIzbUESE8pAlcKcYJqH">/pages/rkIzbUESE8pAlcKcYJqH</a></td></tr></tbody></table>


# DLMM


# DLMM Overview

DLMM stands for Dynamic Liquidity Market Maker, is an advanced AMM model where liquidity is distributed across discrete price bins, allowing zero-slippage swaps within each bin. DLMM introduces volatility-aware fee adjustments and bin-based liquidity targeting, making it ideal for volatile markets and active strategies.

It differs from traditional CLMMs by enabling predictable execution, efficient rebalancing, and dynamic fee tuning, all while offering LPs granular control over capital deployment.

DLMM pools operate with the following components:

* **Bins**: Liquidity is segmented into small price intervals (bins). Each bin represents a fixed exchange rate between two assets. Swaps inside a bin occur with zero slippage.
* **Dynamic Fees**: The protocol adjusts swap fees based on recent volatility. Higher volatility leads to increased fees to protect LPs and reduce arbitrage leakage.
* **Liquidity Distribution**: LPs can choose which bins to deploy liquidity into, concentrating around expected price zones or distributing broadly across the range.
* **Efficient Routing**: When a trade spans multiple bins, Ferra routes the swap through them sequentially, minimizing price impact.
* **Bin Migration & Rebalancing**: LPs can rebalance liquidity to follow price trends or optimize yield.
* **Single-Sided Liquidity**: LPs can provide liquidity using just one token (e.g., token A only), especially effective during token launches or low TVL phases.

{% hint style="success" %}
**Key Insight**\
In normal market conditions, swaps within a bin occur with no slippage. During volatile periods, LPs earn higher fees due to the dynamic fee adjustment. This pool type is suitable for both blue-chip tokens and high-volatility assets such as meme coins.
{% endhint %}


# Core Concepts

## Discrete Price Bins

In Ferra's DLMM, liquidity is organized into discrete price bins, each representing a specific price level for a token pair, such as SUI/USDC at 2.5, 2.51, 2.52 USDC per SUI, up to 4.1. This approach differs significantly from traditional Automated Market Makers (AMMs) like Uniswap V2, where liquidity is distributed continuously across all prices from 0 to infinity using the constant product formula $$x \cdot y = k$$ . This continuous distribution often leads to high slippage for large trades and inefficient capital use, as liquidity is spread thinly across unused price ranges.

Concentrated Liquidity Market Makers (CLMMs), such as Uniswap V3, improve on this by allowing LPs to concentrate liquidity within user-defined price ranges, enhancing capital efficiency. However, within these ranges, the price still changes continuously based on the ratio of reserves, following a piecewise linear function. In contrast, DLMM's discrete bins, inspired by Trader Joe's Liquidity Book, operate at fixed price points, each bin acting as an independent mini-market. This allows LPs to allocate funds precisely, potentially maximizing fee earnings by focusing liquidity where trading activity is expected.

The image below illustrates the distribution of liquidity across price bins for the SUI/USDC pair.

<figure><img src="/files/KW0s8Md8zkhyDPZVofZ8" alt=""><figcaption><p>Ferra's DLMM liquidity distribution example</p></figcaption></figure>

## **Zero-Slippage Swaps**

Zero-slippage swaps are a hallmark of Ferra's DLMM, enabled by the discrete bin structure. Within each bin, trades occur at a fixed price without price impact, as long as the trade size does not exceed the bin's liquidity. This is a significant departure from traditional AMMs, where the price changes continuously with each trade due to the constant product formula, and from CLMMs, where price changes smoothly within tick ranges

In DLMM, each bin operates as a constant sum market maker within its price, meaning trades within the bin maintain a fixed exchange rate. For example, if the active bin for SUI/USDC is at 3.00 USDC per SUI, with 1000 SUI and 3000 USDC, a trader buying 100 SUI pays exactly 300 USDC, experiencing zero slippage, as the price remains 3.00 throughout the trade within the bin's capacity. This is because within a bin, you can add X tokens and take out Y tokens (or vice versa) at the fixed price until one token is depleted, aligning with the constant sum invariant

Mathematically, if a bin has reserves $$X (SUI)$$ and $$Y (USDC)$$, and the price is  $$P = Y/X$$, a trade to buy $$\Delta X (SUI)$$ would cost $$\Delta Y = P\cdot\Delta X$$, as long as $$X\geq\Delta X$$. The price remains $$P$$, ensuring zero slippage. Only when $$X < \Delta X$$ does the trade move to the next bin, say at price $$P' > P$$, introducing slippage for the remaining amount.&#x20;

### Example Calculation

Consider a SUI/USDC pool with:

* **Current Bin**: Price $$P = 3.00$$ USDC per SUI, with reserves $$X = 1,000$$ SUI and $$Y = 3,000$$ USDC
* **Next Bin**: Price $$P' = 3.01$$USDC per SUI.
* **Trade**:  Buy $$\Delta X = 1,500$$ SUI

#### **Step-by-Step Calculation**:

1. **First Bin (P = 3.00)**:
   * The bin supplies up to $$1,000$$ SUI.
   * Cost: $$\Delta Y\_1 = 3.00 \cdot 1,000 = 3,000$$  USDC.
   * Remaining: $$1,500 - 1,000 = 500$$  SUI.
2. **Next Bin (P' = 3.01)**:
   * Cost: $$\Delta Y\_2 = 3.01 \cdot 500 = 1,505$$ USDC.
3. **Total Cost**:
   * Total: $$\Delta Y = 3,000 + 1,505 = 4,505$$ USDC.
4. **Slippage**:
   * Expected cost at $$P = 3.00$$ : $$3.00 \cdot 1,500 = 4,500$$ USDC.
   * Actual cost: $$4, 505$$ USDC.
   * Slippage: $$4,505 - 4,500 = 5$$ USDC.

## Dynamic Fee

Ferra’s DLMM utilizes a dynamic fee mechanism to optimize returns for liquidity providers (LPs). Each swap incurs a total fee that consists of two components:

* A fixed base fee, determined by the pool creator.
* A variable fee, which increases in response to market volatility.

This dynamic fee structure is designed to reward LPs more during periods of high trading activity, compensating them for the increased risk associated with price fluctuations.

To learn more about how the dynamic fee mechanism works and its technical implementation in Ferra’s DLMM, please refer to the detailed explanation in [Ferra's Dynamic Fee](/core-protocols/quickstart/dynamic-fee) session

## Liquidity Shapes and Strategies

Ferra’s DLMM on the SUI network revolutionizes liquidity provision by offering LPs flexible liquidity shapes and corresponding strategies to optimize fee earnings and manage risk.

Ferra provides three primary liquidity shapes: Spot, Curve, and Bid-Ask. Each defining how liquidity is distributed across discrete price bins. These shapes align with volatility strategies tailored to different market conditions, from stablecoin pairs to volatile memecoins.

In addition, Ferra supports single-sided liquidity and custom liquidity distributions, making it well-suited for token launches with limited initial liquidity.

Please refer to the [Shapes & Strategies](/core-protocols/quickstart/shapes-and-strategies) section below for more detailed information.


# Dynamic Fee

## Introduction

Ferra's DLMM implements a sophisticated dynamic fee system that automatically adjusts based on market volatility and trading patterns. This creates a fair and efficient pricing mechanism where fees increase during periods of high volatility or large trades, ensuring liquidity providers are properly compensated for the risks they take.

The system uses surge pricing that responds to real-time market conditions, protecting liquidity providers during volatile periods while maintaining competitive fees during stable market conditions.

## Benefits and Design Goals

#### For Liquidity Providers

* **Fair compensation**: Fees increase with risk during volatile periods
* **Predictable base income**: Base fees provide stable minimum returns
* **Anti-dilution**: Composition fees discourage harmful imbalanced deposits

#### For Traders

* **Competitive fees**: Low fees during stable market conditions
* **Transparent pricing**: Clear fee structure with predictable components
* **Fair pricing**: Surge pricing reflects true cost of liquidity provision

#### For Protocol

* **Sustainable revenue**: Protocol fees support development and operations
* **Market efficiency**: Dynamic fees encourage optimal trading behavior
* **Risk management**: Higher fees during volatile periods protect ecosystem

This dynamic fee system creates a balanced ecosystem where all participants are fairly compensated while maintaining efficient and competitive markets.

## Fee Architecture Overview

### Total Fee Structure

The total fee charged to traders consists of multiple components:

```javascript
Total_Fee = Base_Fee + Variable_Fee
```

All fees are calculated with **9 decimal precision** (1,000,000,000 = 100%) ensuring high accuracy in fee calculations.

**Fee Caps:**

* Maximum Total Fee: **10%**
* Maximum Protocol Share: **25%** of total fees

#### Fee Flow Process

* **Fee Calculation**: Fees computed per bin during swaps
* **Protocol Split**: Percentage allocated to protocol treasury
* **LP Distribution**: Remaining fees distributed to liquidity providers
* **Additional Fees**: Composition and flash loan fees handled separately

### Base Fee

The base fee provides a stable minimum fee floor for each trading pair, ensuring liquidity providers always receive basic compensation.

$$
\text{Base\_Fee} = \text{Base\_Factor} \times \text{Bin\_Step}
$$

Wher&#x65;**:**

* `Base_Factor`: Pool-specific amplification multiplier (set by pool creator)
* `Bin_Step`: Price increment between adjacent bins

#### Purpose

* Ensures minimum compensation for liquidity providers
* Covers basic operational costs
* Provides predictable fee component independent of volatility

### Variable Fee

The variable fee creates dynamic pricing that responds to market volatility, implementing surge pricing during periods of high trading activity or significant price movements:

$$
\text{Variable\_Fee} = \left( \text{Volatility\_Accumulator} \times \text{Bin\_Step} \right)^2 \times \text{Variable\_Fee\_Control} \div 100
$$

Wher&#x65;**:**

* `Volatility_Accumulator`: Current measure of market volatility
* `Bin_Step`: Price increment between bins
* `Variable_Fee_Control`: Sensitivity parameter for volatility response

#### Key Characteristics

* **Quadratic scaling**: Fees increase exponentially with volatility
* **Bin-aware**: Considers price step size of the specific market
* **Configurable sensitivity**: Markets can tune volatility response

### Volatility Management System

The volatility system captures market dynamics while preventing manipulation through sophisticated time-weighted mechanisms.

#### Volatility Accumulator

The core volatility measure that tracks price movement intensity:

$$
\text{Volatility\_Accumulator} = \text{Volatility\_Reference} + \left| \text{Current\_Bin\_ID} - \text{Reference\_Bin\_ID} \right| \times 10,000
$$

Wher&#x65;**:**

* `Volatility_Reference`: Decayed volatility from previous periods
* `Bin_ID_Delta`: Absolute difference in bin positions
* `10,000`: Scaling factor (basis points)

#### Time-Based Volatility Management

The system uses three time periods to manage volatility evolution:

**Filter Period (t**<sub>**f**</sub>**)**

Minimum time before volatility parameters update:

* **Purpose**: Prevents rapid manipulation
* **Effect**: High-frequency trades maintain current volatility reference

**Decay Period (t**<sub>**d**</sub>**)**

Maximum time before volatility resets:

* **Purpose**: Ensures old volatility doesn't persist indefinitely
* **Effect**: Long periods without trades reset volatility to zero

**Volatility Reference Updates**

Based on time elapsed (Δt) since last transaction:

**Case 1: High Frequency (Δt < t**<sub>**f**</sub>**)**

```clike
Volatility_Reference = Previous_Volatility_Reference (unchanged)
Reference_Bin_ID = Previous_Reference_Bin_ID (unchanged)
```

**Case 2: Normal Frequency (t**<sub>**f**</sub>**&#x20;≤ Δt < t**<sub>**d**</sub>**)**

```clike
New_Volatility_Reference = Current_Volatility_Accumulator × Reduction_Factor ÷ 10,000
Reference_Bin_ID = Current_Active_Bin_ID
```

**Case 3: Low Frequency (Δt ≥ t**<sub>**d**</sub>**)**

```clike
Volatility_Reference = 0
Reference_Bin_ID = Current_Active_Bin_ID
```

#### Anti-Manipulation Protection

The system includes multiple layers of protection:

* **Time filtering**: Prevents rapid volatility manipulation through frequent small trades
* **Reference stability**: Maintains stable reference points during high-frequency periods
* **Gradual decay**: Volatility naturally decreases over time
* **Maximum caps**: Limits extreme volatility accumulation

### Composition Fees

Composition fees encourage balanced liquidity provision by charging additional fees when liquidity providers deposit tokens in imbalanced ratios.

#### When Applied

Composition fees are **only charged in the active bin** and only when:

* Liquidity provider deposits create imbalance
* One token is over-represented relative to current bin ratio

#### Fee Calculation

The composition fee uses a quadratic formula to discourage large imbalances:

$$
\text{Composition\_Fee} = \text{Excess\_Amount} \times \text{Total\_Fee\_Rate} \times (\text{Total\_Fee\_Rate} + 1)
$$

Wher&#x65;**:**

* `Excess_Amount`: Amount of over-represented token
* `Total_Fee_Rate`: Current base + variable fee (as decimal)

#### Imbalance Detection

For Token X excess:

```pascal
if (Amount_X_In > Expected_X_Out) AND (Amount_Y_In < Expected_Y_Out) then 
    Composition_Fee = (Amount_X_In - Expected_X_Out) * Fee_Formula
```

For Token Y excess:

```pascal
if (Amount_Y_In > Expected_Y_Out) AND (Amount_X_In < Expected_X_Out) then 
    Composition_Fee = (Amount_Y_In - Expected_Y_Out) * Fee Formula 
```

### Protocol Fee System

Protocol fees support network development and maintenance by extracting a percentage of all collected fees.

#### Protocol Share Distribution

```javascript
Protocol_Fee = Total_Fee * Protocol_Share ÷ 10,000
LP_Fee = Total_Fee - Protocol_Fee
```

Wher&#x65;**:**

* `Protocol_Share`: Configurable percentage (max 25%)
* `Total_Fee`: Sum of all fee components

#### Fee Types Subject to Protocol Share

* Swap fees (base + variable)
* Composition fees
* Flash loan fees

### Flash Loan Fees

Flash loans have a separate fee mechanism independent of the volatility system.

#### Flash Loan Fee Calculation

```javascript
Flash_Loan_Fee = Loan_Amount * Flash_Loan_Rate ÷ 1,000,000,000
```

Wher&#x65;**:**

* `Loan_Amount`: Principal amount borrowed
* `Flash_Loan_Rate`: Configurable rate (max 10%)

#### Characteristics

* **Fixed rate**: Not affected by volatility
* **Immediate**: Charged upon loan initiation
* **Protocol allocation**: Subject to protocol share split

### Fee Distribution Mechanics

#### Per-Bin Fee Tracking

Each liquidity bin maintains detailed fee accounting using growth tracking:

```javascript
Fee_Growth_per_Unit = Accumulated_Fees ÷ Total_Liquidity_in_Bin
```

#### LP Fee Calculation

When liquidity providers claim fees:

```javascript
LP_Fees_Owed = LP_Liquidity * (Current_Fee_Growth - Last_Claimed_Fee_Growth)
```

This ensures proportional fee distribution based on:

* Liquidity amount provided
* Duration of liquidity provision
* Bins crossed during trading

### Parameter Configuration

#### Static Parameters (Set at Pool Creation)

* `Base_Factor`: Determines base fee level
* `Variable_Fee_Control`: Volatility sensitivity
* `Filter_Period`: High-frequency threshold
* `Decay_Period`: Volatility reset threshold
* `Reduction_Factor`: Volatility decay rate
* `Protocol_Share`: Protocol fee percentage
* `Max_Volatility_Accumulator`: Volatility ceiling

#### Dynamic Parameters (Updated During Trading)

* `Volatility_Accumulator`: Current volatility level
* `Volatility_Reference`: Decayed volatility baseline
* `Reference_Bin_ID`: Reference point for volatility calculation
* `Last_Update_Time`: Timestamp for time-based calculations

### Practical Examples

#### Example 1: Variable Fee Calculation

Market Setup:

* Base Factor: 100
* Bin Step: 5
* Volatility Accumulator: 50,000
* Variable Fee Control: 2,500

Fee Calculation:

```javascript
Base_Fee = 100 × 5 × 10 = 5,000 units (0.0005%)
Variable_Fee = (50,000 × 5)² × 2,500 ÷ 100
             = (250,000)² × 2,500 ÷ 100
             = 62,500,000,000 × 2,500 ÷ 100
             = 1,562,500,000,000 units (0.156%)
Total_Fee = 5,000 + 1,562,500,000,000 = 1,562,505,000,000 units (0.1565%)
```

#### Example 2: Volatility Evolution Over Time

Initial State:

* Volatility\_Accumulator: 0
* Reference\_Bin\_ID: 1000
* Filter\_Period: 30 seconds
* Decay\_Period: 300 seconds
* Reduction\_Factor: 5,000 (50%)

Transaction Sequence:

T=0: Large Swap (crosses 8 bins to ID 1008)

```javascript
Volatility_Accumulator = 0 + |1008 - 1000| × 10,000 = 80,000
```

T=45s: Medium Swap (crosses 3 bins to ID 1011) Since 45s > 30s (filter period) and < 300s (decay period):

```javascript
New_Volatility_Reference = 80,000 × 5,000 ÷ 10,000 = 40,000
New_Reference_Bin_ID = 1008
Volatility_Accumulator = 40,000 + |1011 - 1008| × 10,000 = 70,000
```

T=350s: Small Swap (crosses 1 bin to ID 1012) Since 350s > 300s (decay period):

```javascript
Volatility_Reference = 0 (reset)
Reference_Bin_ID = 1011
Volatility_Accumulator = 0 + |1012 - 1011| × 10,000 = 10,000
```

#### Example 3: Composition Fee Application

Bin State:

* Current reserves: 3,000 USDC, 1,000 SUI
* SUI price: 3.0 USDC

LP Deposit:

* Deposits: 1,800 USDC, 500 SUI
* Expected balanced ratio: 1,500 USDC, 500 SUI
* Excess USDC: 300 USDC

Fee Calculation:

```javascript
Total_Fee_Rate = 0.01 (1%)
Composition Fee = 300 × 0.01 × (0.01 + 1)              
                = 300 × 0.01 × 1.01   
                = 3.02 USDC             
```

#### Example 4: Protocol Fee Distribution

Swap generates:

* Total Fee: 100 USDC
* Protocol Share: 20% (2,000 basis points)

Distribution:

```javascript
Protocol_Fee = 100 × 2,000 ÷ 10,000 = 20 USDC
LP_Fee = 100 - 20 = 80 USDC
```


# Shapes & Strategies

### Liquidity Shapes and Strategies Overview

Ferra’s DLMM offers a catalog of pre‑built liquidity shapes—ready‑made templates that define how tokens are distributed across discrete price bins. These shapes empower liquidity providers (LPs) to position their capital in a way that aligns with their strategy and risk appetite: concentrating liquidity near the spot price for high efficiency, spreading it widely for passive exposure, or setting twin peaks to automate “buy low, sell high” cycles.

In Ferra DLMM, each price bin represents a fixed price level (e.g., 2.95, 3.00, 3.05 USDC per SUI), and swaps occur with zero slippage as long as sufficient liquidity exists within the bin. Unlike traditional AMMs that distribute liquidity uniformly or CLMMs that concentrate liquidity within continuous ranges, DLMM uses discrete bins to enable precise capital allocation.

Because LPs earn fees only when swaps interact with bins holding active liquidity, choosing the right shape is crucial to maximizing fee income, reducing idle capital, and minimizing manual rebalancing.

Ferra currently supports three primary liquidity shapes: Spot, Curve and Bid-Ask. Each shape corresponds to a distinct strategy, tailored for different market conditions, from stablecoin stability to memecoin volatility and is designed to help LPs balance fee generation, capital efficiency, and risk exposure.

### **Spot Strategy**

{% columns %}
{% column width="58.333333333333336%" %}
Spot shape follows the Fat/uniform distribution, The liquidity is spread equally across every bin within a user‑defined price window (for example, ±5 % around the current price). Each bin therefore contains the same share of the tokens, allowing the pool price to drift inside that band without pushing your position out of range.
{% endcolumn %}

{% column %}

<figure><img src="/files/RAJNahTllY8AfACz5P9C" alt="" width="276"><figcaption></figcaption></figure>
{% endcolumn %}
{% endcolumns %}

* **When to use**: Stablecoin pairs, blue‑chip assets, or any market where you prefer a passive, “always‑in‑range” stance.
* **Strengths**: Maximum time‑in‑range, minimal upkeep, transparent impermanent‑loss profile.
* **Trade‑offs**: Capital is not concentrated at the mid‑price, so fee APR may lag more focused shapes (like Curve or Bid‑Ask) during high volatility.
* **Considerations**: The even distribution dilutes liquidity per bin, potentially reducing fee earnings in stable markets where trading concentrates around a narrow price range. LPs must select a range wide enough to capture price movements but not so wide as to compromise capital efficiency.

### Curve **Strategy**&#x20;

{% columns %}
{% column width="58.333333333333336%" %}
A Curve shape stacks the majority of the liquidity in bins closest to the current price, tapering smoothly toward the outer bins so it resembles a Gaussian bell curve. The goal is to maximize capital efficiency at the mid‑price, where most trading volume occurs, while still covering a modest price range so your position stays in‑range if the market drifts.
{% endcolumn %}

{% column %}

<figure><img src="/files/Peo72jz7rK5QIW1A6LGn" alt="" width="274"><figcaption></figcaption></figure>
{% endcolumn %}
{% endcolumns %}

* **When to use**: Sideways or range‑bound markets where price oscillates around a mean value (e.g., BTC‑SUI, large‑cap pairs).
* **Strengths**: Higher fee capture and lower capital wastage compared with a flat Spot shape, because more liquidity is sitting exactly where trades happen.
* **Trade‑offs**: As price trends, the mid‑price can leave the peak of the curve; you’ll need to re‑center periodically or accept lower utilization.
* **Considerations**: If the price moves outside the concentrated range, the position becomes inactive, halting fee earnings until rebalanced. This strategy requires active monitoring in volatile markets to maintain effectiveness.

### Bid‑Ask Strategy

{% columns %}
{% column width="58.333333333333336%" %}
A **Bid‑Ask** shape places two liquidity peaks on opposite sides of the current price, leaving a shallow valley or none at all, right at spot. Think of it as providing thick “bid” liquidity below and thick “ask” liquidity above, useful for strategies that buy low and sell high automatically as price swings.
{% endcolumn %}

{% column %}

<figure><img src="/files/DFDAGtBCgAtl3IsCx4Oi" alt="" width="278"><figcaption></figcaption></figure>
{% endcolumn %}
{% endcolumns %}

* **When to use**: Highly volatile or mean‑reverting assets (e.g., meme coins) or whenever you want to run passive grid/DCA strategies.
* **Strengths**: Earn fees twice, once when price moves down into your lower peak (accumulating the asset) and again when it moves up into your upper peak (distributing it back). Effectively captures value from large oscillations.
* **Trade‑offs**: If price stabilizes in the mid‑zone, little or no liquidity is active, reducing fee income. Requires wider monitoring or adaptive shifting.
* **Considerations**: Higher exposure to impermanent loss if prices move significantly, as liquidity is concentrated at range extremes. LPs must be comfortable with volatility and active management.

### Single-Sided Liquidity

Ferra’s DLMM  supports single-sided liquidity, allowing LPs to provide only one token (e.g., USDC in bins below 3.00 to buy SUI if the price drops, or SUI in bins above 3.00 to sell if the price rises). This is particularly useful for:

* **Token Launches**: Projects can bootstrap liquidity with one token, facilitating early trading.
* **Directional Strategies**: LPs with bullish or bearish outlooks can position liquidity to capitalize on expected price movements.
* **Example**: An LP provides 3,000 USDC in bins at 2.90–2.95 USDC per SUI, enabling SUI purchases if the price drops, earning fees and accumulating SUI.

### **Liquidity Shape Example**

The following is an example of liquidity distribution for the SUI/USDC pair, assuming the current SUI price is approximately 3.00 USDC.

{% tabs %}
{% tab title="Curve Strategy" %}

<figure><img src="/files/7nOyO40jW7JqouuZq7tG" alt=""><figcaption><p>Curve Liquidity Distribution for SUI/USDC</p></figcaption></figure>
{% endtab %}

{% tab title="Bid-ask Strategy" %}

<figure><img src="/files/2yriNePcLI1HseG1Pdut" alt=""><figcaption><p>Bid-Ask Liquidity Distribution for SUI/USDC</p></figcaption></figure>
{% endtab %}

{% tab title="Spot Strategy" %}

<figure><img src="/files/4DQBV1rNC8OydBSoN209" alt=""><figcaption><p>Spot Liquidity Distribution for SUI/USDC</p></figcaption></figure>
{% endtab %}
{% endtabs %}

### **🔍 Shape Selector Quick Guide**

| Market Condition | Recommended Shape | Upkeep   | IL Risk    |
| ---------------- | ----------------- | -------- | ---------- |
| Stable / Pegged  | Spot / Curve      | Low      | Low        |
| Sideways Range   | Curve             | Medium   | Medium     |
| High Volatility  | Bid‑Ask           | High     | High       |
| Long‑Term Hold   | Spot/Wide-Bins    | Very Low | Low/Medium |

{% hint style="warning" %}
**Risk Reminder**\
All shapes are subject to impermanent loss (IL) and market risk. Choose a shape that matches your risk tolerance and the level of management effort you’re willing to commit.
{% endhint %}

### **🛠️ Pro Tips**

* **Stay in Active Bins**: Fees accrue only when trades hit your bins.
* **Mix & Match**: Advanced LPs can split liquidity across multiple shapes in the same pool.
* **Automate**: Scripts or forthcoming Auto‑Vaults can auto‑shift curves as price moves.
* **Monitor market volatility**: Rising volatility usually means higher dynamic fees. When the market gets choppy, think about shifting your shape, harvesting fees, or tightening ranges to lock in more returns.


# CLMM


# CLMM Overview

### Introduction to CLMM

The Concentrated Liquidity Market Maker (CLMM) is an advanced evolution of the Automated Market Maker (AMM) model used in decentralized exchanges (DEXs). Unlike traditional AMMs, which distribute liquidity uniformly across all price levels, CLMM enables liquidity providers (LPs) to concentrate their capital within specific price ranges. This targeted approach enhances capital efficiency, reduces slippage for traders, and increases potential returns for LPs. Ferra’s CLMM implementation on the SUI network likely leverages these benefits to provide a robust liquidity solution for DeFi users.

### Evolution from Traditional AMM

Traditional AMMs, such as Uniswap V2, rely on a constant product formula $$X \times Y = K$$ to manage liquidity pools. In these systems, liquidity is spread across the entire price spectrum (from zero to infinity), leading to capital inefficiency. Much of the provided liquidity remains unused, as trades typically occur within a narrow price range. CLMM, introduced by protocols like Uniswap V3 in May 2021, addresses this by allowing LPs to specify price ranges for their liquidity. This ensures that capital is utilized only where trading activity is most likely, significantly improving efficiency and reducing slippage.

<figure><img src="/files/bHksYvLdTTFD0UCr9XM0" alt=""><figcaption><p>AMM and CLMM liquidity distribution comparison</p></figcaption></figure>

### Key Components of CLMM

CLMM operates with several core components that define its functionality:

<table data-header-hidden><thead><tr><th width="200.76171875"></th><th></th></tr></thead><tbody><tr><td><strong>Component</strong></td><td><strong>Description</strong></td></tr><tr><td><strong>Ticks</strong></td><td>Discrete price points dividing the price range. Each tick represents a specific price, with tick spacing determining granularity (e.g., 0.01% price steps).</td></tr><tr><td><strong>Liquidity Positions</strong></td><td>LPs create positions by specifying a lower and upper tick. Liquidity is active only within this range, earning fees when trades occur inside it. Each position is an on‑chain NFT storing range, liquidity, and fees.</td></tr><tr><td><strong>Fee Tiers</strong></td><td>Pools offer different fee levels (e.g., 0.01%, 0.05%, 0.3%), allowing LPs to choose based on expected trading volume and risk.</td></tr></tbody></table>

### Benefits of CLMM

CLMM offers significant advantages over traditional AMMs, particularly in the context of Ferra on the SUI network:

* **Higher Capital Efficiency**: By concentrating liquidity in specific price ranges, LPs can earn higher fees with less capital compared to traditional AMMs.
* **Reduced Slippage**: Traders benefit from deeper liquidity around the current price, resulting in lower price impact for trades.
* **Customizable Risk Management**: LPs can select price ranges based on their market outlook, tailoring their exposure to volatility.
* **Increased Yield Potential**: Concentrated liquidity positions in high-volume price ranges can generate higher fee returns.
* **SUI Network Synergies**: SUI’s low transaction costs and high scalability enable frequent position adjustments and smooth operation during high trading volumes, enhancing CLMM’s effectiveness.


# Technical Design

### Liquidity Distribution

In CLMM, liquidity is concentrated within user-defined price ranges rather than spread uniformly. Each liquidity position is defined by a lower tick $$T\_L$$ and an upper tick $$T\_U$$, corresponding to specific prices $$P\_L$$ and $$P\_U$$. The total liquidity at any price is the sum of all active positions that include that price. When the market price moves outside a position’s range, the liquidity becomes inactive, earning no fees and potentially incurring impermanent loss.

### Tick-Based Model

CLMM discretizes the price spectrum into ticks, each representing a small price increment. For example, a tick spacing of 1 might correspond to a 0.01% price change (e.g., a multiplier of 1.0001). This tick-based model:

* **Enables Precise Liquidity Placement**: LPs can target specific price ranges, such as $100 to $110 for a token pair.
* **Reduces Computational Overhead**: Discrete ticks simplify liquidity calculations compared to a continuous price model.
* **Supports Granularity**: Smaller tick spacing allows finer control, suitable for stablecoin pairs, while larger spacing suits volatile assets.

The price at a given `tick (i)` can be calculated as: `P(i) = 1.0001^i` where (i) is the tick index, and 1.0001 is the price multiplier per tick for a 0.01% fee tier.

<figure><img src="/files/iaDeBiegTAcAH9YrHxxQ" alt=""><figcaption><p>Tick-based in Ferra's CLMM liquidity model</p></figcaption></figure>

### **Fee Structure**

<table><thead><tr><th width="191.37890625">Component</th><th>Description</th></tr></thead><tbody><tr><td>Swap Fee</td><td>Percentage charged on every trade (e.g., 0.05 %, 0.30 %, 1 %).</td></tr><tr><td>Fee Tier</td><td>Each pool has a fixed swap‑fee tier chosen at deployment. Multiple pools with different tiers can coexist for the same token pair.</td></tr><tr><td>Protocol Fee</td><td>A configurable share of the swap fee routed to the Ferra treasury. The remainder goes to LPs. It is set to 20% by default</td></tr></tbody></table>

### Fee Calculation

Fees in CLMM are collected when trades occur within a position’s active price range. The fee rate depends on the pool’s fee tier (e.g., 0.3% for volatile pairs, 0.01% for stable pairs). Fees are distributed proportionally to LPs based on their share of liquidity at the traded price. The protocol tracks fee growth per tick, allowing LPs to claim accumulated fees when they withdraw liquidity or collect earnings.

#### Mathematical Formulas

CLMM relies on mathematical formulas to manage liquidity and fees. Key equations include:

* **Liquidity Amount L**: When an LP adds liquidity, the amount of liquidity provided is calculated based on the token amounts and the price range. For a position between prices $$P\_L$$ and $$P\_U$$, the liquidity (L) is:

$$
L = \frac{\Delta y}{\sqrt{P\_U} - \sqrt{P\_L}} \quad \text{(for token Y when price is below range)}
$$

$$
L = \frac{\Delta x \cdot \sqrt{P\_L} \cdot \sqrt{P\_U}}{\sqrt{P\_U} - \sqrt{P\_L}} \quad \text{(for token X when price is above range)}
$$

Where $$\Delta X$$ and $$\Delta Y$$ are the amounts of token X and token Y provided, respectively.

* **Position Value**: The value of a position depends on the current price (P). If $$P\_L \leq P \leq P\_U$$, the position is active, and its value includes both tokens and accumulated fees.
* **Fee Growth**: Fees are tracked as “fee growth per unit of liquidity” for each tick range. When a trade occurs, the fee growth is updated, and LPs can claim fees proportional to their liquidity share.

### Position Management

LPs interact with CLMM through the following actions:

* **Minting Positions**: Create a new position by specifying a tick range and token amounts.
* **Adding Liquidity**: Increase liquidity in an existing position.
* **Removing (Burning) Positions**: Withdraw liquidity, receiving the underlying tokens and any accumulated fees.
* **Collecting Fees**: Claim earned fees without removing liquidity.

Positions are active only when the current price is within the specified range. If the price moves outside, the position earns no fees and may face impermanent loss.

### Impermanent Loss

CLMM reduces impermanent loss by allowing LPs to focus liquidity in stable price ranges, but it does not eliminate it. If the price moves outside a position’s range, the LP holds only one token (the less valuable one), potentially leading to losses compared to holding the tokens outside the pool. The concentrated nature of CLMM can amplify impermanent loss in volatile markets, requiring active position management.

### **Swap Mechanics (High‑Level)**

* User submits `amountIn` of `TokenA` for `TokenB`.
* Contract computes the maximum amount consumable before hitting the next initialized tick.
* Constant‑product math updates the pool within the current tick and deducts the swap fee.
* If `amountIn` remains, the algorithm crosses the next tick, activates its liquidity, and repeats until the full amount is swapped.
* Fee growth variables are updated so LPs can later collect their share.

### Liquidity Strategies

Unlike DLMM, a CLMM pool has only one fundamental liquidity layout, functionally similar to the Spot shape in DLMM. Your strategy therefore comes from how wide or narrow you set your price range, not from choosing different shapes. The comparison below shows how range width affects performance and maintenance.

<table><thead><tr><th>Range Strategy</th><th>Typical Width</th><th>When to Use</th><th>Pros</th><th>Const</th><th width="114.03125">Upkeep</th></tr></thead><tbody><tr><td><strong>Narrow</strong></td><td>±0.5 – 2 % around spot</td><td>High‑volume, low‑volatility pairs; active LPs</td><td>Highest fee APR per dollar</td><td>Falls out‑of‑range quickly; needs frequent re‑centering</td><td><strong>High</strong></td></tr><tr><td><strong>Medium</strong></td><td>±5 – 10 %</td><td>Range‑bound blue chips</td><td>Balanced uptime vs. efficiency</td><td>Lower APR than narrow; some upkeep</td><td><strong>Medium</strong></td></tr><tr><td><strong>Wide</strong></td><td>±20 – 50 %</td><td>Long‑term passive LPs; volatile tokens</td><td>Stays active longer; low maintenance</td><td>Capital spread thin → lower fee capture</td><td><strong>Low</strong></td></tr><tr><td><strong>Ladder</strong></td><td>Stacked narrow + medium + wide</td><td>Diversify IL risk and fee capture</td><td>Blends benefits of multiple widths</td><td>Higher gas costs; more complex</td><td><p><strong>Medium,</strong></p><p><strong>High</strong></p></td></tr></tbody></table>


# DAMM


# DAMM Overview

**Dynamic Automated Market Maker (DAMM)** is an advanced AMM that dynamically adjusts its pricing and fee strategies based on real-time market conditions, optimizing capital efficiency and minimizing losses for liquidity providers. Built natively on Sui, DAMM gives pool creators full control over fee curves, liquidity ranges, and launch mechanics, while LPs and launchpads benefit from smarter fee optimization out of the box.

### Key Features of Ferra DAMM

* **Dynamic Fee:** Fees adjust automatically based on real-time market volatility. High volatility means higher fees to protect LPs; calm markets bring fees back down.
* **Anti-Sniper Suite:** On-chain fee scheduler starts fees high at pool launch and decays them over time (linear or exponential). Combined with whitelist-gated pre-launch access, bots are priced out from block one.
* **LP Fee Collect Mode:** Pool creators choose how fees are collected: from the input token on every swap (`ON_BOTH`), or always from the quote asset (`ON_QUOTE`), giving precise control over which token accumulates as fee revenue.
* **Concentrated Liquidity:** Pool creators define tick ranges at genesis. All liquidity concentrates within that band, maximizing capital efficiency with the constant-product (x\*y=k) formula.
* **Single-Sided Liquidity Launch:** Bootstrap a pool with only one token. No need to supply both sides of the pair — ideal for token launches and launchpad integrations.
* **Liquidity Locks:** Lock LP positions on-chain with a `lock_until` timestamp. Supports permanent locks and vesting schedules. Lock durations are immutable once set — no admin override.

{% hint style="success" %}
**Key takeaways:**

DAMM is tailored for LPs seeking higher yields, projects launching tokens, and launchpads aiming for sustainable liquidity. Its anti-sniper fee scheduler and single-sided liquidity make it particularly suited for memecoin and new token launches
{% endhint %}


# Technical Design

### Core Mechanism: AMM with Concentrated Liquidity

DAMM implements the constant-product formula **x \* y = k**, where `x` and `y` are the reserves of the two tokens and `k` is the invariant maintained across swaps (excluding fees).

Unlike traditional AMMs that spread liquidity uniformly across all prices, DAMM concentrates liquidity within a defined price range. Pool creators establish a lower tick (`T_L`) and upper tick (`T_U`) during initialization — all liquidity deposits operate within that band, improving capital efficiency.

Prices are discretized into **ticks**:

```
P(i) = 1.0001^i
```

The `tick_spacing` parameter controls granularity — smaller spacing allows finer price resolution but costs more gas per swap. Each fee tier maps to a specific tick spacing.

When the current price is within a position's range, that position's liquidity is **active** and earns fees. If the price moves outside the range, the position becomes inactive and holds only one token.

### Fee Architecture

DAMM implements a multi-layered fee system:

```
total_fee = base_fee + variable_fee
```

All fee calculations use **9-decimal precision** (denominator = 1,000,000,000).

#### Base Fee

A fixed percentage established at pool creation (e.g., 0.25%, 0.30%, 1%), applied to every swap. This is the minimum fee when no fee scheduler is active.

#### Dynamic Fee

The dynamic fee adjusts based on real-time market volatility. It adds a variable component on top of the base fee:

```
variable_fee = (volatility_accumulator × tick_spacing)² × variable_fee_control / 100
```

The `volatility_accumulator` tracks price movement per swap and decays over time via `filter_period`, `decay_period`, and `reduction_factor`. The mechanism is similar to the [*Volatility Accumulator*](/core-protocols/quickstart/dynamic-fee) which explained in DLMM's Dynamic Fee session. Higher volatility leads to increased fees to protect LPs; calm markets bring the variable fee back to zero.

#### Fee Caps

| Limit               | Value            |
| ------------------- | ---------------- |
| Max total fee       | 50%              |
| Max base fee rate   | 10%              |
| Min fee after decay | 0.01%            |
| Max protocol share  | 30% of total fee |

#### Anti-Sniper Fee Scheduler

The fee scheduler deters bots from sniping tokens during early trading by starting with high fees that decrease over time. It **overrides** the base fee with a decaying cliff fee (up to 50%).

**Linear decay:**

```
fee(t) = cliff_fee - (reduction_factor × periods_elapsed)
```

**Exponential decay:**

```
fee(t) = cliff_fee × (1 - reduction_factor / 10000) ^ periods_elapsed
```

**Example**:  A pool might launch with an initial fee of 10%, which decays gradually to 0.3% over a period of 24 hours or 1,000 slots. For highly volatile tokens, such as meme coins, the initial fee can be as high as 80% at launch to mitigate the risks of sudden price swings.

Once the scheduler completes all periods, the fee settles back to the static `fee_rate`

The **linear fee decay** from initial fee formular:  $$F(t) = F\_{\text{init}} - \left( \frac{F\_{\text{init}} - F\_{\text{final}}}{T} \right) \cdot t$$ &#x20;

Where:

* $$F(t)$$ : Fee at time (t)
* $$F\_{\text{init}}$$ : Initial fee, eg: 50%
* $$F\_{\text{final}}$$ : Final fee, eg: 0.3%
* T :  total decay duration
* &#x20;t :  elapsed time or slots

<figure><img src="/files/pbomLTWIiu1WrI6MNWTI" alt=""><figcaption><p>Linear decay function over the time (t)</p></figcaption></figure>

The **exponential fee decay** equation calculates the fee  $$F(t)$$ over time using a decay constant $${\lambda}$$, as described below:  $$F(t) = F\_{\text{final}} + (F\_{\text{init}} - F\_{\text{final}}) \cdot e^{-\lambda t}$$

<figure><img src="/files/elugzZPQNvWPtfYCc7ix" alt=""><figcaption><p>Exponential decay function over the time (t)</p></figcaption></figure>

The chart above shows that the fee is quite high at launch (e.g., 38% at 5 minutes after launch) but decreases to a normal rate once trading stabilizes (e.g., 0.54% at 50 minutes after launch).

#### Protocol Fee

A configurable share of the total fee is routed to the Ferra protocol. The remainder goes to LPs.

```
protocol_fee = total_fee_amount × protocol_fee_rate / 10000
lp_fee = total_fee_amount - protocol_fee
```

#### LP Fee Collect Mode

Pool creators choose how swap fees are collected:

| Mode       | Value | Behavior                                                  |
| ---------- | ----- | --------------------------------------------------------- |
| `ON_BOTH`  | 0     | Fee taken from the **input** token                        |
| `ON_QUOTE` | 1     | Fee taken from the **quote** asset (set via `is_quote_y`) |

`ON_QUOTE` ensures one specific token always accumulates as fee revenue — useful when the quote asset (e.g., USDC, SUI) is preferred.

### Swap Mechanics

#### Swap Flow

1. **Fee Calculation** — Aggregate base fee, dynamic fee, and anti-sniper fee into `total_fee_rate`
2. **Fee Deduction** — Deduct fee from input or output based on `collect_fee_mode`
3. **Price Calculation** — Compute output via constant-product formula:

   ```
   Δy = (y × Δx) / (x + Δx)
   ```
4. **Price Range Validation** — Swaps crossing tick boundaries iterate step by step through ticks
5. **Reserve Updates** — `x_new = x + Δx`, `y_new = y - Δy`, maintaining the invariant
6. **Fee Accumulation** — Split fee into LP fee and protocol fee, update `fee_growth_global`
7. **Volatility Update** — Update dynamic fee volatility parameters if enabled

**Example:** For a 100 SUI input with a 0.36% total fee, 0.36 SUI is deducted, leaving 99.64 SUI for the swap calculation.

#### Swap Result

Each swap emits a detailed breakdown:

```rust
struct SwapResult {
    amount_in: u64,
    amount_out: u64,
    fee_amount: u64,
    base_fee: u64,       // From fee scheduler or static fee_rate
    dynamic_fee: u64,    // From volatility engine
    steps: u64,          // Number of tick crossings
}
```

#### Pre-Swap Simulation

Use `calculate_swap_result` to simulate a swap off-chain without executing it — returns the same result structure including all fee breakdowns and step details.

#### Whitelist (Pre-Launch Access)

* Before `activation_timestamp`, only whitelisted addresses can swap
* After `activation_timestamp`, the pool is open to everyone
* Pool creator or `CONFIG_ROLE` manages the whitelist

### Liquidity Provision

#### Adding Liquidity

LPs deposit tokens into a pool and receive an **NFT position** representing their share. Deposits must be proportional to the current pool price within the established range.

For single-sided contributions, the contract calculates the equivalent based on the current pool price. Supported operations:

* **Open Position** — Create a new position at a tick range
* **Add Liquidity** — Increase liquidity by exact amount or by fixing one coin
* **Add Liquidity (Fix Coin)** — Specify one token amount; the contract computes the other

#### Removing Liquidity

LPs call `remove_liquidity` to withdraw tokens proportional to their pool share. Fee claims are separate from withdrawal — LPs must explicitly collect accumulated fees.

#### Collecting Fees & Rewards

* **Collect Fee** — Claim accumulated LP fees (non-auto-compounded)
* **Collect Reward** — Claim reward token emissions
* **Close Position** — Remove all liquidity + collect fees, then burn the NFT

#### Liquidity Locks

Positions support an on-chain `lock_until` timestamp (in milliseconds):

* While locked, liquidity **cannot** be decreased or removed
* Fee and reward collection **remain available** during lock
* Lock duration is immutable once set — cannot be shortened, no admin override
* `lock_until = 0` means unlocked
* Max lock: \~100 years

**Permanent locks** tokenize as tradeable NFTs, enabling governance participation or secondary market trading of locked positions.

#### Single-Sided Liquidity

DAMM supports depositing only one token when creating a pool. The contract initializes the pool with a starting price, allowing swaps to bootstrap the other side. This is ideal for token launches where the creator supplies only the launch token.

> Note: Single-sided LPs face higher impermanent loss risk due to price volatility during the bootstrapping phase.

### Pool Creation

#### Creation Flow

1. Verify system is not paused and package version is current
2. Check pool creation permission (open or `POOL_MANAGER_ROLE` required)
3. At least one coin type must be in the global token whitelist
4. Load fee scheduler and dynamic fee config from the `FeeTier` for the given `tick_spacing`
5. `activation_timestamp` must be >= current time
6. Generate deterministic pool key from coin types + tick spacing + feature flags
7. Create pool, open initial position, add initial liquidity
8. Share pool object on-chain
9. Return `(Position, remaining_Coin_A, remaining_Coin_B)`

#### Custom Pool Activation

Pool creators assign an `activation_timestamp` (Unix timestamp) controlling when swaps become available. Before this time, only whitelisted addresses can swap — enabling coordinated launches.


# Preparation

This guide provides a step-by-step tutorial on how to use Ferra to:

* [Create Liquidity pool](/user-guide/create-liquidity-pool)
* [Add/Remove Liquidity](/user-guide/add-remove-liquidity)
* [Collect fee reward](/user-guide/collect-fee-reward)
* [Swap](https://docs.ferra.ag/user-guide/swap)
* [LP Guild](https://docs.ferra.ag/user-guide/lp-guild)
* [Lock Liquidity](https://docs.ferra.ag/user-guide/lock-liquidity)

Before starting, make sure you are familiar with Ferra's core concepts like [DLMM](/core-protocols/quickstart), [CLMM](/core-protocols/publish-your-docs), [DAMM](/core-protocols/damm), plus having a basic understanding of SUI network & how DeFi protocols work.

{% hint style="warning" %}
**Reminder**

Take a moment to review our [Legal Disclaimer](/resources/legal-disclaimer) outlining risks, responsibilities, and limitations when using Ferra & participating in on-chain DeFi activities.
{% endhint %}

### **Connect your SUI Wallet**

1. Visit [https://ferra.ag](https://ferra.ag/)
2. Click on  `Connect Wallet` button in the top-right corner.
3. Select a wallet to connect

{% hint style="info" %}
*Ferra supports multiple Sui-compatible wallets, including* [*Slush*](https://slush.app/)*,* [*Suiet*](https://suiet.app/)*,* [*OKX Wallet*](https://web3.okx.com/)*, and more. We recommend using* [*Slush*](https://slush.app/) *for the best experience.*
{% endhint %}

4. Unlock your wallet, pick the address to link, and approve.

*Your wallet is connected if your address is displayed on Ferra's interface.*

*If the connection fails, ensure your wallet is switched to **Sui Mainnet**, unlocked, and up to date.*

<figure><img src="/files/D8GoZKgNYtcxIY5ClCs1" alt=""><figcaption></figcaption></figure>

### **Gas Fee**

Every transactions on Ferra requires a small amount of gas fee. As a rule of thumb, keeping \~0.5–1 SUI in your wallet is generally sufficient.


# Create Liquidity pool

A liquidity pool is an on-chain reserve managed by a smart contract that holds two or more assets and algorithmically quotes swap prices. By launching a pool, you bootstrap the market for a token pair & earn a share of trading fees (and any bonus incentives).

This guide describes the end-to-end walkthrough to **create a liquidity pool** on Ferra's DLMM & CLMM pool and seed it with initial liquidity.

<figure><img src="/files/gGU04NUKlvuh6sLVH4A3" alt=""><figcaption></figcaption></figure>


# DLMM pool

### 1. **Getting started**

Visit <https://ferra.ag/>, connect your Sui wallet, then click `Create Pool`&#x20;

{% hint style="info" %}
*<mark style="color:$info;">A certain amount of SUI is required to create pool, so ensure your wallet has enough balance.</mark>*
{% endhint %}

<figure><img src="/files/WzgJWHrghSHRWbDp74fK" alt=""><figcaption></figcaption></figure>

### 2. Configure your DLMM Liquidity Pool Setup (1)

**Select Pool Type**

* Choose `DLMM`

#### Choose Pool pair

* Select a `Base Token` (e.g., CETUS) & a `Quote Token` (e.g., SUI)

#### Set Base fee

* Learn more about [Base fee](/core-protocols/quickstart/dynamic-fee)
* Then, pick from the available options: 0.01%, 0.05%, 0.1%, 0.5%, 1%, 5%

#### Set Bin Step

* Learn more about [Bin Step](/core-protocols/quickstart/dlmm-overview)
* Pick a base fee; we’ll auto-pick the matching bin steps

#### Set Initial Price

* This is the starting exchange rate between the two tokens when the pool launches.
* *E.g. 1 CETUS = 0.029 SUI (or '0.029 SUI per CETUS')*

Once everything is set, click `Create`

<figure><img src="/files/gdQ06JcQqtiLsnyhd5ua" alt="" width="373"><figcaption></figcaption></figure>

### 3. Configure your DLMM Liquidity Pool Setup (2)

**Enter Deposit Amount**

* Input the amount of both tokens or supply single-sided liquidity that you want to create pool

#### Select your preferred Liquidity Shape strategy

* **Spot**: Evenly distributes liquidity. Simple, low risk, and suitable for most markets
* **Curve**: Concentrates liquidity around current price—ideal for stable pairs or low-volatility assets
* **Bid-ask:** An inverse curve distribution, typically single-sided, perfect for DCA in/out strategies and capturing volatility during price swings

Adjust the Price parameters in `Set Price Range` section if needed.

After reviewing all settings, click `Add Liquidity` & approve the transaction in your wallet.

**🎉 Congratulations! You’ve successfully created your custom DLMM liquidity pool on Ferra.**

<figure><img src="/files/AzN7aWJX9rIGZ4Nx62uq" alt=""><figcaption></figcaption></figure>

### 4. Manage your DLMM Pool

Navigate to **`My Position`** tab to:

* View your pool details & performance
* Claim your accrued yield
* Add more liquidity
* Withdraw liquidity
* Lock liquidity (permanent or non-permanent)


# CLMM pool

### 1. **Getting started**

Visit <https://ferra.ag/>, connect your Sui wallet, then click `Create Pool`&#x20;

<figure><img src="/files/WzgJWHrghSHRWbDp74fK" alt=""><figcaption></figcaption></figure>

### 2. Configure your CLMM Liquidity Pool Setup (1)

#### Select Pool Type

* Choose `CLMM`

#### Choose Pool pair

* Select a `Base Token` (e.g., CETUS) & a `Quote Token` (e.g., SUI)

#### Set Fee Tier

* Learn more about [Fee Tier](/core-protocols/publish-your-docs/clmm-overview)
* Then, pick from the available options: 0.01%, 0.05%, 0.1%, 0.5%, 1%, 5%

#### Set Initial Price:

* This is the starting exchange rate between the two tokens when the pool launches.
* *E.g. 1 CETUS = 0.029 SUI (or '0.029 SUI per CETUS')*

Once you’ve completed these settings, click `Continue`

<figure><img src="/files/K9qq9mO3oxjuchgevI1x" alt="" width="380"><figcaption></figcaption></figure>

### 3. Configure your CLMM Liquidity Pool Setup (2)

#### Enter Deposit Amount

* Input the amount of both tokens or supply single-sided liquidity that you want to create pool

#### Select your preferred Price range strategy

* **Active**: Focus liquidity within a narrow range for higher yields, with higher IL
* **Full Range:** Provide liquidity across the entire price spectrum
* **Custom**: Set a custom range tailored to your strategy

Adjust the Price parameters in `Set Price Range` section if needed.

After reviewing all settings, click ‘**Create Pool’** & approve the transaction in your wallet.

🎉 **Congratulations! You’ve successfully created your own custom CLMM liquidity pool on Ferra.**

<figure><img src="/files/38rtcRtqooZXyxx3CvxU" alt="" width="458"><figcaption></figcaption></figure>

### 4. Manage your CLMM Pool

Navigate to **`My Position`** tab to:

* View your pool details & performance
* Monitor & claim earned yields
* Add more liquidity
* Withdraw liquidity
* Lock liquidity (permanent or non-permanent)


# Add/Remove Liquidity

Use this guide to **increase**, **decrease**, or **rebalance** an existing liquidity position on Ferra's DLMM & CLMM pool. Adjusting your position changes your share of the pool and, by extension, your share of trading fees (and any bonus incentives).


# DLMM pool

### 1. Getting started

Visit <https://ferra.ag/>, then connect your wallet

{% hint style="info" %}
Ferra supports multiple Sui wallets, including [Slush](https://slush.app/), [Suiet](https://suiet.app/), [OKX Wallet](https://web3.okx.com/), and more.

Make sure your wallet has sufficient SUI for gas fees.
{% endhint %}

### 2. Select a DLMM Liquidity Pool

* Navigate to `DLMM` section
* Choose the pair you’d like to provide liquidity to, then click on `Add Liquidity` nearby
* In this guide, we’ll use the `USDC - SUI` pair with a ***5% Base fee*** & ***20 Bin Steps*** as an example.

*<mark style="color:$info;">Learn more about</mark>* [*<mark style="color:$info;">Base fee</mark>* ](/integration/dlmm/typescript-sdk/utilities-and-reference/bin-mathematics#what-are-bins)*<mark style="color:$info;">and</mark>* [*<mark style="color:$info;">Bin Step</mark>*](/core-protocols/quickstart/core-concepts)*<mark style="color:$info;">.</mark>*

<figure><img src="/files/lxeFonTo772GUn4uLdhl" alt=""><figcaption></figcaption></figure>

### 3. Add Liquidity

* Click on `+ Add Position` tab in the `USDC - SUI` Pool Dashboard

<figure><img src="/files/NhCCa5xOXVR5nu5QwFg6" alt=""><figcaption></figcaption></figure>

* Enter the amounts of USDC and/or SUI you want to deposit

<figure><img src="/files/TEi9xhdNnm6yA6oZuqNP" alt=""><figcaption></figcaption></figure>

4. ### Choose Your Liquidity Shape Strategy&#x20;

Select the Liquidity Shape strategy that aligns with your goals:

* **Spot**: Evenly distributes liquidity. Simple, low risk, and suitable for most markets
* **Curve**: Concentrates liquidity around current price—ideal for stable pairs or low-volatility assets
* **Bid-ask**: An inverse curve distribution, typically single-sided, perfect for DCA in/out strategies and capturing volatility during price swings

Each strategy has its own advantages & trade-offs.

Liquidity providers can freely adjust the Price parameters in `Set Price Range` section if needed.

<figure><img src="/files/PWbPY48Ngv3yvM3A1gkq" alt="" width="563"><figcaption></figcaption></figure>

### 5. Complete LP-ing

* After all set, click `Add liquidity`&#x20;
* Approve the transaction in your wallet
* Once confirmed:
  * You’ll see a `Transaction Completed` notification
  * You’ll receive an NFT in your wallet representing your liquidity position

### 6.  Manage your position

In `My Position` tab, LP-ers can:

* View your LP position details (total deposit, APR, liquidity amount, current position status)
* Monitor & harvest your unclaimed yields
* Add more liquidity
* Withdraw liquidity
* Lock liquidity (permanent or non-permanent)

<figure><img src="/files/CDslPjjSxIjuO7R3YMFC" alt=""><figcaption></figcaption></figure>

### 7. Add more Liquidity to an existing position

To add additional liquidity to your current position:

* Click `Add Liquidity` in `My Position` Tab
* Enter the amounts of both tokens or single-sided liquidity
* Select your preferred Liquidity Shape strategy & adjust price range parameters if needed.
* Click `Add Liquidity`, then approve the transaction in your wallet.

Your position will be updated automatically after confirmation.

<figure><img src="/files/ydVQIFNNWOjM886clU1i" alt=""><figcaption></figcaption></figure>

### 8. Withdraw Liquidity

* Click `Withdraw` in `My Position` Tab
* Drag the price range slider to select how much liquidity you’d like to pull out
* Click `Withdraw Liquidity`, then confirm the transaction in your wallet

After successful withdrawal, a certain amount of tokens that you’ve LP-ed will be returned to your wallet, and your position will be updated accordingly.

<figure><img src="/files/Mc47lidp3JbJ55PeizwU" alt=""><figcaption></figcaption></figure>


# CLMM pool

### 1. Getting started

Visit [https://ferra.ag](https://ferra.ag/), then connect your wallet

{% hint style="info" %}
Ferra supports multiple Sui wallets, including [Slush](https://slush.app/), [Suiet](https://suiet.app/), [OKX Wallet](https://web3.okx.com/), and more.

Make sure your wallet has sufficient SUI for gas fees.
{% endhint %}

Make sure your wallet has sufficient SUI for gas fees.

### 2. Select a CLMM Liquidity Pool

* Navigate to `CLMM` section
* Choose the pair you’d like to provide liquidity to, then click on `Add Liquidity` nearby
* In this guide, we’ll use the `SUI - USDC` pair with a ***0.1% fee tier*** as an example

*<mark style="color:$info;">Learn more about</mark>* [*<mark style="color:$info;">Fee Tier</mark>*](https://ferra.gitbook.io/ferra/core-protocols/publish-your-docs/clmm-overview)*<mark style="color:$info;">.</mark>*

<figure><img src="/files/coTF6QZ0fIOUC4AW7fyy" alt=""><figcaption></figcaption></figure>

### 3. Add Liquidity

* Click on  `+ Add Liquidity` tab in the `SUI - USDC` Pool Dashboard

<figure><img src="/files/dF7j5CJ0hLPWyIYYGWrL" alt=""><figcaption></figcaption></figure>

* Enter the amounts of SUI and/or USDC you wish to deposit.

<figure><img src="/files/HeRRUG6osU9fJFXZlaGU" alt=""><figcaption></figcaption></figure>

### 4. Choose Your Price Range Strategy

Select the strategy that best fits your LP goals:

* **Active**: Focus liquidity within a narrow range for higher yields, with higher IL
* **Full Range**: Provide liquidity across the entire price spectrum
* **Custom**: Set a custom range tailored to your strategy

Each option is suited to different market conditions & carries its own advantages and trade-offs.

Liquidity providers can freely adjust the Price parameters in `Set Price Range` section if needed.

<figure><img src="/files/YTIFuiLn1OzkCJTtAHLb" alt=""><figcaption></figcaption></figure>

### 5. Complete LP-ing

* After all set, click `Add Liquidity`
* Approve the transaction in your wallet.
* Once confirmed:
  * You’ll see a Transaction Completed notification
  * You’ll receive an NFT in your wallet as proof of your liquidity position.

### 6. Manage your position

Switch to `My Position` tab to:

* View your detailed LP position info (total deposit, APR, liquidity amount, current position status)
* Monitor & harvest your unclaimed yields
* Add more liquidity
* Withdraw liquidity
* Lock liquidity (permanent or non-permanent)

<figure><img src="/files/4cly6TwzhRWy2mcXA0rd" alt=""><figcaption></figcaption></figure>

### 7. Add more Liquidity to an existing position

To add additional liquidity to your current position:

* Click `Add Liquidity` in `My Position` Tab
* Enter the amounts of both tokens or single-sided liquidity
* Click `Add Liquidity`, then confirm the transaction in your wallet.

Your position will be updated automatically once the transaction is completed.

<figure><img src="/files/SIkzcNiv3oNzrjqccZUv" alt=""><figcaption></figcaption></figure>

### 8. Withdraw Liquidity

* Click `Withdraw` in `My Position` Tab
* Select the withdrawal percentage (25%, 50%, 75%, 100%) or enter a custom percentage.
* Click `Withdraw Liquidity`, then confirm the transaction in your wallet.

After successful withdrawal, a certain amount of tokens you’ve LP-ed will be returned to your wallet, and your position will be updated accordingly.

<figure><img src="/files/YvLZZOyYLmb8FP0UxFpw" alt=""><figcaption></figcaption></figure>


# Lock Liquidity

Locking liquidity escrows your liquidity position in a smart contract for a specified period or permanently based on your preference. It signals long-term commitment, deters rug-pull risk, and may qualify the position for incentives program.&#x20;

Ferra supports liquidity lock for both **DLMM** & **CLMM.**


# DLMM pool

1. Click `Lock Position` in `My Position` Tab

<figure><img src="/files/682vt4GS2xkqsEMa3aV9" alt=""><figcaption></figcaption></figure>

2. **Select your desired Lock Type**

* **Permanent Lock** – Liquidity is locked forever & cannot be withdrawn, but any earned fees remain claimable anytime.

  <figure><img src="/files/F8LiHmQCXF6kn7VVyee1" alt=""><figcaption></figcaption></figure>

* **Non-permanent Lock** – Liquidity is locked for a custom timeframe, after which it becomes withdrawable. Earned fees can be claimed at any time.

  <figure><img src="/files/nhFBiA5YyVWLlBc7TMNg" alt=""><figcaption></figcaption></figure>

3. Click **`Lock Liquidity`**, then confirm the transaction in your wallet.

After locking your liquidity successfully, LPers can return to **`My Position`** tab to view your position’s unlock countdown.

<figure><img src="/files/PSYL3B0BZHOrvnEZ28Cy" alt=""><figcaption></figcaption></figure>


# CLMM pool

1. Click `Lock Position` in `My Position` Tab

<figure><img src="/files/KBvPUUt5rL0HQ7osahVU" alt=""><figcaption></figcaption></figure>

2. **Select your desired Lock Type**

* **Permanent Lock** – Liquidity is locked forever & cannot be withdrawn, but any earned fees remain claimable anytime.

<figure><img src="/files/s2VDuPqPsvnH3FVUL2VN" alt=""><figcaption></figcaption></figure>

* **Non-permanent Lock** – Liquidity is locked for a custom timeframe, after which it becomes withdrawable. Earned fees can be claimed at any time.

  <figure><img src="/files/fnMJj1aurMWdUHaiHzAq" alt=""><figcaption></figcaption></figure>

3. Click **`Lock Liquidity`**, then confirm the transaction in your wallet.

After locking your liquidity successfully, LPers can return to **`My Position`** tab to view your position’s unlock countdown.

<figure><img src="/files/0x6ruewcjnjAh8jX5IAi" alt=""><figcaption></figcaption></figure>


# Collect fee reward

When you provide liquidity on Ferra (**DLMM**, **CLMM**, or **DAMM**), you earn a share of trading fees while your liquidity positions remain active. Fee distribution scales with the size of your **active liquidity**.

To claim your fee rewards:

1. Navigate to the pool that you've supplied liquidity (*`CETUS/SUI` pool in this case*)
2. In `My Position` tab, you can view all your active positions.&#x20;

Choose one of the following:

* `Claim All` — harvest fees from all active positions at once.

<figure><img src="/files/WbWc7kEJV79PojfMmwFN" alt=""><figcaption></figcaption></figure>

* Click on an active pool, then `Claim` to harvest fees from the selected position only.

<figure><img src="/files/iQcjb8o8tn56gQvp9RJV" alt=""><figcaption></figcaption></figure>

3. Approve the transaction

After the transaction is confirmed, the earned fees will be credited to your wallet in a blink.


# Swap

### 1. Getting started

Visit <https://ferra.ag/swap>, then connect your wallet.

Ferra supports multiple Sui wallets, including [Slush](https://slush.app/), [Suiet](https://suiet.app/), [OKX Wallet](https://web3.okx.com/), and more.

In this guide, we’ll use **Slush** as an example.

*<mark style="color:$info;">Note: A small amount of SUI is required to cover gas fees. Make sure you have enough balance in your wallet.</mark>*

### 2. Select Tokens & Enter Amount

* Choose the token you want to swap **from** *(`SUI` in this case)* & the token you want to swap **to** *(`USDC` in this case)*
* Enter the amount you wish to swap in the `From` section
* Adjust your slippage tolerance if needed:

  * The default is **`0.5%`**
  * You can customize this in the **`Settings`** icon

  <figure><img src="/files/P96b01xB6nCB6MQmBNJG" alt=""><figcaption></figcaption></figure>

{% hint style="danger" %}
Token creation on the network is permissionless. This enables imitations of existing tokens and tokens falsely claiming affiliation with projects that have no token. Always verify the contract address via trusted sources & do your own research before trading.
{% endhint %}

### 3. Execute the Swap

* When you’re ready, click `Swap`
* Review the transaction details carefully, then click `Confirm`
* Approve the transaction in your wallet

<figure><img src="/files/9vSFyxQrKI00mhck08Aq" alt=""><figcaption></figcaption></figure>

**🎉 Congratulations \~ You’ve completed your swap on Ferra!**

Once the transaction is complete:

* You’ll see a **`Swap Successful`** notification
* The output token (***`USDC`*** *in this case*) will appear in your wallet
* View the transaction details on **Suivision** if needed

<figure><img src="/files/JQTyduyRCHx3f3BpN4Pu" alt=""><figcaption></figcaption></figure>


# LP Guild

<figure><img src="/files/3q3bhJEkTtP4KpQq4bN2" alt=""><figcaption></figcaption></figure>

#### Single-player LP is so over. LPers together strong.

Introducing **LP Guild**, Ferra's flagship SocialFi feature, and the first-of-its-kind on Sui, built to unite solo LPers into Guilds that together **socialize**, conquer **Weekly Leaderboard**, earn **BONUS FEES**, and beyond.

**→ Build your own LP empire:** <https://ferra.ag/lp-guild>

v1 released on **Nov 6,2025,** marking the beginning of Ferra’s bigger vision of an all-round social DLMM DEX on Sui—where liquidity provision is community-driven, gamified, and accessible to anyone.

*Learn more:* [*HERE*](https://ferraonsui.substack.com/p/ferra-lp-guild-bonus-fee-bonus-fee)


# Point

Social buzz. Community ownership. Gamification.

<figure><img src="/files/AHVv6VMXNFEHyqpHutrt" alt=""><figcaption></figcaption></figure>

We collect Points for a reason.

Point v1 only counted LP, swaps & referrals.

Point v2 now covers every lane you contribute to Ferra:\
· LPers & traders\
· Creators & KOLs\
· Community buidlers, [Questers](https://docs.ferra.ag/user-guide/quests), Raiders & more

Not a single grind gets sidelined.

The new **Point System** opens the door for everyone in the ecosystem to earn through their actions. Not only DeFi interactions, but social engagement, content creation, and community contributions now all flow into the value loop.

Read more: <https://ferraonsui.substack.com/p/point-system-v2-make-all-your-moves>


# Quests

<figure><img src="/files/EbJ84zJRVKRxvtPwp3Hi" alt=""><figcaption></figcaption></figure>

**Quests** is the ultimate gamification portal of on-chain and off-chain missions that let you earn even more Points.

**Enter now:** [**https://ferra.ag/quests**](https://ferra.ag/quests)

A set of Basic & Daily Quests is live to kick thin[^1]gs off.

Soon, we’ll be teaming up with ecosystem partners to ship new Quest campaigns, opening up more ways to stack Points, unlock rewards, and stay deeply involved in the journey ahead.

#### **Leaderboard & Tiers**

A progressive tiered system is built to track your Points and ranks on the [Leaderboard](https://ferra.ag/leaderboard). The more Points you stack, the higher you climb and the better tiers you unlock.

Start as Plankton, aim for Whale. Climb the food chain one milestone at a time.

<figure><img src="/files/6fTnvKg9yI0lPNBN5Nrw" alt=""><figcaption></figcaption></figure>

[^1]:


# DLMM


# Typescript SDK

The typescript SDK to interact with Ferra DLMM protocol

### Getting Started

* [**Overview**](/integration/dlmm/typescript-sdk/getting-started/overview) - Introduction to DLMM protocol and key features for developers
* [**Installation**](/integration/dlmm/typescript-sdk/getting-started/installation) - Package installation and setup requirements
* [**SDK Initialization**](/integration/dlmm/typescript-sdk/getting-started/sdk-initialization) - Configuration and network setup
* [**Key Concepts**](/integration/dlmm/typescript-sdk/getting-started/key-concepts) - Understanding bins, bin steps, and active bins

### Trading Pairs

* [**Create LB Pair**](/integration/dlmm/typescript-sdk/trading-pairs/create-lb-pair) - Deploy new liquidity book trading pair
* [**Get Single Pair**](/integration/dlmm/typescript-sdk/trading-pairs/get-single-pair) - Fetch detailed pair information
* [**Get All Pairs**](/integration/dlmm/typescript-sdk/trading-pairs/get-all-pairs) - List all available trading pairs
* [**Get Pair Bins**](/integration/dlmm/typescript-sdk/trading-pairs/get-pair-bins) - Retrieve bin data for a specific pair
* [**Get Pair Reserves**](/integration/dlmm/typescript-sdk/trading-pairs/get-pair-reserves) - Check liquidity distribution across bins

### Position Management

* [**Open Position**](/integration/dlmm/typescript-sdk/position-management/open-position) - Create new position NFT
* [**Get User Positions**](/integration/dlmm/typescript-sdk/position-management/get-user-positions) - List all positions for an address
* [**Get Single Position**](/integration/dlmm/typescript-sdk/position-management/get-single-position) - Fetch detailed position data
* [**Get Position Bins**](/integration/dlmm/typescript-sdk/position-management/get-position-bins) - View bins within a position
* [**Close Position**](/integration/dlmm/typescript-sdk/position-management/close-position) - Remove liquidity and close position

### Add Liquidity

* [**Add Liquidity Overview**](/integration/dlmm/typescript-sdk/add-liquidity/add-liquidity-overview) - Core concepts and workflow
* [**Add to New Position**](/integration/dlmm/typescript-sdk/add-liquidity/add-to-new-position) - Open position and add liquidity in one transaction
* [**Add to Existing Position**](/integration/dlmm/typescript-sdk/add-liquidity/add-to-existing-position) - Add more liquidity to current position
* [**SPOT Distribution**](/integration/dlmm/typescript-sdk/add-liquidity/spot-distribution) - Uniform liquidity distribution
* [**CURVE Distribution**](/integration/dlmm/typescript-sdk/add-liquidity/curve-distribution) - Gaussian curve distribution
* [**BID-ASK Distribution**](/integration/dlmm/typescript-sdk/add-liquidity/bid-ask-distribution) - Weighted bid-ask spread
* [**Custom Distribution**](/integration/dlmm/typescript-sdk/add-liquidity/custom-distribution) - Manual bin and amount selection
* [**Calculate Token Amounts**](/integration/dlmm/typescript-sdk/add-liquidity/calculate-token-amounts) - Required tokens per bin calculation

### Remove Liquidity

* [**Remove Liquidity Overview**](/integration/dlmm/typescript-sdk/remove-liquidity/remove-liquidity-overview) - Understanding the removal process
* [**Remove from Specific Bins**](/integration/dlmm/typescript-sdk/remove-liquidity/remove-from-specific-bins) - Partial liquidity removal
* [**Remove All Liquidity**](/integration/dlmm/typescript-sdk/remove-liquidity/remove-all-liquidity) - Complete position withdrawal
* [**Calculate Output Amounts**](/integration/dlmm/typescript-sdk/remove-liquidity/calculate-output-amounts) - Estimate tokens to receive

### Swap Operations

* [**Execute Swap**](/integration/dlmm/typescript-sdk/swap-operations/execute-swap) - Perform token swaps
* [**Swap Direction**](/integration/dlmm/typescript-sdk/swap-operations/swap-direction) - Understanding X→Y vs Y→X swaps
* [**Calculate Swap Output**](/integration/dlmm/typescript-sdk/swap-operations/calculate-swap-output) - Preview swap results before execution
* [**Price Impact**](/integration/dlmm/typescript-sdk/swap-operations/price-impact) - Calculate impact across affected bins
* [**Slippage Protection**](/integration/dlmm/typescript-sdk/swap-operations/slippage-protection) - Set minimum output amounts

### Fees & Analytics

* [**Fee Structure**](/integration/dlmm/typescript-sdk/fees-and-analytics/fee-structure) - How protocol fees work in bins
* [**Calculate Position Fees**](/integration/dlmm/typescript-sdk/fees-and-analytics/calculate-position-fees) - Track accumulated trading fees
* [**Collect Fees**](/integration/dlmm/typescript-sdk/fees-and-analytics/collect-fees) - Claim earned fees from positions
* [**Position Value**](/integration/dlmm/typescript-sdk/fees-and-analytics/position-value) - Calculate current position worth
* [**Calculate APR**](/integration/dlmm/typescript-sdk/fees-and-analytics/calculate-apr) - Estimate annualized returns

### Utilities & Reference

* [**Bin Mathematics**](broken://pages/eU49Cxkv4zu3s7a0AyHJ) - Understanding and Calculations
* [**Price Helpers**](/integration/dlmm/typescript-sdk/utilities-and-reference/price-helpers) - Price conversion utilities
* [**Best Practices**](/integration/dlmm/typescript-sdk/utilities-and-reference/best-practices) - Performance optimization tips


# Getting Started

This section covers the fundamentals needed to start building with DLMM:

#### [Overview](/integration/dlmm/typescript-sdk/getting-started/overview)

Understand what DLMM is, how it differs from traditional AMMs, and its key advantages for both liquidity providers and traders.

#### [Installation](/integration/dlmm/typescript-sdk/getting-started/installation)

Set up the DLMM SDK in your project with step-by-step installation instructions and dependency requirements.

#### [SDK Initialization](/integration/dlmm/typescript-sdk/getting-started/sdk-initialization)

Configure the SDK for different networks, set up your connection, and prepare for your first transactions.

#### [Key Concepts](/integration/dlmm/typescript-sdk/getting-started/key-concepts)

Master the core concepts of DLMM including bins, bin steps, active bins, and how price discovery works in a discretized system.

### Quick Example

Here's a taste of what you can do with the DLMM SDK:

```typescript
import { FerraDlmmSDK, BinMath } from '@ferra-labs/dlmm'

// Initialize SDK
const sdk = new FerraDlmmSDK({
  network: 'mainnet',
  fullNodeUrl: 'https://...',
  senderAddress: '0x...'
})

// Get a pair and check price
const pair = await sdk.Pair.getPair('0x...')
const price = BinMath.getPriceFromId(
  pair.parameters.active_id,
  Number(pair.binStep),
  9, // tokenX decimals
  6  // tokenY decimals
)

console.log(`Current price: ${price} tokenY per tokenX`)
```

### Prerequisites

Before you begin, ensure you have:

* Node.js 16+ installed
* Basic understanding of DeFi and liquidity provision
* Familiarity with TypeScript (recommended)
* A Sui wallet with some SUI for gas fees

### Choose Your Path

#### For Developers

Start with [SDK Initialization](/integration/dlmm/typescript-sdk/getting-started/sdk-initialization) to set up your development environment.

#### For Liquidity Providers

Jump to [Key Concepts](/integration/dlmm/typescript-sdk/getting-started/key-concepts) to understand how DLMM liquidity works.

### Support

* **Discord**: Join our community for help
* **GitHub**: Report issues or contribute
* **Docs**: You're here!

Ready to dive in? Let's start with the [Overview](/integration/dlmm/typescript-sdk/getting-started/overview) →


# Overview

This is a quick overview of DLMM for developers to grasp the key concepts before moving on to other sections. More detailed information about the DLMM protocol is provided in [this section](/core-protocols/quickstart).

### What is DLMM?

DLMM is a bin-based liquidity protocol that divides the price space into discrete bins. Each bin represents a specific price point where liquidity can be deposited. Unlike continuous price ranges, DLMM uses a discretized approach for more efficient liquidity management.

### Key Features

#### Bin-Based Liquidity

* Liquidity is concentrated in discrete price bins
* Each bin has a unique ID representing a specific price
* Bins are connected with fixed price steps

#### Zero Slippage Within Bins

* Trades within the same bin execute at a constant price
* No price impact until bin liquidity is exhausted
* Predictable execution for traders

#### Flexible Liquidity Shapes

* **Spot**: Uniform distribution around current price
* **Curve**: Gaussian distribution for concentrated liquidity
* **Bid-Ask**: Weighted distribution for market making

#### Composable Positions

* Each position is an NFT
* Add/remove liquidity to specific bins
* Multiple positions per user

### Next Steps

* [Installation](/integration/dlmm/typescript-sdk/getting-started/installation) - Set up the SDK
* [Key Concepts](/integration/dlmm/typescript-sdk/getting-started/key-concepts) - Understand bins and pricing
* [Create Pair](/integration/dlmm/typescript-sdk/trading-pairs/create-lb-pair) - Deploy your first pair


# Installation

Install and set up the Ferra DLMM SDK in your project.

### Requirements

* Node.js 16.0 or higher
* npm, yarn, or pnpm package manager
* TypeScript 4.5+ (recommended)

### Install Package

#### Using npm

```bash
npm install @ferra-labs/dlmm
```

#### Using yarn

```bash
yarn add @ferra-labs/dlmm
```

#### Using pnpm

```bash
pnpm add @ferra-labs/dlmm
```

### TypeScript Configuration

For TypeScript projects, ensure your `tsconfig.json` includes:

```json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "lib": ["ES2020"],
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "resolveJsonModule": true
  }
}
```

### Verify Installation

Create a test file to verify the installation:

```typescript
import { FerraDlmmSDK, BinMath } from '@ferra-labs/dlmm'

// Should print the SDK version
console.log('DLMM SDK imported successfully')

// Test BinMath utility
const price = BinMath.getPriceFromId(8388608, 10, 9, 6)
console.log('Price at bin 8388608:', price)
```

Run the test:

```bash
npx ts-node test.ts
```

### Development Setup

For contributing or local development:

```bash
git clone https://github.com/ferra-labs/dlmm-sdk.git
cd dlmm-sdk
npm install
npm run build
```

### Next Steps

Installation complete! Continue to:

* [SDK Initialization](/integration/dlmm/typescript-sdk/getting-started/sdk-initialization) - Configure the SDK
* [Key Concepts](/integration/dlmm/typescript-sdk/getting-started/key-concepts) - Understand DLMM basics
* [Create Pair](/integration/dlmm/typescript-sdk/trading-pairs/create-lb-pair) - Deploy your first pair


# SDK Initialization

Configure and initialize the Ferra DLMM SDK for your application.

### Basic Setup

```typescript
import { initFerraDlmmSDK } from '@ferra-labs/dlmm'

const sdk = initFerraDlmmSDK({
  network: 'mainnet',
  fullNodeUrl: 'https://fullnode.mainnet.sui.io:443',
  wallet: '0x...' // Your wallet address
})
```

### Configuration Options

#### Required Parameters

| Parameter       | Type                     | Description                     |
| --------------- | ------------------------ | ------------------------------- |
| `network`       | `'mainnet' \| 'testnet'` | Target network                  |
| `fullNodeUrl`   | `string`                 | Sui RPC endpoint URL            |
| `senderAddress` | `string`                 | Default sender for transactions |

#### Network Configurations

**Mainnet**

```typescript
const sdk = initFerraDlmmSDK({
  network: 'mainnet',
  fullNodeUrl: 'https://fullnode.mainnet.sui.io:443',
  wallet: '0x...'
})
```

**Testnet**

```typescript
const sdk = initFerraDlmmSDK({
  network: 'testnet', 
  fullNodeUrl: 'https://fullnode.testnet.sui.io:443',
  wallet: '0x...'
})
```

**Custom RPC**

```typescript
const sdk = initFerraDlmmSDK({
  network: 'mainnet',
  fullNodeUrl: 'https://your-custom-rpc.com',
  wallet: '0x...'
})
```

### Error Handling

```typescript
try {
  const sdk = initFerraDlmmSDK({
    network: 'mainnet',
    fullNodeUrl: 'invalid-url',
    wallet: '0x...'
  })
} catch (error) {
  if (error.message.includes('Invalid RPC')) {
    console.error('Please check your RPC URL')
  }
}
```

### Best Practices

1. **Single Instance**: Create one SDK instance per app
2. **Error Handling**: Always wrap initialization in try-catch
3. **Network Matching**: Ensure wallet and SDK use same network
4. **RPC Selection**: Use reliable RPC providers for production
5. **Address Validation**: Validate sender address format

### Next Steps

SDK initialized! Continue to:

* [Key Concepts](/integration/dlmm/typescript-sdk/getting-started/key-concepts) - Understand DLMM fundamentals
* [Create Pair](/integration/dlmm/typescript-sdk/trading-pairs/create-lb-pair) - Deploy a trading pair
* [Get All Pairs](/integration/dlmm/typescript-sdk/trading-pairs/get-all-pairs) - Explore existing pairs


# Key Concepts

Understand the fundamental concepts of the DLMM protocol.

### Bins

Bins are the core building blocks of DLMM. Each bin represents a discrete price level where liquidity can be deposited.

#### Key Properties

* **Discrete Prices**: Each bin has one specific price
* **Fixed Width**: Price increment determined by bin step
* **Liquidity Container**: Holds reserves of both tokens
* **Zero Slippage**: Trades within a bin have no price impact

### Bin IDs

Every bin has a unique ID that determines its price.

#### ID System

* **Base ID**: 8388608 represents price = 1.0
* **Lower IDs**: Below 8388608 = price < 1.0
* **Higher IDs**: Above 8388608 = price > 1.0

#### ID to Price Formula

```typescript
price = (1 + binStep/10000) ^ (binId - 8388608)
```

#### Examples

```typescript
import { BinMath } from '@ferra-labs/dlmm'

// Bin 8388608 = price 1.0
const price1 = BinMath.getPriceFromId(8388608, 10, 9, 6)
// Result: 1.0

// Bin 8388618 = higher price
const price2 = BinMath.getPriceFromId(8388618, 10, 9, 6)
// Result: ~1.01 (10 bins × 0.1% = 1% higher)
```

### Bin Steps

The bin step defines the price increment between adjacent bins.

#### Common Bin Steps

| Bin Step | Percentage | Use Case             |
| -------- | ---------- | -------------------- |
| 1        | 0.01%      | Stablecoins          |
| 5        | 0.05%      | Low volatility       |
| 10       | 0.10%      | Medium volatility    |
| 20       | 0.20%      | Medium volatility    |
| 50       | 0.50%      | High volatility      |
| 100      | 1.00%      | very high volatility |
| 200      | 2.00%      | very high volatility |
| 500      | 5.00%      | very high volatility |

#### Bin Step Impact

```typescript
// Smaller bin step = tighter price ranges
binStep = 1  // 0.01% increments
// Bins: [..., 0.9999, 1.0000, 1.0001, ...]

// Larger bin step = wider price ranges  
binStep = 100 // 1% increments
// Bins: [..., 0.99, 1.00, 1.01, ...]
```

### Active Bin

The active bin is where the current market price resides.

#### Properties

* **Current Trading Price**: All swaps start here
* **Mixed Reserves**: Contains both tokens
* **Price Discovery**: Moves as liquidity is consumed

#### Active Bin Behavior

```typescript
// Get current active bin
const pair = await sdk.Pair.getPair(pairId)
const activeId = pair.parameters.active_id

// Calculate current price
const currentPrice = BinMath.getPriceFromId(
  activeId,
  Number(pair.binStep),
  9, // tokenX decimals
  6  // tokenY decimals
)
```

#### Bin Transitions

When active bin liquidity is exhausted:

1. **Buy (X→Y)**: Active bin moves up (higher ID)
2. **Sell (Y→X)**: Active bin moves down (lower ID)

### Price Calculation

#### Price from Bin ID

```typescript
const price = BinMath.getPriceFromId(
  binId,      // Bin ID
  binStep,    // Bin step (basis points)
  decimalsX,  // Token X decimals
  decimalsY   // Token Y decimals
)
```

#### Bin ID from Price

```typescript
const binId = BinMath.getIdFromPrice(
  price,      // Target price
  binStep,    // Bin step
  decimalsX,  // Token X decimals
  decimalsY   // Token Y decimals
)
```

#### Price Impact

```typescript
// Calculate bins needed for swap
const currentBin = 8388608
const targetPrice = 1.05 // 5% higher
const targetBin = BinMath.getIdFromPrice(targetPrice, 10, 9, 6)
const binsToMove = targetBin - currentBin
```

### Practical Example

```typescript
// Understanding a position across bins
const activeId = 8388608
const binStep = 20 // 0.2%

// Create position ±10 bins from active
const minBin = activeId - 10
const maxBin = activeId + 10

// Calculate price range
const minPrice = BinMath.getPriceFromId(minBin, binStep, 9, 6)
const maxPrice = BinMath.getPriceFromId(maxBin, binStep, 9, 6)

console.log(`Position range: ${minPrice} - ${maxPrice}`)
// Result: ~0.98 - 1.02 (±2% from center)
```

### Key Takeaways

1. **Bins = Price Levels**: Each bin is a discrete price point
2. **Bin ID = Price**: Higher ID means higher price
3. **Bin Step = Precision**: Smaller steps mean finer price control
4. **Active Bin = Market**: Current trading happens here
5. **No Continuous Prices**: Everything is discretized

### Next Steps

Now that you understand the concepts:

* [Create Pair](/integration/dlmm/typescript-sdk/trading-pairs/create-lb-pair) - Deploy with chosen bin step
* [Add Liquidity](/integration/dlmm/typescript-sdk/add-liquidity) - Apply distribution strategies


# Trading Pairs

Explore and interact with DLMM trading pairs on the network. This section covers everything from discovering existing pairs to creating new ones, understanding pair configurations, and accessing real-time liquidity data across all bins.

### Prerequisites

Before working with trading pairs, ensure you have:

* Initialized the DLMM SDK with a valid connection
* Understanding of token type formats on Sui network
* Basic knowledge of bins and bin steps
* Wallet with sufficient gas for transactions (when creating pairs)

### What You'll Learn

This section covers the essential operations for working with DLMM trading pairs:

#### [Create LB Pair](/integration/dlmm/typescript-sdk/trading-pairs/create-lb-pair)

Deploy new liquidity book trading pairs with custom configurations. Learn about token ordering requirements, setting initial active bin IDs, and choosing appropriate bin steps for your use case.

#### [Get Single Pair](/integration/dlmm/typescript-sdk/trading-pairs/get-single-pair)

Fetch comprehensive data for a specific trading pair including current reserves, fee parameters, active bin information, and liquidity distribution metrics.

#### [Get All Pairs](/integration/dlmm/typescript-sdk/trading-pairs/get-all-pairs)

Discover and list all available trading pairs on the network. Filter pairs by token types, liquidity thresholds, or trading volume to find opportunities.

#### [Get Pair Bins](/integration/dlmm/typescript-sdk/trading-pairs/get-pair-bins)

Retrieve detailed bin data for a specific pair within a given range. Access reserve information, liquidity depth, and price levels for each bin.

#### [Get Pair Reserves](#get-pair-reserves)

Check the complete liquidity distribution across all active bins. Analyze reserve concentrations and identify optimal entry points for trades or liquidity provision.


# Create LB Pair

Deploy new liquidity book trading pairs with custom configurations for any token combination.

### Prerequisites

Before creating a pair:

* Initialize the SDK with valid configuration
* Have wallet connected with sufficient gas
* Ensure both token types are valid on Sui
* Choose appropriate bin step for your pair type
* Calculate initial active bin ID for starting price

### Basic Usage

```typescript
const tx = await sdk.Factory.createLBPair({
  tokenXType: "0x2::sui::SUI",
  tokenYType: "0x...::usdc::USDC", 
  binStep: 20,        // 20 basis points
  activeId: 8388608   // Starting price = 1.0
});

await sdk.fullClient.signAndExecuteTransaction({
  transaction: tx,
  signer: keypair
});
```

### Parameters

#### tokenXType & tokenYType

* Full type strings of both tokens
* Order doesn't matter (SDK auto-sorts)
* Must be different tokens

#### binStep

* Price increment in basis points (1-10000)
* Cannot be changed after creation

#### activeId

* Starting bin ID (sets initial price)
* Use 8388608 for 1:1 price ratio

### Advanced Example

```typescript
// Create ETH/USDC pair starting at $3,500
const targetPrice = 3500;
const binStep = 20;

// Calculate starting bin ID
const activeId = getBinIdFromPrice(targetPrice, binStep);

const tx = await sdk.Factory.createLBPair({
  tokenXType: SUI_TYPE,
  tokenYType: USDC_TYPE,
  binStep: binStep,
  activeId: activeId  // ~8394879 for $3,500
});
```

### Token Ordering

DLMM requires tokenX < tokenY (by address). The SDK handles this automatically:

```typescript
// Both orders work - SDK will sort
await createLBPair({ 
  tokenXType: USDC, 
  tokenYType: SUI, 
  ...
});

await createLBPair({ 
  tokenXType: SUI,  // Will be swapped to Y
  tokenYType: USDC, // Will be swapped to X
  ...
});
```

### After Creation

1. Pair is deployed but has no liquidity
2. Use [Add to New Position](/integration/dlmm/typescript-sdk/add-liquidity/add-to-new-position) to provide initial liquidity
3. Find your pair with [Get Single Pair](/integration/dlmm/typescript-sdk/trading-pairs/get-single-pair)

### Related Topics

* [Add to New Position](/integration/dlmm/typescript-sdk/add-liquidity/add-to-new-position) - Add initial liquidity
* [Get All Pairs](/integration/dlmm/typescript-sdk/trading-pairs/get-all-pairs) - Find existing pairs first


# Get Single Pair

Fetch comprehensive data for a specific DLMM trading pair including reserves, fees, and configuration.

### Prerequisites

Before fetching pair data:

* Have a valid pair address
* Initialize the SDK with network connection
* Understand basic pair structure and bins

### Basic Usage

```typescript
const pairAddress = "0x123...abc";
const pair = await sdk.Pair.getPair(pairAddress);

if (!pair) {
  console.log("Pair not found");
  return;
}

console.log("Active Bin ID:", pair.parameters.active_id);
console.log("Bin Step:", pair.binStep);
```

### Returned Data Structure

```typescript
interface LBPair {
  id: string;                    // Pair object address
  tokenXType: string;            // Token X full type
  tokenYType: string;            // Token Y full type
  binStep: string;               // Basis points
  reserveX: string;              // Total X reserves
  reserveY: string;              // Total Y reserves
  
  parameters: {
    active_id: number;           // Current trading bin
    base_factor: string;         // Base fee factor
    protocol_share: string;      // Protocol fee %
    volatility_accumulator: string;
    // ... more fee parameters
  };
  
  binManager: string;            // Bins storage address
  positionManager: {
    id: string;                  // Positions storage
    total_supplies: string;      // Supply tracking
  };
}
```

### Common Usage Patterns

#### Check Pair Status

```typescript
const pair = await sdk.Pair.getPair(pairAddress);

// Calculate current price from active bin
const currentPrice = getPriceFromBinId(
  pair.parameters.active_id,
  Number(pair.binStep)
);

// Check liquidity depth
const hasLiquidity = BigInt(pair.reserveX) > 0n || 
                    BigInt(pair.reserveY) > 0n;
```

#### Monitor Pair Metrics

```typescript
// Get fee configuration
const baseFee = Number(pair.parameters.base_factor);
const protocolShare = Number(pair.parameters.protocol_share);

// Calculate TVL (simplified)
const tvl = calculateTVL(
  pair.reserveX, 
  pair.reserveY,
  currentPrice
);
```

### Error Handling

```typescript
try {
  const pair = await sdk.Pair.getPair(invalidAddress);
  if (!pair) {
    // Address valid but pair doesn't exist
    handlePairNotFound();
  }
} catch (error) {
  // Invalid address format
  console.error("Invalid pair address");
}
```

### Use Cases

* **Before Trading**: Check reserves and active bin
* **Analytics**: Monitor TVL and fee parameters
* **Position Management**: Get position manager address
* **Price Discovery**: Find current trading price

### Related Topics

* [Get All Pairs](/integration/dlmm/typescript-sdk/trading-pairs/get-all-pairs) - Discover available pairs
* [Get Pair Bins](/integration/dlmm/typescript-sdk/trading-pairs/get-pair-bins) - Detailed bin information
* [Get Pair Reserves](/integration/dlmm/typescript-sdk/trading-pairs/get-pair-reserves) - Liquidity distribution
* [Execute Swap](/integration/dlmm/typescript-sdk/swap-operations/execute-swap) - Trade on the pair


# Get All Pairs

## Get All Pairs

Discover all available DLMM trading pairs deployed on the network. This method fetches the complete list of pair contracts with their current configuration and state.

### Prerequisites

Before fetching pairs:

* Initialize the SDK with network connection
* Ensure RPC endpoint is accessible
* Understand LBPair data structure

### Basic Usage

```typescript
// Fetch all pairs from the network
const allPairs = await sdk.Pair.getPairs();

console.log(`Total pairs found: ${allPairs.length}`);

// Access pair data
allPairs.forEach(pair => {
  console.log({
    pairId: pair.id,
    tokenX: pair.tokenXType,
    tokenY: pair.tokenYType,
    activeId: pair.parameters.active_id,
    binStep: pair.binStep
  });
});
```

### Method Details

```typescript
async getPairs(): Promise<LBPair[]>
```

Returns an array of all LBPair objects containing:

* Pair configuration (tokens, bin step)
* Current state (reserves, active bin)
* Manager addresses (bins, positions)
* Fee parameters

### Understanding the Response

Each pair in the array contains:

```typescript
{
  id: "0x123...",              // Unique pair address
  tokenXType: "0x2::sui::SUI",  // Full type of token X
  tokenYType: "0x...::USDC",    // Full type of token Y
  binStep: "20",                // Price granularity (basis points)
  reserveX: "1000000000",       // Total X token reserves
  reserveY: "2000000000",       // Total Y token reserves
  parameters: {
    active_id: 8388608,         // Current trading bin
    // ... other parameters
  }
}
```

### Practical Examples

#### Find Specific Token Pairs

```typescript
const pairs = await sdk.Pair.getPairs();

// Find all pairs containing SUI
const suiPairs = pairs.filter(pair => 
  pair.tokenXType === "0x2::sui::SUI" || 
  pair.tokenYType === "0x2::sui::SUI"
);
```

#### Check Pair Activity

```typescript
const pairs = await sdk.Pair.getPairs();

// Find pairs with liquidity
const activePairs = pairs.filter(pair => 
  BigInt(pair.reserveX) > 0n || BigInt(pair.reserveY) > 0n
);

console.log(`Active pairs: ${activePairs.length}/${pairs.length}`);
```

#### Group by Bin Step

```typescript
const pairs = await sdk.Pair.getPairs();

const pairsByBinStep = pairs.reduce((groups, pair) => {
  const step = pair.binStep;
  if (!groups[step]) groups[step] = [];
  groups[step].push(pair);
  return groups;
}, {});

console.log("Pairs grouped by bin step:", Object.keys(pairsByBinStep));
```

### Performance Considerations

* First call may take longer due to network fetching
* Returns all pairs in a single response (no pagination)
* Consider caching results for repeated access
* Large deployments may have significant response size

### Common Use Cases

1. **Market Discovery**: Find available trading opportunities
2. **Liquidity Analysis**: Identify pairs with sufficient depth
3. **Pair Selection**: Choose optimal pairs for trading strategies
4. **Network Overview**: Monitor total market deployment

### Related Topics

* [Get Single Pair](/integration/dlmm/typescript-sdk/trading-pairs/get-single-pair) - Fetch specific pair details
* [Create LB Pair](/integration/dlmm/typescript-sdk/trading-pairs/create-lb-pair) - Deploy new pairs
* [Get Pair Reserves](/integration/dlmm/typescript-sdk/trading-pairs/get-pair-reserves) - Detailed liquidity distribution
* [Execute Swap](/integration/dlmm/typescript-sdk/swap-operations/execute-swap) - Trade on discovered pairs


# Get Pair Bins

Retrieve detailed bin data for a specific trading pair within a given range. This method allows you to inspect liquidity distribution and reserves at each price level.

### Prerequisites

Before fetching bin data:

* Have a valid LBPair object from `getPair()` or `getPairs()`
* Understand bin ID ranges and price relationships
* Know which bins to inspect (around active bin)

### Basic Usage

```typescript
// First, get the pair
const pair = await sdk.Pair.getPair(pairAddress);

// Fetch bins around the active bin
const activeId = pair.parameters.active_id;
const bins = await sdk.Pair.getPairBins(
  pair, 
  [activeId - 10, activeId + 10]  // 20 bins range
);

// Display bin reserves
bins.forEach((bin, index) => {
  const binId = activeId - 10 + index;
  console.log(`Bin ${binId}:`, {
    reserveX: bin.reserve_x.toString(),
    reserveY: bin.reserve_y.toString()
  });
});
```

### Method Signature

```typescript
async getPairBins(
  pair: LBPair, 
  binRange: [from: number, to: number]
): Promise<PairBin[]>
```

#### Parameters

* `pair`: The LBPair object to query
* `binRange`: Tuple of \[fromBinId, toBinId] (inclusive start, exclusive end)

#### Returns

Array of `PairBin` objects:

```typescript
interface PairBin {
  reserve_x: bigint;  // Token X reserves in this bin
  reserve_y: bigint;  // Token Y reserves in this bin
}
```

### Understanding Bin Ranges

```typescript
// Example: Fetch bins for different scenarios

// 1. Around active bin (common for display)
const activeBins = await sdk.Pair.getPairBins(
  pair,
  [pair.parameters.active_id - 5, pair.parameters.active_id + 5]
);

// 2. Above active bin (sell side liquidity)
const sellSide = await sdk.Pair.getPairBins(
  pair,
  [pair.parameters.active_id + 1, pair.parameters.active_id + 20]
);

// 3. Below active bin (buy side liquidity)
const buySide = await sdk.Pair.getPairBins(
  pair,
  [pair.parameters.active_id - 20, pair.parameters.active_id]
);
```

### Practical Examples

#### Analyze Liquidity Depth

```typescript
const pair = await sdk.Pair.getPair(pairAddress);
const range = 50; // Check 50 bins each side

const bins = await sdk.Pair.getPairBins(
  pair,
  [pair.parameters.active_id - range, pair.parameters.active_id + range]
);

// Calculate total liquidity in range
let totalX = 0n;
let totalY = 0n;

bins.forEach(bin => {
  totalX += bin.reserve_x;
  totalY += bin.reserve_y;
});

console.log("Liquidity in range:", { totalX, totalY });
```

#### Find Liquidity Walls

```typescript
// Identify bins with concentrated liquidity
const bins = await sdk.Pair.getPairBins(pair, [activeId - 20, activeId + 20]);

const liquidityWalls = bins
  .map((bin, index) => ({
    binId: activeId - 20 + index,
    totalLiquidity: bin.reserve_x + bin.reserve_y
  }))
  .filter(bin => bin.totalLiquidity > WALL_THRESHOLD)
  .sort((a, b) => Number(b.totalLiquidity - a.totalLiquidity));

console.log("Top liquidity concentrations:", liquidityWalls.slice(0, 5));
```

#### Display Price Levels

```typescript
const binStep = Number(pair.binStep);
const bins = await sdk.Pair.getPairBins(pair, [activeId - 5, activeId + 5]);

bins.forEach((bin, index) => {
  const binId = activeId - 5 + index;
  const price = getPriceFromBinId(binId, binStep);
  
  console.log(`Price ${price.toFixed(4)}: X=${bin.reserve_x}, Y=${bin.reserve_y}`);
});
```

### Performance Notes

* Queries use `devInspectTransactionBlock` (no gas cost)
* Larger ranges take more time to fetch
* Maximum practical range depends on RPC limits
* Consider chunking very large ranges

### Common Use Cases

1. **Order Book Visualization**: Display liquidity at each price level
2. **Slippage Calculation**: Estimate impact before swaps
3. **Liquidity Analysis**: Find optimal entry/exit points
4. **Market Depth**: Assess pair's ability to handle large trades

### Related Topics

* [Get Pair Reserves](/integration/dlmm/typescript-sdk/trading-pairs/get-pair-reserves) - Complete liquidity distribution
* [Price Impact](/integration/dlmm/typescript-sdk/swap-operations/price-impact) - Calculate trade impact
* [Add Liquidity Overview](/integration/dlmm/typescript-sdk/add-liquidity/add-liquidity-overview) - Add to specific bins


# Get Pair Reserves

Fetch complete liquidity distribution across all bins in a pair, including reserves and accumulated fees. This method provides a comprehensive view of the entire liquidity landscape.

### Prerequisites

Before fetching reserves:

* Have a valid LBPair object from `getPair()`
* Understand the difference between reserves and fees
* Be prepared to handle potentially large datasets

### Basic Usage

```typescript
// Get pair first
const pair = await sdk.Pair.getPair(pairAddress);

// Fetch all bin reserves
const reserves = await sdk.Pair.getPairReserves(pair);

console.log(`Total bins with liquidity: ${reserves.length}`);

// Display first few bins
reserves.slice(0, 5).forEach(bin => {
  console.log(`Bin ${bin.id}:`, {
    reserveX: bin.reserve_x.toString(),
    reserveY: bin.reserve_y.toString(),
    feeX: bin.fee_x.toString(),
    feeY: bin.fee_y.toString()
  });
});
```

### Method Signature

```typescript
async getPairReserves(pair: LBPair): Promise<BinReserves[]>
```

#### Returns

Array of `BinReserves` objects sorted by bin ID:

```typescript
interface BinReserves {
  id: number;           // Bin ID
  reserve_x: bigint;    // Token X liquidity
  reserve_y: bigint;    // Token Y liquidity  
  fee_x: bigint;        // Accumulated X fees
  fee_y: bigint;        // Accumulated Y fees
}
```

### Understanding the Data

#### Reserves vs Fees

* **Reserves**: Liquidity available for trading
* **Fees**: Accumulated trading fees not yet collected
* **Total**: reserves + fees = total bin value

```typescript
const reserves = await sdk.Pair.getPairReserves(pair);

reserves.forEach(bin => {
  const totalX = bin.reserve_x + bin.fee_x;
  const totalY = bin.reserve_y + bin.fee_y;
  
  console.log(`Bin ${bin.id} total value:`, { totalX, totalY });
});
```

### Practical Examples

#### Calculate Total Value Locked (TVL)

```typescript
const reserves = await sdk.Pair.getPairReserves(pair);

let totalReserveX = 0n;
let totalReserveY = 0n;
let totalFeesX = 0n;
let totalFeesY = 0n;

reserves.forEach(bin => {
  totalReserveX += bin.reserve_x;
  totalReserveY += bin.reserve_y;
  totalFeesX += bin.fee_x;
  totalFeesY += bin.fee_y;
});

console.log("Pair TVL:", {
  reservesX: totalReserveX,
  reservesY: totalReserveY,
  uncollectedFeesX: totalFeesX,
  uncollectedFeesY: totalFeesY
});
```

#### Find Liquidity Distribution

```typescript
const reserves = await sdk.Pair.getPairReserves(pair);
const activeId = pair.parameters.active_id;

// Categorize bins by position relative to active
const distribution = {
  below: reserves.filter(bin => bin.id < activeId),
  active: reserves.filter(bin => bin.id === activeId),
  above: reserves.filter(bin => bin.id > activeId)
};

console.log("Liquidity distribution:", {
  binsBelow: distribution.below.length,
  binsAbove: distribution.above.length,
  hasActiveBin: distribution.active.length > 0
});
```

#### Analyze Fee Generation

```typescript
const reserves = await sdk.Pair.getPairReserves(pair);

// Find bins with highest accumulated fees
const topFeeBins = reserves
  .filter(bin => bin.fee_x > 0n || bin.fee_y > 0n)
  .sort((a, b) => {
    const totalFeesA = a.fee_x + a.fee_y;
    const totalFeesB = b.fee_x + b.fee_y;
    return Number(totalFeesB - totalFeesA);
  })
  .slice(0, 10);

console.log("Top 10 bins by accumulated fees:", topFeeBins);
```

#### Create Liquidity Heatmap Data

```typescript
const reserves = await sdk.Pair.getPairReserves(pair);
const binStep = Number(pair.binStep);

const heatmapData = reserves.map(bin => ({
  binId: bin.id,
  price: getPriceFromBinId(bin.id, binStep),
  liquidity: bin.reserve_x + bin.reserve_y,
  fees: bin.fee_x + bin.fee_y
}));

// Use for visualization
console.log("Heatmap data ready:", heatmapData.length, "points");
```

### Performance Considerations

* Fetches **all** bins with liquidity (no range limit)
* Uses pagination internally for large datasets
* May return hundreds of bins for active pairs
* Consider caching results for repeated analysis

### Differences from getPairBins

| Feature     | getPairReserves           | getPairBins             |
| ----------- | ------------------------- | ----------------------- |
| Range       | All bins with liquidity   | Specified range only    |
| Fees        | Includes accumulated fees | Reserves only           |
| Performance | Slower for large pairs    | Faster for small ranges |
| Use Case    | Complete analysis         | Targeted inspection     |

### Related Topics

* [Get Pair Bins](/integration/dlmm/typescript-sdk/trading-pairs/get-pair-bins) - Query specific bin ranges
* [Calculate Position Fees](/integration/dlmm/typescript-sdk/fees-and-analytics/calculate-position-fees) - Fee analysis
* [Position Value](/integration/dlmm/typescript-sdk/fees-and-analytics/position-value) - Calculate total worth


# Position Management

Manage your liquidity positions in DLMM pools through position NFTs. This section covers creating, tracking, and closing positions, as well as viewing detailed position data including bin allocations and accumulated fees.

### Prerequisites

Before managing positions, ensure you have:

* Connected wallet with valid Sui address
* Understanding of position NFTs in DLMM
* Basic knowledge of liquidity provision concepts
* Sufficient tokens for liquidity (when opening positions)

### What You'll Learn

This section covers complete position lifecycle management in DLMM:

#### [Open Position](/integration/dlmm/typescript-sdk/position-management/open-position)

Create new position NFTs that represent your liquidity ownership. Learn how positions track your share across multiple bins and enable granular liquidity management.

#### [Get User Positions](/integration/dlmm/typescript-sdk/position-management/get-user-positions)

List all DLMM positions owned by a specific address. Filter positions by pair, status, or value to manage your liquidity portfolio effectively.

#### [Get Single Position](/integration/dlmm/typescript-sdk/position-management/get-single-position)

Fetch detailed data for a specific position including liquidity amounts per bin, accumulated fees, current value, and position metadata.

#### [Get Position Bins](/integration/dlmm/typescript-sdk/position-management/get-position-bins)

View all bins where a position has active liquidity. Analyze your liquidity distribution, concentration ranges, and exposure to different price levels.

#### [Close Position](/integration/dlmm/typescript-sdk/position-management/close-position)

Remove all remaining liquidity and burn the position NFT. Understand the complete withdrawal process and how to recover all tokens and fees efficiently.


# Open Position

Create a new position NFT that represents your liquidity ownership in a DLMM pool. Position NFTs track your share across multiple bins and enable granular liquidity management.

### Prerequisites

Before opening a position:

* Have a valid LBPair object from `getPair()`
* Connected wallet with sufficient gas
* Understanding of position NFTs in DLMM
* Plan for liquidity distribution (can add later)

### Basic Usage

```typescript
// Get the pair first
const pair = await sdk.Pair.getPair(pairAddress);

// Create a new empty position
const tx = await sdk.Pair.openPosition(pair);

// Execute transaction
const result = await sdk.fullClient.signAndExecuteTransaction({
  transaction: tx,
  signer: keypair
});

console.log("Position NFT created:", result.digest);
```

### Method Signature

```typescript
async openPosition(
  pair: LBPair, 
  tx?: Transaction
): Promise<Transaction>
```

#### Parameters

* `pair`: The LBPair to create position for
* `tx`: Optional existing transaction to append to

#### Returns

* Transaction object ready for execution
* Position NFT will be transferred to sender

### Understanding Position NFTs

Position NFTs in DLMM:

* **Unique ownership**: Each NFT represents your specific liquidity
* **Multi-bin support**: Can hold liquidity across many price levels
* **Fee tracking**: Accumulates your share of trading fees
* **Transferable**: Can be sent to other addresses
* **Composable**: Can be used in other protocols

### Creating and Adding Liquidity

#### Two-Step Process

```typescript
// Step 1: Create position
const tx1 = await sdk.Pair.openPosition(pair);
await executeTransaction(tx1);

// Step 2: Add liquidity later
const tx2 = await sdk.Pair.addLiquidity(pair, {
  positionId: "0x...", // From step 1
  amountX: 1000000n,
  amountY: 2000000n,
  // ... distribution params
});
```

#### One-Step Process (Recommended)

```typescript
// Create position and add liquidity together
const tx = await sdk.Pair.openPositionAndAddLiquidity(pair, {
  amountX: 1000000n,
  amountY: 2000000n,
  deltaIds: [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
  distributionX: [0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0],
  distributionY: [0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0]
});
```

### Batch Operations

```typescript
// Create multiple positions in one transaction
const tx = new Transaction();

// Create positions for different strategies
const position1 = await sdk.Pair.openPosition(pairETHUSDC, tx);
const position2 = await sdk.Pair.openPosition(pairSUIUSDC, tx);

// Execute all at once
await sdk.fullClient.signAndExecuteTransaction({
  transaction: tx,
  signer: keypair
});
```

### Finding Your Position ID

After creation, retrieve the position ID from transaction events:

```typescript
const result = await sdk.fullClient.signAndExecuteTransaction({
  transaction: tx,
  signer: keypair
});

// Get created object IDs
const createdObjects = result.effects?.created || [];
const positionId = createdObjects.find(obj => 
  obj.owner === 'AddressOwner' // Your address
)?.reference.objectId;

console.log("New position ID:", positionId);
```

### Gas Optimization

Opening an empty position uses minimal gas. Consider:

* Batch position creation with liquidity addition
* Use `openPositionAndAddLiquidity()` for efficiency
* Reuse existing positions when possible

### Common Patterns

#### Position for Range Order

```typescript
// Create position for future liquidity
const tx = await sdk.Pair.openPosition(pair);

// Can add liquidity when price reaches target
// Position remains empty until then
```

#### Multi-Strategy Positions

```typescript
// Create separate positions for different strategies
const conservativeTx = await sdk.Pair.openPosition(pair);
const aggressiveTx = await sdk.Pair.openPosition(pair);

// Add different distributions to each
```

### Related Topics

* [Add to New Position](/integration/dlmm/typescript-sdk/add-liquidity/add-to-new-position) - Create and fund together
* [Get User Positions](/integration/dlmm/typescript-sdk/position-management/get-user-positions) - List your positions
* [Add to Existing Position](/integration/dlmm/typescript-sdk/add-liquidity/add-to-existing-position) - Fund created positions
* [Close Position](/integration/dlmm/typescript-sdk/position-management/close-position) - Remove and burn position NFT


# Get User Positions

List all DLMM position NFTs owned by a specific address, with optional filtering by trading pairs. This method helps track and manage your liquidity portfolio across multiple pools.

### Prerequisites

Before fetching positions:

* Set valid sender address in SDK
* Understand position NFT structure
* Know pair IDs for filtering (optional)

### Basic Usage

```typescript
// Set the address to query
sdk.senderAddress = "0x123...";

// Fetch all positions for the address
const positions = await sdk.Position.getLbPositions([]);

console.log(`Found ${positions.length} positions`);

// Display position details
positions.forEach(position => {
  console.log({
    positionId: position.id,
    pairId: position.pair_id,
    name: position.name,
    description: position.description
  });
});
```

### Method Signature

```typescript
async getLbPositions(pairIds: string[]): Promise<LBPosition[]>
```

#### Parameters

* `pairIds`: Array of pair addresses to filter by (empty array returns all)

#### Returns

Array of `LBPosition` objects:

```typescript
interface LBPosition {
  id: string;              // Position NFT address
  tokenXType: string;      // Token X type
  tokenYType: string;      // Token Y type
  pair_id: string;         // Associated pair address
  name: string;            // NFT name
  description: string;     // NFT description
  url: string;             // NFT image URL
  index: string;           // Position index
  version: string;         // Object version
}
```

### Filtering Positions

#### By Specific Pairs

```typescript
// Only get positions for specific pairs
const ethUsdcPair = "0xabc...";
const suiUsdcPair = "0xdef...";

const filteredPositions = await sdk.Position.getLbPositions([
  ethUsdcPair,
  suiUsdcPair
]);

console.log(`Positions in selected pairs: ${filteredPositions.length}`);
```

#### By Token Types

```typescript
// Get all positions first
const allPositions = await sdk.Position.getLbPositions([]);

// Filter by token type
const usdcPositions = allPositions.filter(pos => 
  pos.tokenXType.includes("USDC") || pos.tokenYType.includes("USDC")
);

// Group by pair
const positionsByPair = allPositions.reduce((acc, pos) => {
  if (!acc[pos.pair_id]) acc[pos.pair_id] = [];
  acc[pos.pair_id].push(pos);
  return acc;
}, {});
```

### Getting Position Details

```typescript
// First get positions list
const positions = await sdk.Position.getLbPositions([]);

// Then get detailed bin data for each
for (const position of positions) {
  // Get the pair
  const pair = await sdk.Pair.getPair(position.pair_id);
  
  // Get bins for this position
  const bins = await sdk.Position.getPositionBins(pair, position.id);
  
  console.log(`Position ${position.id} has ${bins.length} active bins`);
}
```

### Portfolio Analysis

```typescript
// Analyze entire portfolio
const positions = await sdk.Position.getLbPositions([]);

// Get unique pairs
const uniquePairs = [...new Set(positions.map(p => p.pair_id))];
console.log(`Active in ${uniquePairs.length} different pairs`);

// Count by token
const tokenCounts = {};
positions.forEach(pos => {
  tokenCounts[pos.tokenXType] = (tokenCounts[pos.tokenXType] || 0) + 1;
  tokenCounts[pos.tokenYType] = (tokenCounts[pos.tokenYType] || 0) + 1;
});

console.log("Token exposure:", tokenCounts);
```

### Check Position Status

```typescript
const positions = await sdk.Position.getLbPositions([]);

// Check which positions have liquidity
const positionsWithLiquidity = [];
const emptyPositions = [];

for (const position of positions) {
  const pair = await sdk.Pair.getPair(position.pair_id);
  const bins = await sdk.Position.getPositionBins(pair, position.id);
  
  if (bins.length > 0) {
    positionsWithLiquidity.push(position);
  } else {
    emptyPositions.push(position);
  }
}

console.log(`Active: ${positionsWithLiquidity.length}, Empty: ${emptyPositions.length}`);
```

### Common Use Cases

1. **Portfolio Dashboard**: Display all user positions
2. **Position Management**: Find positions to add/remove liquidity
3. **Fee Collection**: Identify positions with claimable fees
4. **Performance Tracking**: Monitor position returns across pairs

### Performance Notes

* Fetches all positions owned by the address
* Uses efficient pagination internally
* Filtered results are processed client-side
* Consider caching for frequent queries

### Error Handling

```typescript
try {
  // Must set sender address first
  sdk.senderAddress = userAddress;
  
  const positions = await sdk.Position.getLbPositions([]);
  
  if (positions.length === 0) {
    console.log("No positions found for this address");
  }
} catch (error) {
  if (error.message.includes("Invalid sender address")) {
    console.error("Please set a valid sender address");
  }
}
```

### Related Topics

* [Open Position](/integration/dlmm/typescript-sdk/position-management/open-position) - Create new position NFTs
* [Get Single Position](/integration/dlmm/typescript-sdk/position-management/get-single-position) - Detailed position data
* [Get Position Bins](/integration/dlmm/typescript-sdk/position-management/get-position-bins) - View liquidity distribution
* [Position Value](/integration/dlmm/typescript-sdk/fees-and-analytics/position-value) - Calculate portfolio value


# Get Single Position

Fetch comprehensive data for a specific position NFT including metadata, associated pair information, and token types. This method retrieves the on-chain position object with all its properties.

### Prerequisites

Before fetching position data:

* Have a valid position ID (object address)
* Initialize the SDK with network connection
* Understand position NFT structure in DLMM

### Basic Usage

```typescript
const positionId = "0x123...abc";

// Fetch position details
const position = await sdk.Position.getLbPosition(positionId);

console.log("Position details:", {
  id: position.id,
  pairId: position.pair_id,
  tokenX: position.tokenXType,
  tokenY: position.tokenYType,
  name: position.name
});
```

### Method Signature

```typescript
async getLbPosition(positionId: string): Promise<LBPosition>
```

#### Parameters

* `positionId`: The object ID of the position NFT

#### Returns

`LBPosition` object:

```typescript
interface LBPosition {
  id: string;              // Position NFT address
  tokenXType: string;      // Full type of token X
  tokenYType: string;      // Full type of token Y
  pair_id: string;         // Associated pair address
  name: string;            // NFT name (e.g., "Alphafi DLMM Position")
  description: string;     // NFT description
  url: string;             // NFT image URL
  index: string;           // Position index in the pair
  version: string;         // Object version
}
```

### Understanding Position Data

#### NFT Metadata

```typescript
const position = await sdk.Position.getLbPosition(positionId);

// NFT display properties
console.log("NFT Name:", position.name);
console.log("Description:", position.description);
console.log("Image URL:", position.url);

// Position specifics
console.log("Position Index:", position.index);
console.log("Pair ID:", position.pair_id);
```

#### Token Information

```typescript
const position = await sdk.Position.getLbPosition(positionId);

// Extract token symbols from types
const tokenXSymbol = position.tokenXType.split("::").pop();
const tokenYSymbol = position.tokenYType.split("::").pop();

console.log(`Position holds: ${tokenXSymbol}/${tokenYSymbol}`);
```

### Complete Position Analysis

```typescript
// Get position and its associated data
const positionId = "0x123...";
const position = await sdk.Position.getLbPosition(positionId);

// Get the pair for additional context
const pair = await sdk.Pair.getPair(position.pair_id);

// Get bins to see liquidity distribution
const bins = await sdk.Position.getPositionBins(pair, positionId);

// Get current value
const amounts = await sdk.Position.getPositionBinsAmount(pair, positionId);

console.log("Position analysis:", {
  pair: `${position.tokenXType}/${position.tokenYType}`,
  activeBins: bins.length,
  binRange: bins.length > 0 ? [bins[0].id, bins[bins.length - 1].id] : [],
  currentPrice: getPriceFromBinId(pair.parameters.active_id, Number(pair.binStep))
});
```

### Error Handling

```typescript
try {
  const position = await sdk.Position.getLbPosition(invalidId);
} catch (error) {
  if (error.message.includes("Position not found")) {
    console.error("Position does not exist");
  } else if (error.message.includes("Invalid")) {
    console.error("Invalid position ID format");
  }
}
```

### Verify Position Ownership

```typescript
// Check if position belongs to current user
const position = await sdk.Position.getLbPosition(positionId);
const userPositions = await sdk.Position.getLbPositions([]);

const isOwner = userPositions.some(p => p.id === positionId);
console.log("User owns this position:", isOwner);
```

### Position State Checks

```typescript
const position = await sdk.Position.getLbPosition(positionId);
const pair = await sdk.Pair.getPair(position.pair_id);

// Check if position has liquidity
const bins = await sdk.Position.getPositionBins(pair, positionId);
const hasLiquidity = bins.length > 0;

// Check if pair is still active
const pairActive = BigInt(pair.reserveX) > 0n || BigInt(pair.reserveY) > 0n;

console.log("Position state:", {
  hasLiquidity,
  pairActive,
  binCount: bins.length
});
```

### Common Use Cases

1. **Position Details Page**: Display complete position information
2. **Pre-transaction Validation**: Verify position before operations
3. **Position Migration**: Get data before moving to new strategies
4. **Analytics**: Track individual position performance

### Related Topics

* [Get User Positions](/integration/dlmm/typescript-sdk/position-management/get-user-positions) - List all user positions
* [Get Position Bins](/integration/dlmm/typescript-sdk/position-management/get-position-bins) - View liquidity in bins
* [Position Value](/integration/dlmm/typescript-sdk/fees-and-analytics/position-value) - Calculate position worth
* [Remove Liquidity Overview](/integration/dlmm/typescript-sdk/remove-liquidity/remove-liquidity-overview) - Withdraw from position


# Get Position Bins

View all bins where a position has active liquidity. This method shows the liquidity distribution across different price levels within a specific position.

### Prerequisites

Before fetching position bins:

* Have a valid position ID
* Have the associated LBPair object
* Understand bin IDs and price relationships
* Position must belong to the specified pair

### Basic Usage

```typescript
// Get pair and position
const pair = await sdk.Pair.getPair(pairAddress);
const positionId = "0x123...abc";

// Fetch bins with liquidity
const bins = await sdk.Position.getPositionBins(pair, positionId);

console.log(`Position has liquidity in ${bins.length} bins`);

// Display bin details
bins.forEach(bin => {
  console.log(`Bin ${bin.id}: ${bin.liquidity.toString()} liquidity`);
});
```

### Method Signature

```typescript
async getPositionBins(
  pair: LBPair, 
  positionId: string
): Promise<BinData[]>
```

#### Parameters

* `pair`: The LBPair object where position exists
* `positionId`: The position NFT address

#### Returns

Array of `BinData` objects sorted by bin ID:

```typescript
interface BinData {
  id: number;           // Bin ID
  liquidity: bigint;    // LP token amount in this bin
}
```

### Understanding Bin Data

#### Liquidity Values

```typescript
const bins = await sdk.Position.getPositionBins(pair, positionId);

// Analyze liquidity distribution
const totalLiquidity = bins.reduce((sum, bin) => sum + bin.liquidity, 0n);
const averageLiquidity = totalLiquidity / BigInt(bins.length);

console.log("Position statistics:", {
  totalBins: bins.length,
  totalLiquidity: totalLiquidity.toString(),
  averageLiquidity: averageLiquidity.toString()
});
```

#### Price Range Coverage

```typescript
const bins = await sdk.Position.getPositionBins(pair, positionId);
const binStep = Number(pair.binStep);

if (bins.length > 0) {
  const minBinId = bins[0].id;
  const maxBinId = bins[bins.length - 1].id;
  
  const minPrice = getPriceFromBinId(minBinId, binStep);
  const maxPrice = getPriceFromBinId(maxBinId, binStep);
  
  console.log("Position price range:", {
    minPrice: minPrice.toFixed(4),
    maxPrice: maxPrice.toFixed(4),
    binsCount: bins.length,
    activebin: pair.parameters.active_id
  });
}
```

### Get Position Token Amounts

For detailed token amounts per bin:

```typescript
// Get bins with token amounts
const binsWithAmounts = await sdk.Position.getPositionBinsAmount(pair, positionId);

binsWithAmounts.forEach(bin => {
  console.log(`Bin ${bin.id}:`, {
    liquidity: bin.liquidity.toString(),
    amountX: bin.amountX.toString(),
    amountY: bin.amountY.toString()
  });
});

// Calculate total position value
const totals = binsWithAmounts.reduce((acc, bin) => ({
  x: acc.x + bin.amountX,
  y: acc.y + bin.amountY
}), { x: 0n, y: 0n });

console.log("Total position value:", totals);
```

### Analyze Position Shape

```typescript
const bins = await sdk.Position.getPositionBins(pair, positionId);
const activeId = pair.parameters.active_id;

// Categorize bins relative to current price
const analysis = {
  belowActive: bins.filter(b => b.id < activeId),
  atActive: bins.filter(b => b.id === activeId),
  aboveActive: bins.filter(b => b.id > activeId)
};

console.log("Position shape:", {
  binsBelow: analysis.belowActive.length,
  binsAtActive: analysis.atActive.length,
  binsAbove: analysis.aboveActive.length,
  isInRange: analysis.atActive.length > 0
});
```

### Liquidity Concentration

```typescript
const bins = await sdk.Position.getPositionBins(pair, positionId);

// Find bins with most liquidity
const sortedBins = [...bins].sort((a, b) => 
  Number(b.liquidity - a.liquidity)
);

const top5Bins = sortedBins.slice(0, 5);
const top5Liquidity = top5Bins.reduce((sum, b) => sum + b.liquidity, 0n);
const totalLiquidity = bins.reduce((sum, b) => sum + b.liquidity, 0n);

console.log("Concentration:", {
  top5Percentage: (Number(top5Liquidity) / Number(totalLiquidity) * 100).toFixed(2),
  mostConcentratedBin: top5Bins[0]?.id
});
```

### Common Patterns

#### Check if Position is Active

```typescript
const bins = await sdk.Position.getPositionBins(pair, positionId);
const activeId = pair.parameters.active_id;

const isActive = bins.some(bin => bin.id === activeId);
console.log("Position contains active bin:", isActive);
```

#### Visualize Distribution

```typescript
const bins = await sdk.Position.getPositionBins(pair, positionId);

// Create simple ASCII visualization
bins.forEach(bin => {
  const barLength = Number(bin.liquidity * 100n / maxLiquidity);
  const bar = "█".repeat(Math.min(barLength, 50));
  console.log(`Bin ${bin.id}: ${bar}`);
});
```

### Error Handling

```typescript
try {
  const bins = await sdk.Position.getPositionBins(pair, wrongPositionId);
} catch (error) {
  if (error.message.includes("not match with pair")) {
    console.error("Position doesn't belong to this pair");
  }
}
```

### Related Topics

* [Get Single Position](/integration/dlmm/typescript-sdk/position-management/get-single-position) - Fetch position metadata
* [Remove from Specific Bins](/integration/dlmm/typescript-sdk/remove-liquidity/remove-from-specific-bins) - Remove selected bins
* [Position Value](/integration/dlmm/typescript-sdk/fees-and-analytics/position-value) - Calculate bin values


# Close Position

Remove all remaining liquidity and burn the position NFT in a single transaction. This method efficiently withdraws all tokens and fees before permanently closing the position.

### Prerequisites

Before closing a position:

* Own the position NFT you want to close
* Have sufficient gas for transaction
* Understand this action is irreversible
* Consider claiming fees first if needed separately

### Basic Usage

```typescript
// Get the pair
const pair = await sdk.Pair.getPair(pairAddress);
const positionId = "0x123...abc";

// Remove all liquidity and close position
const tx = await sdk.Pair.removeAndClosePosition(pair, positionId);

// Execute transaction
const result = await sdk.fullClient.signAndExecuteTransaction({
  transaction: tx,
  signer: keypair
});

console.log("Position closed:", result.digest);
```

### Method Signature

```typescript
async removeAndClosePosition(
  pair: LBPair, 
  positionId: string, 
  tx?: Transaction
): Promise<Transaction>
```

#### Parameters

* `pair`: The LBPair where position exists
* `positionId`: Position NFT to close
* `tx`: Optional existing transaction to append to

#### Returns

* Transaction that removes liquidity and burns NFT
* Tokens are automatically sent to sender

### How It Works

The method performs these steps automatically:

1. **Fetch position bins** - Identifies all bins with liquidity
2. **Remove liquidity** - Withdraws from all active bins
3. **Collect fees** - Claims accumulated trading fees
4. **Close position** - Burns the position NFT
5. **Transfer tokens** - Sends all tokens to owner

### Complete Example

```typescript
// Close position with validation
async function closePositionSafely(pair: LBPair, positionId: string) {
  try {
    // Verify ownership
    const userPositions = await sdk.Position.getLbPositions([pair.id]);
    const ownsPosition = userPositions.some(p => p.id === positionId);
    
    if (!ownsPosition) {
      throw new Error("You don't own this position");
    }
    
    // Check current value before closing
    const bins = await sdk.Position.getPositionBins(pair, positionId);
    console.log(`Closing position with ${bins.length} active bins`);
    
    // Close position
    const tx = await sdk.Pair.removeAndClosePosition(pair, positionId);
    
    return await sdk.fullClient.signAndExecuteTransaction({
      transaction: tx,
      signer: keypair
    });
    
  } catch (error) {
    console.error("Failed to close position:", error);
    throw error;
  }
}
```

### Handling Empty Positions

```typescript
// The method handles empty positions gracefully
const bins = await sdk.Position.getPositionBins(pair, positionId);

if (bins.length === 0) {
  console.log("Position is empty, will only burn NFT");
}

// Still safe to call - will just burn the empty NFT
const tx = await sdk.Pair.removeAndClosePosition(pair, positionId);
```

### Batch Operations

```typescript
// Close multiple positions in one transaction
const tx = new Transaction();

for (const positionId of positionsToClose) {
  await sdk.Pair.removeAndClosePosition(pair, positionId, tx);
}

// Execute all closures at once
await sdk.fullClient.signAndExecuteTransaction({
  transaction: tx,
  signer: keypair
});
```

### Alternative Approaches

#### Manual Two-Step Process

```typescript
// Step 1: Remove all liquidity (keep position)
const bins = await sdk.Position.getPositionBins(pair, positionId);
const binIds = bins.map(b => b.id);

const removeTx = await sdk.Pair.removeLiquidity(pair, {
  positionId,
  binIds
});

// Step 2: Close empty position later
const closeTx = TransactionUtil.closePosition(
  pair, 
  { positionId }, 
  sdk.sdkOptions
);
```

#### Partial Withdrawal Before Closing

```typescript
// Remove from specific bins first
await sdk.Pair.removeLiquidity(pair, {
  positionId,
  binIds: [8388608, 8388609] // Selected bins only
});

// Then close position with remaining liquidity
await sdk.Pair.removeAndClosePosition(pair, positionId);
```

### What Happens to Your Assets

When closing a position:

* **Liquidity tokens** are burned
* **Token X and Y** are returned based on current bin reserves
* **Accumulated fees** are included in the withdrawal
* **Position NFT** is permanently destroyed
* **All assets** are sent to transaction sender

### Common Use Cases

1. **Exit Strategy**: Complete withdrawal from a pool
2. **Position Migration**: Close old positions before creating new ones
3. **Portfolio Cleanup**: Remove empty or underperforming positions
4. **Emergency Exit**: Quick withdrawal of all funds

### Error Handling

```typescript
try {
  await sdk.Pair.removeAndClosePosition(pair, positionId);
} catch (error) {
  if (error.message.includes("Invalid sender address")) {
    console.error("Set valid sender address first");
  } else if (error.message.includes("not match with pair")) {
    console.error("Position doesn't belong to this pair");
  }
}
```

### Related Topics

* [Remove All Liquidity](/integration/dlmm/typescript-sdk/remove-liquidity/remove-all-liquidity) - Just remove without closing
* [Get Position Bins](/integration/dlmm/typescript-sdk/position-management/get-position-bins) - Check bins before closing
* [Calculate Output Amounts](/integration/dlmm/typescript-sdk/remove-liquidity/calculate-output-amounts) - Preview withdrawal
* [Open Position](/integration/dlmm/typescript-sdk/position-management/open-position) - Create new positions


# Add Liquidity

Add liquidity to DLMM pools using various distribution strategies. This section covers opening new positions, adding to existing ones, and choosing optimal liquidity distributions from uniform SPOT to concentrated CURVE strategies.

### Prerequisites

Before adding liquidity, ensure you have:

* Active position NFT or ability to create one
* Both tokens of the pair in your wallet
* Understanding of impermanent loss and bin mechanics
* Knowledge of different distribution types (SPOT, CURVE, BID-ASK)
* Calculated desired price range for your liquidity

### What You'll Learn

This section covers comprehensive liquidity provision strategies in DLMM:

#### [Add Liquidity Overview](/integration/dlmm/typescript-sdk/add-liquidity/add-liquidity-overview)

Understand the core concepts of adding liquidity in DLMM, including bin selection, token ratios, distribution strategies, and how liquidity affects price discovery.

#### [Add to New Position](/integration/dlmm/typescript-sdk/add-liquidity/add-to-new-position)

Create a position NFT and add liquidity in a single transaction. Learn the most efficient way to start providing liquidity from scratch.

#### [Add to Existing Position](#add-to-existing-position)

Increase liquidity in your current positions without creating new NFTs. Manage position growth and compound accumulated fees back into the pool.

#### [SPOT Distribution](/integration/dlmm/typescript-sdk/add-liquidity/spot-distribution)

Deploy uniform liquidity distribution across all selected bins. Ideal for stable pairs or when expecting price to move freely within a range.

#### [CURVE Distribution](/integration/dlmm/typescript-sdk/add-liquidity/curve-distribution)

Concentrate liquidity using Gaussian curve distribution. Perfect for focusing capital efficiency around the current price while maintaining some range coverage.

#### [BID-ASK Distribution](/integration/dlmm/typescript-sdk/add-liquidity/bid-ask-distribution)

Create weighted bid-ask spreads with more liquidity farther from the active price. Optimal for market-making strategies and volatility harvesting.

#### [Custom Distribution](/integration/dlmm/typescript-sdk/add-liquidity/custom-distribution)

Manually specify exact amounts for each bin. Full control over liquidity placement for advanced strategies and precise position management.

#### [Calculate Token Amounts](/integration/dlmm/typescript-sdk/add-liquidity/calculate-token-amounts)

Determine required token amounts for each bin based on your distribution strategy. Preview capital requirements before committing liquidity.


# Add Liquidity Overview

### What is Adding Liquidity?

In DLMM, adding liquidity means:

* Depositing token pairs into specific price bins
* Receiving a position NFT that tracks your share
* Earning fees from trades that occur in your bins
* Managing liquidity across discrete price levels

### Core Concepts

#### Position NFTs

Your liquidity is represented by a **Position NFT** that:

* Tracks your share across multiple bins
* Accumulates trading fees
* Can be transferred or sold
* Allows partial withdrawals

#### Bins and Price Ranges

* Each bin represents a specific price level
* You choose which bins to add liquidity to
* Only the "active bin" facilitates trades
* Price moves by jumping between bins

#### Distribution Strategies

How you spread liquidity across bins:

* **SPOT**: Uniform distribution
* **CURVE**: Concentrated around current price
* **BID-ASK**: Weighted away from center
* **CUSTOM**: Manual bin selection

### The Workflow

#### 1. Choose Your Pair

```typescript
const pair = await sdk.Pair.getPair(pairAddress);
const currentPrice = calculatePrice(pair.parameters.active_id);
```

#### 2. Select Distribution Strategy

Decide how to allocate liquidity:

* Tight range for higher fee capture
* Wide range for consistent coverage
* Strategic placement for limit orders

#### 3. Calculate Token Amounts

DLMM requires specific ratios per bin:

* Bins below active: 100% token Y
* Active bin: Mixed ratio
* Bins above active: 100% token X

#### 4. Execute Transaction

Two options available:

* Create new position + add liquidity
* Add to existing position

### Key Considerations

#### Capital Efficiency

* Narrower range = higher fee share
* Wider range = more consistent fees
* Active bin earns most fees

#### Impermanent Loss

* Occurs when price moves from entry
* Wider ranges reduce IL risk
* Consider correlation of assets

#### Rebalancing Needs

* Narrow ranges need more management
* Wide ranges are more passive
* Monitor active bin position

### Best Practices

* **Start Small**: Test with small amounts first
* **Monitor Performance**: Track which bins earn most fees
* **Consider Volatility**: Adjust range based on pair behavior
* **Plan Exit**: Know your withdrawal strategy

### Common Patterns

#### Passive Income Strategy

* Use SPOT distribution
* Wide range (50-100 bins)
* Minimal management needed

#### Active Trading Strategy

* Use CURVE distribution
* Tight range (10-20 bins)
* Rebalance frequently

#### Limit Order Strategy

* Use CUSTOM distribution
* Single bin placement
* Acts like a limit order

### Next Steps

Ready to add liquidity? Choose your approach:

1. **Quick Start**: [Add to New Position](/integration/dlmm/typescript-sdk/add-liquidity/add-to-new-position) - One transaction setup
2. **Existing Position**: [Add to Existing Position](/integration/dlmm/typescript-sdk/add-liquidity/add-to-existing-position) - Increase current liquidity
3. **Learn Distributions**: Explore [SPOT](/integration/dlmm/typescript-sdk/add-liquidity/spot-distribution), [CURVE](/integration/dlmm/typescript-sdk/add-liquidity/curve-distribution), or [BID-ASK](/integration/dlmm/typescript-sdk/add-liquidity/bid-ask-distribution)

### Related Topics

* [Calculate Token Amounts](#id-3.-calculate-token-amounts) - Required tokens per bin
* [Position Management](/integration/dlmm/typescript-sdk/position-management) - Managing positions
* [Fee Structure](/integration/dlmm/typescript-sdk/fees-and-analytics/fee-structure) - How fees accumulate


# Add to New Position

Create a position NFT and add liquidity in a single transaction. This is the most efficient way to start providing liquidity in DLMM pools.

### Prerequisites

* Connected wallet with both tokens
* Selected distribution strategy (SPOT, CURVE, etc.)
* Calculated token amounts needed
* Sufficient gas for transaction

### Basic Usage

```typescript
const pair = await sdk.Pair.getPair(pairAddress);

// Create position and add liquidity together
const tx = await sdk.Pair.openPositionAndAddLiquidity(pair, {
  amountX: 1000000000n,  // 1 token X
  amountY: 2000000000n,  // 2 token Y
  ids: [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
  distributionX: [0, 0, 0, 0, 0, 50, 50, 0, 0, 0, 0],
  distributionY: [0, 0, 0, 0, 0, 50, 50, 0, 0, 0, 0]
});

await sdk.fullClient.signAndExecuteTransaction({
  transaction: tx,
  signer: keypair
});
```

### Method Parameters

```typescript
interface AddLiquidityParams {
  amountX: bigint;           // Max token X to deposit
  amountY: bigint;           // Max token Y to deposit
  ids: number[];        // Relative bin IDs from active
  distributionX: number[];   // X distribution (sums to 100)
  distributionY: number[];   // Y distribution (sums to 100)
  minAmountX?: bigint;
  minAmountY?: bigint;
}
```

### Quick Examples

#### Concentrated Liquidity (±5 bins)

```typescript
await sdk.Pair.openPositionAndAddLiquidity(pair, {
  amountX: 1000000n,
  amountY: 1000000n,
  ids: [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
  distributionX: [0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0],
  distributionY: [0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0]
});
```

#### Wide Range (±20 bins)

```typescript
const binCount = 41; // -20 to +20
const ids = Array.from({length: binCount}, (_, i) => i - 20);
const uniformDist = Array(binCount).fill(100 / binCount);

await sdk.Pair.openPositionAndAddLiquidity(pair, {
  amountX: 10000000n,
  amountY: 10000000n,
  ids,
  distributionX: uniformDist,
  distributionY: uniformDist
});
```

### Using Distribution Helpers

```typescript
import { DistributionUtils } from '@ferra/dlmm-sdk';

// SPOT distribution
const spotParams = DistributionUtils.createParams(
  DistributionUtils.SPOT,
  {
    activeId: pair.parameters.active_id,
    binRange: [activeId - 10, activeId + 10],
    parsedAmounts: [amountX, amountY]
  }
);

await sdk.Pair.openPositionAndAddLiquidity(pair, {
  amountX,
  amountY,
  ...spotParams
});
```

### What Happens

* Creates new position NFT
* Deposits tokens into selected bins
* Transfers position NFT to your wallet
* Returns transaction hash

### Common Patterns

#### Add Around Current Price

```typescript
const activeId = pair.parameters.active_id;
const range = 10; // ±10 bins

await sdk.Pair.openPositionAndAddLiquidity(pair, {
  amountX: amount,
  amountY: amount,
  ids: Array.from({length: range * 2 + 1}, (_, i) => i - range),
  distributionX: [/* your distribution */],
  distributionY: [/* your distribution */]
});
```

#### Single Bin Liquidity

```typescript
// Add to active bin only
await sdk.Pair.openPositionAndAddLiquidity(pair, {
  amountX: amount,
  amountY: amount,
  ids: [0],  // Just active bin
  distributionX: [100],
  distributionY: [100]
});
```

### Related Topics

* [SPOT Distribution](/integration/dlmm/typescript-sdk/add-liquidity/spot-distribution) - Uniform liquidity spread
* [CURVE Distribution](/integration/dlmm/typescript-sdk/add-liquidity/curve-distribution) - Concentrated liquidity
* [Calculate Token Amounts](/integration/dlmm/typescript-sdk/add-liquidity/calculate-token-amounts) - Required tokens
* [Add to Existing Position](/integration/dlmm/typescript-sdk/add-liquidity/add-to-existing-position) - Increase liquidity


# Add to Existing Position

Increase liquidity in your current positions without creating new NFTs. Add more tokens to existing bins or expand to new price ranges within the same position.

### Prerequisites

* Existing position NFT ID
* Additional tokens to deposit
* Same distribution strategy or new bins
* Position must belong to the pair

### Basic Usage

```typescript
const pair = await sdk.Pair.getPair(pairAddress);
const positionId = "0x123...abc";

// Add more liquidity to existing position
const tx = await sdk.Pair.addLiquidity(pair, {
  positionId,
  amountX: 500000000n,   // 0.5 token X
  amountY: 1000000000n,  // 1 token Y
  deltaIds: [-2, -1, 0, 1, 2],
  distributionX: [0, 0, 100, 0, 0],
  distributionY: [0, 0, 100, 0, 0]
});

await sdk.fullClient.signAndExecuteTransaction({
  transaction: tx,
  signer: keypair
});
```

### Method Parameters

```typescript
interface AddLiquidityParams {
  positionId: string;        // Existing position NFT
  amountX: bigint;          // Additional token X
  amountY: bigint;          // Additional token Y
  deltaIds: number[];       // Bins to add liquidity to
  distributionX: number[];  // X distribution
  distributionY: number[];  // Y distribution
}
```

### Adding to Same Bins

```typescript
// Get current position bins
const currentBins = await sdk.Position.getPositionBins(pair, positionId);
const binIds = currentBins.map(b => b.id - pair.parameters.active_id);

// Double liquidity in existing bins
await sdk.Pair.addLiquidity(pair, {
  positionId,
  amountX: originalAmountX,
  amountY: originalAmountY,
  deltaIds: binIds,
  distributionX: Array(binIds.length).fill(100 / binIds.length),
  distributionY: Array(binIds.length).fill(100 / binIds.length)
});
```

### Expanding Range

```typescript
// Current position: bins -5 to +5
// Expand to: bins -10 to +10

const newBins = [
  ...Array.from({length: 5}, (_, i) => -10 + i),  // -10 to -6
  ...Array.from({length: 5}, (_, i) => 6 + i)     // +6 to +10
];

await sdk.Pair.addLiquidity(pair, {
  positionId,
  amountX: expansionAmountX,
  amountY: expansionAmountY,
  deltaIds: newBins,
  distributionX: Array(10).fill(10),  // Even distribution
  distributionY: Array(10).fill(10)
});
```

### Compound Fees Strategy

```typescript
// Calculate accumulated fees
const binsWithAmounts = await sdk.Position.getPositionBinsAmount(pair, positionId);
const fees = calculateAccumulatedFees(binsWithAmounts);

// Reinvest fees back into position
await sdk.Pair.addLiquidity(pair, {
  positionId,
  amountX: fees.x,
  amountY: fees.y,
  deltaIds: [0],  // Add to active bin
  distributionX: [100],
  distributionY: [100]
});
```

### Add to Specific Bins Only

```typescript
// Add liquidity to bins that need rebalancing
const bins = await sdk.Position.getPositionBins(pair, positionId);
const activeId = pair.parameters.active_id;

// Find bins below threshold
const lowLiquidityBins = bins
  .filter(b => b.liquidity < THRESHOLD)
  .map(b => b.id - activeId);

if (lowLiquidityBins.length > 0) {
  await sdk.Pair.addLiquidity(pair, {
    positionId,
    amountX: rebalanceAmount,
    amountY: rebalanceAmount,
    deltaIds: lowLiquidityBins,
    distributionX: Array(lowLiquidityBins.length).fill(100 / lowLiquidityBins.length),
    distributionY: Array(lowLiquidityBins.length).fill(100 / lowLiquidityBins.length)
  });
}
```

### What Happens

* Validates position ownership
* Deposits additional tokens into specified bins
* Updates position's liquidity shares
* Maintains existing fee accumulation
* No new NFT created

### Important Notes

* Can add to new bins not in original position
* Existing liquidity remains untouched
* Fees continue accumulating normally
* Gas efficient for position scaling

### Common Use Cases

* **DCA Strategy**: Regular liquidity additions
* **Reinvestment**: Compound earned fees
* **Range Extension**: Adapt to market moves
* **Concentration**: Focus on profitable bins

### Related Topics

* [Get Position Bins](/integration/dlmm/typescript-sdk/position-management/get-position-bins) - Check current bins
* [Calculate Token Amounts](/integration/dlmm/typescript-sdk/add-liquidity/calculate-token-amounts) - Required tokens
* [Remove from Specific Bins](/integration/dlmm/typescript-sdk/remove-liquidity/remove-from-specific-bins) - Rebalance
* [Position Value](/integration/dlmm/typescript-sdk/fees-and-analytics/position-value) - Track growth


# SPOT Distribution

Spot distribution (or Spot strategy) is a uniform liquidity distribution that spreads tokens equally across all selected bins. It is best suited for stable pairs and passive strategies. Visit the [Shapes and Strategy](/core-protocols/quickstart/shapes-and-strategies) section for more details.

### Quick Implementation

```typescript
import { DistributionUtils } from '@ferra-labs/dlmm';

// Create SPOT distribution
const spotParams = DistributionUtils.createParams(
  DistributionUtils.SPOT,
  {
    activeId: pair.parameters.active_id,
    binRange: [activeId - 10, activeId + 10],  // 20 bins
    parsedAmounts: [
      new Decimal(1000),  // token X
      new Decimal(1000)   // token Y
    ]
  }
);

// Add liquidity
const tx = await sdk.Pair.openPositionAndAddLiquidity(pair, {
  amountX: 1000000000n,
  amountY: 1000000000n,
  ...spotParams
});
```

### Manual Setup

```typescript
// 21 bins from -10 to +10
const binCount = 21;
const deltaIds = Array.from({length: binCount}, (_, i) => i - 10);
const distribution = Array(binCount).fill(100 / binCount);

await sdk.Pair.openPositionAndAddLiquidity(pair, {
  amountX,
  amountY,
  deltaIds,
  distributionX: distribution,
  distributionY: distribution
});
```

### Best Use Cases

✅ **Ideal for:**

* USDC/USDT (stable pairs)
* wETH/ETH (correlated assets)
* Range-bound markets
* Passive income strategy

❌ **Not optimal for:**

* Volatile pairs
* Trending markets
* Capital efficiency needs

### Range Guidelines

* **Stable pairs**: 50-100 bins (±0.5-1%)
* **Correlated**: 20-40 bins (±0.2-0.4%)
* **Standard**: 20-30 bins (varies by volatility)

### Key Benefits

* No rebalancing needed
* Predictable returns
* Works in all conditions
* Simple to understand

### Related Topics

* [CURVE Distribution](/integration/dlmm/typescript-sdk/add-liquidity/curve-distribution) - Concentrated approach
* [BID-ASK Distribution](/integration/dlmm/typescript-sdk/add-liquidity/bid-ask-distribution) - Market making
* [Calculate Token Amounts](/integration/dlmm/typescript-sdk/add-liquidity/calculate-token-amounts) - Required tokens


# CURVE Distribution

Concentrate liquidity using a Gaussian (bell curve) distribution around the current price. Balances capital efficiency with range coverage. Visit the [Shapes and Strategy](/core-protocols/quickstart/shapes-and-strategies) section for more details.

### Quick Implementation

```typescript
import { DistributionUtils } from '@ferra-labs/dlmm';

// Create CURVE distribution
const curveParams = DistributionUtils.createParams(
  DistributionUtils.CURVE,
  {
    activeId: pair.parameters.active_id,
    binRange: [activeId - 20, activeId + 20],
    parsedAmounts: [
      new Decimal(1000),  // token X
      new Decimal(1000)   // token Y
    ],
    alpha: 0.1  // Concentration factor (0-1)
  }
);

// Add liquidity
const tx = await sdk.Pair.openPositionAndAddLiquidity(pair, {
  amountX: 1000000000n,
  amountY: 1000000000n,
  ...curveParams
});
```

### Alpha Parameter

Controls concentration level:

* **0.1**: High concentration (narrow peak)
* **0.5**: Moderate concentration
* **0.9**: Low concentration (approaches uniform)

```typescript
// Tight concentration for stable pairs
alpha: 0.1  // 90% liquidity in ±5 bins

// Balanced for volatile pairs
alpha: 0.5  // Wider distribution
```

### Manual Implementation

```typescript
// Simple approximation of Gaussian
const radius = 10;
const center = 0;

const deltaIds = Array.from({length: 21}, (_, i) => i - radius);
const distribution = deltaIds.map(id => {
  // Higher values near center
  const distance = Math.abs(id - center);
  return Math.exp(-(distance ** 2) / (2 * 4)); // sigma = 2
});

// Normalize to 100
const sum = distribution.reduce((a, b) => a + b);
const normalized = distribution.map(v => (v / sum) * 100);
```

### Best Use Cases

✅ **Ideal for:**

* ETH/USDC (major pairs)
* Most trading strategies
* Fee optimization
* Active markets

❌ **Less suitable for:**

* Extreme volatility
* Wide range needs
* Passive strategies

### Choosing Alpha

| Asset Type | Recommended Alpha | Description         |
| ---------- | ----------------- | ------------------- |
| Blue-chip  | 0.1-0.3           | Tight concentration |
| Standard   | 0.3-0.5           | Balanced approach   |
| Volatile   | 0.5-0.7           | Wider coverage      |

### Key Benefits

* Higher fee capture near price
* Maintains some range coverage
* Efficient capital usage
* Natural rebalancing incentive

### Related Topics

* [SPOT Distribution](/integration/dlmm/typescript-sdk/add-liquidity/spot-distribution) - Uniform alternative
* [BID-ASK Distribution](/integration/dlmm/typescript-sdk/add-liquidity/bid-ask-distribution) - Market making
* [Bin Selection](broken://pages/pBEKaHkdvZrCYWt7Kd0C) - Optimize placement


# BID-ASK Distribution

Create weighted liquidity that increases farther from the current price. Ideal for market making and volatility harvesting strategies. Visit the [Shapes and Strategy](/core-protocols/quickstart/shapes-and-strategies) section for more details.

### Quick Implementation

```typescript
import { DistributionUtils } from '@ferra-labs/dlmm';

// Create BID-ASK distribution
const bidAskParams = DistributionUtils.createParams(
  DistributionUtils.BID_ASK,
  {
    activeId: pair.parameters.active_id,
    binRange: [activeId - 15, activeId + 15],
    parsedAmounts: [
      new Decimal(1000),  // token X
      new Decimal(1000)   // token Y
    ]
  }
);

// Add liquidity
const tx = await sdk.Pair.openPositionAndAddLiquidity(pair, {
  amountX: 1000000000n,
  amountY: 1000000000n,
  ...bidAskParams
});
```

### How It Works

Liquidity weight increases with distance from active bin:

* Bin ±1: Weight = 2
* Bin ±2: Weight = 3
* Bin ±3: Weight = 4
* And so on...

```typescript
// Manual implementation
const range = 10;
const deltaIds = Array.from({length: range * 2 + 1}, (_, i) => i - range);

const distribution = deltaIds.map(id => {
  const weight = Math.abs(id) + 1;
  return id === 0 ? weight / 2 : weight;  // Half weight at active
});

// Normalize to 100
const normalized = distribution.map(v => (v / sum) * 100);
```

### Best Use Cases

✅ **Ideal for:**

* Market making strategies
* Mean reversion trading
* Volatility harvesting
* Large price swings

❌ **Not suitable for:**

* Stable pairs
* Trending markets
* Passive strategies

### Strategy Benefits

#### Volatility Capture

* Profits from price swings
* Better fills at extremes
* Natural buy low/sell high

#### Risk Management

* Less IL at current price
* Protected from small moves
* Captures large deviations

### Configuration Tips

* **Volatile pairs**: 20-30 bin range
* **Standard pairs**: 15-20 bin range
* **Adjust range**: Based on historical volatility

### Example Scenarios

```typescript
// High volatility pair
const volatileParams = {
  binRange: [activeId - 25, activeId + 25],  // Wide range
  // More capital for larger swings
};

// Moderate volatility
const standardParams = {
  binRange: [activeId - 15, activeId + 15],  // Standard range
  // Balanced approach
};
```

### Key Advantages

* Captures volatility premium
* Reduces impermanent loss risk
* Better pricing at extremes
* Automated mean reversion

### Related Topics

* [SPOT Distribution](/integration/dlmm/typescript-sdk/add-liquidity/spot-distribution) - Uniform approach
* [CURVE Distribution](/integration/dlmm/typescript-sdk/add-liquidity/curve-distribution) - Concentrated liquidity
* [Liquidity Strategies](broken://pages/QDLzlzw07vfHJEB6TvIY) - Advanced tactics


# Custom Distribution

Besides the three basic liquidity distributions, Ferra allows users to manually specify the exact liquidity amount for each bin—offering full control over placement for advanced strategies and precise position management.

### Basic Implementation

```typescript
// Manual distribution - you control everything
const tx = await sdk.Pair.openPositionAndAddLiquidity(pair, {
  amountX: 5000000000n,
  amountY: 10000000000n,
  deltaIds: [-10, -5, -1, 0, 3, 7, 10],  // Specific bins
  distributionX: [0, 0, 10, 30, 60, 0, 0],   // Custom weights
  distributionY: [5, 15, 40, 30, 10, 0, 0]   // Different for each token
});
```

### Strategic Placements

#### Limit Order Style

```typescript
// Single bin acts like limit order
await sdk.Pair.openPositionAndAddLiquidity(pair, {
  amountX: sellAmount,
  amountY: 0n,
  deltaIds: [5],  // 5 bins above current
  distributionX: [100],
  distributionY: [0]
});
```

#### Support/Resistance Levels

```typescript
// Place at key price levels
const supportBin = -15;   // Support level
const resistanceBin = 20; // Resistance level

await sdk.Pair.openPositionAndAddLiquidity(pair, {
  amountX,
  amountY,
  deltaIds: [supportBin, 0, resistanceBin],
  distributionX: [20, 30, 50],  // More at resistance
  distributionY: [50, 30, 20]   // More at support
});
```

#### Ladder Strategy

```typescript
// DCA-style ladder
const ladderBins = [-20, -15, -10, -5];
const equalWeight = 100 / ladderBins.length;

await sdk.Pair.openPositionAndAddLiquidity(pair, {
  amountX: 0n,  // Buy ladder
  amountY: buyAmount,
  deltaIds: ladderBins,
  distributionX: Array(ladderBins.length).fill(0),
  distributionY: Array(ladderBins.length).fill(equalWeight)
});
```

### Advanced Patterns

#### Barbell Strategy

```typescript
// Concentrate at extremes
const deltaIds = [-20, -19, 0, 19, 20];
const distributionX = [0, 0, 20, 40, 40];
const distributionY = [40, 40, 20, 0, 0];
```

#### Asymmetric Distribution

```typescript
// Different shapes for buy/sell
const deltaIds = Array.from({length: 21}, (_, i) => i - 10);
const distributionX = deltaIds.map(id => id > 0 ? id * 2 : 0);
const distributionY = deltaIds.map(id => id < 0 ? Math.abs(id) * 3 : 0);

// Normalize to 100
normalize(distributionX);
normalize(distributionY);
```

### Use Cases

✅ **Perfect for:**

* Technical analysis levels
* Limit order simulation
* Complex strategies
* Precise positioning

❌ **Avoid if:**

* New to DLMM
* Want simple management
* Need quick setup

### Tips & Best Practices

1. **Plan First**: Map out bins before executing
2. **Test Small**: Try strategy with minimal amounts
3. **Track Performance**: Monitor which bins perform best
4. **Adjust Actively**: Custom positions need management

### Common Strategies

* **Range Orders**: Single bins at target prices
* **Fibonacci Levels**: Place at fib retracements
* **Volume Profile**: Match historical volume
* **Mean Reversion**: Concentrate at extremes

### Related Topics

* [Calculate Token Amounts](/integration/dlmm/typescript-sdk/add-liquidity/calculate-token-amounts) - Preview requirements
* [Add to Existing Position](/integration/dlmm/typescript-sdk/add-liquidity/add-to-existing-position) - Modify custom positions


# Calculate Token Amounts

Calculate the exact token amounts required for each bin based on your liquidity distribution. Essential for preparing transactions and avoiding slippage.

### Token Requirements by Bin

DLMM requires different token ratios based on bin position:

* **Below active bin**: 100% token Y (quote token)
* **At active bin**: Both tokens (ratio depends on position in bin)
* **Above active bin**: 100% token X (base token)

### Basic Calculation

```typescript
import { getAmountXFromLiquidity, getAmountYFromLiquidity } from '@ferra-labs/dlmm';

// For a specific bin
const binId = 8388610;
const liquidity = 1000000n;
const binPrice = getPriceFromBinId(binId, binStep);

// Calculate required amounts
const amountX = getAmountXFromLiquidity(liquidity, 0n, binPrice);
const amountY = getAmountYFromLiquidity(liquidity, amountX, binPrice);

console.log(`Bin ${binId} needs:`, {
  tokenX: amountX,
  tokenY: amountY
});
```

### Multi-Bin Calculation

```typescript
// Calculate for distribution across bins
function calculateTokensForDistribution(
  pair: LBPair,
  deltaIds: number[],
  distribution: number[],
  totalLiquidity: bigint
) {
  let totalX = 0n;
  let totalY = 0n;
  
  deltaIds.forEach((delta, i) => {
    const binId = pair.parameters.active_id + delta;
    const binLiquidity = (totalLiquidity * BigInt(distribution[i])) / 100n;
    
    if (delta < 0) {
      // Below active - only Y needed
      totalY += binLiquidity;
    } else if (delta > 0) {
      // Above active - only X needed
      totalX += binLiquidity;
    } else {
      // Active bin - both needed (simplified 50/50)
      totalX += binLiquidity / 2n;
      totalY += binLiquidity / 2n;
    }
  });
  
  return { totalX, totalY };
}
```

### Using SDK Helpers

```typescript
// Preview before adding liquidity
const pair = await sdk.Pair.getPair(pairAddress);
const bins = await sdk.Pair.getPairBins(pair, [activeId - 5, activeId + 5]);

// Check current reserves
bins.forEach((bin, index) => {
  const binId = activeId - 5 + index;
  console.log(`Bin ${binId}:`, {
    currentX: bin.reserve_x,
    currentY: bin.reserve_y
  });
});
```

### Active Bin Calculation

For the active bin, token ratio depends on position within the bin:

```typescript
// Simplified active bin calculation
function getActiveBinAmounts(
  totalLiquidity: bigint,
  currentPrice: number
) {
  // Price determines ratio (simplified)
  // Real calculation uses bin position
  const ratio = currentPrice / (1 + currentPrice);
  
  const amountX = (totalLiquidity * BigInt(Math.floor(ratio * 1000))) / 1000n;
  const amountY = totalLiquidity - amountX;
  
  return { amountX, amountY };
}
```

### Practical Example

```typescript
// Planning liquidity for ETH/USDC
const activeId = pair.parameters.active_id;
const distribution = {
  deltaIds: [-2, -1, 0, 1, 2],
  weights: [20, 20, 20, 20, 20]  // Even distribution
};

// Calculate requirements
const required = calculateTokensForDistribution(
  pair,
  distribution.deltaIds,
  distribution.weights,
  parseEther("1")  // 1 ETH worth of liquidity
);

console.log("Need to prepare:", {
  ETH: formatEther(required.totalX),
  USDC: formatUnits(required.totalY, 6)
});
```

### Important Notes

* Always add buffer for slippage (1-2%)
* Active bin requirements change with price
* Use current bin reserves for accurate calculation
* Consider gas costs for many bins

### Common Patterns

```typescript
// Add 5% buffer for safety
const buffer = 1.05;
const safeAmountX = totalX * BigInt(Math.ceil(buffer * 100)) / 100n;
const safeAmountY = totalY * BigInt(Math.ceil(buffer * 100)) / 100n;
```

### Related Topics

* [Add Liquidity Overview](/integration/dlmm/typescript-sdk/add-liquidity/add-liquidity-overview) - Understanding requirements
* [SPOT Distribution](/integration/dlmm/typescript-sdk/add-liquidity/spot-distribution) - Simple calculations
* [Slippage Protection](/integration/dlmm/typescript-sdk/swap-operations/slippage-protection) - Handle price changes


# Remove Liquidity

Withdraw liquidity from DLMM positions with precision. This section covers partial and complete withdrawals, calculating expected outputs, and strategies for efficient liquidity removal while minimizing losses.

### Prerequisites

Before removing liquidity, ensure you have:

* Existing position NFT with active liquidity
* Understanding of current bin prices and reserves
* Knowledge of accumulated fees in your positions
* Gas tokens for transaction execution
* Calculated minimum acceptable output amounts

### What You'll Learn

This section covers complete liquidity removal workflows in DLMM:

#### [Remove Liquidity Overview](/integration/dlmm/typescript-sdk/remove-liquidity/remove-liquidity-overview)

Understand the mechanics of liquidity removal, including how bin reserves are calculated, fee collection during withdrawal, and impact on remaining positions.

#### [Remove from Specific Bins](#remove-from-specific-bins)

Selectively withdraw liquidity from individual bins while maintaining positions in others. Perfect for rebalancing or taking partial profits.

#### [Remove All Liquidity](#remove-all-liquidity)

Complete position withdrawal in a single transaction. Learn to efficiently exit positions and collect all accumulated fees simultaneously.

#### [Calculate Output Amounts](/integration/dlmm/typescript-sdk/remove-liquidity/calculate-output-amounts)

Preview exact token amounts you'll receive before executing withdrawal. Account for current bin reserves, fees, and price movements since deposit.


# Remove Liquidity Overview

Understand how to withdraw liquidity from DLMM positions, collect accumulated fees, and manage partial or complete exits from pools.

### What is Removing Liquidity?

In DLMM, removing liquidity means:

* Withdrawing tokens from specific bins
* Collecting accumulated trading fees
* Reducing or closing your position
* Receiving tokens based on current bin reserves

### Core Concepts

#### Partial vs Complete Removal

* **Partial**: Remove from selected bins only
* **Complete**: Withdraw all liquidity and fees
* **Close Position**: Complete removal + burn NFT

#### Token Returns

You receive tokens based on:

* Your share of each bin's reserves
* Accumulated fees in those bins
* Current price (may differ from entry)

#### Fee Collection

* Fees are automatically included in removal
* No separate claim transaction needed
* Proportional to your liquidity share

### The Workflow

#### 1. Check Position Status

```typescript
const bins = await sdk.Position.getPositionBins(pair, positionId);
const amounts = await sdk.Position.getPositionBinsAmount(pair, positionId);
```

#### 2. Calculate Expected Output

Preview what you'll receive:

```typescript
// Check each bin's value
amounts.forEach(bin => {
  console.log(`Bin ${bin.id}: ${bin.amountX} X, ${bin.amountY} Y`);
});
```

#### 3. Execute Removal

Two main options:

* Remove from specific bins
* Remove all and close position

### Removal Strategies

#### Range Exit

Remove liquidity outside active range:

```
Before: ████ ████ ████ ████ ████
After:  ████ ████ [active] 
        (removed)         (removed)
```

#### Profit Taking

Remove from bins with accumulated fees:

```
Fees:   Low  Low  HIGH  Low  Low
Action: Keep Keep REMOVE Keep Keep
```

#### Rebalancing

Remove and re-add with new distribution:

1. Remove from current bins
2. Calculate new optimal range
3. Add with updated strategy

### What You Receive

For each bin removed:

```
Your tokens = (Your liquidity / Total liquidity) × (Reserves + Fees)
```

Example:

* Your liquidity: 1000
* Total in bin: 10000
* Bin reserves: 50000 X, 100000 Y
* Your share: 10% of everything

### Important Considerations

#### Price Impact

* Current price may differ from entry
* Can result in more of one token
* This is impermanent loss realized

#### Gas Optimization

* Removing many bins costs more gas
* Consider batching operations
* Complete removal is gas-efficient

#### Timing

* No lock periods in DLMM
* Remove anytime markets are open
* Consider fee accumulation rates

### Common Patterns

#### Emergency Exit

```typescript
// Quick complete withdrawal
await sdk.Pair.removeAndClosePosition(pair, positionId);
```

#### Selective Removal

```typescript
// Remove only profitable bins
const profitableBins = bins.filter(b => b.fees > threshold);
await sdk.Pair.removeLiquidity(pair, {
  positionId,
  binIds: profitableBins.map(b => b.id)
});
```

#### Fee Harvesting

```typescript
// Remove and re-add to collect fees
// 1. Remove all
// 2. Add back immediately
```

### Next Steps

Choose your removal approach:

1. **Partial Removal**: [Remove from Specific Bins](/integration/dlmm/typescript-sdk/remove-liquidity/remove-from-specific-bins)
2. **Complete Exit**: [Remove All Liquidity](/integration/dlmm/typescript-sdk/remove-liquidity/remove-all-liquidity)
3. **Preview First**: [Calculate Output Amounts](/integration/dlmm/typescript-sdk/remove-liquidity/calculate-output-amounts)

### Related Topics

* [Position Management](/integration/dlmm/typescript-sdk/position-management) - Check bins
* [Close Position](/integration/dlmm/typescript-sdk/position-management/close-position) - Remove and burn
* [Calculate Position Fees](/integration/dlmm/typescript-sdk/fees-and-analytics/calculate-position-fees) - Track earnings


# Remove from Specific Bins

Selectively withdraw liquidity from individual bins while maintaining positions in others. Perfect for rebalancing, profit-taking, or adjusting your range.

### Prerequisites

* Position ID with active liquidity
* List of bin IDs to remove from
* Understanding of current bin distribution
* Gas for transaction execution

### Basic Usage

```typescript
const pair = await sdk.Pair.getPair(pairAddress);

// Remove from specific bins only
const tx = await sdk.Pair.removeLiquidity(pair, {
  positionId: "0x123...abc",
  binIds: [8388606, 8388607, 8388609]  // Selected bins
});

await sdk.fullClient.signAndExecuteTransaction({
  transaction: tx,
  signer: keypair
});
```

### Method Parameters

```typescript
interface RemoveLiquidityParams {
  positionId: string;    // Your position NFT
  binIds: number[];      // Array of bin IDs to remove
}
```

### Common Strategies

#### Remove Out-of-Range Bins

```typescript
// Get current position bins
const bins = await sdk.Position.getPositionBins(pair, positionId);
const activeId = pair.parameters.active_id;

// Find bins far from current price
const outOfRangeBins = bins
  .filter(bin => Math.abs(bin.id - activeId) > 10)
  .map(bin => bin.id);

// Remove distant liquidity
await sdk.Pair.removeLiquidity(pair, {
  positionId,
  binIds: outOfRangeBins
});
```

#### Harvest High-Fee Bins

```typescript
// Get bins with accumulated fees
const binsWithAmounts = await sdk.Position.getPositionBinsAmount(pair, positionId);

// Find bins with significant fees
const highFeeBins = binsWithAmounts
  .filter(bin => {
    const fees = calculateBinFees(bin);
    return fees > FEE_THRESHOLD;
  })
  .map(bin => bin.id);

// Collect from profitable bins
await sdk.Pair.removeLiquidity(pair, {
  positionId,
  binIds: highFeeBins
});
```

#### Rebalance Toward Active Price

```typescript
const activeId = pair.parameters.active_id;
const bins = await sdk.Position.getPositionBins(pair, positionId);

// Remove from one side to rebalance
const removeBins = bins
  .filter(bin => bin.id < activeId - 5)  // Remove below
  .map(bin => bin.id);

await sdk.Pair.removeLiquidity(pair, {
  positionId,
  binIds: removeBins
});
```

### What Happens

1. **Liquidity Burned**: Your shares in selected bins are burned
2. **Tokens Returned**: Receive proportional reserves + fees
3. **Position Updated**: Remaining bins stay untouched
4. **Fees Collected**: Accumulated fees automatically included

### Calculate Before Removing

```typescript
// Preview removal amounts
const binsToRemove = [8388606, 8388607];
const amounts = await sdk.Position.getPositionBinsAmount(pair, positionId);

const totalOut = amounts
  .filter(bin => binsToRemove.includes(bin.id))
  .reduce((acc, bin) => ({
    x: acc.x + bin.amountX,
    y: acc.y + bin.amountY
  }), { x: 0n, y: 0n });

console.log("Will receive:", totalOut);
```

### Gas Optimization

```typescript
// Batch remove many bins efficiently
const allBinIds = bins.map(b => b.id);

// More efficient than multiple transactions
await sdk.Pair.removeLiquidity(pair, {
  positionId,
  binIds: allBinIds  // Remove all at once
});
```

### Important Notes

* Cannot remove from bins without liquidity
* Fees are included automatically
* Position NFT remains active
* Can add back to same bins later

### Related Topics

* [Remove All Liquidity](/integration/dlmm/typescript-sdk/remove-liquidity/remove-all-liquidity) - Complete withdrawal
* [Get Position Bins](/integration/dlmm/typescript-sdk/position-management/get-position-bins) - Check bins first
* [Calculate Output Amounts](/integration/dlmm/typescript-sdk/remove-liquidity/calculate-output-amounts) - Preview returns


# Remove All Liquidity

Withdraw all liquidity from a position in a single transaction while keeping the position NFT active. Useful for complete exits without burning the NFT.

### Prerequisites

* Position ID with active liquidity
* Understanding this keeps NFT alive
* Gas for transaction execution
* Consider `removeAndClosePosition` if burning NFT

### Basic Usage

```typescript
// Get all bins for the position
const bins = await sdk.Position.getPositionBins(pair, positionId);
const allBinIds = bins.map(bin => bin.id);

// Remove from all bins at once
const tx = await sdk.Pair.removeLiquidity(pair, {
  positionId: "0x123...abc",
  binIds: allBinIds
});

await sdk.fullClient.signAndExecuteTransaction({
  transaction: tx,
  signer: keypair
});
```

### Complete Withdrawal Pattern

```typescript
async function removeAllLiquidity(pair: LBPair, positionId: string) {
  // Fetch current bins
  const bins = await sdk.Position.getPositionBins(pair, positionId);
  
  if (bins.length === 0) {
    console.log("Position already empty");
    return;
  }
  
  // Remove all liquidity
  const tx = await sdk.Pair.removeLiquidity(pair, {
    positionId,
    binIds: bins.map(b => b.id)
  });
  
  return await sdk.fullClient.signAndExecuteTransaction({
    transaction: tx,
    signer: keypair
  });
}
```

### Preview Total Returns

```typescript
// Calculate what you'll receive
const amounts = await sdk.Position.getPositionBinsAmount(pair, positionId);

const totals = amounts.reduce((sum, bin) => ({
  tokenX: sum.tokenX + bin.amountX,
  tokenY: sum.tokenY + bin.amountY
}), { tokenX: 0n, tokenY: 0n });

console.log("Total withdrawal:", {
  tokenX: formatUnits(totals.tokenX, 18),
  tokenY: formatUnits(totals.tokenY, 6)
});
```

### Remove All vs Close Position

#### Remove All Liquidity

```typescript
// Keeps position NFT alive
await sdk.Pair.removeLiquidity(pair, {
  positionId,
  binIds: allBinIds
});
// ✅ NFT still exists (empty)
// ✅ Can add liquidity again
// ❌ Two transactions if closing later
```

#### Remove AND Close

```typescript
// One transaction - removes all + burns NFT
await sdk.Pair.removeAndClosePosition(pair, positionId);
// ✅ Single transaction
// ✅ Clean exit
// ❌ Need new NFT to re-enter
```

### Common Use Cases

#### Temporary Exit

```typescript
// Market uncertainty - exit but keep position
const bins = await sdk.Position.getPositionBins(pair, positionId);
await sdk.Pair.removeLiquidity(pair, {
  positionId,
  binIds: bins.map(b => b.id)
});
// Can re-enter later with same NFT
```

#### Fee Collection

```typescript
// Remove all to collect fees, then re-add
// Step 1: Remove everything
await removeAllLiquidity(pair, positionId);

// Step 2: Add back with new distribution
await sdk.Pair.addLiquidity(pair, {
  positionId,  // Same position
  // ... new parameters
});
```

### What You Receive

* All tokens from all bins
* Accumulated trading fees
* Based on current bin reserves
* Single transfer to your wallet

### Error Handling

```typescript
try {
  const bins = await sdk.Position.getPositionBins(pair, positionId);
  
  if (bins.length === 0) {
    throw new Error("No liquidity to remove");
  }
  
  await sdk.Pair.removeLiquidity(pair, {
    positionId,
    binIds: bins.map(b => b.id)
  });
  
} catch (error) {
  console.error("Removal failed:", error);
}
```

### Related Topics

* [Close Position](/integration/dlmm/typescript-sdk/position-management/close-position) - Remove + burn NFT
* [Calculate Output Amounts](/integration/dlmm/typescript-sdk/remove-liquidity/calculate-output-amounts) - Preview returns
* [Remove from Specific Bins](/integration/dlmm/typescript-sdk/remove-liquidity/remove-from-specific-bins) - Partial removal
* [Add to Existing Position](/integration/dlmm/typescript-sdk/add-liquidity/add-to-existing-position) - Re-add later


# Calculate Output Amounts

Preview exact token amounts you'll receive before removing liquidity. Essential for setting expectations and avoiding surprises from price movements.

### Prerequisites

* Position ID to analyze
* Understanding of bin reserves and fees
* Current pair state data

### Basic Calculation

```typescript
// Get amounts for all bins in position
const amounts = await sdk.Position.getPositionBinsAmount(pair, positionId);

// Calculate total returns
const totals = amounts.reduce((sum, bin) => ({
  x: sum.x + bin.amountX,
  y: sum.y + bin.amountY
}), { x: 0n, y: 0n });

console.log("Will receive:", {
  tokenX: formatUnits(totals.x, 18),
  tokenY: formatUnits(totals.y, 6)
});
```

### Understanding the Calculation

For each bin, you receive:

```
Your Amount = (Your Liquidity / Total Supply) × (Reserves + Fees)
```

The SDK's `getPositionBinsAmount` handles this automatically.

### Preview Specific Bins

```typescript
// Calculate for selected bins only
const binsToRemove = [8388606, 8388607, 8388608];
const amounts = await sdk.Position.getPositionBinsAmount(pair, positionId);

const selectedAmounts = amounts
  .filter(bin => binsToRemove.includes(bin.id))
  .reduce((sum, bin) => ({
    x: sum.x + bin.amountX,
    y: sum.y + bin.amountY
  }), { x: 0n, y: 0n });

console.log("Selected bins will return:", selectedAmounts);
```

### Manual Calculation Method

```typescript
import { getAmountOutOfBin } from '@ferra-labs/dlmm';

// For precise control
async function calculateBinReturns(
  pair: LBPair,
  positionId: string,
  binId: number
) {
  // Get position's liquidity in bin
  const positionBins = await sdk.Position.getPositionBins(pair, positionId);
  const positionLiquidity = positionBins.find(b => b.id === binId)?.liquidity || 0n;
  
  // Get bin reserves and total supply
  const reserves = await sdk.Pair.getPairReserves(pair);
  const binReserve = reserves.find(r => r.id === binId);
  const totalSupply = await getTotalSupply(pair, binId);
  
  // Calculate output
  return getAmountOutOfBin(binReserve, positionLiquidity, totalSupply);
}
```

### Compare to Initial Investment

```typescript
// Track profit/loss
const currentAmounts = await sdk.Position.getPositionBinsAmount(pair, positionId);
const currentValue = {
  x: currentAmounts.reduce((sum, b) => sum + b.amountX, 0n),
  y: currentAmounts.reduce((sum, b) => sum + b.amountY, 0n)
};

// Compare to initial (stored elsewhere)
const pnl = {
  tokenX: currentValue.x - initialDeposit.x,
  tokenY: currentValue.y - initialDeposit.y
};

console.log("P&L:", pnl);
```

### Include Price Impact

```typescript
// Consider current price vs entry
const currentPrice = getPriceFromBinId(
  pair.parameters.active_id,
  Number(pair.binStep)
);

// Value in quote token
const totalValueUSD = 
  Number(totals.x) * currentPrice + 
  Number(totals.y);

console.log("Total value:", totalValueUSD);
```

### Preview with Slippage

```typescript
// Add safety margin for price movements
const slippage = 0.01; // 1%

const minAmounts = {
  x: totals.x * BigInt(Math.floor((1 - slippage) * 1000)) / 1000n,
  y: totals.y * BigInt(Math.floor((1 - slippage) * 1000)) / 1000n
};

console.log("Minimum expected (1% slippage):", minAmounts);
```

### Common Patterns

#### Full Position Preview

```typescript
async function previewFullRemoval(pair: LBPair, positionId: string) {
  const amounts = await sdk.Position.getPositionBinsAmount(pair, positionId);
  
  return {
    totalX: amounts.reduce((sum, b) => sum + b.amountX, 0n),
    totalY: amounts.reduce((sum, b) => sum + b.amountY, 0n),
    binCount: amounts.length,
    bins: amounts.map(b => ({
      id: b.id,
      amountX: b.amountX,
      amountY: b.amountY
    }))
  };
}
```

### Important Notes

* Amounts include accumulated fees
* Price changes affect token ratios
* Calculation assumes no other trades
* Always use recent data

### Related Topics

* [Remove from Specific Bins](/integration/dlmm/typescript-sdk/remove-liquidity/remove-from-specific-bins) - Execute removal
* [Position Value](/integration/dlmm/typescript-sdk/fees-and-analytics/position-value) - Current worth
* [Calculate Position Fees](/integration/dlmm/typescript-sdk/fees-and-analytics/calculate-position-fees) - Fee portion
* [Get Position Bins](/integration/dlmm/typescript-sdk/position-management/get-position-bins) - Check liquidity


# Swap Operations

Execute token swaps through DLMM's bin-based liquidity system. This section covers swap execution, direction handling, output calculations, and advanced features like price impact analysis and slippage protection for optimal trading experiences.

### Prerequisites

Before performing swaps, ensure you have:

* Sufficient balance of the input token
* Understanding of X→Y and Y→X swap directions
* Knowledge of bin-based price impact
* Familiarity with slippage concepts
* Connected wallet with gas for transactions

### What You'll Learn

This section covers comprehensive swap operations in DLMM:

#### [Execute Swap](/integration/dlmm/typescript-sdk/swap-operations/execute-swap)

Perform token swaps through DLMM pairs. Learn transaction building, input validation, and how swaps traverse through multiple bins to fill orders.

#### [Swap Direction](/integration/dlmm/typescript-sdk/swap-operations/swap-direction)

Master the difference between X→Y and Y→X swaps, token ordering conventions, and how swap direction affects price calculations and fee structures.

#### [Calculate Swap Output](/integration/dlmm/typescript-sdk/swap-operations/calculate-swap-output)

Preview exact output amounts before executing swaps. Simulate trades through bins, account for fees, and optimize trade sizes for best execution.

#### [Price Impact](/integration/dlmm/typescript-sdk/swap-operations/price-impact)

Calculate and analyze price impact across affected bins. Understand how trade size moves through liquidity tiers and impacts final execution price.

#### [Slippage Protection](/integration/dlmm/typescript-sdk/swap-operations/slippage-protection)

Set minimum output amounts to protect against MEV and price movements. Learn optimal slippage tolerances for different market conditions and trade sizes.


# Execute Swap

Perform token swaps through DLMM pairs using the bin-based liquidity system. Swaps traverse through multiple bins as needed to fill the order.

### Prerequisites

* Valid sender address with tokens
* Pair address for trading
* Sufficient balance of input token
* Gas for transaction execution

### Basic Swap

```typescript
const pair = await sdk.Pair.getPair(pairAddress);

// Swap 1 SUI for USDC
const tx = await sdk.Swap.prepareSwap(pair, {
  amount: 1000000000n,  // 1 SUI (9 decimals)
  xtoy: true,           // SUI (X) to USDC (Y)
  recipient: userAddress // Optional, defaults to sender
});

await sdk.fullClient.signAndExecuteTransaction({
  transaction: tx,
  signer: keypair
});
```

### Swap Parameters

```typescript
interface PrepareSwapParams {
  amount: bigint;        // Input token amount
  xtoy: boolean;         // Direction: true = X→Y, false = Y→X
  recipient?: string;    // Receiver address (optional)
}
```

### Swap Directions

#### X to Y Swap

```typescript
// Token X → Token Y
const swapXtoY = await sdk.Swap.prepareSwap(pair, {
  amount: inputAmount,
  xtoy: true
});
```

#### Y to X Swap

```typescript
// Token Y → Token X
const swapYtoX = await sdk.Swap.prepareSwap(pair, {
  amount: inputAmount,
  xtoy: false
});
```

### Understanding Token Order

```typescript
// Check pair token order
console.log("Token X:", pair.tokenXType); // e.g., "0x2::sui::SUI"
console.log("Token Y:", pair.tokenYType); // e.g., "0x...::usdc::USDC"

// X is always the "smaller" address
// SDK handles this automatically
```

### Complete Swap Example

```typescript
async function executeSwap(
  pairAddress: string,
  inputAmount: bigint,
  isXtoY: boolean
) {
  try {
    // Get pair info
    const pair = await sdk.Pair.getPair(pairAddress);
    
    // Prepare swap transaction
    const tx = await sdk.Swap.prepareSwap(pair, {
      amount: inputAmount,
      xtoy: isXtoY,
    });
    
    // Execute
    const result = await sdk.fullClient.signAndExecuteTransaction({
      transaction: tx,
      signer: keypair
    });
    
    console.log("Swap completed:", result.digest);
    return result;
    
  } catch (error) {
    console.error("Swap failed:", error);
    throw error;
  }
}
```

### Swap with Custom Recipient

```typescript
// Send output to different address
const tx = await sdk.Swap.prepareSwap(pair, {
  amount: swapAmount,
  xtoy: true,
  recipient: "0x456..." // Different wallet
});
```

### How Swaps Work

1. **Start at active bin**: Current trading price
2. **Consume liquidity**: Use available reserves
3. **Move to next bin**: If more needed
4. **Continue until filled**: Or liquidity exhausted

```
Before: [Bin-2][Bin-1][Active][Bin+1][Bin+2]
Swap:    →→→→→→→→→→→→→→→→→→→→→→→
After:  [Bin-2][Bin-1][Empty][Active][Bin+2]
```

### Common Patterns

#### Swap Exact Input

```typescript
// Know input amount, accept any output
const exactInput = 1000000000n; // 1 token

const tx = await sdk.Swap.prepareSwap(pair, {
  amount: exactInput,
  xtoy: true
});
```

#### Check Balance First

```typescript
// Verify sufficient balance
const balance = await getTokenBalance(userAddress, tokenType);

if (balance < swapAmount) {
  throw new Error("Insufficient balance");
}

const tx = await sdk.Swap.prepareSwap(pair, {
  amount: swapAmount,
  xtoy: true
});
```

### Error Handling

```typescript
try {
  const tx = await sdk.Swap.prepareSwap(pair, params);
  // Execute transaction
} catch (error) {
  if (error.message.includes("Invalid sender")) {
    console.error("Set sender address first");
  } else if (error.message.includes("Insufficient")) {
    console.error("Not enough input tokens");
  }
}
```

### Gas Optimization

* Swaps through fewer bins cost less gas
* Large swaps may traverse many bins
* Consider splitting very large swaps
* Active bin swaps are most efficient

### Related Topics

* [Swap Direction](/integration/dlmm/typescript-sdk/swap-operations/swap-direction) - Understanding X→Y vs Y→X
* [Calculate Swap Output](/integration/dlmm/typescript-sdk/swap-operations/calculate-swap-output) - Preview results
* [Price Impact](/integration/dlmm/typescript-sdk/swap-operations/price-impact) - Large swap effects
* [Slippage Protection](/integration/dlmm/typescript-sdk/swap-operations/slippage-protection) - Set minimum output


# Swap Direction

Understanding X→Y vs Y→X swaps in DLMM. Token ordering determines swap direction and affects how you interact with pairs.

### Token Ordering in DLMM

In every pair:

* **Token X**: The token with the smaller address (sorted)
* **Token Y**: The token with the larger address
* This ordering is **permanent** and **automatic**

```typescript
// Example ordering
TokenX: "0x2::sui::SUI"        // Smaller address
TokenY: "0xabc...::usdc::USDC" // Larger address

// SUI is always X, USDC is always Y in this pair
```

### Understanding Swap Directions

#### X→Y Swap (xtoy = true)

* Selling Token X
* Buying Token Y
* Price moves UP (more Y per X)

#### Y→X Swap (xtoy = false)

* Selling Token Y
* Buying Token X
* Price moves DOWN (less Y per X)

### Checking Token Order

```typescript
const pair = await sdk.Pair.getPair(pairAddress);

console.log("Token X:", pair.tokenXType);
console.log("Token Y:", pair.tokenYType);

// Determine what xtoy means for this pair
if (pair.tokenXType.includes("SUI")) {
  console.log("xtoy=true: Sell SUI for USDC");
  console.log("xtoy=false: Sell USDC for SUI");
}
```

### Common Patterns

#### Swap by Token Type

```typescript
async function swapTokens(
  pair: LBPair,
  inputToken: string,
  amount: bigint
) {
  // Determine direction based on input token
  const xtoy = inputToken === pair.tokenXType;
  
  return await sdk.Swap.prepareSwap(pair, {
    amount,
    xtoy
  });
}
```

#### User-Friendly Wrapper

```typescript
// Helper for intuitive swapping
async function swap(
  pair: LBPair,
  fromToken: string,
  toToken: string,
  amount: bigint
) {
  // Validate tokens belong to pair
  const hasTokens = 
    (fromToken === pair.tokenXType || fromToken === pair.tokenYType) &&
    (toToken === pair.tokenXType || toToken === pair.tokenYType);
    
  if (!hasTokens) {
    throw new Error("Invalid token pair");
  }
  
  // Set direction
  const xtoy = fromToken === pair.tokenXType;
  
  return await sdk.Swap.prepareSwap(pair, {
    amount,
    xtoy
  });
}
```

### Price Movement

```typescript
// How swaps affect price
if (xtoy) {
  // Selling X for Y
  // Depletes Y reserves in bins
  // Price moves to higher bins
  // X becomes cheaper relative to Y
} else {
  // Selling Y for X
  // Depletes X reserves in bins
  // Price moves to lower bins
  // X becomes more expensive relative to Y
}
```

### Quick Reference

| Want to   | Token In | Token Out | Use           |
| --------- | -------- | --------- | ------------- |
| Buy SUI   | USDC (Y) | SUI (X)   | `xtoy: false` |
| Sell SUI  | SUI (X)  | USDC (Y)  | `xtoy: true`  |
| Buy USDC  | SUI (X)  | USDC (Y)  | `xtoy: true`  |
| Sell USDC | USDC (Y) | SUI (X)   | `xtoy: false` |

### Common Mistakes

```typescript
// ❌ Wrong: Assuming token order
const tx = await sdk.Swap.prepareSwap(pair, {
  amount,
  xtoy: true // Assumes first token is X
});

// ✅ Correct: Check actual order
const sellingTokenX = inputToken === pair.tokenXType;
const tx = await sdk.Swap.prepareSwap(pair, {
  amount,
  xtoy: sellingTokenX
});
```

### Related Topics

* [Execute Swap](/integration/dlmm/typescript-sdk/swap-operations/execute-swap) - Perform swaps
* [Get Single Pair](/integration/dlmm/typescript-sdk/trading-pairs/get-single-pair) - Check token order
* [Calculate Swap Output](/integration/dlmm/typescript-sdk/swap-operations/calculate-swap-output) - Preview results


# Calculate Swap Output

The `calculateRates` function is used to estimate the result of a token swap within Ferra’s **Discrete Liquidity Market Maker (DLMM)** system. It calculates how much output you would receive from swapping a given amount, along with estimated fees and liquidity availability.

### Prerequisites

* Pair object with current state
* Input amount and direction

### Basic Output Calculation

```typescript
// Calculate output for a swap
const result = sdk.Swap.calculateRates(pair, {
  amount: bigint,
  swapBins: LbPairBinData[],
  xtoy: boolean,
});
```

### Example

```typescript
async function estimateSwap() {
    const pair = await sdk.Pair.getPair(pairId);
    const bins = await sdk.Pair.getPairBinsData(pairId);
    const decimals = a2b ? coinA.decimals : coinB.decimals;
    const parsedAmount = parseUnits(amount.toString(), decimals);

    const result = sdk.Swap.calculateRates(pair, {
      amount: parsedAmount,
      swapBins: bins,
      xtoy: a2b,
    });
}
```

| Name       | Type              | Description                                                                                                            |
| ---------- | ----------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `pair`     | `DLMM.Pair`       | The liquidity pair object, retrieved via `getPair(pairId)`                                                             |
| `amount`   | `bigint`          | The amount to swap, expressed in smallest units (e.g., wei). Use `parseUnits()` to convert from human-readable numbers |
| `swapBins` | `LbPairBinData[]` | The list of liquidity bins for the pair, retrieved via `getPairBinsData(pairId)`                                       |
| `xtoy`     | `boolean`         | Direction of the swap. `true` means swap token X to Y (A to B), `false` for the reverse                                |

| Property             | Type      | Description                                                        |
| -------------------- | --------- | ------------------------------------------------------------------ |
| `amount`             | `bigint`  | Input amount (as provided)                                         |
| `estimatedAmountIn`  | `bigint`  | Estimated actual input required to complete the swap               |
| `estimatedAmountOut` | `bigint`  | Estimated amount received from the swap                            |
| `estimatedFeeAmount` | `bigint`  | Estimated fee deducted during the swap                             |
| `isExceed`           | `boolean` | Indicates whether the requested amount exceeds available liquidity |
| `estimatedEndBinId`  | `bigint`  | The estimated ending square root price                             |
| `isMaxLoop`          | `boolean` | Indicates if the estimated amount exceeds the limit                |
| `priceImpactPct`     | `number`  | The price impact percentage                                        |

### Important Notes

* Output depends on current bin liquidity
* Large swaps may have significant impact
* Always include fees in calculations

### Related Topics

* [Execute Swap](/integration/dlmm/typescript-sdk/swap-operations/execute-swap) - Perform the swap
* [Price Impact](/integration/dlmm/typescript-sdk/swap-operations/price-impact) - Calculate market impact
* [Slippage Protection](#slippage-calculation) - Set safety limits
* [Get Pair Bins](/integration/dlmm/typescript-sdk/trading-pairs/get-pair-bins) - Check liquidity


# Price Impact

Calculate how your swap affects price as it consumes liquidity across multiple bins. Essential for understanding large trade costs and market impact.

### What is Price Impact?

Price impact measures the difference between:

* **Spot Price**: Current price at active bin
* **Execution Price**: Average price after swap

```
Small swap:  Uses 1 bin   → Low impact
Large swap:  Uses 5 bins  → High impact
```

### Basic Impact Calculation

```typescript
async function calculatePriceImpact(
  pair: LBPair,
  amountIn: bigint,
  xtoy: boolean
): Promise<number> {
  // Get spot price
  const spotPrice = getPriceFromBinId(
    pair.parameters.active_id,
    Number(pair.binStep)
  );
  
  // Calculate output
  const amountOut = await calculateSwapOutput(pair, amountIn, xtoy);
  
  // Calculate execution price
  const executionPrice = xtoy
    ? Number(amountOut) / Number(amountIn)
    : Number(amountIn) / Number(amountOut);
  
  // Price impact percentage
  return ((executionPrice - spotPrice) / spotPrice) * 100;
}
```

### Bin-by-Bin Analysis

```typescript
async function analyzeBinImpact(
  pair: LBPair,
  amountIn: bigint,
  xtoy: boolean
) {
  const activeId = pair.parameters.active_id;
  const binStep = Number(pair.binStep);
  
  // Get affected bins
  const binRange = xtoy 
    ? [activeId, activeId + 50]
    : [activeId - 50, activeId];
    
  const bins = await sdk.Pair.getPairBins(pair, binRange);
  
  let remaining = amountIn;
  let totalOut = 0n;
  const binImpacts = [];
  
  for (let i = 0; i < bins.length; i++) {
    const binId = binRange[0] + i;
    const bin = bins[i];
    const binPrice = getPriceFromBinId(binId, binStep);
    
    // How much liquidity consumed from this bin
    const available = xtoy ? bin.reserve_y : bin.reserve_x;
    const consumed = remaining > available ? available : remaining;
    
    if (consumed > 0n) {
      binImpacts.push({
        binId,
        price: binPrice,
        consumed: consumed.toString(),
        percentOfTotal: (Number(consumed) / Number(amountIn) * 100).toFixed(2)
      });
      
      remaining -= consumed;
      totalOut += consumed; // Simplified
    }
    
    if (remaining === 0n) break;
  }
  
  return {
    binsUsed: binImpacts.length,
    startPrice: binImpacts[0]?.price,
    endPrice: binImpacts[binImpacts.length - 1]?.price,
    details: binImpacts
  };
}
```

### Impact Thresholds

```typescript
function categorizeImpact(impactPercent: number): string {
  if (impactPercent < 0.1) return "Negligible";
  if (impactPercent < 0.5) return "Low";
  if (impactPercent < 1.0) return "Moderate";
  if (impactPercent < 3.0) return "High";
  return "Severe";
}

// Warning system
async function checkSwapViability(
  pair: LBPair,
  amountIn: bigint,
  xtoy: boolean
) {
  const impact = await calculatePriceImpact(pair, amountIn, xtoy);
  
  return {
    impact: impact.toFixed(2) + "%",
    category: categorizeImpact(Math.abs(impact)),
    warning: Math.abs(impact) > 1.0 ? "Consider smaller trade size" : null
  };
}
```

### Depth Chart Analysis

```typescript
// Calculate cumulative depth and impact
async function getMarketDepth(
  pair: LBPair,
  xtoy: boolean,
  maxBins: number = 50
) {
  const activeId = pair.parameters.active_id;
  const binRange = xtoy
    ? [activeId, activeId + maxBins]
    : [activeId - maxBins, activeId];
    
  const bins = await sdk.Pair.getPairBins(pair, binRange);
  
  let cumulativeLiquidity = 0n;
  const depthLevels = [];
  
  for (let i = 0; i < bins.length; i++) {
    const bin = bins[i];
    const liquidity = xtoy ? bin.reserve_y : bin.reserve_x;
    cumulativeLiquidity += liquidity;
    
    const impact = await calculatePriceImpact(
      pair,
      cumulativeLiquidity,
      xtoy
    );
    
    depthLevels.push({
      cumulative: cumulativeLiquidity,
      impactPercent: impact,
      binId: binRange[0] + i
    });
  }
  
  return depthLevels;
}
```

### Optimal Trade Size

```typescript
// Find max trade size for target impact
async function findOptimalTradeSize(
  pair: LBPair,
  maxImpactPercent: number,
  xtoy: boolean
): Promise<bigint> {
  const testAmounts = [
    parseEther("0.1"),
    parseEther("1"),
    parseEther("10"),
    parseEther("100"),
    parseEther("1000")
  ];
  
  let optimalAmount = 0n;
  
  for (const amount of testAmounts) {
    const impact = await calculatePriceImpact(pair, amount, xtoy);
    
    if (Math.abs(impact) <= maxImpactPercent) {
      optimalAmount = amount;
    } else {
      break;
    }
  }
  
  return optimalAmount;
}
```

### Real-Time Display

```typescript
// User-friendly impact display
async function displayPriceImpact(
  pair: LBPair,
  inputAmount: string,
  decimals: number,
  xtoy: boolean
) {
  const amount = parseUnits(inputAmount, decimals);
  const analysis = await analyzeBinImpact(pair, amount, xtoy);
  const impact = await calculatePriceImpact(pair, amount, xtoy);
  
  return {
    priceImpact: impact.toFixed(2) + "%",
    binsAffected: analysis.binsUsed,
    priceRange: {
      start: analysis.startPrice?.toFixed(4),
      end: analysis.endPrice?.toFixed(4)
    },
    recommendation: Math.abs(impact) > 2 
      ? "Consider splitting into smaller trades"
      : "Acceptable impact"
  };
}
```

### Key Insights

* **Linear in bins**: Each bin adds similar impact
* **Exponential in size**: Doubling size > doubles impact
* **Direction matters**: Buy/sell impacts differ
* **Time sensitive**: Impact changes with liquidity

### Related Topics

* [Calculate Swap Output](/integration/dlmm/typescript-sdk/swap-operations/calculate-swap-output) - Get exact amounts
* [Get Pair Bins](/integration/dlmm/typescript-sdk/trading-pairs/get-pair-bins) - Check liquidity
* [Slippage Protection](/integration/dlmm/typescript-sdk/swap-operations/slippage-protection) - Set safety limits


# Slippage Protection

Set minimum output amounts to protect swaps from price movements between quote and execution. Essential for safe trading in volatile markets.

### What is Slippage?

Slippage occurs when:

* Price moves between quote and execution
* Other trades consume liquidity
* Network delays cause stale quotes

Protection ensures you receive at least a minimum amount.

### Basic Implementation

```typescript
// Calculate minimum output with slippage tolerance
function calculateMinimumOutput(
  expectedOutput: bigint,
  slippagePercent: number = 0.5 // 0.5% default
): bigint {
  const slippageFactor = 10000 - Math.floor(slippagePercent * 100);
  return (expectedOutput * BigInt(slippageFactor)) / 10000n;
}

// Usage
const expectedOut = await calculateSwapOutput(pair, amountIn, xtoy);
const minimumOut = calculateMinimumOutput(expectedOut, 1.0); // 1% slippage
```

### Protected Swap Pattern

```typescript
async function swapWithProtection(
  pair: LBPair,
  amountIn: bigint,
  xtoy: boolean,
  slippagePercent: number = 0.5
) {
  // 1. Calculate expected output
  const expectedOutput = await calculateSwapOutput(pair, amountIn, xtoy);
  
  // 2. Set minimum acceptable
  const minOutput = calculateMinimumOutput(expectedOutput, slippagePercent);
  
  // 3. Build transaction with protection
  const tx = new Transaction();
  
  // Add swap
  const [_, coinXOut, coinYOut] = await sdk.Pair.prepareSwap(
    pair,
    { amount: amountIn, xtoy },
    tx
  );
  
  // Add minimum check (pseudo-code - actual implementation varies)
  const outputCoin = xtoy ? coinYOut : coinXOut;
  tx.moveCall({
    target: "0x2::coin::value",
    arguments: [outputCoin],
    // Assert minimum amount
  });
  
  return tx;
}
```

### Dynamic Slippage

```typescript
// Adjust slippage based on conditions
function getDynamicSlippage(
  pair: LBPair,
  tradeSize: bigint,
  volatility: number
): number {
  // Base slippage
  let slippage = 0.3;
  
  // Increase for large trades
  const avgTradeSize = getAverageTradeSize(pair);
  if (tradeSize > avgTradeSize * 10n) {
    slippage += 0.5;
  }
  
  // Increase for volatile pairs
  if (volatility > 0.05) { // 5% daily volatility
    slippage += 0.5;
  }
  
  // Cap at reasonable maximum
  return Math.min(slippage, 3.0);
}
```

### Slippage by Asset Type

```typescript
const SLIPPAGE_PRESETS = {
  stable: 0.1,      // USDC/USDT: 0.1%
  bluechip: 0.5,    // ETH/USDC: 0.5%
  volatile: 2.0,    // MEME/USDC: 2%
  illiquid: 5.0     // Low liquidity: 5%
};

function getRecommendedSlippage(
  tokenX: string,
  tokenY: string
): number {
  if (isStablePair(tokenX, tokenY)) return SLIPPAGE_PRESETS.stable;
  if (isBluechipPair(tokenX, tokenY)) return SLIPPAGE_PRESETS.bluechip;
  if (hasLowLiquidity(tokenX, tokenY)) return SLIPPAGE_PRESETS.illiquid;
  return SLIPPAGE_PRESETS.volatile;
}
```

### User-Friendly Display

```typescript
interface SlippageSettings {
  percent: number;
  minOutput: bigint;
  maxSlippage: bigint;
}

function calculateSlippageInfo(
  expectedOutput: bigint,
  slippagePercent: number
): SlippageSettings {
  const minOutput = calculateMinimumOutput(expectedOutput, slippagePercent);
  const maxSlippage = expectedOutput - minOutput;
  
  return {
    percent: slippagePercent,
    minOutput,
    maxSlippage
  };
}

// Display to user
const info = calculateSlippageInfo(expectedOut, 1.0);
console.log(`Minimum received: ${formatUnits(info.minOutput, decimals)}`);
console.log(`Max slippage: ${formatUnits(info.maxSlippage, decimals)}`);
```

### MEV Protection

```typescript
// Tight slippage for MEV protection
function getMEVProtectedSlippage(
  normalSlippage: number,
  isMEVProne: boolean
): number {
  if (isMEVProne) {
    // Tighter tolerance for sandwich protection
    return Math.min(normalSlippage * 0.5, 0.3);
  }
  return normalSlippage;
}

// High-value trades need extra protection
const needsMEVProtection = tradeValueUSD > 10000;
const slippage = getMEVProtectedSlippage(1.0, needsMEVProtection);
```

### Handling Failures

```typescript
async function swapWithRetry(
  pair: LBPair,
  amountIn: bigint,
  xtoy: boolean,
  maxRetries: number = 3
) {
  let slippage = 0.5;
  
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await swapWithProtection(pair, amountIn, xtoy, slippage);
    } catch (error) {
      if (error.message.includes("Slippage exceeded")) {
        // Increase tolerance and retry
        slippage = Math.min(slippage * 1.5, 5.0);
        console.log(`Retry with ${slippage}% slippage`);
      } else {
        throw error;
      }
    }
  }
  
  throw new Error("Max retries exceeded");
}
```

### Best Practices

1. **Default Settings**
   * 0.5% for normal conditions
   * 0.1% for stable pairs
   * 2-5% for volatile assets
2. **User Control**
   * Allow manual adjustment
   * Show impact clearly
   * Warn on high settings
3. **Auto-adjust**
   * Monitor recent volatility
   * Check liquidity depth
   * Consider trade size

### Related Topics

* [Calculate Swap Output](/integration/dlmm/typescript-sdk/swap-operations/calculate-swap-output) - Get expected amounts
* [Price Impact](/integration/dlmm/typescript-sdk/swap-operations/price-impact) - Understand trade effects
* [Execute Swap](/integration/dlmm/typescript-sdk/swap-operations/execute-swap) - Perform protected swaps


# Collect Yield

Calculate and claim accumulated rewards and fees from their DLMM positions

### Rewards Management

#### `getPositionRewards(pair, positionId, binIds)`

Calculate pending reward amounts for a position across all rewarders.

**Parameters:**

* `pair: LBPair` - The LBPair containing the position
* `positionId: string` - ID of the position to check rewards for
* `binIds: number[]` - Array of bin IDs to calculate rewards for

**Returns:** `Promise<PositionReward[]>`

**Example:**

```typescript
const rewards = await ferraSDK.Position.getPositionRewards(pair, "0x123...", [1, 2, 3]);
rewards.forEach(reward => {
  console.log(`Pending ${reward.amount} of ${reward.coinType}`);
});
```

#### `getPositionRewardsV2(pair, positionId, binIds)`

Updated version of rewards calculation with improved performance.

**Parameters:** Same as V1 **Returns:** `Promise<PositionReward[]>`

#### `claimPositionRewards(pair, positionId, binIds, tx?)`

Claim all pending rewards for a position and transfer to sender.

**Parameters:**

* `pair: LBPair` - The LBPair containing the position
* `positionId: string` - ID of the position to claim rewards for
* `binIds: number[]` - Array of bin IDs to claim rewards from
* `tx?: Transaction` - Optional existing transaction to add operations to

**Returns:** `Promise<Transaction>`

**Example:**

```typescript
const tx = await ferraSDK.Position.claimPositionRewards(pair, "0x123...", binIds);
// All pending rewards will be transferred to sender
```

#### `claimPositionRewardsV2(pair, positionId, binIds, tx?)`

Updated version of reward claiming with optimized transaction structure.

**Parameters:** Same as V1 **Returns:** `Promise<Transaction>`

### Fees Management

#### `getPositionFees(pair, positionId, binIds)`

Calculate pending fee amounts for specific bins of a position.

**Parameters:**

* `pair: LBPair` - The LBPair containing the position
* `positionId: string` - ID of the position to check fees for
* `binIds: number[]` - Array of bin IDs to calculate fees for

**Returns:** `Promise<[PositionReward, PositionReward] | null>`

Returns a tuple of `[tokenX fees, tokenY fees]` or `null` if no fees found.

**Example:**

```typescript
const fees = await ferraSDK.Position.getPositionFees(pair, "0x123...", [8388608, 8388609]);
if (fees) {
  console.log(`Fees: ${fees[0].amount} tokenX, ${fees[1].amount} tokenY`);
}
```

#### `getPositionFeesV2(pair, positionId, binIds)`

Updated version of fee calculation with improved accuracy.

**Parameters:** Same as V1 **Returns:** `Promise<[PositionReward, PositionReward] | null>`

#### `claimPositionFee(pair, positionId, binIds, tx?)`

Claim accumulated fees for specific bins of a position.

**Parameters:**

* `pair: LBPair` - The LBPair containing the position
* `positionId: string` - ID of the position to claim fees for
* `binIds: number[]` - Array of bin IDs to claim fees from
* `tx?: Transaction` - Optional existing transaction to add operations to

**Returns:** `Promise<Transaction>`

**Example:**

```typescript
const binIds = [8388608, 8388609, 8388610];
const tx = await ferraSDK.Position.claimPositionFee(pair, "0x123...", binIds);
// Fees from specified bins will be transferred to sender
```

#### `claimPositionFeeV2(pair, positionId, binIds, tx?)`

Updated version of fee claiming with optimized batching.

**Parameters:** Same as V1 **Returns:** `Promise<Transaction>`

### Data Types

#### PositionReward

```typescript
interface PositionReward {
  amount: bigint;    // Amount of tokens
  coinType: string;  // Token type identifier
}
```

### V2 Methods

The module provides V2 versions of key methods that offer:

* **Improved Performance**: Optimized transaction structures
* **Better Accuracy**: Enhanced calculation algorithms
* **Gas Optimization**: Reduced transaction costs

**When to use V2:**

* For new integrations, prefer V2 methods
* V2 methods are backward compatible
* Better suited for high-volume operations

### Transaction Batching

The module implements intelligent batching for large operations:

```typescript
const BATCH_SIZE = 1000; // Process up to 1000 bins per transaction
```

This prevents transaction size limits and optimizes gas usage.

### Error Handling

#### Common Errors

1. **"Invalid sender address"**: SDK sender address not configured
2. **"Position not found"**: Position ID doesn't exist
3. **"Position is not match with pair id"**: Position doesn't belong to specified pair

### Performance Optimization

#### Caching Strategy

The module uses internal caching to optimize performance:

```typescript
private readonly _cache: Record<string, CachedContent> = {}
```

#### RPC Batching

Uses `RpcBatcher` for efficient data fetching:

```typescript
const batcher = new RpcBatcher(async () => {
  // Batch multiple RPC calls together
});
```

### Troubleshooting

#### Debug Checklist

1. **Verify sender address is set**: `sdk.senderAddress`
2. **Check position exists**: Use `getLbPosition(positionId)`
3. **Validate bin IDs**: Ensure bins belong to the position
4. **Test with small amounts first**: Avoid large transaction failures

#### Common Solutions

* **Gas estimation failures**: Use V2 methods for better optimization
* **Empty rewards**: Position may not have accumulated rewards yet
* **Transaction timeouts**: Reduce batch sizes or split operations


# Fees & Analytics

Track performance and maximize returns from your DLMM positions. This section covers fee structures, calculating accumulated rewards, collecting earnings, and comprehensive analytics including APR calculations and profit/loss tracking.

### Prerequisites

Before analyzing fees and performance, ensure you have:

* Active positions with liquidity in DLMM pools
* Understanding of protocol fee tiers and structures
* Basic knowledge of impermanent loss concepts
* Historical position data for P\&L calculations
* Familiarity with APR/APY calculations

### What You'll Learn

This section covers comprehensive fee management and position analytics:

#### [Fee Structure](#fee-structure)

Understand how protocol fees work in bins, including base fees, dynamic fees based on volatility, and how fees accumulate in different price ranges.

#### [Calculate Position Fees](#calculate-position-fees)

Track accumulated trading fees across all your position's bins. Monitor fee generation rates and identify most profitable price ranges.

#### [Collect Fees](/integration/dlmm/typescript-sdk/fees-and-analytics/collect-fees)

Claim earned fees from positions without removing liquidity. Learn optimal collection strategies and gas-efficient fee harvesting techniques.

#### [Position Value](/integration/dlmm/typescript-sdk/fees-and-analytics/position-value)

Calculate current total value of positions including principal liquidity and accumulated fees. Track portfolio performance in real-time.

#### [Calculate APR](/integration/dlmm/typescript-sdk/fees-and-analytics/calculate-apr)

Estimate annualized returns based on fee generation, price movements, and liquidity utilization. Compare strategies and optimize position placement.


# Fee Structure

Understand how trading fees work in DLMM's bin-based system, including fee accumulation, distribution to liquidity providers, and protocol revenue sharing.

### How Fees Work in DLMM

Trading fees in DLMM:

* Charged on each swap
* Accumulate in the bins used
* Distributed proportionally to LPs
* Include base + variable components

```
Swap → Fee charged → Stored in bins → LPs collect share
```

### Fee Components

#### Base Fee

```typescript
interface PairParameters {
  base_factor: string;     // Base fee rate (e.g., 3000 = 0.3%)
  protocol_share: string;  // Protocol's share (e.g., 1000 = 10%)
}

// Example: 0.3% base fee
const baseFeePercent = Number(pair.parameters.base_factor) / 10000;
```

#### Variable Fee

Adjusts based on volatility:

```typescript
// Increases during high volatility
// Decreases during stable periods
const variableFee = calculateVariableFee(
  pair.parameters.volatility_accumulator,
  pair.parameters.max_volatility_accumulator
);

const totalFee = baseFee + variableFee;
```

### Fee Accumulation in Bins

```typescript
// Fees stored separately from reserves
interface BinReserves {
  reserve_x: bigint;    // Trading liquidity
  reserve_y: bigint;    // Trading liquidity
  fee_x: bigint;        // Accumulated fees
  fee_y: bigint;        // Accumulated fees
}

// Total bin value = reserves + fees
const totalX = bin.reserve_x + bin.fee_x;
const totalY = bin.reserve_y + bin.fee_y;
```

### LP Fee Share Calculation

```typescript
// Your share of fees in a bin
function calculateLPFeeShare(
  yourLiquidity: bigint,
  totalSupply: bigint,
  binFees: { fee_x: bigint, fee_y: bigint }
): { feeX: bigint, feeY: bigint } {
  const feeX = (yourLiquidity * binFees.fee_x) / totalSupply;
  const feeY = (yourLiquidity * binFees.fee_y) / totalSupply;
  
  return { feeX, feeY };
}
```

### Protocol Revenue

```typescript
// Protocol takes a percentage of fees
function calculateProtocolFee(
  swapFee: bigint,
  protocolShare: number // basis points
): bigint {
  return (swapFee * BigInt(protocolShare)) / 10000n;
}

// Example: 10% protocol share of 0.3% swap fee
const swapAmount = parseEther("100");
const swapFee = (swapAmount * 30n) / 10000n; // 0.3%
const protocolFee = (swapFee * 1000n) / 10000n; // 10% of fee
const lpFee = swapFee - protocolFee; // 90% to LPs
```

### Fee Tiers by Pair Type

Common configurations:

| Pair Type  | Base Fee | Typical Range |
| ---------- | -------- | ------------- |
| Stable     | 0.01%    | 0.01-0.05%    |
| Correlated | 0.05%    | 0.05-0.10%    |
| Blue-chip  | 0.20%    | 0.20-0.30%    |
| Volatile   | 0.30%    | 0.30-5.00%    |

### Viewing Pair Fees

```typescript
// Check current fee parameters
const pair = await sdk.Pair.getPair(pairAddress);

const feeInfo = {
  baseFee: Number(pair.parameters.base_factor) / 100, // basis points to percent
  protocolShare: Number(pair.parameters.protocol_share) / 100,
  currentVolatility: pair.parameters.volatility_accumulator
};

console.log(`Base fee: ${feeInfo.baseFee}%`);
console.log(`Protocol takes: ${feeInfo.protocolShare}% of fees`);
```

### Fee Collection Process

Fees are collected automatically when:

1. **Removing liquidity** - Proportional share included
2. **Closing position** - All fees withdrawn
3. **No separate claim** - Unlike some protocols

```typescript
// Fees included in removal
const removal = await sdk.Position.getPositionBinsAmount(pair, positionId);

removal.forEach(bin => {
  // amountX and amountY already include fees
  console.log(`Bin ${bin.id}: Includes accumulated fees`);
});
```

### Estimating Fee Income

```typescript
// Estimate daily fees for a position
async function estimateDailyFees(
  pair: LBPair,
  positionId: string,
  dailyVolume: bigint
): Promise<bigint> {
  const bins = await sdk.Position.getPositionBins(pair, positionId);
  const activeId = pair.parameters.active_id;
  
  // Assume volume concentrates around active bin
  const activeBin = bins.find(b => b.id === activeId);
  if (!activeBin) return 0n;
  
  const baseFee = BigInt(pair.parameters.base_factor);
  const yourShare = activeBin.liquidity; // Simplified
  
  // Rough estimate
  return (dailyVolume * baseFee * yourShare) / (10000n * totalLiquidity);
}
```

### Key Points

* Fees accumulate in bins, not positions
* Automatically collected on withdrawal
* Base + variable fee structure
* Protocol takes percentage of fees
* Higher volatility = higher fees

### Related Topics

* [Calculate Position Fees](/integration/dlmm/typescript-sdk/fees-and-analytics/calculate-position-fees) - Track your earnings
* [Collect Fees](/integration/dlmm/typescript-sdk/fees-and-analytics/collect-fees) - Claim accumulated fees
* [Calculate APR](/integration/dlmm/typescript-sdk/fees-and-analytics/calculate-apr) - Fee-based returns
* [Get Pair Reserves](/integration/dlmm/typescript-sdk/trading-pairs/get-pair-reserves) - View fee accumulation


# Calculate Position Fees

Track accumulated trading fees across all bins in your position. Monitor fee generation and calculate your earnings from providing liquidity.

### Prerequisites

* Position ID with active liquidity
* Understanding of fee structure
* Pair reserves data access

### Get Position Fees

```typescript
// Fetch position with fee data
async function getPositionFees(
  pair: LBPair,
  positionId: string
) {
  // Get reserves including fees
  const reserves = await sdk.Pair.getPairReserves(pair);
  const positionBins = await sdk.Position.getPositionBins(pair, positionId);
  
  let totalFeesX = 0n;
  let totalFeesY = 0n;
  
  for (const posBin of positionBins) {
    const binReserve = reserves.find(r => r.id === posBin.id);
    if (!binReserve) continue;
    
    // Calculate position's share of fees
    const shareOfFees = await calculateBinFeeShare(
      posBin.liquidity,
      binReserve,
      pair
    );
    
    totalFeesX += shareOfFees.feeX;
    totalFeesY += shareOfFees.feeY;
  }
  
  return { totalFeesX, totalFeesY };
}
```

### Calculate Fee Share

```typescript
async function calculateBinFeeShare(
  positionLiquidity: bigint,
  binReserve: BinReserves,
  pair: LBPair
): Promise<{ feeX: bigint, feeY: bigint }> {
  // Get total supply for this bin
  const totalSupply = await getTotalSupply(pair, binReserve.id);
  
  if (totalSupply === 0n) return { feeX: 0n, feeY: 0n };
  
  // Your share = (your liquidity / total) * fees
  const feeX = (positionLiquidity * binReserve.fee_x) / totalSupply;
  const feeY = (positionLiquidity * binReserve.fee_y) / totalSupply;
  
  return { feeX, feeY };
}
```

### Simplified Fee Tracking

```typescript
// Quick fee estimation using amounts
async function trackPositionFees(
  pair: LBPair,
  positionId: string
) {
  const amounts = await sdk.Position.getPositionBinsAmount(pair, positionId);
  const reserves = await sdk.Pair.getPairReserves(pair);
  
  // Calculate fees by comparing with initial deposit
  const fees = amounts.map(amount => {
    const bin = reserves.find(r => r.id === amount.id);
    if (!bin) return { id: amount.id, feeX: 0n, feeY: 0n };
    
    // Rough approximation: fees = total value - initial liquidity
    return {
      id: amount.id,
      estimatedFeesX: (bin.fee_x * amount.liquidity) / (bin.reserve_x + bin.fee_x),
      estimatedFeesY: (bin.fee_y * amount.liquidity) / (bin.reserve_y + bin.fee_y)
    };
  });
  
  return fees;
}
```

### Fee Performance Metrics

```typescript
// Calculate fee metrics
async function calculateFeeMetrics(
  pair: LBPair,
  positionId: string,
  positionAge: number // hours
) {
  const fees = await getPositionFees(pair, positionId);
  
  // Convert to USD value
  const price = getPriceFromBinId(
    pair.parameters.active_id,
    Number(pair.binStep)
  );
  
  const feesUSD = 
    Number(fees.totalFeesX) * price + 
    Number(fees.totalFeesY);
  
  // Calculate rates
  return {
    totalFeesX: fees.totalFeesX,
    totalFeesY: fees.totalFeesY,
    totalUSD: feesUSD,
    hourlyRate: feesUSD / positionAge,
    dailyRate: (feesUSD / positionAge) * 24,
    weeklyRate: (feesUSD / positionAge) * 24 * 7
  };
}
```

### Track Fee Generation by Bin

```typescript
// See which bins generate most fees
async function analyzeFeeGeneration(
  pair: LBPair,
  positionId: string
) {
  const amounts = await sdk.Position.getPositionBinsAmount(pair, positionId);
  const reserves = await sdk.Pair.getPairReserves(pair);
  
  const binAnalysis = amounts.map(amount => {
    const reserve = reserves.find(r => r.id === amount.id);
    const binPrice = getPriceFromBinId(amount.id, Number(pair.binStep));
    
    // Estimate fee portion
    const feeRatio = reserve 
      ? (reserve.fee_x + reserve.fee_y) / (reserve.reserve_x + reserve.reserve_y)
      : 0n;
    
    return {
      binId: amount.id,
      price: binPrice,
      feeGeneration: feeRatio,
      isActive: amount.id === pair.parameters.active_id
    };
  });
  
  // Sort by fee generation
  return binAnalysis.sort((a, b) => 
    Number(b.feeGeneration) - Number(a.feeGeneration)
  );
}
```

### Display Fee Summary

```typescript
// User-friendly fee display
async function getFeeSummary(
  pair: LBPair,
  positionId: string
) {
  const fees = await getPositionFees(pair, positionId);
  const amounts = await sdk.Position.getPositionBinsAmount(pair, positionId);
  
  // Calculate total position value
  const totalValue = amounts.reduce((sum, bin) => ({
    x: sum.x + bin.amountX,
    y: sum.y + bin.amountY
  }), { x: 0n, y: 0n });
  
  // Fee percentage of position
  const feePercentX = (Number(fees.totalFeesX) / Number(totalValue.x) * 100);
  const feePercentY = (Number(fees.totalFeesY) / Number(totalValue.y) * 100);
  
  return {
    fees: {
      tokenX: formatUnits(fees.totalFeesX, 18),
      tokenY: formatUnits(fees.totalFeesY, 6)
    },
    feePercent: {
      tokenX: feePercentX.toFixed(2) + "%",
      tokenY: feePercentY.toFixed(2) + "%"
    },
    totalValue: {
      tokenX: formatUnits(totalValue.x, 18),
      tokenY: formatUnits(totalValue.y, 6)
    }
  };
}
```

### Important Notes

* Fees accumulate in bins, not positions
* Calculation requires bin reserves data
* Fees auto-compound into liquidity
* Active bins typically generate most fees

### Related Topics

* [Fee Structure](/integration/dlmm/typescript-sdk/fees-and-analytics/fee-structure) - How fees work
* [Collect Fees](/integration/dlmm/typescript-sdk/fees-and-analytics/collect-fees) - Claim your fees
* [Position Value](/integration/dlmm/typescript-sdk/fees-and-analytics/position-value) - Total worth including fees
* [Calculate APR](/integration/dlmm/typescript-sdk/fees-and-analytics/calculate-apr) - Annualized returns


# Collect Fees

Understanding fee collection in DLMM - fees are automatically included when removing liquidity, with no separate claim mechanism required.

### How DLMM Handles Fees

Unlike some protocols, DLMM:

* **No separate claim**: Fees included in withdrawals
* **Auto-compound**: Fees increase bin reserves
* **Proportional distribution**: Based on liquidity share
* **Gas efficient**: No extra transactions needed

### Fees Are Included Automatically

```typescript
// When you remove liquidity, fees are included
const tx = await sdk.Pair.removeLiquidity(pair, {
  positionId,
  binIds: selectedBins
});

// The returned tokens include:
// 1. Your share of reserves
// 2. Your share of accumulated fees
// No separate fee claim needed!
```

### Understanding Fee Distribution

```typescript
// Fees are part of bin reserves
interface BinReserves {
  reserve_x: bigint;    // Original liquidity
  reserve_y: bigint;    // Original liquidity
  fee_x: bigint;        // Accumulated fees
  fee_y: bigint;        // Accumulated fees
}

// When removing, you get proportional share of both
const totalX = reserve_x + fee_x;  // Your withdrawal includes both
const totalY = reserve_y + fee_y;
```

### Compound Fees Strategy

Since fees auto-compound, you can:

```typescript
// Option 1: Leave fees to compound
// Fees automatically increase your position value
// No action needed - most gas efficient

// Option 2: Remove and re-add to "collect"
async function compoundFees(
  pair: LBPair,
  positionId: string
) {
  // Step 1: Remove all liquidity (includes fees)
  const bins = await sdk.Position.getPositionBins(pair, positionId);
  const removal = await sdk.Pair.removeLiquidity(pair, {
    positionId,
    binIds: bins.map(b => b.id)
  });
  
  // Step 2: Re-add with new distribution if desired
  // This effectively "collects" and reinvests fees
}
```

### Calculate Claimable Fees

```typescript
// Preview fees included in withdrawal
async function previewFeesInWithdrawal(
  pair: LBPair,
  positionId: string,
  binIds: number[]
) {
  const amounts = await sdk.Position.getPositionBinsAmount(pair, positionId);
  
  // Filter to selected bins
  const selectedBins = amounts.filter(a => binIds.includes(a.id));
  
  // These amounts already include fees
  const totals = selectedBins.reduce((sum, bin) => ({
    totalWithFeesX: sum.totalWithFeesX + bin.amountX,
    totalWithFeesY: sum.totalWithFeesY + bin.amountY
  }), { totalWithFeesX: 0n, totalWithFeesY: 0n });
  
  return totals;
}
```

### Partial Fee Collection

```typescript
// Remove from high-fee bins only
async function collectFromProfitableBins(
  pair: LBPair,
  positionId: string
) {
  const reserves = await sdk.Pair.getPairReserves(pair);
  const positionBins = await sdk.Position.getPositionBins(pair, positionId);
  
  // Find bins with high fee ratios
  const profitableBins = positionBins.filter(pBin => {
    const reserve = reserves.find(r => r.id === pBin.id);
    if (!reserve) return false;
    
    const feeRatio = (reserve.fee_x + reserve.fee_y) / 
                    (reserve.reserve_x + reserve.reserve_y);
    return feeRatio > FEE_THRESHOLD;
  });
  
  // Remove from these bins to "collect" fees
  if (profitableBins.length > 0) {
    await sdk.Pair.removeLiquidity(pair, {
      positionId,
      binIds: profitableBins.map(b => b.id)
    });
  }
}
```

### Why No Separate Collection?

DLMM's design benefits:

1. **Gas Efficiency**: One less transaction type
2. **Simplicity**: Fees always included
3. **Auto-Compounding**: Fees earn more fees
4. **Fair Distribution**: Proportional to contribution

### Common Patterns

#### Monitor Fee Growth

```typescript
// Track fee accumulation over time
async function trackFeeGrowth(
  pair: LBPair,
  positionId: string
) {
  const initial = await sdk.Position.getPositionBinsAmount(pair, positionId);
  
  // Check periodically
  setInterval(async () => {
    const current = await sdk.Position.getPositionBinsAmount(pair, positionId);
    
    const growth = {
      x: current.reduce((s, b) => s + b.amountX, 0n) - 
         initial.reduce((s, b) => s + b.amountX, 0n),
      y: current.reduce((s, b) => s + b.amountY, 0n) - 
         initial.reduce((s, b) => s + b.amountY, 0n)
    };
    
    console.log("Fee growth:", growth);
  }, 3600000); // Hourly
}
```

#### Exit Strategy

```typescript
// Full exit includes all fees
const tx = await sdk.Pair.removeAndClosePosition(pair, positionId);
// All reserves + fees sent to wallet
```

### Key Takeaways

* **No claim button**: Fees included in removals
* **Always earning**: Fees compound automatically
* **Flexible collection**: Remove any bins anytime
* **Gas efficient**: No separate transactions

### Related Topics

* [Calculate Position Fees](/integration/dlmm/typescript-sdk/fees-and-analytics/calculate-position-fees) - Track earnings
* [Remove from Specific Bins](/integration/dlmm/typescript-sdk/remove-liquidity/remove-from-specific-bins) - Partial collection
* [Fee Structure](/integration/dlmm/typescript-sdk/fees-and-analytics/fee-structure) - How fees accumulate
* [Position Value](/integration/dlmm/typescript-sdk/fees-and-analytics/position-value) - Total worth with fees


# Position Value

## Position Value

Calculate the current total value of your position including liquidity and accumulated fees.

### Basic Value Calculation

```typescript
// Get total position value
async function getPositionValue(
  pair: LBPair,
  positionId: string
) {
  // Get amounts from all bins (includes fees)
  const amounts = await sdk.Position.getPositionBinsAmount(pair, positionId);
  
  // Sum totals
  const totals = amounts.reduce((sum, bin) => ({
    x: sum.x + bin.amountX,
    y: sum.y + bin.amountY
  }), { x: 0n, y: 0n });
  
  return totals;
}
```

### Calculate USD Value

```typescript
// Convert to USD
async function getPositionValueUSD(
  pair: LBPair,
  positionId: string
) {
  const totals = await getPositionValue(pair, positionId);
  
  // Get current price
  const price = getPriceFromBinId(
    pair.parameters.active_id,
    Number(pair.binStep)
  );
  
  // Calculate USD value
  const valueUSD = 
    Number(totals.x) * price + // Token X value
    Number(totals.y);          // Token Y value
  
  return {
    tokenX: totals.x,
    tokenY: totals.y,
    totalUSD: valueUSD
  };
}
```

### Quick Value Check

```typescript
// One-liner value check
const amounts = await sdk.Position.getPositionBinsAmount(pair, positionId);
const total = amounts.reduce((sum, b) => ({
  x: sum.x + b.amountX,
  y: sum.y + b.amountY
}), { x: 0n, y: 0n });
```

### What's Included

The value includes:

* Original liquidity amounts
* Accumulated trading fees
* Current market prices
* All bins in the position

### Display Format

```typescript
// Format for display
const value = await getPositionValueUSD(pair, positionId);

console.log("Position Value:", {
  tokenX: formatUnits(value.tokenX, 18),
  tokenY: formatUnits(value.tokenY, 6),
  totalUSD: `$${value.totalUSD.toFixed(2)}`
});
```

### Track Changes

```typescript
// Compare values over time
const before = await getPositionValueUSD(pair, positionId);

// ... later ...

const after = await getPositionValueUSD(pair, positionId);
const change = after.totalUSD - before.totalUSD;
const changePercent = (change / before.totalUSD) * 100;

console.log(`Change: $${change.toFixed(2)} (${changePercent.toFixed(2)}%)`);
```

### Important Notes

* Value changes with price movements
* Includes all accumulated fees
* No need for manual fee calculations
* Updates in real-time with market

### Related Topics

* [Get Position Bins](/integration/dlmm/typescript-sdk/position-management/get-position-bins) - Detailed amounts
* [Calculate Position Fees](/integration/dlmm/typescript-sdk/fees-and-analytics/calculate-position-fees) - Fee tracking
* [Calculate APR](/integration/dlmm/typescript-sdk/fees-and-analytics/calculate-apr) - Return rates


# Calculate APR

Estimate annualized returns from trading fees based on your position's performance and current market activity.

### Basic APR Calculation

```typescript
// Calculate simple APR from fees
async function calculateAPR(
  pair: LBPair,
  positionId: string,
  positionAgeHours: number,
  initialValueUSD: number
) {
  // Get current amounts using SDK
  const amounts = await sdk.Position.getPositionBinsAmount(pair, positionId);
  
  // Calculate current value
  const price = getPriceFromBinId(
    pair.parameters.active_id,
    Number(pair.binStep)
  );
  
  const currentValueUSD = amounts.reduce((total, bin) => {
    const xValue = Number(bin.amountX) * price;
    const yValue = Number(bin.amountY);
    return total + xValue + yValue;
  }, 0);
  
  // Calculate returns
  const profit = currentValueUSD - initialValueUSD;
  const hourlyReturn = profit / positionAgeHours;
  const annualReturn = hourlyReturn * 24 * 365;
  
  const apr = (annualReturn / initialValueUSD) * 100;
  
  return apr;
}
```

### Fee-Based APR

```typescript
// APR from trading fees only
async function calculateFeeAPR(
  pair: LBPair,
  positionId: string,
  daysActive: number,
  initialInvestment: number
) {
  // Get amounts from SDK
  const amounts = await sdk.Position.getPositionBinsAmount(pair, positionId);
  const price = getPriceFromBinId(pair.parameters.active_id, Number(pair.binStep));
  
  // Calculate current value
  const currentValue = amounts.reduce((sum, bin) => {
    return sum + (Number(bin.amountX) * price) + Number(bin.amountY);
  }, 0);
  
  // Estimate fees (current value - initial)
  const estimatedFees = currentValue - initialInvestment;
  const dailyFeeRate = estimatedFees / daysActive;
  const annualFees = dailyFeeRate * 365;
  
  const apr = (annualFees / initialInvestment) * 100;
  
  return {
    dailyFees: dailyFeeRate,
    projectedAnnualFees: annualFees,
    apr: apr.toFixed(2) + "%"
  };
}
```

### Volume-Based Estimation

```typescript
// Estimate APR from pair volume
async function estimateVolumeAPR(
  pair: LBPair,
  positionId: string,
  dailyVolume: number
) {
  const bins = await sdk.Position.getPositionBins(pair, positionId);
  const amounts = await sdk.Position.getPositionBinsAmount(pair, positionId);
  const activeId = pair.parameters.active_id;
  
  // Check if position is in range
  const inRange = bins.some(b => b.id === activeId);
  if (!inRange) return 0;
  
  // Calculate position value
  const price = getPriceFromBinId(activeId, Number(pair.binStep));
  const positionValue = amounts.reduce((sum, bin) => {
    return sum + (Number(bin.amountX) * price) + Number(bin.amountY);
  }, 0);
  
  // Fee rate
  const feeRate = Number(pair.parameters.base_factor) / 10000;
  
  // Estimate share of fees (simplified)
  const pairTVL = Number(pair.reserveX) * price + Number(pair.reserveY);
  const shareOfPool = positionValue / pairTVL;
  
  // Calculate APR
  const dailyFees = dailyVolume * feeRate * shareOfPool;
  const annualFees = dailyFees * 365;
  const apr = (annualFees / positionValue) * 100;
  
  return apr;
}
```

### Quick APR Display

```typescript
// Simple APR for UI display
async function displayAPR(
  pair: LBPair,
  positionId: string,
  hoursActive: number,
  initialUSD: number
) {
  const amounts = await sdk.Position.getPositionBinsAmount(pair, positionId);
  const price = getPriceFromBinId(pair.parameters.active_id, Number(pair.binStep));
  
  // Current value
  const currentUSD = amounts.reduce((sum, bin) => {
    return sum + (Number(bin.amountX) * price) + Number(bin.amountY);
  }, 0);
  
  // Simple APR calculation
  const profit = currentUSD - initialUSD;
  const hourlyRate = profit / hoursActive;
  const apr = (hourlyRate * 24 * 365 / initialUSD) * 100;
  
  return {
    current: apr.toFixed(1) + "%",
    daily: (apr / 365).toFixed(2) + "%"
  };
}
```

### Important Notes

* All calculations use actual SDK methods
* Price calculations use `getPriceFromBinId`
* Position data from `getPositionBinsAmount`
* No made-up functions

### Related Topics

* [Calculate Position Fees](/integration/dlmm/typescript-sdk/fees-and-analytics/calculate-position-fees) - Track fee earnings
* [Position Value](/integration/dlmm/typescript-sdk/fees-and-analytics/position-value) - Current worth
* [Get Position Bins](/integration/dlmm/typescript-sdk/position-management/get-position-bins) - Position data


# Utilities & Reference

Essential tools, helpers, and reference materials for DLMM development. This section provides utility functions for common calculations, comprehensive error handling guides, optimization best practices, and migration paths from other protocols.

### Prerequisites

Before using utilities and references, ensure you have:

* Basic DLMM SDK integration experience
* Understanding of common protocol operations
* Familiarity with TypeScript/JavaScript
* Knowledge of error handling patterns
* Experience with AMM protocols (for migration)

### What You'll Learn

This section provides comprehensive development resources and tools:

#### [Price Helpers](/integration/dlmm/typescript-sdk/utilities-and-reference/price-helpers)

Utility functions for price conversions between human-readable formats and protocol values. Convert between decimal prices, bin IDs, and fixed-point representations.

#### [Amount Helpers](broken://pages/eVFjJ017LCvmdkci0Axv)

Calculate and format token amounts for protocol interactions. Handle decimal conversions, precise rounding, and amount validations for safe operations.

#### [Error Handling](broken://pages/SeVkw6BAY2olbKcQS95N)

Comprehensive guide to DLMM error types, common issues, and solutions. Learn to gracefully handle transaction failures and provide meaningful user feedback.

#### [Best Practices](/integration/dlmm/typescript-sdk/utilities-and-reference/best-practices)

Performance optimization tips for efficient DLMM interactions. Minimize gas usage, batch operations effectively, and implement robust retry mechanisms.

#### [Migration Guide](broken://pages/TubxuJUpAukMUUxrnfOS)

Step-by-step guide for transitioning from CLMM to DLMM. Understand key differences, map equivalent operations, and migrate existing positions efficiently.


# Bin Mathematics

## What are Bins?

Think of bins as **price buckets** that hold liquidity. Each bin represents a specific price point where tokens can be traded. Instead of spreading liquidity across a continuous range, DLMM splits it into these discrete bins.

#### **Key characteristics:**

* Each bin has a unique ID
* Bins contain reserves of both tokens (X and Y)
* Only one bin is "active" for trading at any time
* Price moves by jumping between adjacent bins

#### How Bin IDs Work

Bin IDs are 24-bit integers that directly map to price levels:

```
Bin ID 8388608 (2^23) = Price of 1.0 (X/Y = 1)
Bin ID > 8388608 = Price > 1.0
Bin ID < 8388608 = Price < 1.0
```

**Important concepts:**

* **Active Bin**: The current trading bin where swaps occur
* **Bin Step**: The price difference between adjacent bins (in basis points)
* **Bin Range**: A sequence of bins where you can place liquidity

#### Example

For a USDC/USDT pair with 1 basis point bin step:

* Bin 8388608: 1 USDC = 1.0000 USDT
* Bin 8388609: 1 USDC = 1.0001 USDT
* Bin 8388610: 1 USDC = 1.0002 USDT

This discretization enables gas-efficient trading and precise liquidity placement at exact price points.

## Bin ID Calculations

Find the appropriate bin ID for any target price using the reverse price formula.

#### The Reverse Formula

To calculate bin ID from a target price:

```
binId = log(price) / log(1 + binStep/10000) + 8388608
```

Where:

* `price`: Target price (X/Y ratio)
* `binStep`: The step size in basis points
* `8388608`: The reference bin where price = 1.0 for example
* Result must be rounded to nearest integer

#### Quick Examples

**Finding bin IDs for common prices:**

```typescript
// With 10 basis point bin step
price: 1.0000 → binId = 8388608
price: 1.0050 → binId = 8388613 (5 bins above)
price: 0.9950 → binId = 8388603 (5 bins below)

// With 100 basis point bin step  
price: 1.1000 → binId = 8388618 (10 bins above)
price: 0.9000 → binId = 8388597 (11 bins below)
```

#### Code Implementation

```typescript
function getBinIdFromPrice(
  targetPrice: number, 
  binStep: number
): number {
  const base = 1 + binStep / 10000;
  const rawBinId = Math.log(targetPrice) / Math.log(base) + 8388608;
  
  // Round to nearest bin
  return Math.round(rawBinId);
}

// Example: Place liquidity at $3.00 for SUI/USDC
const binId = getBinIdFromPrice(3.0, 20);
// Result: binId = 8394879
```

#### Rounding Considerations

* **Round**: For finding nearest tradeable bin
* **Floor**: For conservative sell orders
* **Ceil**: For conservative buy orders

```typescript
// Different rounding strategies
const exact = 8388612.7;
Math.round(exact); // 8388613 - nearest bin
Math.floor(exact); // 8388612 - lower price
Math.ceil(exact);  // 8388613 - higher price
```

## Bin step guides

Choose the optimal bin step for your trading pair based on asset volatility, trading patterns, and capital efficiency needs.

#### What is Bin Step?

Bin step defines the **price increment** between adjacent bins in basis points (bps):

* 1 bps = 0.01% price difference
* Range: 1 to 10,000 bps
* Affects price granularity and capital efficiency

#### Choosing Your Bin Step

#### Stable Pairs (1-10 bps)

**Best for:** USDC/USDT, wBTC/BTC, haSUI/SUI

```
1 bps → 0.01% increments → Finest granularity
5 bps → 0.05% increments → Good for most stables
```

#### Blue-chip Pairs (10-50 bps)

**Best for:** SUI/USDC, DEEP/USDC, WAL/USDC

```
20 bps → 0.20% increments → Popular choice
50 bps → 0.50% increments → Wider but efficient
```

#### Volatile Pairs (50-200 bps)

**Best for:** MEME/USDC, New tokens, Low liquidity pairs

```
100 bps → 1.00% increments → Handles volatility well
200 bps → 2.00% increments → Maximum capital efficiency
```

#### Trade-offs

| Bin Step             | Pros                                                                                         | Cons                                                                                      |
| -------------------- | -------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| **Small** (1-10 bps) | <p>• Minimal slippage</p><p>• Precise pricing</p><p>• Better for limit orders</p>            | <p>• Liquidity spread thin</p><p>• More bins needed</p><p>• Higher gas on rebalancing</p> |
| **Large** (100+ bps) | <p>• Concentrated liquidity</p><p>• Fewer bins to manage</p><p>• Lower rebalancing costs</p> | <p>• Higher slippage</p><p>• Larger price jumps</p><p>• Less precise entries</p>          |

#### Quick Reference

```typescript
// Recommended bin steps by pair type
const binStepGuide = {
  stablecoin: 1,      // USDC/USDT
  correlated: 5,      // haSUI/SUI  
  bluechip: 20,       // SUI/USDC
  volatile: 100,      // MEME/USDC
  exotic: 200         // New launches
};
```

#### Practical Example

For SUI/USDC with 20 bps bin step:

* Bin 8388608: $3.00
* Bin 8388609: $3.01 (0.3% higher)
* Bin 8388610: $3.02 (0.6% higher)

This provides good balance between capital efficiency and price precision.


# Price Helpers

## Price Helpers

Utility functions for price conversions between human-readable formats and protocol values. Essential helpers for working with DLMM's bin-based pricing system.

### Core Price Functions

```typescript
// Convert bin ID to price
function getPriceFromBinId(binId: number, binStep: number): number {
  const base = 1 + binStep / 10000;
  const exponent = binId - 8388608;
  return Math.pow(base, exponent);
}

// Convert price to bin ID
function getBinIdFromPrice(price: number, binStep: number): number {
  const base = 1 + binStep / 10000;
  const binId = Math.log(price) / Math.log(base) + 8388608;
  return Math.round(binId);
}

// Get price with high precision
function getPriceFromBinIdPrecise(binId: number, binStep: number): string {
  const base = 1 + binStep / 10000;
  const exponent = binId - 8388608;
  const price = Math.pow(base, exponent);
  return price.toPrecision(18);
}
```

### Price Formatting

```typescript
// Format price for display
function formatPrice(price: number, decimals: number = 4): string {
  if (price > 10000) {
    return price.toLocaleString('en-US', { 
      maximumFractionDigits: 0 
    });
  } else if (price > 1) {
    return price.toFixed(decimals);
  } else {
    return price.toPrecision(decimals);
  }
}

// Smart format based on value
function autoFormatPrice(price: number): string {
  if (price >= 1000) return formatPrice(price, 0);
  if (price >= 1) return formatPrice(price, 2);
  if (price >= 0.01) return formatPrice(price, 4);
  return price.toPrecision(4);
}
```

### Price Range Calculations

```typescript
// Get price range from bin range
function getPriceRange(
  centerBinId: number,
  binRadius: number,
  binStep: number
): { min: number, max: number, spread: number } {
  const minPrice = getPriceFromBinId(centerBinId - binRadius, binStep);
  const maxPrice = getPriceFromBinId(centerBinId + binRadius, binStep);
  
  return {
    min: minPrice,
    max: maxPrice,
    spread: ((maxPrice - minPrice) / minPrice) * 100
  };
}

// Calculate bins needed for price range
function getBinsForPriceRange(
  currentPrice: number,
  minPrice: number,
  maxPrice: number,
  binStep: number
): { lower: number, upper: number, total: number } {
  const currentBin = getBinIdFromPrice(currentPrice, binStep);
  const minBin = getBinIdFromPrice(minPrice, binStep);
  const maxBin = getBinIdFromPrice(maxPrice, binStep);
  
  return {
    lower: currentBin - minBin,
    upper: maxBin - currentBin,
    total: maxBin - minBin + 1
  };
}
```

### Price Conversions

```typescript
// Convert between token ratios
function calculateTokenRatio(
  amountX: bigint,
  amountY: bigint,
  decimalsX: number,
  decimalsY: number
): number {
  const x = Number(amountX) / (10 ** decimalsX);
  const y = Number(amountY) / (10 ** decimalsY);
  return y / x; // Price of X in terms of Y
}

// Normalize price for different decimals
function normalizePrice(
  price: number,
  decimalsX: number,
  decimalsY: number
): number {
  const decimalDiff = decimalsY - decimalsX;
  return price * Math.pow(10, decimalDiff);
}
```

### Bin Navigation

```typescript
// Get next/previous bin prices
function getAdjacentPrices(
  binId: number,
  binStep: number,
  count: number = 5
): { below: number[], current: number, above: number[] } {
  const below = [];
  const above = [];
  
  for (let i = 1; i <= count; i++) {
    below.push(getPriceFromBinId(binId - i, binStep));
    above.push(getPriceFromBinId(binId + i, binStep));
  }
  
  return {
    below: below.reverse(),
    current: getPriceFromBinId(binId, binStep),
    above
  };
}

// Find nearest bin to target price
function getNearestBin(
  targetPrice: number,
  binStep: number
): { binId: number, actualPrice: number, difference: number } {
  const binId = getBinIdFromPrice(targetPrice, binStep);
  const actualPrice = getPriceFromBinId(binId, binStep);
  
  return {
    binId,
    actualPrice,
    difference: Math.abs(targetPrice - actualPrice)
  };
}
```

### Price Validation

```typescript
// DLMM constants
const PRICE_BOUNDS = {
  MIN_BIN_ID: 0,
  MAX_BIN_ID: 16777215, // 2^24 - 1
  REFERENCE_BIN_ID: 8388608 // Price = 1.0
};

// Validate price is within DLMM bounds
function isValidPrice(price: number, binStep: number): boolean {
  try {
    const binId = getBinIdFromPrice(price, binStep);
    return binId >= PRICE_BOUNDS.MIN_BIN_ID && 
           binId <= PRICE_BOUNDS.MAX_BIN_ID;
  } catch {
    return false;
  }
}

// Get max/min prices for bin step
function getPriceBounds(binStep: number): { min: number, max: number } {
  return {
    min: getPriceFromBinId(PRICE_BOUNDS.MIN_BIN_ID, binStep),
    max: getPriceFromBinId(PRICE_BOUNDS.MAX_BIN_ID, binStep)
  };
}
```

### Utility Functions

```typescript
// Quick helpers
const priceHelpers = {
  // Get current price from pair
  getCurrentPrice: (pair: LBPair) => 
    getPriceFromBinId(pair.parameters.active_id, Number(pair.binStep)),
  
  // Calculate price impact
  getPriceImpact: (startBin: number, endBin: number, binStep: number) => {
    const startPrice = getPriceFromBinId(startBin, binStep);
    const endPrice = getPriceFromBinId(endBin, binStep);
    return ((endPrice - startPrice) / startPrice) * 100;
  },
  
  // Format pair price for display
  formatPairPrice: (pair: LBPair, decimals?: number) => {
    const price = getPriceFromBinId(pair.parameters.active_id, Number(pair.binStep));
    return autoFormatPrice(price);
  }
};
```


# Best Practices

Performance optimization tips for efficient DLMM interactions. Minimize gas usage, batch operations effectively, and implement robust patterns for production applications.

### Gas Optimization

#### Batch Operations

```typescript
// ❌ Bad: Multiple transactions
await sdk.Pair.removeLiquidity(pair, { positionId, binIds: [1] });
await sdk.Pair.removeLiquidity(pair, { positionId, binIds: [2] });
await sdk.Pair.removeLiquidity(pair, { positionId, binIds: [3] });

// ✅ Good: Single transaction
await sdk.Pair.removeLiquidity(pair, { 
  positionId, 
  binIds: [1, 2, 3] 
});
```

#### Minimize Bin Count

```typescript
// Gas scales with number of bins
const gasEfficient = {
  // ❌ Excessive: 100 bins
  wide: Array.from({length: 101}, (_, i) => i - 50),
  
  // ✅ Efficient: Focused liquidity
  focused: Array.from({length: 21}, (_, i) => i - 10)
};

// Consider gas cost vs capital efficiency
function optimizeBinCount(expectedVolatility: number): number {
  if (expectedVolatility < 0.01) return 10;  // Stable
  if (expectedVolatility < 0.05) return 20;  // Normal
  return 30; // Volatile
}
```

### Efficient Data Fetching

#### Cache Pair Data

```typescript
class PairCache {
  private cache = new Map<string, { data: LBPair, timestamp: number }>();
  private ttl = 60000; // 1 minute
  
  async getPair(pairAddress: string): Promise<LBPair> {
    const cached = this.cache.get(pairAddress);
    
    if (cached && Date.now() - cached.timestamp < this.ttl) {
      return cached.data;
    }
    
    const pair = await sdk.Pair.getPair(pairAddress);
    if (pair) {
      this.cache.set(pairAddress, { data: pair, timestamp: Date.now() });
    }
    
    return pair;
  }
}
```

#### Batch RPC Calls

```typescript
// ❌ Bad: Sequential calls
const pairs = [];
for (const address of pairAddresses) {
  const pair = await sdk.Pair.getPair(address);
  pairs.push(pair);
}

// ✅ Good: Parallel calls
const pairs = await Promise.all(
  pairAddresses.map(address => sdk.Pair.getPair(address))
);
```

#### Selective Data Loading

```typescript
// Only fetch what you need
async function getPositionSummary(positionId: string) {
  // Don't fetch full bin details if only need count
  const position = await sdk.Position.getLbPosition(positionId);
  
  // Lazy load expensive data
  let bins = null;
  const getBins = async () => {
    if (!bins) {
      const pair = await sdk.Pair.getPair(position.pair_id);
      bins = await sdk.Position.getPositionBins(pair, positionId);
    }
    return bins;
  };
  
  return { position, getBins };
}
```

### Smart Contract Interactions

#### Reuse Transactions

```typescript
// Build transaction once, execute multiple times
const buildAddLiquidityTx = (params: AddLiquidityParams) => {
  const tx = new Transaction();
  // ... build transaction
  return tx;
};

// Reuse for similar operations
const template = buildAddLiquidityTx(baseParams);
```

#### Optimize Call Data

```typescript
// Minimize distribution arrays
function compressDistribution(
  distribution: number[]
): { indices: number[], values: number[] } {
  const indices = [];
  const values = [];
  
  distribution.forEach((value, index) => {
    if (value > 0) {
      indices.push(index);
      values.push(value);
    }
  });
  
  return { indices, values };
}
```

### Error Handling Patterns

#### Retry with Backoff

```typescript
async function retryWithBackoff<T>(
  fn: () => Promise<T>,
  maxRetries: number = 3
): Promise<T> {
  let lastError;
  
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error;
      
      // Exponential backoff
      const delay = Math.min(1000 * Math.pow(2, i), 10000);
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
  
  throw lastError;
}
```

#### Circuit Breaker

```typescript
class CircuitBreaker {
  private failures = 0;
  private lastFailTime = 0;
  private threshold = 5;
  private timeout = 60000; // 1 minute
  
  async execute<T>(fn: () => Promise<T>): Promise<T> {
    if (this.isOpen()) {
      throw new Error("Circuit breaker is open");
    }
    
    try {
      const result = await fn();
      this.onSuccess();
      return result;
    } catch (error) {
      this.onFailure();
      throw error;
    }
  }
  
  private isOpen(): boolean {
    return this.failures >= this.threshold && 
           Date.now() - this.lastFailTime < this.timeout;
  }
  
  private onSuccess() {
    this.failures = 0;
  }
  
  private onFailure() {
    this.failures++;
    this.lastFailTime = Date.now();
  }
}
```

### Memory Management

#### Clean Up Large Objects

```typescript
// Release memory after use
async function processLargeDataset(pair: LBPair) {
  let reserves = await sdk.Pair.getPairReserves(pair);
  
  // Process data
  const result = processReserves(reserves);
  
  // Clear reference
  reserves = null;
  
  return result;
}
```

#### Stream Large Results

```typescript
// Process in chunks for large datasets
async function* streamPositionBins(
  pair: LBPair,
  positionId: string,
  chunkSize: number = 50
) {
  const bins = await sdk.Position.getPositionBins(pair, positionId);
  
  for (let i = 0; i < bins.length; i += chunkSize) {
    yield bins.slice(i, i + chunkSize);
  }
}

// Usage
for await (const chunk of streamPositionBins(pair, positionId)) {
  processChunk(chunk);
}
```

### Production Checklist

#### Pre-deployment

```typescript
const productionChecks = {
  // 1. Input validation
  validateInputs: (params: any) => {
    if (!params.amount || params.amount <= 0n) {
      throw new Error("Invalid amount");
    }
  },
  
  // 2. Network check
  ensureMainnet: () => {
    if (sdk.network !== "mainnet") {
      throw new Error("Not on mainnet");
    }
  },
  
  // 3. Gas buffer
  addGasBuffer: (estimatedGas: bigint) => {
    return (estimatedGas * 120n) / 100n; // 20% buffer
  },
  
  // 4. Slippage protection
  defaultSlippage: 0.5, // 0.5%
  maxSlippage: 5.0     // 5%
};
```

### Performance Monitoring

```typescript
// Track operation performance
class PerformanceMonitor {
  private metrics = new Map<string, number[]>();
  
  async measure<T>(
    name: string,
    fn: () => Promise<T>
  ): Promise<T> {
    const start = performance.now();
    
    try {
      return await fn();
    } finally {
      const duration = performance.now() - start;
      
      if (!this.metrics.has(name)) {
        this.metrics.set(name, []);
      }
      
      this.metrics.get(name)!.push(duration);
      
      // Log slow operations
      if (duration > 5000) {
        console.warn(`Slow operation: ${name} took ${duration}ms`);
      }
    }
  }
  
  getStats(name: string) {
    const times = this.metrics.get(name) || [];
    return {
      avg: times.reduce((a, b) => a + b, 0) / times.length,
      min: Math.min(...times),
      max: Math.max(...times)
    };
  }
}
```

### Key Principles

1. **Batch when possible** - Reduce transaction count
2. **Cache frequently used data** - Minimize RPC calls
3. **Validate early** - Fail fast with clear errors
4. **Handle errors gracefully** - Implement retries
5. **Monitor performance** - Track and optimize
6. **Use appropriate data structures** - Maps for lookups
7. **Clean up resources** - Prevent memory leaks


# Smart Contract

Smart contract interface to interact with DLMM protocol

## Overview

Ferra DLMM (Dynamic Liquidity Market Maker) is an advanced AMM protocol built on Sui blockchain that provides concentrated liquidity and dynamic fee structures. The protocol consists of four main modules working together to provide efficient trading and liquidity provision.

## Core Modules

### Architecture Overview

* [**Factory**](/integration/dlmm/smart-contract/factory-module): Creates and manages trading pairs
* [**LB Pair**](/integration/dlmm/smart-contract/lb-pair-module): Handles trading logic, liquidity management, and fee collection
* [**Position**](/integration/dlmm/smart-contract/position-module): Manages user liquidity positions and fee tracking
* [**Rewarder**](/integration/dlmm/smart-contract/rewarder-module): Distributes additional rewards to liquidity provider


# Interface Documentation

This repository contains the Move interface definitions for the Ferra Dynamic Liquidity Market Maker (DLMM) protocol. These interfaces provide type definitions and function signatures without implemen

## Contract Address

[0x5a5c1d10e4782dbbdec3eb8327ede04bd078b294b97cfdba447b11b846b383ac](https://suivision.xyz/package/0x5a5c1d10e4782dbbdec3eb8327ede04bd078b294b97cfdba447b11b846b383ac)

## Overview

The Ferra DLMM interface consists of three main modules:

1. **`lb_pair.move`** - Main liquidity pair management interface (core trading and swap operations)
2. **`bin_manager.move`** - Bin management and liquidity operations interface (liquidity distribution and calculations)
3. **`lb_position.move`** - Liquidity Book Position management interface (user position tracking, fee collection, and reward distribution)

**Github**:  <https://github.com/Ferra-Labs/dlmm-interface>


# Factory Module

Creates and manages trading pairs

#### Structs

**`Pairs`**

```move
struct Pairs has key, store {
    id: UID,
    list: LinkedTable<ID, PairSimpleInfo>,
    index: u64,
}
```

* **Purpose**: Global registry of all trading pairs
* **Access**: Shared object accessible by all users

**`PairSimpleInfo`**

```move
struct PairSimpleInfo has copy, drop, store {
    pair_id: ID,
    pair_key: ID,
    bin_step: u16,
    coin_type_a: TypeName,
    coin_type_b: TypeName
}
```

* **Purpose**: Basic information about a trading pair

#### Events

**`InitFactoryEvent`**

```move
struct InitFactoryEvent has copy, drop {
    pairs_id: ID,
}
```

* **Emitted**: When factory is initialized

**`CreatePairEvent`**

```move
struct CreatePairEvent has copy, drop {
    pair_id: ID,
    bin_step: u16,
    coin_type_a: String,
    coin_type_b: String,
    active_id: u32,
}
```

* **Emitted**: When new trading pair is created

#### Core Functions

**Pair Creation**

* **`create_pair<X, Y>`**: Creates a new trading pair
  * Validates token types and whitelist status
  * Initializes pair with specified parameters
  * Registers pair in global registry
  * Emits creation event

**Pair Management**

* **`set_static_fee_parameters`**: Updates fee structure (admin only)
* **`force_decay`**: Forces volatility decay (admin only)
* **`pause_pair`**: Pauses/unpauses trading (admin only)
* **`increase_oracle_length`**: Expands price oracle capacity (admin only)

**View Functions**

* **`new_pair_key<X, Y>`**: Generates deterministic pair key
* **`pool_simple_info`**: Retrieves pair information
* **`index`**: Gets total number of pairs created


# LB Pair Module

Handles trading logic, liquidity management, and fee collection

#### Structs

**`LBPair<X, Y>`**

```move
struct LBPair<phantom X, phantom Y> has key, store {
    id: UID,
    is_pause: bool,
    bin_step: u16,
    parameters: PairParameters,
    protocol_fee_x: u64,
    protocol_fee_y: u64,
    bin_manager: BinManager,
    oracle: Oracle,
    position_manager: LBPositionManager,
    balance_x: Balance<X>,
    balance_y: Balance<Y>,
    rewarder_manager: RewarderManager,
    global_reward_state: GlobalRewardState,
}
```

* **Purpose**: Core trading pair implementation

**`GlobalRewardState`**

```move
struct GlobalRewardState has store {
    reward_per_fee_cumulative: vector<u128>,        
    total_fees_ever: u128,
    last_update_time: u64,
}
```

* **Purpose**: Tracks fees for reward distribution

**`FlashLoanReceipt`**

```move
struct FlashLoanReceipt {
    pool_id: ID,
    loan_x: bool,
    amount: u64,
    fee_amount: u64,
}
```

* **Purpose**: Receipt for flash loan repayment validation

#### Events

**`SwapEvent`**

```move
struct SwapEvent has copy, drop {
    sender: address,
    pair: ID,
    id_before: u32,
    id_after: u32,
    swap_for_y: bool,
    amounts_in_x: u64,
    amounts_in_y: u64,
    amounts_out_x: u64,
    amounts_out_y: u64,
    volatility_accumulator: u32,
    total_fees_x: u64,
    total_fees_y: u64,
    protocol_fees_x: u64,
    protocol_fees_y: u64,
    current_bin: Bin,
}
```

* **Emitted**: When swap is executed

**`CollectedProtocolFeesEvent`**

```move
struct CollectedProtocolFeesEvent has copy, drop {
    fee_recipient: address,
    protocol_fees_x: u64,
    protocol_fees_y: u64,
}
```

* **Emitted**: When protocol fees are collected

**`OracleLengthIncreasedEvent`**

```move
struct OracleLengthIncreasedEvent has copy, drop {
    sender: address,
    new_length: u16,
}
```

* **Emitted**: When oracle capacity is increased

**`ForcedDecayEvent`**

```move
struct ForcedDecayEvent has copy, drop {
    sender: address,
    id_reference: u32,
    volatility_reference: u32,
}
```

* **Emitted**: When volatility decay is forced

**`StaticFeeParametersSetEvent`**

```move
struct StaticFeeParametersSetEvent has copy, drop {
    sender: address,
    base_factor: u32,
    filter_period: u16,
    decay_period: u16,
    reduction_factor: u16,
    variable_fee_control: u32,
    protocol_share: u16,
    max_volatility_accumulator: u32,
}
```

* **Emitted**: When fee parameters are updated

**`CompositionFeesEvent`**

```move
struct CompositionFeesEvent has copy, drop {
    id: u32,
    fee_x: u64,
    fee_y: u64,
}
```

* **Emitted**: When composition fees are charged

**`FeesCollectedEvent`**

```move
struct FeesCollectedEvent has copy, drop {
    position: ID,
    bin_ids: vector<u32>,
    amounts_x: vector<u64>,
    amounts_y: vector<u64>,
}
```

* **Emitted**: When position fees are collected

**`RewarderAddedEvent`**

```move
struct RewarderAddedEvent has copy, drop {
    pool: ID,
    rewarder_type: TypeName,
}
```

* **Emitted**: When new rewarder is added

**`RewarderEmissionUpdatedEvent`**

```move
struct RewarderEmissionUpdatedEvent has copy, drop {
    pool: ID,
    rewarder_type: TypeName,
    emissions_per_second: u128,
}
```

* **Emitted**: When emission rate is updated

**`RewardsCollectedEvent`**

```move
struct RewardsCollectedEvent has copy, drop {
    pool: ID,
    bucket_id: ID,
    reward_type: TypeName,
    amount: u64,
}
```

* **Emitted**: When rewards are collected

**`FeeParametersQueriedEvent`**

```move
struct FeeParametersQueriedEvent has copy, drop {
    base_factor: u32,
    filter_period: u16,
    decay_period: u16,
    reduction_factor: u16,
    variable_fee_control: u32,
    protocol_share: u16,
    max_volatility_accumulator: u32,
    volatility_accumulator: u32,
    volatility_reference: u32,
    id_reference: u32,
    time_of_last_update: u64,
}
```

* **Emitted**: When fee parameters are queried

**`OracleParametersQueriedEvent`**

```move
struct OracleParametersQueriedEvent has copy, drop {
    sample_lifetime: u8,
    size: u16,
    active_size: u16,
    last_updated: u64,
    first_timestamp: u64,
}
```

* **Emitted**: When oracle parameters are queried

**`OracleSampleQueriedEvent`**

```move
struct OracleSampleQueriedEvent has copy, drop {
    time: u64,
    cumulative_id: u64,
    cumulative_volatility: u64,
    cumulative_bin_crossed: u64,
}
```

* **Emitted**: When oracle sample is queried

**`PriceFromIdQueriedEvent`**

```move
struct PriceFromIdQueriedEvent has copy, drop {
    id: u32,
    price: u128,
}
```

* **Emitted**: When price is calculated from ID

**`IdFromPriceQueriedEvent`**

```move
struct IdFromPriceQueriedEvent has copy, drop {
    price: u128,
    id: u32,
}
```

* **Emitted**: When ID is calculated from price

**`FlashLoanEvent`**

```move
struct FlashLoanEvent has copy, drop {
    pool: ID,
    loan_x: bool,
    amount: u64,
    fee_amount: u64,
}
```

* **Emitted**: When flash loan is taken

**`OpenPositionEvent`**

```move
struct OpenPositionEvent has copy, drop {
    pair: ID,
    position: ID,
    owner: address,
}
```

* **Emitted**: When position is opened

**`ClosePositionEvent`**

```move
struct ClosePositionEvent has copy, drop {
    pair: ID,
    position: ID,
    owner: address,
}
```

* **Emitted**: When position is closed

**`AddLiquidityEvent`**

```move
struct AddLiquidityEvent has copy, drop {
    pair: ID,
    position: ID,
    ids: vector<u32>,
    amounts_x: vector<u64>,
    amounts_y: vector<u64>,
    tokens: vector<u128>,
}
```

* **Emitted**: When liquidity is added

**`RemoveLiquidityEvent`**

```move
struct RemoveLiquidityEvent has copy, drop {
    pair: ID,
    position: ID,
    ids: vector<u32>,
    tokens_x: u64,
    tokens_y: u64,
    token_bins_x: vector<u64>,
    token_bins_y: vector<u64>,
    liquidity_burned: vector<u128>,
}
```

* **Emitted**: When liquidity is removed

**`LockPositionEvent`**

```move
struct LockPositionEvent has copy, drop {
    pair: ID,
    position: ID,
    lock_until: u64,
    owner: address,
}
```

* **Emitted**: When position is locked

**`PairPausedEvent`**

```move
struct PairPausedEvent has copy, drop {
    pair: ID,
    paused: bool,
}
```

* **Emitted**: When pair is paused/unpaused

#### Trading Functions

**`swap<X, Y>`**

* **Purpose**: Execute token swaps
* **Parameters**:
  * `swap_for_y`: Direction of swap
  * `min_amount_out`: Slippage protection
  * `amount_in_x`, `amount_in_y`: Input tokens
* **Returns**: Output tokens after swap

**`get_swap_in` / `get_swap_out`**

* **Purpose**: Calculate swap amounts (view functions)
* **Use Cases**: Price quotation, slippage calculation
* **Returns**: Expected amounts and fees

#### Liquidity Management

**`add_liquidity<X, Y>`**

* **Purpose**: Add liquidity to specific price bins
* **Parameters**:
  * `ids`: Target bin IDs
  * `distribution_x`, `distribution_y`: Liquidity distribution
  * `coin_x`, `coin_y`: Token amounts
* **Features**: Handles composition fees and refunds

**`remove_liquidity<X, Y>`**

* **Purpose**: Remove liquidity from positions
* **Parameters**:
  * `ids`: Bin IDs to remove from
* **Returns**: Withdrawn tokens
* **Validations**: Position ownership, lock period

#### Position Management

**`open_position<X, Y>`**

* **Purpose**: Create new liquidity position
* **Returns**: NFT-like position object
* **Features**: Optional time lock mechanism

**`close_position<X, Y>`**

* **Purpose**: Destroy empty position
* **Requirements**: Position must be completely empty

#### Fee Collection

**`collect_position_fees<X, Y>`**

* **Purpose**: Collect accumulated trading fees
* **Parameters**: `bin_ids` - specific bins to collect from
* **Returns**: Fee tokens earned
* **Side Effects**: Updates global reward tracking

**`collect_protocol_fees<X, Y>`**

* **Purpose**: Collect protocol revenue (admin only)
* **Returns**: Protocol fee tokens

#### Reward System

**`add_rewarder<X, Y, RewardCoin>`**

* **Purpose**: Add new reward token (admin only)
* **Limits**: Maximum 5 reward types per pair

**`update_emission<X, Y, RewardCoin>`**

* **Purpose**: Update reward emission rate (admin only)
* **Validation**: Sufficient vault balance

**`collect_position_rewards<X, Y, RewardCoin>`**

* **Purpose**: Claim earned rewards
* **Returns**: Reward tokens based on fee generation

#### Flash Loans

**`flash_loan<X, Y>`**

* **Purpose**: Borrow tokens temporarily
* **Returns**: Borrowed tokens and repayment receipt
* **Fee**: Configurable flash loan fee

**`repay_flash_loan<X, Y>`**

* **Purpose**: Repay borrowed tokens with fee
* **Requirements**: Must repay principal + fee

#### View Functions

* **`get_reserves`**: Current token reserves
* **`get_active_id`**: Current active price bin
* **`get_bin`**: Specific bin reserves
* **`get_protocol_fees`**: Accumulated protocol fees
* **`get_pending_rewards`**: Unclaimed rewards for position
* **`get_pending_fees`**: Unclaimed fees for position


# Position Module

Manages user liquidity positions and fee tracking

#### Structs

**`LBPosition`**

```move
struct LBPosition has key, store {
    id: UID,
    pair_id: ID,
    coin_type_a: TypeName,
    coin_type_b: TypeName,
    lock_until: u64,
}
```

* **Purpose**: NFT representing liquidity position

**`LBPositionManager`**

```move
struct LBPositionManager has store {
    positions: Table<ID, LBPositionInfo>,
}
```

* **Purpose**: Manages all position data

**`LBPositionInfo`**

```move
struct LBPositionInfo has store {
    position_id: ID,
    bins: Table<u32, LBBinPosition>,
    fee_reward_info: FeeRewardInfo,
}
```

* **Purpose**: Detailed position data

**`LBBinPosition`**

```move
struct LBBinPosition has copy, drop, store {
    amount: u128,
    fee_growth_inside_last_x: u128,
    fee_growth_inside_last_y: u128,
}
```

* **Purpose**: Position data for specific price bin

**`FeeRewardInfo`**

```move
struct FeeRewardInfo has store {
    total_fees_generated: u128,        
    reward_per_fee_snapshot: vector<u128>,
}
```

* **Purpose**: Fee generation and reward tracking

#### Core Functions

**Position Lifecycle**

* **`open_position`**: Create new position NFT
* **`close_position`**: Destroy empty position
* **`lock_position`**: Apply time lock
* **`is_position_locked`**: Check lock status

**Liquidity Operations**

* **`increase_liquidity`**: Add liquidity to position
* **`decrease_liquidity`**: Remove liquidity (respects locks)

**Fee Management**

* **`collect_fees`**: Harvest accumulated fees
* **`get_pending_fees`**: View unclaimed fees
* **`update_fee_info`**: Update fee tracking (internal)

**Position Queries**

* **`position_token_amount`**: Liquidity in specific bin
* **`get_tokens_in_position`**: All position liquidity
* **`has_tokens_in_bins`**: Check if position has liquidity


# Rewarder Module

Distributes additional rewards to liquidity provider

### Data Structures

#### RewarderManager

The main structure that manages all rewarders for a specific pair.

```rust
struct RewarderManager has store {
    rewarders: vector<Rewarder>,
    last_update_timestamp: u64,
}
```

**Fields:**

* `rewarders`: Vector containing up to 5 rewarder configurations
* `last_update_timestamp`: Timestamp of the last reward calculation update

#### Rewarder

Individual rewarder configuration for a specific token type.

```rust
struct Rewarder has copy, drop, store {
    emission_per_ms: u128,
    reward_coin: TypeName
}
```

**Fields:**

* `emission_per_ms`: Rate of token emission per millisecond
* `reward_coin`: Type name of the reward token

#### RewarderGlobalVault

Global storage for all reward token balances across the system.

```rust
struct RewarderGlobalVault has key, store {
    id: UID,
    balances: Bag,
}
```

**Fields:**

* `id`: Unique identifier for the vault
* `balances`: Bag containing balance objects for each token type

***

### Core Functions

#### Initialization

**`new()`**

Creates a new RewarderManager instance.

```rust
public(friend) fun new(): RewarderManager
```

**Returns:** Empty RewarderManager with no rewarders configured

**`add_rewarder<RewardCoin>()`**

Adds a new rewarder for a specific reward token type.

```rust
public(friend) fun add_rewarder<RewardCoin>(
    manager: &mut RewarderManager
)
```

**Parameters:**

* `manager`: Mutable reference to RewarderManager
* `RewardCoin`: Type parameter for the reward token

**Validations:**

* Maximum 5 rewarders per pair
* No duplicate rewarders for the same token type

#### Reward Calculation

**`calculate_reward_emission()`**

Calculates and updates reward emissions based on elapsed time.

```rust
public(friend) fun calculate_reward_emission(
    manager: &mut RewarderManager,
    clock: &Clock
): vector<u128>
```

**Parameters:**

* `manager`: Mutable reference to RewarderManager
* `clock`: Clock object for timestamp access

**Returns:** Vector of reward amounts for each rewarder

**Logic:**

```rust
reward_emission = emission_per_ms × (current_time - last_update_time)
```

**`simulate_reward_emission()`**

Simulates reward calculation without updating the manager state.

```rust
public fun simulate_reward_emission(
    manager: &RewarderManager,
    clock: &Clock
): vector<u128>
```

**Use Case:** Preview reward amounts before actual distribution

***

### Reward Management

#### Deposit Rewards

**`deposit_reward<RewardCoin>()`**

Deposits reward tokens into the global vault.

```rust
public fun deposit_reward<RewardCoin>(
    global_config: &GlobalConfig,
    vault: &mut RewarderGlobalVault,
    reward: Balance<RewardCoin>
)
```

**Parameters:**

* `global_config`: Global configuration for access control
* `vault`: Mutable reference to global vault
* `reward`: Balance object containing tokens to deposit

**Process:**

1. Validate package version
2. Create balance entry if doesn't exist
3. Join deposited balance with existing balance
4. Emit deposit event

#### Withdraw Rewards

**`withdraw_reward<RewardCoin>()`**

Withdraws reward tokens from the global vault.

```rust
public(friend) fun withdraw_reward<RewardCoin>(
    vault: &mut RewarderGlobalVault,
    amount: u64,
): Balance<RewardCoin>
```

**Parameters:**

* `vault`: Mutable reference to global vault
* `amount`: Amount to withdraw

**Validations:**

* Rewarder must exist for the token type
* Sufficient balance must be available

#### Emergency Withdrawal

**`emergent_withdraw<CoinType>()`**

Emergency withdrawal function for administrators.

```rust
public fun emergent_withdraw<CoinType>(
    global_config: &GlobalConfig,
    vault: &mut RewarderGlobalVault,
    amount: u64,
    ctx: &mut TxContext
)
```

**Access Control:**

* Only pool managers can execute
* Withdrawn funds sent to designated receiver

#### Error Codes

| Code | Constant                    | Description                        |
| ---- | --------------------------- | ---------------------------------- |
| 3000 | `E_TOO_MANY_REWARDERS`      | Exceeds maximum rewarders per pair |
| 3001 | `E_REWARDER_ALREADY_EXISTS` | Duplicate rewarder for token type  |
| 3003 | `E_INSUFFICIENT_BALANCE`    | Insufficient vault balance         |
| 3004 | `E_REWARDER_NOT_FOUND`      | Rewarder not found for token type  |

### Events

#### Key Events

**RewardEmissionUpdateEvent**

```rust
struct RewardEmissionUpdateEvent has copy, drop {
    pair: ID,
    reward_type: TypeName,
    old_emission: u128,
    new_emission: u128,
}
```

Emitted when emission rates are updated.

**DepositEvent**

```rust
struct DepositEvent has copy, drop {
    vault: ID,
    reward_type: TypeName,
    amount: u64,
    total_balance: u64,
}
```

Emitted when tokens are deposited into the vault.

**WithdrawEvent**

```rust
struct WithdrawEvent has copy, drop {
    vault: ID,
    reward_type: TypeName,
    amount: u64,
    remaining_balance: u64,
}
```

Emitted when tokens are withdrawn from the vault.


# CLMM


# Typescript SDK

The typescript SDK to interact with Ferra CLMM protocol

## Getting started

Install Ferra CLMM SDK: `npm install @ferra-labs/clmm`&#x20;

Optional: `npm install @mysten/sui`

The Ferra CLMM npm package is located here: [https://npmjs.com/package/@ferra-labs/clmm](https://www.npmjs.com/package/@ferra-labs/clmm)

Initialize the Ferra CLMM SDK with default values using the Sui mainnet RPC

```javascript
// Initilize CLMM instance
import { clmmMainnet, initFerraSDK }  from "@ferra-labs/clmm"

const useFerraSDK = (address: string) => {
  return useMemo(
    () =>
      initFerraSDK({
        ...clmmMainnet,
        network: 'beta' // can be 'mainnet' or 'testnet', 'beta',
        fullNodeUrl: 'YOUR_PRC_FULL_NODE',
        wallet: 'YOUR_WALLET_ADDRES',
      }),
    [address]
  );
};
```


# Get CLMM Pools

Retrieve pool information from the Ferra protocol.

### Overview

The SDK provides multiple methods to fetch pool data, from basic immutable info to complete pool state with real-time data.

### Quick Start

```typescript
import { clmmMainnet, initFerraSDK } from '@ferra-labs/clmm'

// Initialize SDK
const sdk = initFerraSDK({
  ...clmmMainnet,
  network: 'beta',
  fullNodeUrl: 'https://...',
  wallet: '0x...'
})

// Get a single pool
const pool = await sdk.Pool.getPool('0x...')

// Get multiple pools
const pools = await sdk.Pool.getPools()
```

### Get Single Pool

Fetch complete pool data with current state:

```typescript
const poolId = '0x...'
const pool = await sdk.Pool.getPool(poolId)

console.log({
  address: pool.poolAddress,
  coinTypeA: pool.coinTypeA,
  coinTypeB: pool.coinTypeB,
  currentPrice: pool.current_sqrt_price,
  currentTick: pool.current_tick_index,
  liquidity: pool.liquidity,
  feeRate: pool.fee_rate,
  tvlA: pool.coinAmountA,
  tvlB: pool.coinAmountB
})
```

### Get Multiple Pools

#### Basic Usage

```typescript
// Get first 10 pools
const pools = await sdk.Pool.getPools([], 0, 10)

// Get specific pools
const poolIds = ['0x...', '0x...']
const pools = await sdk.Pool.getPools(poolIds)
```

#### With Pagination

```typescript
// Advanced pagination
const result = await sdk.Pool.getPoolsWithPage(
  [],           // Pool IDs (empty = all)
  {             // Pagination args
    limit: 20,
    cursor: nextCursor
  }
)
```

### Get Pool Immutables

Lightweight method for basic pool info:

```typescript
// Get pool addresses and coin types only
const poolInfos = await sdk.Pool.getPoolImmutables()

poolInfos.forEach(info => {
  console.log({
    pool: info.poolAddress,
    coinA: info.coinTypeA,
    coinB: info.coinTypeB,
    tickSpacing: info.tickSpacing
  })
})
```

### Pool Data Structure

```typescript
interface Pool {
  poolAddress: string
  coinTypeA: string
  coinTypeB: string
  
  // Current state
  current_sqrt_price: string
  current_tick_index: number
  liquidity: string
  
  // TVL
  coinAmountA: string
  coinAmountB: string
  
  // Config
  fee_rate: string
  tick_spacing: number
  
  // Rewarders
  rewarder_infos: RewarderInfo[]
  
  // Internal handles
  ticks_handle: string
  positions_handle: string
}
```

### Filtering Pools

#### By Coin Types

```typescript
// Get all pools
const allPools = await sdk.Pool.getPools()

// Filter by coin pair
const suiUsdcPools = allPools.filter(pool => 
  (pool.coinTypeA.includes('sui') && pool.coinTypeB.includes('usdc')) ||
  (pool.coinTypeA.includes('usdc') && pool.coinTypeB.includes('sui'))
)
```

### Real-time Updates

```typescript
// Force refresh for latest data
const pool = await sdk.Pool.getPool(poolId, true)

// Auto-refresh example
setInterval(async () => {
  const freshPool = await sdk.Pool.getPool(poolId, true)
  updateUI(freshPool)
}, 5000) // Every 5 seconds
```

### Error Handling

```typescript
try {
  const pool = await sdk.Pool.getPool(poolId)
} catch (error) {
  if (error.code === 'InvalidPoolObject') {
    console.error('Pool not found')
  }
}
```


# Get Positions

Retrieve liquidity positions from Ferra pools.

### Quick Start

```typescript
// Get all user positions
const positions = await sdk.Position.getPositionList('0x...')

// Get single position
const position = await sdk.Position.getPositionById('0x...')
```

### Get User Positions

```typescript
const userAddress = '0x...'

// All positions
const allPositions = await sdk.Position.getPositionList(userAddress)

// Filter by pools
const poolIds = ['0xpool1...', '0xpool2...']
const poolPositions = await sdk.Position.getPositionList(userAddress, poolIds)

// Without display data (faster)
const positions = await sdk.Position.getPositionList(userAddress, [], false)
```

### Get Single Position

```typescript
const positionId = '0x...'

// With rewards
const position = await sdk.Position.getPositionById(positionId)

// Without rewards (faster)
const position = await sdk.Position.getPositionById(positionId, false)
```

### Position Data

```typescript
interface Position {
  pos_object_id: string
  owner: string
  pool: string
  
  // Price range
  tick_lower_index: number
  tick_upper_index: number
  liquidity: string
  
  // Tokens
  coin_type_a: string
  coin_type_b: string
  
  // Fees (if calculated)
  fee_owed_a?: string
  fee_owed_b?: string
}
```

### Get Token Amounts

```rust
const lowerSqrtPrice = TickMath.tickIndexToSqrtPriceX64(
    position.tick_lower_index
  );
  const upperSqrtPrice = TickMath.tickIndexToSqrtPriceX64(
    position.tick_upper_index
  );
  
  const liquidity = new BN(position.liquidity);
  const slippage = new Percentage(new BN(0), new BN(1000));
  const curSqrtPrice = new BN(pool.current_sqrt_price);
  const coinAmounts = ClmmPoolUtil.getCoinAmountFromLiquidity(
    liquidity,
    curSqrtPrice,
    lowerSqrtPrice,
    upperSqrtPrice,
    true
  );

  const { tokenMaxA, tokenMaxB } = adjustForCoinSlippage(
    coinAmounts,
    slippage,
    false
  );
  
  const amountA = tokenMaxA.toNumber();
  const amountB = tokenMaxB.toNumber();
  
  console.log(amountA, amountB)
```

### Get Position Fees

```typescript
// Single position
const fees = await sdk.Position.calculateFee({
  pool_id: poolId,
  pos_id: positionId,
  coinTypeA: '0x2::sui::SUI',
  coinTypeB: '0x...::usdc::USDC'
})

console.log('Fees:', fees.feeOwedA, fees.feeOwedB)

// Multiple positions
const positionIds = ['0x1...', '0x2...']
const allFees = await sdk.Position.batchFetchPositionFees(positionIds)
```

### Filter Positions

#### Active Positions

```typescript
const activePositions = positions.filter(
  pos => BigInt(pos.liquidity) > 0n
)
```

#### In-Range Positions

```typescript
const pool = await sdk.Pool.getPool(poolId)
const currentTick = pool.current_tick_index

const inRange = positions.filter(pos => 
  pos.tick_lower_index <= currentTick && 
  currentTick < pos.tick_upper_index
)
```

#### By Token Pair

```typescript
const suiUsdc = positions.filter(pos =>
  pos.coin_type_a.includes('sui') && 
  pos.coin_type_b.includes('usdc')
)
```

### Complete Example

```typescript
// 1. Get all user positions
const userAddress = '0x...'
const positions = await sdk.Position.getPositionList(userAddress)

// 2. Check each position
for (const pos of positions) {
  // Get pool info
  const pool = await sdk.Pool.getPool(pos.pool)
  
  // Check if in range
  const inRange = pos.tick_lower_index <= pool.current_tick_index && 
                  pool.current_tick_index < pos.tick_upper_index
  
  // Calculate fees
  const fees = await sdk.Position.calculateFee({
    pool_id: pos.pool,
    pos_id: pos.pos_object_id,
    coinTypeA: pos.coin_type_a,
    coinTypeB: pos.coin_type_b
  })
  
  console.log({
    position: pos.pos_object_id,
    liquidity: pos.liquidity,
    inRange,
    fees: {
      tokenA: fees.feeOwedA,
      tokenB: fees.feeOwedB
    }
  })
}
```

### Transaction History

```typescript
const history = await sdk.Position.getPositionTransactionList({
  posId: positionId,
  paginationArgs: { limit: 10 }
})

history.data.forEach(tx => {
  console.log(tx.type, tx.timestamp)
})
```

### Error Handling

```typescript
try {
  const position = await sdk.Position.getPositionById(posId)
} catch (error) {
  console.log('Position not found')
}
```


# Get Pool Position Rewards

Check reward amounts for liquidity positions.

### Quick Start

```typescript
// Get rewards for single position
const pool = await sdk.Pool.getPool(poolId)
const rewards = await sdk.Rewarder.fetchPositionRewarders(pool, positionId)

rewards.forEach(reward => {
  console.log({
    token: reward.coin_address,
    amount: reward.amount_owed.toString()
  })
})
```

### Check Pool Rewards

```typescript
// Get position with full reward info
const position = await sdk.Position.getPositionById(positionId, true)

// Access reward amounts
if (position.rewards) {
  position.rewards.forEach(reward => {
    console.log({
      token: reward.coin_type,
      amount: reward.amount_owed
    })
  })
}
```

### Batch Check Rewards

Check rewards for multiple positions:

```typescript
const positionIds = ['0x1...', '0x2...', '0x3...']

// Batch fetch all rewards
const allRewards = await sdk.Rewarder.batchFetchPositionRewarders(positionIds)

// Display results
Object.entries(allRewards).forEach(([posId, rewards]) => {
  console.log(`Position ${posId}:`)
  rewards.forEach(reward => {
    console.log(`  ${reward.coin_address}: ${reward.amount_owed}`)
  })
})

// Batch fetch fees
const allFees = await sdk.Position.batchFetchPositionFees(positionIds)
```

### Pool Reward Info

#### Daily Emissions

```typescript
// Check how much rewards pool distributes daily
const emissions = await sdk.Rewarder.emissionsEveryDay(poolId)

emissions?.forEach(emission => {
  console.log({
    token: emission.coin_address,
    perDay: emission.emissions
  })
})
```

#### Total User Rewards

```typescript
// Get total rewards across all positions in pool
const totalRewards = await sdk.Rewarder.fetchPoolRewardersAmount(
  userAddress,
  poolId
)

totalRewards.forEach((amount, index) => {
  if (amount.gt(new BN(0))) {
    console.log(`Rewarder ${index}: ${amount.toString()}`)
  }
})
```

### Complete Example

```typescript
// 1. Get user positions
const positions = await sdk.Position.getPositionList(userAddress)

// 2. Group by pool
const poolGroups = {}
positions.forEach(pos => {
  if (!poolGroups[pos.pool]) poolGroups[pos.pool] = []
  poolGroups[pos.pool].push(pos)
})

// 3. Check rewards for each pool
for (const [poolId, poolPositions] of Object.entries(poolGroups)) {
  const pool = await sdk.Pool.getPool(poolId)
  
  console.log(`\nPool: ${poolId}`)
  console.log(`Rewarders: ${pool.rewarder_infos.length}`)
  
  // Check emissions
  const emissions = await sdk.Rewarder.emissionsEveryDay(poolId)
  emissions?.forEach(e => {
    console.log(`  Daily ${e.coin_address}: ${e.emissions}`)
  })
  
  // Check each position
  for (const pos of poolPositions) {
    // Fees
    const fees = await sdk.Position.calculateFee({
      pool_id: pos.pool,
      pos_id: pos.pos_object_id,
      coinTypeA: pos.coin_type_a,
      coinTypeB: pos.coin_type_b
    })
    
    // Rewards
    const rewards = await sdk.Rewarder.fetchPositionRewarders(pool, pos.pos_object_id)
    
    console.log(`\n  Position ${pos.pos_object_id}:`)
    console.log(`    Fees: ${fees.feeOwedA} / ${fees.feeOwedB}`)
    rewards.forEach(r => {
      console.log(`    ${r.coin_address}: ${r.amount_owed}`)
    })
  }
}
```

### Response Types

```typescript
interface RewarderAmountOwed {
  amount_owed: BN
  coin_address: string
}

interface CollectFeesQuote {
  feeOwedA: BN
  feeOwedB: BN
  position_id: string
}
```

### Important Notes

* Rewards accumulate in real-time based on liquidity and time
* Trading fees come from swaps in your price range
* Pool rewards are additional incentives from protocol
* Use `calculateRewards: true` when getting position to include rewards
* Batch methods are more efficient for multiple positions


# Create CLMM Pool

Ferra is a permissionless liquidity infrastructure where anyone can create a CLMM pool.

### Overview

Create a new CLMM pool with initial liquidity in a single transaction. The SDK automatically handles coin type ordering according to protocol requirements.

### Prerequisites

* Valid sender address configured in SDK
* Sufficient balance of both tokens
* Token metadata (optional)

### Quick Start

```typescript
import { clmmMainnet, initFerraSDK } from '@ferra-labs/clmm'

// Initialize SDK
const sdk = initFerraSDK({
  ...clmmMainnet,
  network: 'beta',
  fullNodeUrl: 'https://...',
  wallet: '0x...'
})

// Create pool with initial liquidity
const params = {
  // Token types
  coinTypeA: '0x2::sui::SUI',
  coinTypeB: '0x...::usdc::USDC',
  
  // Pool parameters
  tick_spacing: 60,              // Tick spacing (1, 10, 60, 200)
  initialize_sqrt_price: '...',  // Initial price (sqrt format)
  uri: 'https://...',   // Pool uri
  
  // Liquidity position
  tick_lower: -1000,             // Lower price bound
  tick_upper: 1000,              // Upper price bound
  amount_a: '1000000000',        // Amount of token A (with decimals)
  amount_b: '1000000',           // Amount of token B (with decimals)
  fix_amount_a: true,            // Fix token A amount
  slippage: 0.05,
  // Optional metadata
  metadata_a: coinAMetadata,
  metadata_b: coinBMetadata
}

const tx = await sdk.Pool.createPoolTransactionPayloadslippage(params)
const result = await sdk.fullClient.signAndExecuteTransaction({
  transaction: tx,
  signer: keypair
})
```

### Parameters

| Parameter               | Type         | Description                                                     |
| ----------------------- | ------------ | --------------------------------------------------------------- |
| `coinTypeA`             | string       | First token type                                                |
| `coinTypeB`             | string       | Second token type                                               |
| `tick_spacing`          | number       | Fee tier (2=0.01%, 10=0.05%, 20=0.1%, 60=0.5%, 200=1%, 220=5% ) |
| `initialize_sqrt_price` | string       | Initial sqrt price (X64 format)                                 |
| `uri`                   | string       | Pool description                                                |
| `tick_lower`            | number       | Lower tick boundary                                             |
| `tick_upper`            | number       | Upper tick boundary                                             |
| `amount_a`              | string       | Amount of token A                                               |
| `amount_b`              | string       | Amount of token B                                               |
| `fix_amount_a`          | boolean      | If true, fix token A amount; else fix token B                   |
| `metadata_a`            | CoinMetadata | Token A metadata (optional)                                     |
| `metadata_b`            | CoinMetadata | Token B metadata (optional)                                     |

### Price Calculation

Calculate initial sqrt price:

```typescript
import { TickMath } from '@ferra-labs/clmm'

// Convert price to sqrt price
const price = 1.5 // 1 tokenA = 1.5 tokenB
const sqrtPrice = TickMath.priceToSqrtPriceX64(
  price, 
  decimalsA, 
  decimalsB
)
```

### Tick Spacing Guide

<table><thead><tr><th>Tick Spacing</th><th width="249">Fee</th></tr></thead><tbody><tr><td>2</td><td>0.01%</td></tr><tr><td>10</td><td>0.05%</td></tr><tr><td>20</td><td>0.1%</td></tr><tr><td>60</td><td>0.5%</td></tr><tr><td>200</td><td>1%</td></tr><tr><td>220</td><td>5%</td></tr></tbody></table>

### Error Handling

```typescript
try {
  const tx = await sdk.Pool.createPoolTransactionPayload(params)
  // Execute transaction
} catch (error) {
  if (error.code === 'InvalidSendAddress') {
    // Set sender address
    sdk.senderAddress = '0x...'
  } else if (error.code === 'InsufficientBalance') {
    // Handle insufficient funds
  }
}
```

### Important Notes

* Coin types are automatically sorted according to protocol rules
* The SDK handles coin selection from your wallet
* Remaining coins are automatically returned to sender
* Pool creation includes initial liquidity position (NFT)


# Open Position

Create a new liquidity position NFT in a Ferra pool.

### Quick Start

<pre class="language-typescript"><code class="lang-typescript">import { clmmMainnet, initFerraSDK } from '@ferra-labs/clmm'

// Initialize SDK
const sdk = initFerraSDK({
  ...clmmMainnet,
  network: 'beta',
  fullNodeUrl: 'https://...',
  wallet: '0x...'
})

<strong>const tx = sdk.Position.openPositionTransactionPayload({
</strong>  pool_id: '0x...',
  coinTypeA: '0x2::sui::SUI', 
  coinTypeB: '0x...::usdc::USDC',
  tick_lower: -1000,
  tick_upper: 1000
})

// Execute
const result = await sdk.fullClient.signAndExecuteTransaction({
  transaction: tx,
  signer: keypair
})
</code></pre>

### Parameters

| Parameter    | Type   | Description          |
| ------------ | ------ | -------------------- |
| `pool_id`    | string | Pool object ID       |
| `coinTypeA`  | string | First token type     |
| `coinTypeB`  | string | Second token type    |
| `tick_lower` | number | Lower price boundary |
| `tick_upper` | number | Upper price boundary |

### Price Range

#### Set Custom Range

```typescript
import { TickMath } from '@ferra-labs/clmm'

// Price range: 0.9 - 1.1 USDC per SUI
const lowerPrice = 0.9
const upperPrice = 1.1

const tickLower = TickMath.priceToTickIndex(lowerPrice, 9, 6)
const tickUpper = TickMath.priceToTickIndex(upperPrice, 9, 6)
```

#### Range Around Current Price

```typescript
const pool = await sdk.Pool.getPool(poolId)
const currentTick = pool.current_tick_index
const tickSpacing = pool.tick_spacing

// ±10 ticks from current price
const tickLower = currentTick - 10 * tickSpacing
const tickUpper = currentTick + 10 * tickSpacing
```

### Complete Example

```typescript
// 1. Get pool info
const pool = await sdk.Pool.getPool(poolId)

// 2. Set price range
const tickLower = -1000
const tickUpper = 1000

// 3. Open position
const tx = sdk.Position.openPositionTransactionPayload({
  pool_id: poolId,
  coinTypeA: pool.coinTypeA,
  coinTypeB: pool.coinTypeB,
  tick_lower: tickLower,
  tick_upper: tickUpper
})

// 4. Execute
const result = await sdk.fullClient.signAndExecuteTransaction({
  transaction: tx,
  signer: keypair
})

console.log('Position NFT created:', result.digest)
```

### Important Notes

* Opening a position creates an empty NFT without liquidity
* Add liquidity separately after position creation
* Position NFT is transferable and tradeable
* Each position has unique tick range that cannot be changed


# Add Liquidity

Add tokens to a liquidity position in Ferra pools.

### Methods

#### 1. Add Liquidity by Amount

Specify exact liquidity amount to add:

```typescript
import { clmmMainnet, initFerraSDK } from '@ferra-labs/clmm'

// Initialize SDK
const sdk = initFerraSDK({
  ...clmmMainnet,
  network: 'beta',
  fullNodeUrl: 'https://...',
  wallet: '0x...'
})
const poolId = "0x17f11b892f27ba3797c7d0b15164c7873294ce3df44e287efc195c126b0afec6"
const pool = sdk.Pool.getPool(poolId)
const curSqrtPrice = new BN(pool.current_sqrt_price)
const tick_lower_index = -220
const tick_upper_index = 200
const slippageTolerance = new Percentage(new BN(5), new BN(100))

const totalAmount = '10'
const tokenPriceA = '0.2'
const tokenPriceB = '1'

const coinAmounts = ClmmPoolUtil.estCoinAmountsFromTotalAmount(
  tick_lower_index,
  tick_upper_index,
  curSqrtPrice,
  totalAmount,
  tokenPriceA,
  tokenPriceB
)
const amountA = toDecimalsAmount(coinAmounts.amountA.toFixed(6, Decimal.ROUND_UP).toString(), 6)
const amountB = toDecimalsAmount(coinAmounts.amountB.toFixed(6, Decimal.ROUND_UP).toString(), 6)

const tokenAmounts = {
  coinA: new BN(amountA),
  coinB: new BN(amountB),
}

const liquidity = ClmmPoolUtil.estimateLiquidityFromcoinAmounts(curSqrtPrice, tick_lower_index, tick_upper_index, tokenAmounts)

console.log('liquidity: ', liquidity.toString())

const { tokenMaxA, tokenMaxB } = adjustForCoinSlippage(tokenAmounts, slippageTolerance, true)

const addLiquidityPayloadParams: AddLiquidityParams = {
  coinTypeA: pool.coinTypeA,
  coinTypeB: pool.coinTypeB,
  pool_id: pool.poolAddress,
  tick_lower: tick_lower_index.toString(),
  tick_upper: tick_upper_index.toString(),
  delta_liquidity: liquidity.toString(),
  max_amount_a: tokenMaxA.toString(),
  max_amount_b: tokenMaxB.toString(),
  pos_id: '',
  rewarder_coin_types: [],
  collect_fee: false,
}

const payload = await sdk.Position.createAddLiquidityPayload(addLiquidityPayloadParams)

printTransaction(payload)

const transferTxn = await sdk.fullClient.sendTransaction(sendKeypair, payload)
console.log('createAddLiquidityPayload: ', transferTxn)
```

#### 2. Add Liquidity Fixed Token

Fix one token amount, calculate the other:

```typescript
import { clmmMainnet, initFerraSDK } from '@ferra-labs/clmm'

// Initialize SDK
const sdk = initFerraSDK({
  ...clmmMainnet,
  network: 'beta',
  fullNodeUrl: 'https://...',
  wallet: '0x...'
})

const poolId = '0x17f11b892f27ba3797c7d0b15164c7873294ce3df44e287efc195c126b0afec6'
const pool = sdk.Pool.getPool(poolId)
const lowerTick = TickMath.getPrevInitializableTickIndex(
  new BN(pool.current_tick_index).toNumber(),
  new BN(pool.tickSpacing).toNumber()
)
const upperTick = TickMath.getNextInitializableTickIndex(
  new BN(pool.current_tick_index).toNumber(),
  new BN(pool.tickSpacing).toNumber()
)
const coinAmount = new BN(100)
const fix_amount_a = true
const slippage = 0.01
const curSqrtPrice = new BN(pool.current_sqrt_price)

const liquidityInput = ClmmPoolUtil.estLiquidityAndcoinAmountFromOneAmounts(
  lowerTick,
  upperTick,
  coinAmount,
  fix_amount_a,
  true,
  slippage,
  curSqrtPrice
)

const amount_a = fix_amount_a ? coinAmount.toNumber() : liquidityInput.tokenMaxA.toNumber()
const amount_b = fix_amount_a ? liquidityInput.tokenMaxB.toNumber() : coinAmount.toNumber()

const addLiquidityPayloadParams: AddLiquidityFixTokenParams = {
  coinTypeA: pool.coinTypeA,
  coinTypeB: pool.coinTypeB,
  pool_id: pool.poolAddress,
  tick_lower: lowerTick.toString(),
  tick_upper: upperTick.toString(),
  fix_amount_a,
  amount_a,
  amount_b,
  slippage,
  is_open: true,
  rewarder_coin_types: [],
  collect_fee: false,
  pos_id: '',
}
const tx = await sdk.Position.createAddLiquidityFixTokenPayload(addLiquidityPayloadParams, {
  slippage: slippage,
  curSqrtPrice: curSqrtPrice,
})

const result = await sdk.fullClient.signAndExecuteTransaction({
  transaction: tx,
  signer: keypair
})
```

### Important Notes

* SDK automatically collects fees before adding liquidity
* Excess tokens are returned to sender
* Position must exist before adding liquidity
* Cannot change position's tick range


# Remove Liquidity

Withdraw tokens from a liquidity position in Ferra pools.

### Quick Start

```typescript
const tx = await sdk.Position.removeLiquidityTransactionPayload({
  pool_id: '0x...',
  pos_id: '0x...',
  coinTypeA: '0x2::sui::SUI',
  coinTypeB: '0x...::usdc::USDC',
  delta_liquidity: '500000',
  min_amount_a: '490000000',
  min_amount_b: '490000'
  collect_fee: true
  rewarder_coin_types: [],
})
```

### Parameters

| Parameter             | Type      | Description                                           |
| --------------------- | --------- | ----------------------------------------------------- |
| `pool_id`             | string    | The object id about which pool you want to operation. |
| `pos_id`              | string    | Position object ID                                    |
| `coinTypeA`           | string    | First token type                                      |
| `coinTypeB`           | string    | Second token type                                     |
| `delta_liquidity`     | string    | Liquidity amount to remove                            |
| `min_amount_a`        | string    | Minimum token A to receive                            |
| `min_amount_b`        | string    | Minimum token B to receive                            |
| `collect_fee`         | boolean   | Indicates whether to collect fees during the removal. |
| `rewarder_coin_types` | string\[] | Coin types associated with rewarder contracts.        |

### Calculate Token Amounts

```typescript
async function estimateRemovePosition(
  pool,
  position,
  amountABigint,
  amountBBigint
) {
  const slippage = new Percentage(new BN(5), new BN(1000));
  const { curSqrtPrice, lowerSqrtPrice, upperSqrtPrice } = getCoinsRate(
    pool,
    position
  );

  const tokenAmounts = adjustForCoinSlippage(
    {
      coinA: new BN(amountABigint.toFixed(0)),
      coinB: new BN(amountBBigint.toFixed(0)),
    },
    slippage,
    false
  );
  const liquidity = ClmmPoolUtil.estimateLiquidityFromcoinAmounts(
    curSqrtPrice,
    Number(position.tick_lower_index),
    Number(position.tick_upper_index),
    {
      coinA: new BN(Math.ceil(amountABigint)),
      coinB: new BN(Math.ceil(amountBBigint)),
    }
  );
  return {
    tokenMaxA: tokenAmounts.tokenMaxA,
    tokenMaxB: tokenAmounts.tokenMaxB,
    lowerTick: lowerSqrtPrice,
    upperTick: upperSqrtPrice,
    curSqrtPrice,
    liquidity,
  };
}

function getCoinsRate(pool, position) {
  const curSqrtPrice = new BN(pool.current_sqrt_price);

  const lowerSqrtPrice = TickMath.tickIndexToSqrtPriceX64(
    position.tick_lower_index
  );

  const upperSqrtPrice = TickMath.tickIndexToSqrtPriceX64(
    position.tick_upper_index
  );

  const amounts = ClmmPoolUtil.getCoinAmountFromLiquidity(
    new BN(position.liquidity),
    curSqrtPrice,
    lowerSqrtPrice,
    upperSqrtPrice,
    false
  );

  const coinAValue = fromDecimalsAmount(
    amounts.coinA.toNumber(),
    pool.coinA?.decimals ?? 1
  );

  const coinBValue = fromDecimalsAmount(
    amounts.coinB.toNumber(),
    pool.coinB?.decimals ?? 1
  );

  const total = coinAValue + coinBValue;
  const percent = {
    ratioA: coinAValue / total,
    ratioB: coinBValue / total,
  };

  return {
    percent,
    curSqrtPrice,
    lowerSqrtPrice,
    upperSqrtPrice,
  };
}

```

### Complete Example

```typescript
const position = await sdk.Position.getPositionById(positionId)
const pool = await sdk.Pool.getPool(position.pool)
const amountA = 100000000
const amountB = 2000000000
const percent = 0.2 
const { tokenMaxA, tokenMaxB, liquidity } = await estimateRemovePosition(poolOnchain, position, amountA, amountB);
const rewards = await ferraSDK.Rewarder.fetchPositionRewarders(
        poolOnchain,
        position.pos_object_id
      );
const rewardCoinTypes = rewards
        .filter((item) => Number(item.amount_owed) > 0)
        .map((item) => item.coin_address);
const removeLiquidityPayloadParams: RemoveLiquidityParams = {
        coinTypeA: poolOnchain.coinTypeA,
        coinTypeB: poolOnchain.coinTypeB,
        pool_id: poolOnchain.poolAddress,
        min_amount_a: tokenMaxA.toString(),
        min_amount_b: tokenMaxB.toString(),
        collect_fee: true,
        rewarder_coin_types: rewardCoinTypes,
        pos_id: position.pos_object_id,
        delta_liquidity: new BN(position.liquidity).lte(new BN(liquidity))
          ? position.liquidity.toString()
          : liquidity.toString(),
      };
const tx = await sdk.Position.removeLiquidityTransactionPayload(removeLiquidityPayloadParams);  
const result = await sdk.fullClient.signAndExecuteTransaction({
  transaction: tx,
  signer: keypair
})
```

### Error Handling

```typescript
try {
  const tx = await sdk.Position.removeLiquidityTransactionPayload(params)
} catch (error) {
  if (error.message.includes('Slippage exceeded')) {
    console.log('Price moved too much, increase slippage')
  }
  if (error.message.includes('Insufficient liquidity')) {
    console.log('Not enough liquidity in position')
  }
}
```

### Important Notes

* SDK automatically collects fees before removing liquidity
* Tokens are sent directly to sender address
* Cannot remove more liquidity than position contains
* Position NFT remains after removing all liqui


# Close Position

Remove all liquidity and close a position permanently.

### Quick Start

```typescript
const tx = await sdk.Position.closePositionTransactionPayload({
  pool_id: '0x...',
  pos_id: '0x...',
  coinTypeA: '0x2::sui::SUI',
  coinTypeB: '0x...::usdc::USDC',
  min_amount_a: '490000000',
  min_amount_b: '490000'
  collect_fee: true
  rewarder_coin_types: [],
})
```

### Parameters

| Parameter             | Type      | Description                                           |
| --------------------- | --------- | ----------------------------------------------------- |
| `pool_id`             | string    | The object id about which pool you want to operation. |
| `pos_id`              | string    | Position object ID                                    |
| `coinTypeA`           | string    | First token type                                      |
| `coinTypeB`           | string    | Second token type                                     |
| `min_amount_a`        | string    | Minimum token A to receive                            |
| `min_amount_b`        | string    | Minimum token B to receive                            |
| `collect_fee`         | boolean   | Indicates whether to collect fees during the removal. |
| `rewarder_coin_types` | string\[] | Coin types associated with rewarder contracts.        |

### Close with Slippage Protection

```typescript
async function estimateClosePosition(
  pool,
  position,
  amountABigint,
  amountBBigint
) {
  const slippage = new Percentage(new BN(5), new BN(1000));
  const { curSqrtPrice, lowerSqrtPrice, upperSqrtPrice } = getCoinsRate(
    pool,
    position
  );

  const tokenAmounts = adjustForCoinSlippage(
    {
      coinA: new BN(amountABigint.toFixed(0)),
      coinB: new BN(amountBBigint.toFixed(0)),
    },
    slippage,
    false
  );
  const liquidity = ClmmPoolUtil.estimateLiquidityFromcoinAmounts(
    curSqrtPrice,
    Number(position.tick_lower_index),
    Number(position.tick_upper_index),
    {
      coinA: new BN(Math.ceil(amountABigint)),
      coinB: new BN(Math.ceil(amountBBigint)),
    }
  );
  return {
    tokenMaxA: tokenAmounts.tokenMaxA,
    tokenMaxB: tokenAmounts.tokenMaxB,
    lowerTick: lowerSqrtPrice,
    upperTick: upperSqrtPrice,
    curSqrtPrice,
    liquidity,
  };
}

function getCoinsRate(pool, position) {
  const curSqrtPrice = new BN(pool.current_sqrt_price);

  const lowerSqrtPrice = TickMath.tickIndexToSqrtPriceX64(
    position.tick_lower_index
  );

  const upperSqrtPrice = TickMath.tickIndexToSqrtPriceX64(
    position.tick_upper_index
  );

  const amounts = ClmmPoolUtil.getCoinAmountFromLiquidity(
    new BN(position.liquidity),
    curSqrtPrice,
    lowerSqrtPrice,
    upperSqrtPrice,
    false
  );

  const coinAValue = fromDecimalsAmount(
    amounts.coinA.toNumber(),
    pool.coinA?.decimals ?? 1
  );

  const coinBValue = fromDecimalsAmount(
    amounts.coinB.toNumber(),
    pool.coinB?.decimals ?? 1
  );

  const total = coinAValue + coinBValue;
  const percent = {
    ratioA: coinAValue / total,
    ratioB: coinBValue / total,
  };

  return {
    percent,
    curSqrtPrice,
    lowerSqrtPrice,
    upperSqrtPrice,
  };
}

```

### Complete Example

<pre class="language-typescript"><code class="lang-typescript">const position = await sdk.Position.getPositionById(positionId)
const pool = await sdk.Pool.getPool(position.pool)
const amountA = 100000000
const amountB = 2000000000
const percent = 0.2 
const { tokenMaxA, tokenMaxB, liquidity } = await estimateRemovePosition(poolOnchain, position, amountA, amountB);
const rewards = await ferraSDK.Rewarder.fetchPositionRewarders(
        poolOnchain,
        position.pos_object_id
      );
const rewardCoinTypes = rewards
        .filter((item) => Number(item.amount_owed) > 0)
        .map((item) => item.coin_address);
const closeLiquidityPayloadParams: ClosePositionParams = {
        coinTypeA: poolOnchain.coinTypeA,
        coinTypeB: poolOnchain.coinTypeB,
        pool_id: poolOnchain.poolAddress,
        min_amount_a: tokenMaxA.toString(),
        min_amount_b: tokenMaxB.toString(),
        collect_fee: true,
        rewarder_coin_types: rewardCoinTypes,
        pos_id: position.pos_object_id,
      };
<strong>const tx = await sdk.Position.closePositionTransactionPayload(closeLiquidityPayloadParams);       
</strong>const result = await sdk.fullClient.signAndExecuteTransaction({
  transaction: tx,
  signer: keypair
})
</code></pre>

### Close vs Remove Liquidity

| Action           | Effect                    | Position NFT    |
| ---------------- | ------------------------- | --------------- |
| Remove Liquidity | Withdraws tokens          | Remains (empty) |
| Close Position   | Withdraws all + burns NFT | Destroyed       |

### Error Handling

```typescript
try {
  const tx = await sdk.Position.closePositionTransactionPayload(params)
} catch (error) {
  if (error.message.includes('Slippage exceeded')) {
    console.log('Price moved, increase slippage')
  }
  if (error.message.includes('Position not found')) {
    console.log('Position does not exist')
  }
}
```

### Important Notes

* Closing automatically collects all fees and rewards
* Position NFT is burned and cannot be recovered
* All liquidity must be removed (SDK handles this)
* Cannot close position with active orders
*


# Collect Rewards

Claim trading fees and pool rewards from liquidity positions.

### Overview

Positions earn two types of rewards:

* **Trading Fees**: Earned from swaps in your price range
* **Pool Rewards**: Additional incentive tokens from the protocol

### Quick Start

```typescript
// Collect only trading fees
const tx = await sdk.Position.collectFeeTransactionPayload({
  pool_id: '0x...',
  pos_id: '0x...',
  coinTypeA: '0x2::sui::SUI',
  coinTypeB: '0x...::usdc::USDC'
})

// Collect fees + rewards
const tx = await sdk.Rewarder.collectRewarderTransactionPayload({
  pool_id: '0x...',
  pos_id: '0x...',
  coinTypeA: '0x2::sui::SUI',
  coinTypeB: '0x...::usdc::USDC',
  rewarder_coin_types: ['0x...::ferra::FERRA'],
  collect_fee: true
})
```

### Check Claimable Rewards

#### Trading Fees Only

```typescript
const fees = await sdk.Position.calculateFee({
  pool_id: poolId,
  pos_id: positionId,
  coinTypeA: '0x2::sui::SUI',
  coinTypeB: '0x...::usdc::USDC'
})

console.log({
  tokenA: fees.feeOwedA,
  tokenB: fees.feeOwedB
})
```

#### All Rewards (Fees + Pool Rewards)

```typescript
// Get position with full reward info
const position = await sdk.Position.getPositionById(positionId, true)

// Check pool rewards
if (position.rewards) {
  position.rewards.forEach(reward => {
    console.log({
      token: reward.coin_type,
      amount: reward.amount_owed
    })
  })
}

// Or use Rewarder module
const pool = await sdk.Pool.getPool(poolId)
const rewards = await sdk.Rewarder.fetchPositionRewarders(pool, positionId)

rewards.forEach(reward => {
  console.log({
    coin: reward.coin_address,
    amount: reward.amount_owed.toString()
  })
})
```

### Collect Trading Fees

```typescript
const params = {
  pool_id: poolId,
  pos_id: positionId,
  coinTypeA: pool.coinTypeA,
  coinTypeB: pool.coinTypeB
}

// Create transaction
const tx = await sdk.Position.collectFeeTransactionPayload(params)

// Execute
const result = await sdk.fullClient.signAndExecuteTransaction({
  transaction: tx,
  signer: keypair
})

console.log('Fees collected:', result.digest)
```

### Collect Pool Rewards

Collect additional reward tokens (e.g., FERRA tokens):

```typescript
// Get pool info to check rewarders
const pool = await sdk.Pool.getPool(poolId)
const rewarderTokens = pool.rewarder_infos.map(r => r.coinAddress)

// Collect rewards + fees
const tx = await sdk.Rewarder.collectRewarderTransactionPayload({
  pool_id: poolId,
  pos_id: positionId,
  coinTypeA: pool.coinTypeA,
  coinTypeB: pool.coinTypeB,
  rewarder_coin_types: rewarderTokens,
  collect_fee: true  // Also collect trading fees
})

const result = await sdk.fullClient.signAndExecuteTransaction({
  transaction: tx,
  signer: keypair
})
```

### Batch Collect

Collect from multiple positions efficiently:

```typescript
const positions = await sdk.Position.getPositionList(userAddress)

// Prepare batch params
const batchParams = []
for (const pos of positions) {
  const pool = await sdk.Pool.getPool(pos.pool)
  
  // Check if position has rewards
  const rewards = await sdk.Rewarder.fetchPositionRewarders(pool, pos.pos_object_id)
  const fees = await sdk.Position.calculateFee({
    pool_id: pos.pool,
    pos_id: pos.pos_object_id,
    coinTypeA: pos.coin_type_a,
    coinTypeB: pos.coin_type_b
  })
  
  const hasRewards = rewards.some(r => r.amount_owed.gt(new BN(0)))
  const hasFees = BigInt(fees.feeOwedA) > 0n || BigInt(fees.feeOwedB) > 0n
  
  if (hasRewards || hasFees) {
    batchParams.push({
      pool_id: pos.pool,
      pos_id: pos.pos_object_id,
      coinTypeA: pos.coin_type_a,
      coinTypeB: pos.coin_type_b,
      rewarder_coin_types: pool.rewarder_infos.map(r => r.coinAddress),
      collect_fee: true
    })
  }
}

// Execute batch collection
if (batchParams.length > 0) {
  const tx = await sdk.Rewarder.batchCollectRewardePayload(batchParams)
  const result = await sdk.fullClient.signAndExecuteTransaction({
    transaction: tx,
    signer: keypair
  })
  console.log(`Collected from ${batchParams.length} positions`)
}
```

### Pool Rewards Info

Check pool reward emissions:

```typescript
// Get daily emissions for each rewarder
const emissions = await sdk.Rewarder.emissionsEveryDay(poolId)

emissions?.forEach(emission => {
  console.log({
    token: emission.coin_address,
    dailyAmount: emission.emissions
  })
})

// Get total pool rewards for all your positions
const totalRewards = await sdk.Rewarder.fetchPoolRewardersAmount(
  userAddress,
  poolId
)

console.log('Total rewards:', totalRewards)
```

### Complete Example

```typescript
// 1. Get all positions
const positions = await sdk.Position.getPositionList(userAddress)

// 2. Check each position's rewards
for (const pos of positions) {
  const pool = await sdk.Pool.getPool(pos.pool)
  
  // Check trading fees
  const fees = await sdk.Position.calculateFee({
    pool_id: pos.pool,
    pos_id: pos.pos_object_id,
    coinTypeA: pos.coin_type_a,
    coinTypeB: pos.coin_type_b
  })
  
  // Check pool rewards
  const rewards = await sdk.Rewarder.fetchPositionRewarders(pool, pos.pos_object_id)
  
  console.log(`Position ${pos.pos_object_id}:`)
  console.log(`  Fees: ${fees.feeOwedA} / ${fees.feeOwedB}`)
  rewards.forEach((r, i) => {
    console.log(`  Reward ${i}: ${r.amount_owed} ${r.coin_address}`)
  })
  
  // Collect if has rewards
  const hasRewards = rewards.some(r => r.amount_owed.gt(new BN(0)))
  const hasFees = BigInt(fees.feeOwedA) > 0n || BigInt(fees.feeOwedB) > 0n
  
  if (hasRewards || hasFees) {
    const tx = await sdk.Rewarder.collectRewarderTransactionPayload({
      pool_id: pos.pool,
      pos_id: pos.pos_object_id,
      coinTypeA: pos.coin_type_a,
      coinTypeB: pos.coin_type_b,
      rewarder_coin_types: pool.rewarder_infos.map(r => r.coinAddress),
      collect_fee: true
    })
    
    const result = await sdk.fullClient.signAndExecuteTransaction({
      transaction: tx,
      signer: keypair
    })
    
    console.log('  Collected:', result.digest)
  }
}
```

### Important Notes

* Trading fees accumulate from swaps in your price range
* Pool rewards are additional incentives (e.g., FERRA tokens)
* Use `collect_fee: true` to collect both in one transaction
* Rewards are sent directly to sender address
* Some operations auto-collect rewards (add/remove liquidity)
* Batch collection saves gas for multiple positions


# Calculate Swap Rates

### Overview

`calculateRates` is a method used to calculate and estimate swap results before executing an actual transaction. This method helps determine input/output token amounts, transaction fees, and other important parameters without spending gas.

### Method Signature

```typescript
sdk.Swap.calculateRates(params: CalculateRatesParams): CalculateRatesResult
```

### Parameters

| Parameter     | Type      | Description                                                                                               |
| ------------- | --------- | --------------------------------------------------------------------------------------------------------- |
| `decimalsA`   | `number`  | Number of decimals for token A                                                                            |
| `decimalsB`   | `number`  | Number of decimals for token B                                                                            |
| `a2b`         | `boolean` | Swap direction. `true` for A→B, `false` for B→A                                                           |
| `byAmountIn`  | `boolean` | Calculation mode. `true` to specify input amount, `false` to specify output amount                        |
| `amount`      | `BN`      | Amount to swap (in smallest unit). Input amount if `byAmountIn=true`, output amount if `byAmountIn=false` |
| `swapTicks`   | `Tick[]`  | Array of tick data from the pool                                                                          |
| `currentPool` | `Pool`    | Current pool object containing pool state                                                                 |

### Return Values

| Field                   | Type      | Description                                     |
| ----------------------- | --------- | ----------------------------------------------- |
| `estimatedAmountIn`     | `BN`      | Estimated amount of tokens to be swapped in     |
| `estimatedAmountOut`    | `BN`      | Estimated amount of tokens to be received       |
| `estimatedEndSqrtPrice` | `BN`      | Estimated sqrt price after swap                 |
| `estimatedFeeAmount`    | `BN`      | Estimated fee amount for the swap               |
| `isExceed`              | `boolean` | Whether the swap exceeds pool liquidity         |
| `extraComputeLimit`     | `number`  | Additional compute units needed for transaction |
| `amount`                | `BN`      | The input amount used for calculation           |
| `aToB`                  | `boolean` | Swap direction used in calculation              |
| `byAmountIn`            | `boolean` | Calculation mode used                           |

### Examples

#### Example 1: Calculate swap from token B to token A by specifying input amount

```javascript
const pool = await sdk.Pool.getPool('0xc8d7a1503dc2f9f5b05449a87d8733593e2f0f3e7bffd90541252782e4d2ca20')

// Fetch current tick data
const swapTicks = await sdk.Pool.fetchTicks({
  pool_id: pool.poolAddress,
  coinTypeA: pool.coinTypeA,
  coinTypeB: pool.coinTypeB,
})

// Calculate rates for swapping 80 token B to token A
const result = sdk.Swap.calculateRates({
  decimalsA: 6,
  decimalsB: 6,
  a2b: false,           // B to A swap
  byAmountIn: true,     // Specifying input amount
  amount: new BN('80000000'),  // 80 tokens with 6 decimals
  swapTicks: swapTicks,
  currentPool: pool,
})

console.log('Swap Calculation Results:')
console.log('Input Amount:', result.estimatedAmountIn.toString())
console.log('Output Amount:', result.estimatedAmountOut.toString())
console.log('Fee Amount:', result.estimatedFeeAmount.toString())
console.log('Exceeds Liquidity:', result.isExceed)
```

#### Example 2: Calculate swap from token A to token B by specifying output amount

```javascript
const pool = await sdk.Pool.getPool('0xYOUR_POOL_ADDRESS')
const swapTicks = await sdk.Pool.fetchTicks({
  pool_id: pool.poolAddress,
  coinTypeA: pool.coinTypeA,
  coinTypeB: pool.coinTypeB,
})

// Calculate how much token A needed to get exactly 100 token B
const result = sdk.Swap.calculateRates({
  decimalsA: 9,
  decimalsB: 6,
  a2b: true,            // A to B swap
  byAmountIn: false,    // Specifying output amount
  amount: new BN('100000000'),  // Want exactly 100 token B
  swapTicks: swapTicks,
  currentPool: pool,
})

console.log('To receive', result.estimatedAmountOut.toString(), 'token B')
console.log('You need', result.estimatedAmountIn.toString(), 'token A')
console.log('Estimated fee:', result.estimatedFeeAmount.toString())
```

#### Example 3: Check for large swap impact

```javascript
const pool = await sdk.Pool.getPool('0xYOUR_POOL_ADDRESS')
const swapTicks = await sdk.Pool.fetchTicks({
  pool_id: pool.poolAddress,
  coinTypeA: pool.coinTypeA,
  coinTypeB: pool.coinTypeB,
})

// Try to swap a large amount
const largeAmount = new BN('1000000000000')  // 1M tokens with 6 decimals

const result = sdk.Swap.calculateRates({
  decimalsA: 6,
  decimalsB: 6,
  a2b: true,
  byAmountIn: true,
  amount: largeAmount,
  swapTicks: swapTicks,
  currentPool: pool,
})

if (result.isExceed) {
  console.log('⚠️ Warning: Swap exceeds available liquidity!')
  console.log('Maximum possible output:', result.estimatedAmountOut.toString())
} else {
  console.log('✅ Swap can be executed')
  console.log('Expected output:', result.estimatedAmountOut.toString())
}

// Check price impact
const priceImpact = calculatePriceImpact(
  pool.current_sqrt_price,
  result.estimatedEndSqrtPrice
)
console.log('Price impact:', priceImpact, '%')
```

### Important Notes

1. **Amount Units**: All amounts must be in the smallest unit (considering decimals). For example, 1 token with 6 decimals = 1000000.
2. **Tick Data**: Always fetch fresh tick data before calculating rates to ensure accurate results:

   ```javascript
   // Option 1: Fetch via API
   const swapTicks = await sdk.Pool.fetchTicks({
     pool_id: pool.poolAddress,
     coinTypeA: pool.coinTypeA,
     coinTypeB: pool.coinTypeB,
   })

   // Option 2: Fetch via RPC (if available)
   const swapTicks = await sdk.Pool.fetchTicksByRpc(pool.ticks_handle)
   ```
3. **Gas Estimation**: Use `extraComputeLimit` to adjust transaction gas limits when executing the actual swap:

   ```javascript
   const txOptions = {
     gasLimit: DEFAULT_GAS_LIMIT + result.extraComputeLimit
   }
   ```
4. **Slippage Protection**: The calculated amounts are estimates. Always implement slippage tolerance when executing actual swaps:

   ```javascript
   const slippageTolerance = 0.01  // 1%
   const minAmountOut = result.estimatedAmountOut * (1 - slippageTolerance)
   ```
5. **Error Handling**: Always check `isExceed` before executing swaps to avoid failed transactions due to insufficient liquidity.


# PreSwap

Calculate swap amounts and check price impact before executing trades.

### Quick Start

```typescript
// Simulate swap before execution
const pool = await sdk.Pool.getPool(poolId)

const preSwapResult = await sdk.Swap.preswap({
  pool: pool,
  coinTypeA: pool.coinTypeA,
  coinTypeB: pool.coinTypeB,
  decimalsA: 9,  // SUI decimals
  decimalsB: 6,  // USDC decimals
  a2b: true,     // SUI -> USDC
  byAmountIn: true,
  amount: '1000000000',  // 1 SUI
  currentSqrtPrice: pool.current_sqrt_price
})

console.log({
  amountIn: preSwapResult.estimatedAmountIn,
  amountOut: preSwapResult.estimatedAmountOut,
  fee: preSwapResult.estimatedFeeAmount,
  priceAfter: preSwapResult.estimatedEndSqrtPrice
})
```

### PreSwap Methods

#### Single Pool PreSwap

```typescript
const params = {
  pool: pool,
  coinTypeA: '0x2::sui::SUI',
  coinTypeB: '0x...::usdc::USDC',
  decimalsA: 9,
  decimalsB: 6,
  a2b: true,           // Direction: A to B
  byAmountIn: true,    // Fix input amount
  amount: '1000000000',
  currentSqrtPrice: pool.current_sqrt_price
}

const result = await sdk.Swap.preswap(params)

if (result.isExceed) {
  console.log('Swap exceeds pool liquidity')
}
```

#### Multi-Pool PreSwap

Find best pool for swap:

```typescript
const pools = ['0xpool1...', '0xpool2...', '0xpool3...']

const result = await sdk.Swap.preSwapWithMultiPool({
  poolAddresses: pools,
  coinTypeA: '0x2::sui::SUI',
  coinTypeB: '0x...::usdc::USDC',
  a2b: true,
  byAmountIn: true,
  amount: '1000000000'
})

console.log('Best pool:', result.poolAddress)
console.log('Best output:', result.estimatedAmountOut)
```

### Calculate Price Impact

```typescript
// Local calculation with tick data
const ticks = await sdk.Pool.fetchTicks({
  pool_id: poolId,
  coinTypeA: pool.coinTypeA,
  coinTypeB: pool.coinTypeB
})

const rateResult = sdk.Swap.calculateRates({
  currentPool: pool,
  coinTypeA: pool.coinTypeA,
  coinTypeB: pool.coinTypeB,
  decimalsA: 9,
  decimalsB: 6,
  a2b: true,
  byAmountIn: true,
  amount: new BN('1000000000'),
  swapTicks: ticks
})

console.log({
  priceImpact: rateResult.priceImpactPct.toFixed(2) + '%',
  isExceed: rateResult.isExceed,
  extraGas: rateResult.extraComputeLimit
})
```

### Response Types

```typescript
interface PreSwapResult {
  poolAddress: string
  estimatedAmountIn: string
  estimatedAmountOut: string
  estimatedEndSqrtPrice: string
  estimatedFeeAmount: string
  isExceed: boolean        // Exceeds liquidity
  amount: string
  aToB: boolean
  byAmountIn: boolean
}

interface CalculateRatesResult {
  estimatedAmountIn: BN
  estimatedAmountOut: BN
  estimatedEndSqrtPrice: BN
  estimatedFeeAmount: BN
  isExceed: boolean
  extraComputeLimit: number
  priceImpactPct: number
}
```

### Usage Examples

#### Check Slippage

```typescript
// 1. PreSwap to get expected output
const preSwap = await sdk.Swap.preswap(params)

// 2. Calculate minimum output with 1% slippage
const expectedOut = new BN(preSwap.estimatedAmountOut)
const minOutput = expectedOut.mul(new BN(99)).div(new BN(100))

console.log({
  expected: expectedOut.toString(),
  minimum: minOutput.toString()
})
```

#### Compare Pools

```typescript
const pools = await sdk.Pool.getPools()
const suiUsdcPools = pools.filter(p => 
  p.coinTypeA.includes('sui') && 
  p.coinTypeB.includes('usdc')
)

// Test each pool
const results = []
for (const pool of suiUsdcPools) {
  const result = await sdk.Swap.preswap({
    pool,
    coinTypeA: pool.coinTypeA,
    coinTypeB: pool.coinTypeB,
    decimalsA: 9,
    decimalsB: 6,
    a2b: true,
    byAmountIn: true,
    amount: '1000000000',
    currentSqrtPrice: pool.current_sqrt_price
  })
  
  if (!result.isExceed) {
    results.push({
      pool: pool.poolAddress,
      output: result.estimatedAmountOut,
      fee: result.estimatedFeeAmount
    })
  }
}

// Find best rate
const best = results.sort((a, b) => 
  Number(b.output) - Number(a.output)
)[0]
```

#### Fix Output Amount

```typescript
// Want exactly 1000 USDC output
const params = {
  pool,
  coinTypeA: pool.coinTypeA,
  coinTypeB: pool.coinTypeB,
  decimalsA: 9,
  decimalsB: 6,
  a2b: true,
  byAmountIn: false,    // Fix output
  amount: '1000000000', // 1000 USDC
  currentSqrtPrice: pool.current_sqrt_price
}

const result = await sdk.Swap.preswap(params)
console.log('Need SUI:', result.estimatedAmountIn)
```

### Important Notes

* PreSwap uses simulation for accurate results
* Multi-pool automatically finds best execution
* `isExceed` indicates insufficient liquidity
* Always check price impact before large swaps
* Results include fees in calculations


# Swap

Execute token swaps on Ferra pools.

### Quick Start

```typescript
// Simple swap: 1 SUI -> USDC
const swapParams = {
  pool_id: '0x...',
  coinTypeA: '0x2::sui::SUI',
  coinTypeB: '0x...::usdc::USDC',
  a2b: true,              // SUI -> USDC
  by_amount_in: true,     // Fix input amount
  amount: '1000000000',   // 1 SUI
  amount_limit: '990000', // Min 990 USDC (1% slippage)
}

const tx = await sdk.Swap.createSwapTransactionPayload(swapParams)

const result = await sdk.fullClient.signAndExecuteTransaction({
  transaction: tx,
  signer: keypair
})
```

### Swap Parameters

| Parameter      | Type    | Description                        |
| -------------- | ------- | ---------------------------------- |
| `pool_id`      | string  | Pool object ID                     |
| `coinTypeA`    | string  | First token type                   |
| `coinTypeB`    | string  | Second token type                  |
| `a2b`          | boolean | Direction (true: A→B, false: B→A)  |
| `by_amount_in` | boolean | Fix input (true) or output (false) |
| `amount`       | string  | Fixed amount                       |
| `amount_limit` | string  | Slippage limit                     |
| `swap_partner` | string  | Optional partner for fees          |

### Swap Directions

#### Swap A to B

```typescript
const params = {
  pool_id: poolId,
  coinTypeA: '0x2::sui::SUI',
  coinTypeB: '0x...::usdc::USDC',
  a2b: true,              // SUI -> USDC
  by_amount_in: true,
  amount: '1000000000',   // Input: 1 SUI
  amount_limit: '990000'  // Min output: 990 USDC
}
```

#### Swap B to A

```typescript
const params = {
  pool_id: poolId,
  coinTypeA: '0x2::sui::SUI',
  coinTypeB: '0x...::usdc::USDC',
  a2b: false,             // USDC -> SUI
  by_amount_in: true,
  amount: '1000000',      // Input: 1000 USDC
  amount_limit: '990000000' // Min output: 0.99 SUI
}
```

### Fix Output Amount

Get exactly the amount you want:

```typescript
const params = {
  pool_id: poolId,
  coinTypeA: '0x2::sui::SUI',
  coinTypeB: '0x...::usdc::USDC',
  a2b: true,
  by_amount_in: false,    // Fix output amount
  amount: '1000000',      // Want exactly 1000 USDC
  amount_limit: '1010000000' // Max input: 1.01 SUI
}
```

### Calculate Slippage

```typescript
// 1. PreSwap to get expected amounts
const preSwap = await sdk.Swap.preswap({
  pool,
  coinTypeA: pool.coinTypeA,
  coinTypeB: pool.coinTypeB,
  decimalsA: 9,
  decimalsB: 6,
  a2b: true,
  byAmountIn: true,
  amount: '1000000000',
  currentSqrtPrice: pool.current_sqrt_price
})

// 2. Apply slippage (1%)
const slippage = 0.01
const expectedOut = Number(preSwap.estimatedAmountOut)
const minOutput = Math.floor(expectedOut * (1 - slippage))

// 3. Create swap transaction
const swapParams = {
  pool_id: pool.poolAddress,
  coinTypeA: pool.coinTypeA,
  coinTypeB: pool.coinTypeB,
  a2b: true,
  by_amount_in: true,
  amount: '1000000000',
  amount_limit: minOutput.toString()
}

const tx = await sdk.Swap.createSwapTransactionPayload(swapParams)
```

### Gas Optimization (SUI)

Optimize gas when swapping SUI:

```typescript
const gasConfig = {
  byAmountIn: true,
  slippage: new Percentage(1, 100), // 1%
  decimalsA: 9,
  decimalsB: 6,
  swapTicks: ticks,
  currentPool: pool
}

const tx = await sdk.Swap.createSwapTransactionPayload(
  swapParams,
  gasConfig
)
```

### Partner Swaps

Route fees to partners:

```typescript
const params = {
  ...swapParams,
  swap_partner: '0xpartner...' // Partner object ID
}

const tx = await sdk.Swap.createSwapTransactionPayload(params)
```

### Advanced Usage

#### Manual Coin Management

```typescript
// Get swap transaction without auto-transfer
const { tx, coinABs } = await sdk.Swap.createSwapTransactionWithoutTransferCoinsPayload(swapParams)

// coinABs[0] = coin A after swap
// coinABs[1] = coin B after swap

// Custom handling
tx.transferObjects([coinABs[1]], recipient)

const result = await sdk.fullClient.signAndExecuteTransaction({
  transaction: tx,
  signer: keypair
})
```

#### Batch Swaps

```typescript
const tx = new Transaction()

// First swap: SUI -> USDC
await sdk.Swap.createSwapTransactionPayload(swap1Params, undefined, tx)

// Second swap: USDC -> USDT
await sdk.Swap.createSwapTransactionPayload(swap2Params, undefined, tx)

// Execute both
const result = await sdk.fullClient.signAndExecuteTransaction({
  transaction: tx,
  signer: keypair
})
```

### Complete Example

```typescript
async function swapTokens() {
  // 1. Get pool
  const pool = await sdk.Pool.getPool(poolId)
  
  // 2. Check current price
  const price = TickMath.sqrtPriceX64ToPrice(
    pool.current_sqrt_price,
    9, // SUI decimals
    6  // USDC decimals
  )
  console.log('Current price:', price.toString())
  
  // 3. PreSwap simulation
  const preSwap = await sdk.Swap.preswap({
    pool,
    coinTypeA: pool.coinTypeA,
    coinTypeB: pool.coinTypeB,
    decimalsA: 9,
    decimalsB: 6,
    a2b: true,
    byAmountIn: true,
    amount: '1000000000',
    currentSqrtPrice: pool.current_sqrt_price
  })
  
  // 4. Check if swap is viable
  if (preSwap.isExceed) {
    throw new Error('Insufficient liquidity')
  }
  
  // 5. Create swap with 0.5% slippage
  const minOutput = Math.floor(Number(preSwap.estimatedAmountOut) * 0.995)
  
  const swapParams = {
    pool_id: pool.poolAddress,
    coinTypeA: pool.coinTypeA,
    coinTypeB: pool.coinTypeB,
    a2b: true,
    by_amount_in: true,
    amount: '1000000000',
    amount_limit: minOutput.toString()
  }
  
  // 6. Execute swap
  const tx = await sdk.Swap.createSwapTransactionPayload(swapParams)
  
  const result = await sdk.fullClient.signAndExecuteTransaction({
    transaction: tx,
    signer: keypair
  })
  
  console.log('Swap completed:', result.digest)
}
```

### Error Handling

```typescript
try {
  const tx = await sdk.Swap.createSwapTransactionPayload(swapParams)
} catch (error) {
  if (error.code === 'InvalidSendAddress') {
    sdk.senderAddress = '0x...'
  }
  if (error.message.includes('Insufficient balance')) {
    console.log('Not enough tokens')
  }
  if (error.message.includes('Slippage exceeded')) {
    console.log('Price moved, increase slippage')
  }
}
```

### Important Notes

* Always use `amount_limit` for slippage protection
* SDK automatically handles coin selection
* Excess coins are returned to sender
* Partner swaps share fees with partners
* Gas optimization available for SUI swaps


# Calculate Pool Metrics

Calculate key performance indicators for liquidity pools.

### Quick Start

```typescript
// Get pool and calculate basic metrics
const pool = await sdk.Pool.getPool(poolId)

// Current price
const price = TickMath.sqrtPriceX64ToPrice(
  pool.current_sqrt_price,
  9,  // decimalsA
  6   // decimalsB
)

// TVL in tokens
const tvlA = Number(pool.coinAmountA) / 10**9
const tvlB = Number(pool.coinAmountB) / 10**6

console.log({
  price: price.toFixed(4),
  tvlSUI: tvlA,
  tvlUSDC: tvlB
})
```

### Price Calculations

#### Current Price

```typescript
import { TickMath } from '@ferra-labs/clmm'

// From sqrt price
const price = TickMath.sqrtPriceX64ToPrice(
  pool.current_sqrt_price,
  pool.coinTypeA.decimals,
  pool.coinTypeB.decimals
)

// From tick index
const priceFromTick = TickMath.tickIndexToPrice(
  pool.current_tick_index,
  decimalsA,
  decimalsB
)
```

#### Price Range

```typescript
// Get min/max prices for a position
const position = await sdk.Position.getPositionById(positionId)

const minPrice = TickMath.tickIndexToPrice(
  position.tick_lower_index,
  decimalsA,
  decimalsB
)

const maxPrice = TickMath.tickIndexToPrice(
  position.tick_upper_index,
  decimalsA,
  decimalsB
)

console.log(`Price range: ${minPrice} - ${maxPrice}`)
```

### TVL Calculation

#### Pool TVL

```typescript
async function calculatePoolTVL(pool: Pool, priceInUSD: number) {
  // Get token amounts
  const amountA = Number(pool.coinAmountA) / 10**decimalsA
  const amountB = Number(pool.coinAmountB) / 10**decimalsB
  
  // Calculate USD values (assuming token B is USD stable)
  const tvlB_USD = amountB
  const tvlA_USD = amountA * priceInUSD
  
  const totalTVL = tvlA_USD + tvlB_USD
  
  return {
    tvl: totalTVL,
    breakdown: {
      tokenA: { amount: amountA, usd: tvlA_USD },
      tokenB: { amount: amountB, usd: tvlB_USD }
    }
  }
}
```

#### Position Value

```typescript
async function calculatePositionValue(positionId: string) {
  const position = await sdk.Position.getPositionById(positionId)
  const pool = await sdk.Pool.getPool(position.pool)
  
  // Get token amounts from liquidity
  const amounts = ClmmPoolUtil.getCoinAmountFromLiquidity(
    new BN(pool.current_sqrt_price),
    position.tick_lower_index,
    position.tick_upper_index,
    new BN(position.liquidity),
    false
  )
  
  const valueA = Number(amounts.coinA) / 10**decimalsA
  const valueB = Number(amounts.coinB) / 10**decimalsB
  
  return { valueA, valueB }
}
```

### APR Calculations

#### Fee APR

```typescript
async function calculateFeeAPR(poolId: string, period = 7) {
  // Get historical fees (from your API/indexer)
  const historicalFees = await getPoolFees(poolId, period)
  
  // Get current TVL
  const pool = await sdk.Pool.getPool(poolId)
  const tvl = await calculatePoolTVL(pool, currentPrice)
  
  // Calculate daily average
  const avgDailyFees = historicalFees.total / period
  
  // Annualize
  const annualFees = avgDailyFees * 365
  const feeAPR = (annualFees / tvl.tvl) * 100
  
  return feeAPR
}
```

#### Total APR with Rewards

```typescript
async function calculateTotalAPR(poolId: string) {
  // Fee APR
  const feeAPR = await calculateFeeAPR(poolId)
  
  // Reward APR
  const emissions = await sdk.Rewarder.emissionsEveryDay(poolId)
  const pool = await sdk.Pool.getPool(poolId)
  const tvl = await calculatePoolTVL(pool, currentPrice)
  
  let rewardAPR = 0
  if (emissions) {
    emissions.forEach(emission => {
      const yearlyEmissions = emission.emissions * 365
      const emissionValueUSD = yearlyEmissions * tokenPriceUSD
      rewardAPR += (emissionValueUSD / tvl.tvl) * 100
    })
  }
  
  return {
    feeAPR,
    rewardAPR,
    totalAPR: feeAPR + rewardAPR
  }
}
```

### Volume Metrics

#### Estimate from Fees

```typescript
function estimateVolume(fees: number, feeRate: number) {
  // Volume = Fees / Fee Rate
  const feePercent = feeRate / 1000000 // Convert from basis points
  return fees / feePercent
}

// Example: 24h volume from fees
async function get24hVolume(poolId: string) {
  const pool = await sdk.Pool.getPool(poolId)
  const feeRate = Number(pool.fee_rate)
  
  // Get 24h fees (from your data source)
  const fees24h = await get24hFees(poolId)
  
  return estimateVolume(fees24h, feeRate)
}
```

### Price Impact

#### Calculate for Swap

```typescript
async function calculatePriceImpact(
  pool: Pool,
  amount: BN,
  a2b: boolean,
  byAmountIn: boolean
) {
  // Get ticks for calculation
  const ticks = await sdk.Pool.fetchTicks({
    pool_id: pool.poolAddress,
    coinTypeA: pool.coinTypeA,
    coinTypeB: pool.coinTypeB
  })
  
  // Calculate swap result
  const result = sdk.Swap.calculateRates({
    currentPool: pool,
    coinTypeA: pool.coinTypeA,
    coinTypeB: pool.coinTypeB,
    decimalsA: 9,
    decimalsB: 6,
    a2b,
    byAmountIn,
    amount,
    swapTicks: ticks
  })
  
  return {
    priceImpact: result.priceImpactPct,
    effectivePrice: calculateEffectivePrice(result),
    isExceed: result.isExceed
  }
}
```

### Liquidity Distribution

#### Active Liquidity

```typescript
async function getActiveLiquidity(poolId: string) {
  const pool = await sdk.Pool.getPool(poolId)
  const currentTick = pool.current_tick_index
  
  // Get positions
  const positions = await sdk.Pool.getPositionList(
    pool.position_manager.positions_handle
  )
  
  // Calculate in-range liquidity
  let activeLiquidity = new BN(0)
  let totalLiquidity = new BN(0)
  
  positions.data.forEach(pos => {
    const liquidity = new BN(pos.liquidity)
    totalLiquidity = totalLiquidity.add(liquidity)
    
    if (pos.tick_lower_index <= currentTick && 
        currentTick < pos.tick_upper_index) {
      activeLiquidity = activeLiquidity.add(liquidity)
    }
  })
  
  return {
    active: activeLiquidity.toString(),
    total: totalLiquidity.toString(),
    utilization: activeLiquidity.mul(new BN(100)).div(totalLiquidity)
  }
}
```

### Complete Example

```typescript
async function getPoolMetrics(poolId: string) {
  const pool = await sdk.Pool.getPool(poolId)
  
  // 1. Price metrics
  const currentPrice = TickMath.sqrtPriceX64ToPrice(
    pool.current_sqrt_price, 9, 6
  )
  
  // 2. TVL calculation
  const tvl = await calculatePoolTVL(pool, currentPrice.toNumber())
  
  // 3. Volume (24h)
  const volume24h = await get24hVolume(poolId)
  
  // 4. APR calculation
  const apr = await calculateTotalAPR(poolId)
  
  // 5. Liquidity metrics
  const liquidity = await getActiveLiquidity(poolId)
  
  // 6. Fee tier
  const feeTier = Number(pool.fee_rate) / 10000 // basis points to percent
  
  return {
    price: currentPrice.toFixed(6),
    tvl: {
      usd: tvl.tvl.toFixed(2),
      tokenA: tvl.breakdown.tokenA,
      tokenB: tvl.breakdown.tokenB
    },
    volume24h: volume24h.toFixed(2),
    apr: {
      fee: apr.feeAPR.toFixed(2),
      rewards: apr.rewardAPR.toFixed(2),
      total: apr.totalAPR.toFixed(2)
    },
    liquidity: {
      utilization: `${liquidity.utilization}%`,
      active: liquidity.active,
      total: liquidity.total
    },
    feeTier: `${feeTier}%`
  }
}

// Usage
const metrics = await getPoolMetrics(poolId)
console.log('Pool Metrics:', metrics)
```

### Important Notes

* TVL calculation requires external price feeds
* APR is estimated based on historical data
* Volume calculation depends on fee collection data
* Price impact varies with liquidity distribution
* Metrics should be cached for performance
* Consider using indexer for historical data


# Router Swap

Execute optimized swaps through Ferra's smart router for best rates.

### Overview

The Router module automatically finds the optimal path for your swap by:

* **Path Discovery**: Finds all possible routes between tokens
* **TVL Optimization**: Prioritizes pools with higher liquidity
* **Multi-hop Support**: Routes through intermediate tokens when beneficial
* **Best Rate Selection**: Compares all paths to find optimal execution

### Quick Start

```typescript
// 1. Find best route
const bestRoute = await sdk.Router.getBestInternalRouter(
  '0x2::sui::SUI',           // From token
  '0x...::usdc::USDC',       // To token
  new BN('1000000000'),      // Amount (1 SUI)
  true,                      // Fix input amount
  0.01,                      // 1% slippage
  ''                         // No partner
)

// 2. Create swap transaction
if (bestRoute && !bestRoute.isExceed) {
  const tx = await TransactionUtil.buildRouterSwapTransaction(
    sdk,
    bestRoute.createTxParams,
    true,  // byAmountIn
    allCoinAssets
  )
  
  const result = await sdk.fullClient.signAndExecuteTransaction({
    transaction: tx,
    signer: keypair
  })
}
```

### Find Optimal Route

#### Basic Route Finding

```typescript
const bestRoute = await sdk.Router.getBestInternalRouter(
  fromCoin,      // Source token
  toCoin,        // Target token
  amount,        // Swap amount
  true,          // Fix input (true) or output (false)
  0.005,         // 0.5% slippage
  ''             // Partner ID (optional)
)

console.log({
  inputAmount: bestRoute.amountIn.toString(),
  outputAmount: bestRoute.amountOut.toString(),
  paths: bestRoute.paths.length,
  isExceed: bestRoute.isExceed
})
```

#### With Fallback Options

```typescript
// Fallback params for direct swap if no route found
const fallbackParams = {
  poolAddresses: ['0xpool1...', '0xpool2...'],
  coinTypeA: fromCoin,
  coinTypeB: toCoin,
  a2b: true,
  byAmountIn: true,
  amount: swapAmount.toString()
}

const bestRoute = await sdk.Router.getBestInternalRouter(
  fromCoin,
  toCoin,
  swapAmount,
  true,
  0.01,
  '',
  fallbackParams  // Will use direct swap if no route
)
```

### Route Types

#### Single-Hop Route

Direct swap in one pool:

```typescript
// Route: SUI -> USDC
bestRoute.paths[0] = {
  poolAddress: ['0xpool...'],
  a2b: [true],
  coinType: ['0x2::sui::SUI', '0x...::usdc::USDC'],
  amountIn: BN,
  amountOut: BN
}
```

#### Multi-Hop Route

Through intermediate token:

```typescript
// Route: SUI -> USDT -> USDC
bestRoute.paths[0] = {
  poolAddress: ['0xpool1...', '0xpool2...'],
  a2b: [true, true],
  coinType: ['0x2::sui::SUI', '0x...::usdt::USDT', '0x...::usdc::USDC'],
  amountIn: BN,
  amountOut: BN
}
```

### Complete Swap Flow

```typescript
async function smartRouterSwap() {
  try {
    // 1. Get user's coin assets
    const allCoinAssets = await sdk.getOwnerCoinAssets(userAddress)
    
    // 2. Define swap parameters
    const fromCoin = '0x2::sui::SUI'
    const toCoin = '0x...::usdc::USDC'
    const swapAmount = new BN('10000000000') // 10 SUI
    const slippage = 0.01 // 1%
    
    // 3. Find optimal route
    console.log('Finding best route...')
    const bestRoute = await sdk.Router.getBestInternalRouter(
      fromCoin,
      toCoin,
      swapAmount,
      true,     // Fix input
      slippage,
      ''        // No partner
    )
    
    // 4. Check if route is valid
    if (!bestRoute || bestRoute.isExceed) {
      throw new Error('No valid route found')
    }
    
    // 5. Display route info
    console.log('Route found:')
    console.log(`  Input: ${bestRoute.amountIn.toString()}`)
    console.log(`  Output: ${bestRoute.amountOut.toString()}`)
    console.log(`  Hops: ${bestRoute.paths[0].poolAddress.length}`)
    console.log(`  Pools: ${bestRoute.paths[0].poolAddress.join(' -> ')}`)
    
    // 6. Create swap transaction
    const tx = await TransactionUtil.buildRouterSwapTransaction(
      sdk,
      bestRoute.createTxParams,
      true,  // byAmountIn
      allCoinAssets
    )
    
    // 7. Execute swap
    const result = await sdk.fullClient.signAndExecuteTransaction({
      transaction: tx,
      signer: keypair
    })
    
    console.log('Swap completed:', result.digest)
    
  } catch (error) {
    console.error('Router swap failed:', error)
  }
}
```

### Load Graph Data

Router needs graph data to find paths:

```typescript
// Method 1: Auto-load from API
if (!sdk.Router.isGraphLoaded) {
  await sdk.Router.loadGraphData()
}

// Method 2: Manual load with custom data
const coinData = {
  coins: [
    { address: '0x2::sui::SUI', decimals: 9 },
    { address: '0x...::usdc::USDC', decimals: 6 },
    { address: '0x...::usdt::USDT', decimals: 6 }
  ]
}

const pathData = {
  paths: [
    {
      base: '0x2::sui::SUI',
      quote: '0x...::usdc::USDC',
      addressMap: new Map([[30, '0xpool1...']]) // 0.3% fee
    }
  ]
}

sdk.Router.loadGraph(coinData, pathData)
```

### Advanced Features

#### TVL-Based Routing

Router prioritizes high-TVL pools:

```typescript
// Router automatically sorts multi-hop paths by minimum TVL
const poolsWithTvl = await sdk.Router.getPoolWithTVL()

poolsWithTvl.forEach(pool => {
  console.log(`Pool ${pool.poolAddress}: ${pool.tvl}`)
})
```

#### Custom Path Preferences

```typescript
// Find route with specific requirements
const bestRoute = await sdk.Router.getBestInternalRouter(
  fromCoin,
  toCoin,
  swapAmount,
  true,
  0.005,    // Tighter slippage for large trades
  partnerID // Route fees to partner
)

// Check route quality
if (bestRoute.paths[0].poolAddress.length > 2) {
  console.log('Route too complex, consider direct swap')
}
```

#### Fix Output Amount

```typescript
// Want exactly 1000 USDC
const bestRoute = await sdk.Router.getBestInternalRouter(
  '0x2::sui::SUI',
  '0x...::usdc::USDC',
  new BN('1000000000'),  // 1000 USDC
  false,                 // Fix output
  0.01,
  ''
)

console.log(`Need ${bestRoute.amountIn} SUI`)
```

### Error Handling

```typescript
try {
  const bestRoute = await sdk.Router.getBestInternalRouter(
    fromCoin, toCoin, amount, true, 0.01, ''
  )
  
  if (!bestRoute) {
    console.log('No route available')
  } else if (bestRoute.isExceed) {
    console.log('Insufficient liquidity in pools')
  } else if (bestRoute.paths.length === 0) {
    console.log('No valid path found')
  }
  
} catch (error) {
  if (error.code === 'InvalidCoin') {
    console.log('Token not supported')
  } else if (error.code === 'NotFoundPath') {
    console.log('No trading path exists')
  }
}
```

### Route Information

```typescript
interface BestInternalRouterResult {
  amountIn: BN              // Input amount
  amountOut: BN             // Output amount
  paths: OnePath[]          // Route paths
  isExceed: boolean         // Exceeds liquidity
  createTxParams: SwapWithRouterParams  // Ready-to-use params
}

interface OnePath {
  poolAddress: string[]     // Pool addresses in order
  a2b: boolean[]           // Swap directions
  coinType: string[]       // Coin types in path
  amountIn: BN             // Path input
  amountOut: BN            // Path output
}
```

### Important Notes

* Router automatically loads graph data on first use
* Multi-hop limited to 2 pools (3 tokens max)
* TVL-based routing improves execution quality
* Partner fees not supported with split paths
* Always check `isExceed` before executing
* Graph data cached for performance


# Price Impact

Calculate and manage price impact for swaps in CLMM pools.

### Quick Start

```typescript
// Calculate price impact before swap
const pool = await sdk.Pool.getPool(poolId)
const ticks = await sdk.Pool.fetchTicks({
  pool_id: poolId,
  coinTypeA: pool.coinTypeA,
  coinTypeB: pool.coinTypeB
})

const result = sdk.Swap.calculateRates({
  currentPool: pool,
  coinTypeA: pool.coinTypeA,
  coinTypeB: pool.coinTypeB,
  decimalsA: 9,
  decimalsB: 6,
  a2b: true,
  byAmountIn: true,
  amount: new BN('1000000000'), // 1 SUI
  swapTicks: ticks
})

console.log(`Price Impact: ${result.priceImpactPct.toFixed(2)}%`)
```

### Understanding Price Impact

Price impact occurs when your trade moves the pool price:

* **Small trades**: Minimal impact (<0.1%)
* **Medium trades**: Noticeable impact (0.1-1%)
* **Large trades**: Significant impact (>1%)

### Calculate Methods

#### Method 1: Local Calculation

```typescript
async function calculatePriceImpact(
  poolId: string,
  amount: string,
  a2b: boolean,
  byAmountIn: boolean
) {
  // 1. Get pool and ticks
  const pool = await sdk.Pool.getPool(poolId)
  const ticks = await sdk.Pool.fetchTicks({
    pool_id: poolId,
    coinTypeA: pool.coinTypeA,
    coinTypeB: pool.coinTypeB
  })
  
  // 2. Calculate swap result
  const result = sdk.Swap.calculateRates({
    currentPool: pool,
    coinTypeA: pool.coinTypeA,
    coinTypeB: pool.coinTypeB,
    decimalsA: 9,
    decimalsB: 6,
    a2b,
    byAmountIn,
    amount: new BN(amount),
    swapTicks: ticks
  })
  
  return {
    priceImpact: result.priceImpactPct,
    currentPrice: TickMath.sqrtPriceX64ToPrice(
      pool.current_sqrt_price, 9, 6
    ),
    endPrice: TickMath.sqrtPriceX64ToPrice(
      result.estimatedEndSqrtPrice, 9, 6
    ),
    isExceed: result.isExceed
  }
}
```

#### Method 2: From PreSwap

```typescript
async function getImpactFromPreSwap(
  pool: Pool,
  amount: string,
  a2b: boolean
) {
  // Get current price
  const currentPrice = TickMath.sqrtPriceX64ToPrice(
    pool.current_sqrt_price,
    9, 6
  )
  
  // Simulate swap
  const preSwap = await sdk.Swap.preswap({
    pool,
    coinTypeA: pool.coinTypeA,
    coinTypeB: pool.coinTypeB,
    decimalsA: 9,
    decimalsB: 6,
    a2b,
    byAmountIn: true,
    amount,
    currentSqrtPrice: pool.current_sqrt_price
  })
  
  // Calculate end price
  const endPrice = TickMath.sqrtPriceX64ToPrice(
    new BN(preSwap.estimatedEndSqrtPrice),
    9, 6
  )
  
  // Price impact = |endPrice - currentPrice| / currentPrice * 100
  const impact = Math.abs(
    endPrice.minus(currentPrice).div(currentPrice).mul(100).toNumber()
  )
  
  return {
    impact,
    currentPrice: currentPrice.toNumber(),
    endPrice: endPrice.toNumber()
  }
}
```

### Router Price Impact

Calculate impact across multiple pools:

```typescript
async function calculateRouterImpact(splitPaths: SplitPath[]) {
  // Use SDK's built-in method
  const totalImpact = sdk.Swap.calculateSwapPriceImpact(splitPaths)
  
  console.log(`Total route impact: ${totalImpact}%`)
  
  // Or calculate manually for each path
  let weightedImpact = 0
  
  splitPaths.forEach(path => {
    const pathWeight = path.percent / 100
    let pathImpact = 0
    
    if (path.basePaths.length === 1) {
      // Single hop
      const exchangeRate = path.outputAmount / path.inputAmount
      const marketPrice = path.basePaths[0].currentPrice
      pathImpact = calculateSingleImpact(exchangeRate, marketPrice)
    } else {
      // Multi-hop
      const totalExchangeRate = path.outputAmount / path.inputAmount
      const marketPrice = path.basePaths.reduce(
        (acc, bp) => acc.mul(bp.currentPrice),
        new Decimal(1)
      )
      pathImpact = calculateSingleImpact(totalExchangeRate, marketPrice)
    }
    
    weightedImpact += pathImpact * pathWeight
  })
  
  return weightedImpact
}
```

### Impact Thresholds

Set appropriate warnings based on impact:

```typescript
function getPriceImpactSeverity(impactPercent: number) {
  if (impactPercent < 0.1) return { level: 'low', color: 'green' }
  if (impactPercent < 0.5) return { level: 'medium', color: 'yellow' }
  if (impactPercent < 1.0) return { level: 'high', color: 'orange' }
  return { level: 'severe', color: 'red' }
}

// Usage
const impact = await calculatePriceImpact(poolId, amount, true, true)
const severity = getPriceImpactSeverity(impact.priceImpact)

if (severity.level === 'severe') {
  console.warn('⚠️ High price impact! Consider reducing trade size.')
}
```

### Minimize Price Impact

#### 1. Split Large Trades

```typescript
async function splitTradeForBetterPrice(
  totalAmount: BN,
  maxImpactPercent: number
) {
  const chunks = []
  let remainingAmount = totalAmount
  
  while (remainingAmount.gt(new BN(0))) {
    // Find chunk size with acceptable impact
    const chunkSize = await findMaxAmountForImpact(
      poolId,
      maxImpactPercent
    )
    
    if (chunkSize.gte(remainingAmount)) {
      chunks.push(remainingAmount)
      break
    }
    
    chunks.push(chunkSize)
    remainingAmount = remainingAmount.sub(chunkSize)
  }
  
  return chunks
}
```

#### 2. Use Multiple Pools

```typescript
async function findLowImpactRoute(
  amount: BN,
  fromCoin: string,
  toCoin: string
) {
  // Router automatically minimizes impact
  const bestRoute = await sdk.Router.getBestInternalRouter(
    fromCoin,
    toCoin,
    amount,
    true,
    0.005, // 0.5% slippage
    ''
  )
  
  // Check if multi-hop has lower impact
  if (bestRoute.paths[0].poolAddress.length > 1) {
    console.log('Using multi-hop route for lower impact')
  }
  
  return bestRoute
}
```

### Complete Example

```typescript
async function executeSwapWithImpactCheck(
  poolId: string,
  amount: string,
  maxImpactPercent: number
) {
  // 1. Calculate impact
  const impact = await calculatePriceImpact(
    poolId,
    amount,
    true,
    true
  )
  
  console.log(`Price Impact: ${impact.priceImpact.toFixed(3)}%`)
  console.log(`Price: ${impact.currentPrice} → ${impact.endPrice}`)
  
  // 2. Check threshold
  if (impact.priceImpact > maxImpactPercent) {
    throw new Error(
      `Price impact ${impact.priceImpact}% exceeds max ${maxImpactPercent}%`
    )
  }
  
  // 3. Warn user
  const severity = getPriceImpactSeverity(impact.priceImpact)
  if (severity.level !== 'low') {
    console.warn(`${severity.level.toUpperCase()} price impact detected`)
  }
  
  // 4. Execute swap with slippage
  const slippage = Math.max(0.5, impact.priceImpact * 1.5)
  
  const swapParams = {
    pool_id: poolId,
    coinTypeA: pool.coinTypeA,
    coinTypeB: pool.coinTypeB,
    a2b: true,
    by_amount_in: true,
    amount,
    amount_limit: calculateMinOutput(amount, slippage)
  }
  
  const tx = await sdk.Swap.createSwapTransactionPayload(swapParams)
  return tx
}
```

### Price Impact Formula

```typescript
// Basic formula
priceImpact = |endPrice - startPrice| / startPrice * 100

// For CLMM
function calculateCLMMImpact(
  startSqrtPrice: BN,
  endSqrtPrice: BN,
  decimalsA: number,
  decimalsB: number
): number {
  const startPrice = TickMath.sqrtPriceX64ToPrice(
    startSqrtPrice, decimalsA, decimalsB
  )
  const endPrice = TickMath.sqrtPriceX64ToPrice(
    endSqrtPrice, decimalsA, decimalsB
  )
  
  return Math.abs(
    endPrice.minus(startPrice)
      .div(startPrice)
      .mul(100)
      .toNumber()
  )
}
```

### Important Notes

* Price impact increases with trade size
* Impact is higher in low liquidity pools
* Concentrated liquidity affects impact differently
* Always set appropriate slippage tolerance
* Consider splitting large trades
* Monitor impact in real-time for better UX




---

[Next Page](/llms-full.txt/1)

