Saltar para o conteúdo principal
Esta página foi traduzida automaticamente por IA. A versão em inglês é a fonte oficial.Ver versão em inglês →
Esta página descreve o layout e a função de cada conta. As seeds canônicas estão listadas em reference/program-addresses. Um pool CLMM utiliza mais contas do que um pool CPMM porque a liquidez é armazenada de forma esparsa ao longo do intervalo de ticks; entender essa esparsidade é o tema principal desta página.

Inventário de contas

Um pool CLMM ativo é descrito pelas seguintes famílias de contas. Todas pertencem ao programa CLMM, exceto as duas mints e seus vaults.

PoolState

O estado ativo do pool, lido a cada swap e a cada alteração de posição.
Campos que você realmente utilizará:
  • sqrt_price_x64 e tick_current representam o estado de preço do pool. São atualizados juntos a cada swap. tick_current é o piso de log_{1.0001}(price).
  • liquidity é a liquidez ativa — a soma dos valores L de todas as posições cujo intervalo contém tick_current. Ela muda sempre que um swap cruza um tick e sempre que uma posição é aberta, fechada ou redimensionada.
  • fee_growth_global_{0,1}_x64 são as taxas acumuladas por unidade de liquidez ao longo de todo o histórico do pool. As posições consultam esses valores para calcular o que lhes é devido.
  • tick_spacing é fixado no AmmConfig na inicialização e nunca muda. Ele determina quais índices de tick podem ser endpoints de posição.
  • tick_array_bitmap é um bitmap inline que cobre o intervalo de tick mais utilizado em torno do preço spot. Para pools cujas posições se estendem muito além desse intervalo, o rastreamento de overflow fica na conta separada TickArrayBitmapExtension.
  • fee_on é definido na criação do pool. 0 (FromInput) reproduz o comportamento clássico do Uniswap-V3. 1 e 2 direcionam a taxa de swap para um único lado do livro — veja products/clmm/fees para as implicações.
  • dynamic_fee_info carrega o estado de volatilidade para o acréscimo de taxa dinâmica. Quando habilitado, cada swap recalcula um dynamic_fee_component sobre o AmmConfig.trade_fee_rate. O layout está documentado em DynamicFeeInfo abaixo; pools sem taxa dinâmica mantêm toda a struct zerada.

AmmConfig

Um conjunto típico de fee tiers CLMM publicados (confirme em GET https://api-v3.raydium.io/main/clmm-config): protocol_fee_rate e fund_fee_rate são frações da taxa de negociação; mesma convenção do CPMM. Veja products/clmm/fees.

TickArrayState

O CLMM não armazena um registro por tick — isso resultaria em bilhões de contas. Em vez disso, ele agrupa TICK_ARRAY_SIZE ticks adjacentes (tipicamente 60 ou 88, dependendo da versão do programa), sejam eles inicializados ou não, em um TickArrayState criado de forma lazy no primeiro uso.
Os quatro campos de ordem limitada são zero em qualquer tick que nunca foi utilizado para uma ordem limitada. Quando ordens são abertas em um tick, o programa as rastreia como uma sequência de coortes:
  • order_phase é o id da coorte. Ele incrementa toda vez que uma coorte transita de “totalmente não preenchida” para “parcialmente preenchida.”
  • orders_amount é o total do token de entrada da coorte atual (a mais recente).
  • part_filled_orders_remaining rastreia a coorte anterior que está sendo preenchida pelos swaps em andamento.
  • unfilled_ratio_x64 é um multiplicador Q64.64 mantido na coorte: quando um swap preenche X% da coorte, a razão é multiplicada por (1 − X). Cada ordem aberta armazena seu próprio snapshot de (order_phase, unfilled_ratio_x64) no momento da abertura, de modo que o cálculo de liquidação se reduz a comparar snapshots.
Regras:
  • Um tick de endpoint de posição t deve satisfazer t % tick_spacing == 0. O programa rejeita posições fora do espaçamento.
  • O array do tick está localizado em floor(t / (TICK_ARRAY_SIZE * tick_spacing)) * (TICK_ARRAY_SIZE * tick_spacing).
  • Um tick array é inicializado de forma lazy: a primeira posição ou swap que toca um array não inicializado o cria, pagando o rent.
  • Um tick array nunca é fechado pelo programa. Uma vez alocado, ele persiste durante toda a vida do pool, mesmo após todos os ticks dentro dele retornarem a liquidity_gross == 0. Posições e swaps subsequentes reutilizam a conta existente sem custo adicional de rent. Não há caminho de limpeza via ClosePosition para tick arrays.

TickArrayBitmapExtension

PoolState.tick_array_bitmap (inline) cobre o intervalo “próximo ao spot” — ±1.024 tick arrays. Além desse intervalo (para valores extremos de tick), o programa mantém uma conta de extensão:
Se o intervalo da sua posição for “normal”, você nunca precisará pensar na conta de extensão. Posições de intervalo completo (p. ex., (MIN_TICK, MAX_TICK)) exigem ela; o SDK resolve isso por você.

Posições

Uma posição CLMM é um conjunto de três contas mais uma mint:

NFT mint da posição

Uma mint SPL Token com supply 1. O endereço da mint é um PDA determinístico; o NFT da posição na carteira do dono é simplesmente uma ATA que guarda esse único token. Transferir o NFT é como uma posição muda de titular — o programa vincula a autorização ao portador atual do saldo da ATA do NFT, não a uma Pubkey armazenada no estado.

PersonalPositionState

Um por posição aberta. Indexado pela mint do NFT.

ProtocolPositionState (obsoleto)

Versões anteriores do CLMM armazenavam bookkeeping agregado por (pool, tick_lower, tick_upper) em um PDA ProtocolPositionState. As versões mais recentes não criam nem leem mais essa conta. O slot ainda aparece nas listas de contas de OpenPosition / IncreaseLiquidity / DecreaseLiquidity como UncheckedAccount por compatibilidade de ABI, mas o programa não escreve nela. As contas existentes on-chain são vestigiais; o admin pode chamar CloseProtocolPosition para recuperar o rent delas.O bookkeeping agregado de intervalos agora é derivado diretamente dos dois ticks de endpoint (liquidity_gross, liquidity_net e os fee_growth_outside_* / reward_growths_outside_x64 por tick) no TickArrayState. A fórmula de crescimento de taxa interna fee_growth_inside = global − outside_lower − outside_upper continua funcionando sem uma conta de posição agregada.

Observation

O buffer de observação do CLMM armazena um tick cumulativo, não um preço cumulativo. Consumidores externos calculam o preço médio geométrico em um intervalo a partir de (tick_cumulative[t1] − tick_cumulative[t0]) / (t1 − t0) e depois price = 1.0001 ** tick. Veja algorithms/clmm-math.

DynamicFeeConfig e DynamicFeeInfo

Os parâmetros de taxa dinâmica residem em dois lugares. O template reutilizável — DynamicFeeConfig — é gerenciado pelo admin e compartilhado entre os pools que aderem. O estado de execução por pool — DynamicFeeInfo — está embutido em PoolState e atualizado a cada swap.

DynamicFeeConfig

Seed do PDA: ["dynamic_fee_config", index.to_be_bytes()]. Criado via create_dynamic_fee_config (restrito ao admin) e modificado via update_dynamic_fee_config. Um pool criado com enable_dynamic_fee = true copia os cinco parâmetros de calibração da config (filter_period, decay_period, reduction_factor, dynamic_fee_control, max_volatility_accumulator) para seu próprio DynamicFeeInfo no momento da criação; edições posteriores ao DynamicFeeConfig não afetam retroativamente pools já existentes.

DynamicFeeInfo (embutido em PoolState)

Os quatro campos inferiores são estado; os cinco superiores são parâmetros de calibração copiados do DynamicFeeConfig. O cálculo da taxa e as regras de decaimento estão documentados em products/clmm/math e products/clmm/fees. Constantes utilizadas pela fórmula:

LimitOrderState

Uma conta por ordem limitada aberta.
Ciclo de vida:
  1. Abrir — o usuário chama open_limit_order, deposita total_amount do token de entrada; a ordem é vinculada a uma coorte do TickState.
  2. (opcional) Aumentar / Diminuirincrease_limit_order adiciona a total_amount; decrease_limit_order devolve os tokens não preenchidos (e qualquer saída liquidada até aquele ponto).
  3. Liquidar — quando a coorte é total ou parcialmente preenchida, o dono ou o keeper operacional chama settle_limit_order para enviar os tokens de saída para a ATA do dono.
  4. Fechar — assim que unfilled_amount == 0, a conta pode ser fechada. O rent sempre retorna ao owner.
Seed do PDA: [owner.as_ref(), limit_order_nonce.key().as_ref(), limit_order_nonce.order_nonce.to_be_bytes().as_ref()]. O PDA da ordem é, portanto, único por (owner, nonce_index, order_nonce).

LimitOrderNonce

Contador por (wallet, nonce_index) que permite a um único usuário executar múltiplos pipelines paralelos de ordens limitadas sem colisão de PDAs.
Seed do PDA: [user_wallet.as_ref(), &[nonce_index]]. A maioria dos clientes usa nonce_index = 0 e deixa o order_nonce controlar a cardinalidade.

Derivando as contas principais

As strings de seed exatas devem sempre ser verificadas contra o IDL on-chain e reference/program-addresses.

Referência rápida do ciclo de vida

As contas TickArrayState nunca são fechadas pelo programa — elas persistem durante toda a vida do pool. Uma vez que um tick array tenha sido inicializado, ele permanece on-chain mesmo quando todos os ticks dentro dele retornam a liquidity_gross == 0. Reutilizar um tick array existente é gratuito; apenas a primeira posição a tocar um array nunca inicializado paga seu rent.

O que ler e onde

Fontes: