Skip to main content

DLMM Integration Guide

This guide covers the contract-level flow for Ramses DLMM pools. Start with DLMM & Liquidity Bins if bins, active IDs, or per-bin LP shares are unfamiliar.
The addresses in this guide refer to the Robinhood snapshot at block 22,674,244. 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.

1. Discover a pool

A token pair may have multiple pools with different bin steps. Query the factory instead of assuming a single address:
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:
Then verify:
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:
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:
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:
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:
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:
At execution, the router reads the pool’s current active ID, checks it against activeIdDesired and idSlippage, and calculates each destination bin as:
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.
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.
The distribution values are 1e18-scaled weights. To allocate all of a supplied token:
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.
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.
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:
Then submit matching bin IDs and share amounts:
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: 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