> ## Documentation Index
> Fetch the complete documentation index at: https://tech.ramses.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Integrating DLMM

> Discover, quote, swap, add, and remove Ramses DLMM liquidity safely.

# DLMM Integration Guide

This guide covers the contract-level flow for Ramses DLMM pools. Start with [DLMM & Liquidity Bins](/concepts/protocol/dlmm) if bins, active IDs, or per-bin LP shares are unfamiliar.

<Warning title="Deployment boundary">
  The addresses in this guide refer to the [Robinhood snapshot at block 22,674,244](/contracts/reference/dlmm/deployments). At that block, its factory was not connected to `Voter` and it had no live DLMM rewarder system. Re-read live state before sending transactions.
</Warning>

## 1. Discover a pool

A token pair may have multiple pools with different bin steps. Query the factory instead of assuming a single address:

```solidity theme={null}
IDLMMFactory.LBPairInformation[] memory candidates =
    factory.getAllLBPairs(tokenIn, tokenOut);

for (uint256 i; i < candidates.length; ++i) {
    if (candidates[i].ignoredForRouting) continue;

    IDLMMPool pool = candidates[i].LBPair;
    uint16 binStep = candidates[i].binStep;
    // Quote this candidate and select the best complete fill.
}
```

`ignoredForRouting` tells the official quoter to skip a pool. It does not delete the pool or prevent direct interaction.

For a known bin step, use:

```solidity theme={null}
IDLMMFactory.LBPairInformation memory info =
    factory.getLBPairInformation(tokenX, tokenY, binStep);
```

Then verify:

```solidity theme={null}
require(factory.isPool(address(info.LBPair)), "unregistered pool");

IERC20 tokenX = info.LBPair.getTokenX();
IERC20 tokenY = info.LBPair.getTokenY();
uint24 activeId = info.LBPair.getActiveId();
```

The factory sorts tokens internally for lookup, but the pool's `tokenX` and `tokenY` order controls swap direction, price, and valid liquidity composition.

## 2. Quote a swap

For a single pool:

```solidity theme={null}
bool swapForY = address(pool.getTokenY()) == address(tokenOut);
require(amountIn <= type(uint128).max, "amount exceeds pool quote range");
uint128 amountIn128 = uint128(amountIn);

(uint128 amountInLeft, uint128 amountOut, uint128 fee) =
    pool.getSwapOut(amountIn128, swapForY);

require(amountInLeft == 0, "insufficient DLMM liquidity");
```

`swapForY = true` means token X is sold for token Y. A non-zero `amountInLeft` means the pool cannot completely fill the input.

For route discovery, `DLMMQuoter.findBestPathFromAmountIn()` and `findBestPathFromAmountOut()` compare all registered, non-ignored bin steps for each hop. The returned quote contains:

* The selected pool and bin step for every hop
* `Version.V2_1` for each DLMM hop
* Per-hop amounts and a fee fraction scaled by `1e18` (`1e18` means 100%)
* A virtual no-slippage amount for price-impact calculation

The quoter can leave a hop's pair, bin step, or amount fields at zero when it finds no complete candidate. Reject any result containing those zero fields.

Quotes are state-dependent. Re-quote near submission and apply explicit slippage protection.

## 3. Execute through the router

Build a router path:

```solidity theme={null}
IDLMMRouter.Path memory path = IDLMMRouter.Path({
    pairBinSteps: binSteps,
    versions: versions,
    tokenPath: tokenPath
});
```

The enum retains `V1` and `V2` placeholders for path compatibility, but the current Ramses DLMM router executes `Version.V2_1`.

For an exact-input ERC20 swap:

```solidity theme={null}
tokenIn.approve(address(router), amountIn);

uint256 amountOut = router.swapExactTokensForTokens(
    amountIn,
    amountOutMin,
    path,
    recipient,
    deadline
);
```

Native-asset, exact-output, and fee-on-transfer swap variants are also exposed. Do not call the core pool directly unless your integration deliberately handles pre-transfers, complete-fill checks, and its own slippage protection.

## 4. Create a pool

Regular callers create a pool directly through:

```solidity theme={null}
factory.createLBPair(tokenX, tokenY, activeId, binStep);
```

Creation succeeds only when:

* The selected bin-step preset exists and is open
* `tokenY` is already a whitelisted quote asset
* Both token addresses are non-zero and the tokens are distinct
* The token pair and bin step do not already exist
* `activeId` produces a valid price

Protocol operators use the dedicated `AccessHub.createDLMMPool()` route. That route can add the quote asset before creating the pool. Ordinary users should not wrap factory creation in `AccessHub.execute()`.

Choose `activeId` from the intended starting price using the factory-compatible price helper. Reversing token order also reverses the price orientation.

## 5. Add liquidity

The router accepts:

```solidity theme={null}
struct LiquidityParameters {
    IERC20 tokenX;
    IERC20 tokenY;
    uint256 binStep;
    uint256 amountX;
    uint256 amountY;
    uint256 amountXMin;
    uint256 amountYMin;
    uint256 activeIdDesired;
    uint256 idSlippage;
    int256[] deltaIds;
    uint256[] distributionX;
    uint256[] distributionY;
    address to;
    address refundTo;
    uint256 deadline;
}
```

At execution, the router reads the pool's current active ID, checks it against `activeIdDesired` and `idSlippage`, and calculates each destination bin as:

```text theme={null}
destinationBin = liveActiveId + deltaIds[i]
```

When building the transaction, set `activeIdDesired` to the active ID you just quoted and use `deltaIds[i] = desiredBinAtQuote - activeIdDesired`. Negative offsets are below the active bin; positive offsets are above it.

<Warning title="Offsets follow the live active bin">
  If the active ID moves but remains inside `idSlippage`, the router accepts the transaction and shifts every destination bin by the same amount. Use `idSlippage = 0` if the position must land on exact absolute bin IDs.
</Warning>

The distribution values are `1e18`-scaled weights. To allocate all of a supplied token:

```text theme={null}
sum(distributionX) = 1e18
sum(distributionY) = 1e18
```

An array may sum below `1e18`; the unallocated amount is returned to `refundTo`. Over-allocation can revert and should be rejected before submission. Use an all-zero array when supplying none of that token.

Respect pool composition:

* Token Y can be assigned at or below the active bin
* Token X can be assigned at or above the active bin
* A two-sided range needs both tokens

Approve ERC20 inputs to the router, then call `addLiquidity()` or `addLiquidityNATIVE()`. The router requires `to == msg.sender`, so the caller must receive the LP shares. Set `refundTo` explicitly: unused amounts go to that address, not automatically to `to`.

<Note title="Composition fee">
  Adding an asset ratio to the active bin that differs from its existing composition performs an implicit composition swap and charges a fee. Amount minimums should account for this.
</Note>

Large bin arrays can exceed practical gas limits. Estimate each transaction and split wide distributions into bounded chunks while preserving the intended aggregate weights.

## 6. Remove liquidity

First authorize the router to burn the caller's per-bin shares:

```solidity theme={null}
pool.approveForAll(address(router), true);
```

Then submit matching bin IDs and share amounts:

```solidity theme={null}
(uint256 amountX, uint256 amountY) = router.removeLiquidity(
    tokenX,
    tokenY,
    binStep,
    amountXMin,
    amountYMin,
    ids,
    amountsToBurn,
    recipient,
    deadline
);
```

Each burn is pro rata to that bin's reserves and total share supply. Use the native variant only when one side is the router's configured wrapped-native token.

## 7. Index positions and activity

DLMM positions are variable-length collections of per-bin balances. A practical index should keep:

* One aggregate owner/pool position
* One owner/pool/bin balance row
* One pool/bin row with reserves and total supply
* Swap, deposit, withdrawal, and transfer activity

Primary events:

| Event                    | Use                                                 |
| ------------------------ | --------------------------------------------------- |
| `LBPairCreated`          | Register a pool                                     |
| `Swap`                   | Update price, active ID, reserves, fees, and volume |
| `DepositedToBins`        | Record liquidity additions                          |
| `WithdrawnFromBins`      | Record liquidity removals                           |
| `TransferBatch`          | Track bin-share ownership                           |
| `CompositionFees`        | Track active-bin composition fees                   |
| `CollectedProtocolFees`  | Track fees removed from pool accounting             |
| `StaticFeeParametersSet` | Refresh mutable fee configuration                   |
| `HooksParametersSet`     | Refresh hook and rewarder state                     |

Do not treat a frontend's bounded active-bin window as a complete ownership index. Positions can remain in bins far from the current price.

## Final checks

Before submitting a transaction:

1. Confirm chain ID and deployed addresses.
2. Confirm pool registration and token order.
3. Read the current active ID, fees, and hook parameters.
4. Require a complete quote.
5. Set minimum amounts, ID slippage, `to` equal to the caller, refund recipient, and deadline.
6. Simulate and estimate gas.
7. Re-read state after confirmation.

## Additional resources

* [DLMM Contract Architecture](/contracts/reference/dlmm/overview)
* [Robinhood DLMM Deployments](/contracts/reference/dlmm/deployments)
* [Fees](/concepts/protocol/fees)
