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

# DLMM Rust Integration CPI

> Learn how to call DLMM lb_clmm from Rust programs with generated bindings, event CPI accounts, remaining accounts, and validation.

Use CPI when your Solana program must call DLMM directly, for example to swap,
add or remove liquidity, claim fees or rewards, or manage a DLMM position as
part of a larger protocol instruction.

<Note>
  The stable integration contract is the published DLMM IDL and the official SDK
  behavior. This page is cross-checked against the `lb_clmm` source so the
  account ordering and remaining-account notes match the on-chain handlers.
</Note>

## Program ID

```rust theme={"system"}
pub const DLMM_PROGRAM_ID: Pubkey =
    pubkey!("LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo");
```

The public program ID is the same for mainnet and devnet.

## Generated Bindings

Prefer generated CPI bindings from the DLMM IDL. The `lb_clmm` handlers use
Anchor accounts, optional accounts, generated instruction args, and `#[event_cpi]`
on many v2 instructions.

<Warning>
  Do not hand-type account order from memory. Generate bindings or compare
  against SDK-built instructions. Most CPI failures come from one misplaced
  optional account, event authority account, bin array, or Token-2022 remaining
  account.
</Warning>

If you use Anchor-generated CPI modules, the handler usually looks like:

```rust theme={"system"}
let cpi_ctx = CpiContext::new(
    ctx.accounts.dlmm_program.to_account_info(),
    dlmm::cpi::accounts::Swap2 {
        // generated account struct fields
    },
)
.with_remaining_accounts(ctx.remaining_accounts.to_vec());

dlmm::cpi::swap2(
    cpi_ctx,
    amount_in,
    min_amount_out,
    remaining_accounts_info,
)?;
```

If your integration uses raw `invoke` / `invoke_signed`, build the same
instruction data and account metas produced by the generated client.

## CPI Instruction Scope

For the full `lb_clmm` instruction-family map, see
[DLMM Program Instructions](/developer-guides/dlmm/program/instructions). This
page focuses on CPI implementation details for the v2 instructions most
integrations call: `swap2`, `swap_exact_out2`, `add_liquidity2`,
`remove_liquidity2`, `claim_fee2`, `claim_reward2`, and `rebalance_liquidity`.

New CPI integrations should prefer v2 instructions because they support SPL
Token, Token-2022, dynamic positions, and explicit remaining account metadata.

## Event CPI Accounts

Several `lb_clmm` account structs use `#[event_cpi]`, including `Swap2`,
`AddLiquidity2`, `RemoveLiquidity2`, `ClaimFee2`, and `ClaimReward2`. Generated
clients include extra event CPI accounts, commonly:

| Account           | Why it appears                                                |
| ----------------- | ------------------------------------------------------------- |
| `program`         | The DLMM program account used by Anchor event CPI emission.   |
| `event_authority` | PDA derived from `b"__event_authority"` for the DLMM program. |

When you inspect an SDK or generated Rust instruction, keep these accounts in
the exact generated position. If they are missing or moved, Anchor account
deserialization can fail before the DLMM handler reaches your intended logic.

## Remaining Accounts Ordering

`lb_clmm` v2 handlers parse remaining accounts in two phases.

1. They consume Token-2022 transfer-hook account groups according to
   `RemainingAccountsInfo.slices`.
2. They consume the leftover accounts as instruction-specific accounts, usually
   bin arrays.

This ordering comes from `src/utils/remaining_accounts_util.rs` and calls like:

```rust theme={"system"}
let parsed_transfer_hook_accounts = parse_remaining_accounts(
    &mut remaining_accounts,
    &remaining_accounts_info.slices,
    &[AccountsType::TransferHookX, AccountsType::TransferHookY],
)?;

// remaining_accounts now starts at the first bin array account.
```

The order of `ctx.remaining_accounts` must match the order of
`RemainingAccountsInfo.slices`.

| Slice type                       | Used by                                                                |
| -------------------------------- | ---------------------------------------------------------------------- |
| `TransferHookX`                  | Token X transfers in swap, add liquidity, remove liquidity, claim fee. |
| `TransferHookY`                  | Token Y transfers in swap, add liquidity, remove liquidity, claim fee. |
| `TransferHookReward`             | Single reward transfer in `claim_reward2`.                             |
| `TransferHookMultiReward(index)` | Multi-reward flows.                                                    |
| `TransferHookReferral`           | Host/referral fee transfer in swaps.                                   |

<Tip>
  If you have no transfer-hook accounts, pass `RemainingAccountsInfo { slices:
      vec![] }`, then put bin arrays first in `remaining_accounts`.
</Tip>

## Instruction Account Notes

| Instruction                                           | Main accounts                                                                                                                                                                                | Remaining accounts after transfer-hook slices                                                                                                      |
| ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `swap2`, `swap_exact_out2`, `swap_with_price_impact2` | `lb_pair`, optional `bin_array_bitmap_extension`, reserves, user token accounts, token mints, oracle, optional `host_fee_in`, user signer, token programs, memo program, event CPI accounts. | Bin arrays for the swap route, in the order expected by the quote.                                                                                 |
| `add_liquidity2`                                      | `position`, `lb_pair`, optional bitmap extension, user token X/Y, reserves, mints, sender signer, token programs, event CPI accounts.                                                        | Bin arrays covering the bins in `LiquidityParameter.amounts`.                                                                                      |
| `remove_liquidity2`                                   | `position`, `lb_pair`, optional bitmap extension, user token X/Y, reserves, mints, sender signer, token programs, memo program, event CPI accounts.                                          | Bin arrays covering the `BinLiquidityReduction` bins, in ascending bin order.                                                                      |
| `claim_fee2`                                          | `lb_pair`, `position`, sender signer, reserves, user token X/Y, mints, token programs, memo program, event CPI accounts.                                                                     | Bin arrays from `min_bin_id` through `max_bin_id`.                                                                                                 |
| `claim_reward2`                                       | `lb_pair`, `position`, sender signer, reward vault, reward mint, user reward token account, token program, memo program, event CPI accounts.                                                 | Bin arrays from `min_bin_id` through `max_bin_id`.                                                                                                 |
| `rebalance_liquidity`                                 | Position, pool, token, reserve, and authority accounts for the rebalance wrapper.                                                                                                            | Transfer-hook slices, reward accounts, bin arrays, resize accounts, and other operation-specific accounts in the order produced by SDK simulation. |

## Swap2 CPI Shape

This shape mirrors `swap2` on the program.

```rust theme={"system"}
use anchor_lang::prelude::*;

#[derive(Accounts)]
pub struct SwapThroughDlmm<'info> {
    /// CHECK: DLMM program account.
    pub dlmm_program: AccountInfo<'info>,

    /// CHECK: Validated by DLMM.
    #[account(mut)]
    pub lb_pair: AccountInfo<'info>,

    /// CHECK: Optional. Use the generated optional-account representation.
    #[account(mut)]
    pub bin_array_bitmap_extension: AccountInfo<'info>,

    /// CHECK: DLMM reserve token accounts.
    #[account(mut)]
    pub reserve_x: AccountInfo<'info>,
    /// CHECK: DLMM reserve token accounts.
    #[account(mut)]
    pub reserve_y: AccountInfo<'info>,

    /// CHECK: User token accounts.
    #[account(mut)]
    pub user_token_in: AccountInfo<'info>,
    /// CHECK: User token accounts.
    #[account(mut)]
    pub user_token_out: AccountInfo<'info>,

    /// CHECK: Pool token mints.
    pub token_x_mint: AccountInfo<'info>,
    /// CHECK: Pool token mints.
    pub token_y_mint: AccountInfo<'info>,

    /// CHECK: DLMM oracle.
    #[account(mut)]
    pub oracle: AccountInfo<'info>,

    /// CHECK: Optional host fee account.
    #[account(mut)]
    pub host_fee_in: AccountInfo<'info>,

    pub user: Signer<'info>,

    /// CHECK: SPL Token or Token-2022 program for token X.
    pub token_x_program: AccountInfo<'info>,
    /// CHECK: SPL Token or Token-2022 program for token Y.
    pub token_y_program: AccountInfo<'info>,

    /// CHECK: Memo program.
    pub memo_program: AccountInfo<'info>,

    /// CHECK: Event CPI program account generated by Anchor.
    pub event_program: AccountInfo<'info>,
    /// CHECK: Event authority PDA generated by Anchor.
    pub event_authority: AccountInfo<'info>,
}
```

Then call the generated CPI and forward remaining accounts:

```rust theme={"system"}
let remaining_accounts_info = RemainingAccountsInfo { slices: vec![] };

let cpi_ctx = CpiContext::new(
    ctx.accounts.dlmm_program.to_account_info(),
    dlmm::cpi::accounts::Swap2 {
        lb_pair: ctx.accounts.lb_pair.to_account_info(),
        bin_array_bitmap_extension: Some(
            ctx.accounts.bin_array_bitmap_extension.to_account_info(),
        ),
        reserve_x: ctx.accounts.reserve_x.to_account_info(),
        reserve_y: ctx.accounts.reserve_y.to_account_info(),
        user_token_in: ctx.accounts.user_token_in.to_account_info(),
        user_token_out: ctx.accounts.user_token_out.to_account_info(),
        token_x_mint: ctx.accounts.token_x_mint.to_account_info(),
        token_y_mint: ctx.accounts.token_y_mint.to_account_info(),
        oracle: ctx.accounts.oracle.to_account_info(),
        host_fee_in: None,
        user: ctx.accounts.user.to_account_info(),
        token_x_program: ctx.accounts.token_x_program.to_account_info(),
        token_y_program: ctx.accounts.token_y_program.to_account_info(),
        memo_program: ctx.accounts.memo_program.to_account_info(),
        program: ctx.accounts.event_program.to_account_info(),
        event_authority: ctx.accounts.event_authority.to_account_info(),
    },
)
.with_remaining_accounts(ctx.remaining_accounts.to_vec());

dlmm::cpi::swap2(cpi_ctx, amount_in, min_amount_out, remaining_accounts_info)?;
```

If your program signs for the DLMM `user` account with a PDA, use
`CpiContext::new_with_signer` and pass your signer seeds. The token account owner
must still match the authority DLMM expects for the transfer.

## Token-2022 Transfer Hooks

For Token-2022 mints with transfer hooks, include the extra account metas in
`ctx.remaining_accounts` and describe them with `RemainingAccountsInfo.slices`.

Example ordering for `swap2` with token X and token Y transfer hooks:

```rust theme={"system"}
let remaining_accounts_info = RemainingAccountsInfo {
    slices: vec![
        RemainingAccountsSlice {
            accounts_type: AccountsType::TransferHookX,
            length: x_hook_accounts_len,
        },
        RemainingAccountsSlice {
            accounts_type: AccountsType::TransferHookY,
            length: y_hook_accounts_len,
        },
    ],
};
```

Then pass remaining accounts in this exact order:

1. Token X transfer-hook accounts.
2. Token Y transfer-hook accounts.
3. Bin array accounts for the swap route.

For host/referral fee transfers, include `TransferHookReferral` when the host fee
mint requires hook accounts.

## Optional Accounts

Some generated clients represent optional accounts as `Option<AccountInfo>`.
Others use an explicit placeholder account when constructing raw account metas.

| Optional account             | Required when                                                           |
| ---------------------------- | ----------------------------------------------------------------------- |
| `bin_array_bitmap_extension` | The route may touch bin array indexes outside the default bitmap range. |
| `host_fee_in`                | A swap includes host/referral fee collection.                           |

When using generated Anchor CPI bindings, follow the generated optional-account
type. When using raw `Instruction` construction, compare against the TypeScript
SDK or `commons` generated client output for the exact placeholder behavior.

## Off-Chain Account Planning

Use the Typescript SDK or `commons` crate to plan accounts before calling your CPI program.

```typescript theme={"system"}
const swapYtoX = true
const binArrays = await pool.getBinArrayForSwap(swapYtoX, 4)
const quote = pool.swapQuote(inAmount, swapYtoX, slippageBps, binArrays, false)

const tx = await pool.swap({
  inToken,
  outToken,
  inAmount: quote.consumedInAmount,
  minOutAmount: quote.minOutAmount,
  lbPair: pool.pubkey,
  user: user.publicKey,
  binArraysPubkey: quote.binArraysPubkey,
})

const dlmmIx = tx.instructions.find((ix) =>
  ix.programId.equals(new PublicKey("LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo")),
)

console.log(dlmmIx?.keys.map((key) => ({
  pubkey: key.pubkey.toBase58(),
  isSigner: key.isSigner,
  isWritable: key.isWritable,
})))
```

Mirror this account order in the accounts your program forwards to DLMM.

## Common CPI Failures

| Error                                        | Likely cause                                                                               |
| -------------------------------------------- | ------------------------------------------------------------------------------------------ |
| `InvalidRemainingAccountSlice`               | A `RemainingAccountsInfo` slice type is not valid for that instruction.                    |
| `InsufficientRemainingAccounts`              | Slice lengths require more accounts than were forwarded.                                   |
| `DuplicatedRemainingAccountTypes`            | The same transfer-hook slice type was listed twice.                                        |
| `MissingRemainingAccountForTransferHook`     | A Token-2022 transfer hook exists but the hook accounts were not provided.                 |
| `NoTransferHookProgram`                      | Transfer-hook accounts were supplied for a mint without a transfer hook.                   |
| `InvalidBinArray` / `NonContinuousBinArrays` | Bin arrays are missing, out of order, or not the arrays expected for the target bin range. |
| `BitmapExtensionAccountIsNotProvided`        | A route needs the bitmap extension account but the optional account was omitted.           |
| `ExceededAmountSlippageTolerance`            | The quoted amount is stale or slippage bounds are too tight.                               |
| `PoolDisabled`                               | The pool status or activation state does not allow the action.                             |

## Best Practices

| Check                                | Detail                                                                                                            |
| ------------------------------------ | ----------------------------------------------------------------------------------------------------------------- |
| Quote before CPI                     | Quote off-chain with the TypeScript SDK or `commons`, then pass explicit bounds into your program.                |
| Forward generated order              | Use generated bindings or compare against an SDK instruction. Main account order is not negotiable.               |
| Put transfer hooks before bin arrays | `parse_remaining_accounts` consumes hook slices first, then handlers consume leftover bin arrays.                 |
| Include event CPI accounts           | v2 handlers with `#[event_cpi]` expect generated event accounts.                                                  |
| Handle Token-2022 per mint           | Token X and token Y can use different token programs and different hook requirements.                             |
| Budget compute                       | Swaps across many bin arrays, wide positions, claims across many bins, and Token-2022 hooks can be compute-heavy. |
| Avoid stale state                    | Re-fetch pool, bitmap extension, bin arrays, mints, and clock before quoting.                                     |
| Test with real pools                 | Simulate on devnet and compare logs against a direct SDK-built transaction.                                       |
