Skip to main content
DLMM Pro keeps its state in nine program accounts. Token badges live in a separate access registry program. The IDL exported by the SDK as DlmmProIDL has the exact layout of each fixed header. It does not define the entries of the variable tails (PoolRewardInfo, PoolActivation, Tick, Position, LimitOrderData, Observation and the reward variants), so those layouts are given in the tables below.
Pool, TickArray, PositionArray, LimitOrder and Oracle accounts have a variable-length tail after the fixed header the IDL describes. Anchor’s AccountDeserialize on the generated types reads the whole account after the discriminator as the header, so it fails or panics on these accounts. Decode them with the SDK helpers (client.state.fetchPoolWithExtensions, decodePositions, decodeTicks, decodeLimitOrderData, decodeObservations) or the Dynamic* parsers of the commons crate. See TypeScript SDK and Rust library.
All accounts start with an 8-byte Anchor discriminator. Sizes below count the fixed header after it. Integers are little-endian, and the field tables leave out padding bytes.

PDA Map

The program ID is dPro1D5wiqnGMH8vMDG23jh35btTHoWdyqXjqkqxe8n on mainnet and devnet. The access registry program is regUFehW9oycnaV2auYyfqyYjKG1W1uLNT7rtUeemQr. Each SDK helper returns the address only, and its last argument is an optional program ID. The pool authority FSUXyUKbTJ4A5Ccz5M6Uo4J2myc3RsDKTPU9vZwyeXru signs token transfers out of the reserve and reward vaults. It holds no data. A tick array index is floor(floor(tick_id / tick_spacing) / 128). The SDK helper tickIndexFromId computes it.

Finding Accounts

Each account type starts with its discriminator, the first 8 bytes of sha256("account:<Name>"). Combine it with the offsets below in getProgramAccounts memcmp filters. Offsets count from the start of the account data, discriminator included. To list the positions or limit orders of a pool, filter PositionArray or LimitOrder accounts by pool. To list those of a wallet, read the wallet’s Token 2022 accounts that hold 1 token of a 0-decimal mint, then try derivePosition(mint) and deriveLimitOrder(mint) for each mint. Position and limit order NFT mints have the pool authority as mint authority and their pool as freeze authority.

Constants

Ticks and Prices

Positions, Limit Orders, Rewards and Oracle

Fees

Fee numerators use FEE_DENOMINATOR as the denominator. Share constants are basis points.

Pool

Pool is the root account of a market. The fixed header is 640 bytes.

Identity and Reserves

Price and Liquidity

Fees

FeeConfig: BaseFeeConfig: data by mode, with offsets inside data. Unused bytes are zero. DynamicFeeConfig: VariableFee: PoolPartner:

Status, Type and Activation

Pool Enums

Dynamic Tail

Bytes after the 640-byte header depend on extensions. The pool tail order is fixed: the rewards block first when LiquidityMining is set, then the activation block when ActivationAccess is set. A pool with neither extension has no tail. A pool account is therefore 648 bytes with no extension, 1,000 with LiquidityMining, 776 with ActivationAccess and 1,128 with both. PoolRewardInfo (176 bytes): PoolActivation: data by activation_mode, with offsets inside data: Use isSupportExtension(pool.extensions, PoolExtension.LiquidityMining) to test a bit. Tick arrays and position arrays use their reward variants (TickWithReward, PositionWithReward) on pools with LiquidityMining. The SDK withReward options and the commons DynamicTickArray and DynamicPositionArray parsers take that choice from the pool.

StaticConfig

StaticConfig fixes fees, tick spacing, collect fee mode, activation type and extensions for every pool created from it. The size is 120 bytes with no tail. The configs that exist are listed in Pool Fee Configs.

DynamicConfig

DynamicConfig authorizes a pool creator authority to create pools with its own parameters. It carries only the permissions and the partner fee share. The size is 80 bytes with no tail.

TickArray

TickArray stores the initialized ticks of one tick array index (see Ticks and Liquidity). The fixed header is 56 bytes. The tail holds the initialized ticks sorted by tick_id. A new tick array has no ticks, and the account grows or shrinks as positions and limit orders add or drop references to ticks. The tail starts at byte 64 of the account data and uses Tick (160 bytes) or TickWithReward (192 bytes) on LiquidityMining pools. One instruction can grow a tick array by at most 10,240 bytes. That is 64 new ticks (53 on LiquidityMining pools), or 63 when the instruction also creates the tick array on a pool without LiquidityMining. An open_position or expand_position that needs more new ticks in one array fails with ReallocExceedMaxLengthPerInstruction, so split the ranges across instructions.

Tick

TickWithReward

TickArrayBitmapExtension

The extension tracks initialized tick arrays beyond the 1024-bit inline bitmap of the pool. The size is 800 bytes with no tail. Liquidity or limit orders in a tick array outside indexes -512 to 511 need this account in the instruction, or the program returns TickArrayBitmapExtensionAccountIsNotProvided. On a pool whose is_bitmap_extension_initialized is 1, pass it to swap as well: a swap whose search leaves the inline bitmap without it fails with BitmapExtensionAccountIsNotProvided. A pool with no extension has no liquidity outside the inline bitmap, so the search stops there. Create it with initialize_tick_bitmap_extension, or pass it when creating the pool.

PositionArray

A position is one NFT plus one PositionArray account that holds up to 45 range slots. Each slot has its own tick range, liquidity, fees and rewards, and ranges can overlap. The fixed header is 136 bytes. The tail holds one slot per range, starting at byte 144 of the account data (vesting is at byte 120). Instructions select slots with a u64 bitmap in which bit i is slot i. The tail uses Position (128 bytes) or PositionWithReward (192 bytes) on LiquidityMining pools.

Position

A slot is empty when its total liquidity is zero and it has no pending fees or rewards.

PositionWithReward

PositionVesting

Vesting is part of the PositionArray header (24 bytes) and applies to all slots. lock_vesting_position moves liquidity from unlocked_liquidity into vesting_liquidity. At the cliff point the program releases cliff_unlock_bps of it, then bps_per_period for each elapsed period_frequency, up to number_of_period periods. The time from the lock to the last release is capped at MAX_VESTING_DURATION (315,360,000 seconds, 10 years) on Timestamp pools and MAX_VESTING_DURATION_SLOT (1,261,440,000 slots) on Slot pools.

LimitOrder

A limit order is one NFT plus one LimitOrder account holding up to 25 ticks on one side. The fixed header is 136 bytes. The tail holds one LimitOrderData (24 bytes) per tick, starting at byte 144 of the account data. Canceling a tick zeroes its amount, age and is_ask but keeps tick_id, because close_limit_order needs the tick arrays of every entry.

LimitOrderData

Oracle

The oracle records the cumulative tick for time-weighted average prices. The fixed header is 48 bytes, followed by a ring buffer of Observation entries of 16 bytes each, starting at byte 56 of the account data. A new oracle holds one observation. increase_oracle_length grows it to at most 500.

Observation

The average tick over an interval is (tick_cumulative_end - tick_cumulative_start) / (timestamp_end - timestamp_start).

Operator

Operator grants an address a set of operator permissions. The admin creates it with create_operator_account. The size is 48 bytes with no tail. OperatorPermission bits: 0 CreateConfigKey, 1 RemoveConfigKey, 2 SetPoolStatus, 3 InitializeReward, 4 UpdateRewardAuthority, 5 UpdatePartnerFee.

TokenBadge

TokenBadge belongs to the access registry program, not to DLMM Pro. A badge for a mint lets pool creation (see Token 2022 Support) and reward initialization accept it when the program would otherwise reject its Token 2022 extensions or freeze authority. The size is 64 bytes with no tail. The SDK reads it with client.state.fetchTokenBadge(deriveTokenBadge(mint)).