Skip to main content
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 o papel de cada conta. As seeds são canônicas e estão listadas em reference/program-addresses. Um pool CLMM é mais pesado em contas do que um pool CPMM porque a liquidez é armazenada de forma esparsa ao longo do intervalo de ticks; entender essa esparsidade é a maior parte desta página.

Inventário de contas

Um pool CLMM ativo é descrito pelas seguintes famílias de contas. Todas são de propriedade do programa CLMM, exceto os dois mints e seus vaults.

PoolState

O estado ativo do pool, lido em cada swap e em cada mudança de posição.
Campos que você realmente tocará:
  • sqrt_price_x64 e tick_current são o estado de preço do pool. Eles são atualizados juntos em cada swap. tick_current é o piso de log_{1.0001}(price).
  • liquidity é a liquidez ativa — a soma dos valores L para todas as posições cujo intervalo contém tick_current. Muda toda vez que um swap cruza um tick e toda vez que uma posição é aberta/fechada/redimensionada.
  • fee_growth_global_{0,1}_x64 são as taxas cumulativas ganhas por unidade de liquidez em todo o histórico do pool. As posições leem isso para calcular o que lhes é devido.
  • tick_spacing é bloqueado no AmmConfig na inicialização e nunca muda. Determina quais índices de tick são permitidos como endpoints de posição.
  • tick_array_bitmap é um bitmap inline cobrindo o intervalo comumente usado ao redor do preço spot. Para pools cujas posições alcançam muito longe, o rastreamento de overflow vive na conta separada TickArrayBitmapExtension.
  • fee_on é fixo na criação do pool. 0 (FromInput) reproduz o comportamento clássico do Uniswap-V3. 1 e 2 roteiam a taxa de swap para um único lado do livro — veja products/clmm/fees para trade-offs.
  • seed_index é [0, 0] para cada pool criado através de CreatePool / CreateCustomizablePool (um pool canônico por par). Um valor não-zero significa que o pool foi criado via CreatePermissionedPool e o índice faz parte das seeds do PDA do pool, permitindo que vários pools coexistam para o mesmo (config, mint0, mint1). Para re-derivar o endereço de tal pool você deve conhecer seu seed_index.
  • dynamic_fee_info carrega o estado de volatilidade para a sobretaxa de taxa dinâmica. Quando habilitado, cada swap recalcula um dynamic_fee_component no topo de AmmConfig.trade_fee_rate. O layout é documentado sob DynamicFeeInfo abaixo; pools sem taxa dinâmica deixam toda a struct em zero.

AmmConfig

Um conjunto típico publicado de tiers de taxa CLMM (confirme contra 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 que CPMM. Veja products/clmm/fees.

TickArrayState

CLMM não armazena um único registro por tick. Isso seria bilhões de contas. Em vez disso, agrupa TICK_ARRAY_SIZE ticks adjacentes inicializados ou não (tipicamente 60 ou 88 dependendo da versão do programa) em um TickArrayState que é criado preguiçosamente no primeiro uso.
Os quatro campos de ordem de limite são zero em qualquer tick que nunca foi usado para uma ordem de limite. Quando ordens são abertas em um tick, o programa as rastreia como uma sequência de coortes:
  • order_phase é o id da coorte. Incrementa toda vez que uma coorte faz a transição de “totalmente não preenchida” para “parcialmente preenchida”.
  • orders_amount é o total de token de entrada da coorte atual (mais nova).
  • part_filled_orders_remaining rastreia a coorte anterior que está sendo preenchida por swaps contínuos.
  • unfilled_ratio_x64 é um multiplicador Q64.64 carregado na coorte: quando um swap preenche X% da coorte, a razão é multiplicada por (1 − X). Cada ordem aberta armazena seu próprio snapshot (order_phase, unfilled_ratio_x64) no momento da abertura, então a matemática 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 array de tick é inicializado preguiçosamente: a primeira posição ou swap que toca um array não inicializado o cria, pagando o aluguel.
  • Um array de tick é nunca fechado pelo programa. Uma vez alocado, persiste pela vida do pool, mesmo depois que cada tick dentro dele retorna a liquidity_gross == 0. Posições e swaps subsequentes reutilizam a conta existente sem aluguel extra. Não há caminho de limpeza acionado por ClosePosition para arrays de tick.

TickArrayBitmapExtension

PoolState.tick_array_bitmap (inline) cobre o intervalo “próximo ao spot” — ±1.024 arrays de tick. Fora desse intervalo (para valores de tick extremos), o programa mantém uma conta de extensão:
Se o intervalo da sua posição é “normal”, você nunca pensa na conta de extensão. Posições de intervalo completo (por exemplo, (MIN_TICK, MAX_TICK)) a requerem; o SDK a resolve para você.

Posições

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

Mint de NFT de posição

Um mint SPL Token ou Token-2022 com suprimento 1. O NFT de posição na carteira do proprietário é um ATA mantendo esse único token. O programa vincula a autorização ao detentor atual do saldo ATA do NFT, não a um Pubkey armazenado no estado. Novos mints de NFT de posição definem pool_state como sua autoridade de congelamento antes de cunhar o único token e remover a autoridade de mint. Definir uma autoridade de congelamento não congela a conta do NFT em si. A conta permanece descongelada e transferível a menos que ambas as condições se mantenham: o chamador usa OpenPositionV2 ou OpenPositionWithToken22Nft, e a autoridade de congelamento de pelo menos um mint de vault subjacente aparece na lista de emissores restritos do CLMM. Apenas então o CLMM congela a conta do NFT após cunhar. Isso não muda nenhum byte de PersonalPositionState ou PoolState.

PersonalPositionState

Um por posição aberta. Chaveado no mint do NFT.

ProtocolPositionState (descontinuado)

Versões mais antigas do CLMM armazenavam agregação por (pool, tick_lower, tick_upper) em um PDA ProtocolPositionState. Versões mais recentes não criam ou leem mais essa conta. O slot ainda aparece nas listas de contas OpenPosition / IncreaseLiquidity / DecreaseLiquidity como um UncheckedAccount para compatibilidade ABI, mas o programa não escreve nela. Contas existentes on-chain são vestigiais; o admin pode chamar CloseProtocolPosition para recuperar aluguel para elas.A agregação de bookkeeping de intervalo agora é derivada diretamente dos dois ticks de endpoint (liquidity_gross, liquidity_net e os fee_growth_outside_* / reward_growths_outside_x64 por tick) em TickArrayState. A fórmula de crescimento de taxa dentro 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 de média geométrica em um intervalo de (tick_cumulative[t1] − tick_cumulative[t0]) / (t1 − t0) e então price = 1.0001 ** tick. Veja algorithms/clmm-math.

DynamicFeeConfig e DynamicFeeInfo

Os parâmetros de taxa dinâmica vivem em dois lugares. O template reutilizável — DynamicFeeConfig — é gerenciado por admin e compartilhado entre pools que optam por participar. O estado de tempo de execução por pool — DynamicFeeInfo — é incorporado em PoolState e atualizado por cada swap.

DynamicFeeConfig

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

DynamicFeeInfo (incorporado em PoolState)

Os quatro campos inferiores são estado; os cinco superiores são calibração copiada de DynamicFeeConfig. A matemática de taxa e as regras de decaimento são documentadas sob products/clmm/math e products/clmm/fees. Constantes usadas pela fórmula:

LimitOrderState

Uma conta por ordem de limite aberta.
Ciclo de vida:
  1. Abrir — usuário chama open_limit_order, deposita total_amount do token de entrada, a ordem é vinculada a uma coorte TickState.
  2. (opcional) Aumentar / Diminuirincrease_limit_order adiciona a total_amount; decrease_limit_order retorna tokens não preenchidos (e qualquer saída liquidada até esse ponto).
  3. Liquidar — quando a coorte é totalmente ou parcialmente preenchida, o proprietário ou o operador chamador chama settle_limit_order para enviar tokens de saída para o ATA do proprietário.
  4. Fechar — uma vez que unfilled_amount == 0, a conta é fechável. O aluguel sempre retorna para 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 de ordem é portanto único por (owner, nonce_index, order_nonce).

LimitOrderNonce

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

Permission

Uma conta de capacidade cuja existência é a concessão: se um PDA Permission é derivado para uma determinada autoridade, essa autoridade pode chamar CreatePermissionedPool. Ele não armazena nada além da autoridade para a qual foi criado.
Seed do PDA: ["permission", authority.as_ref()]. Criado por um admin via CreatePermissionPda e destruído via ClosePermissionPda (aluguel reembolsado ao chamador). Ambas as instruções de admin aceitam a admin do programa ou uma chave dedicada permission_pda_admin. Fechar o PDA revoga a concessão — a autoridade não pode mais criar pools adicionais, mas pools que já criou não são afetados.

Derivando as contas-chave

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

Referência rápida de ciclo de vida

Contas TickArrayState são nunca fechadas pelo programa — elas persistem pela vida do pool. Uma vez que um array de tick foi inicializado, ele permanece on-chain mesmo quando cada tick dentro dele retorna a liquidity_gross == 0. Reutilizar um array de tick existente é gratuito; apenas a primeira posição a tocar um array nunca inicializado paga seu aluguel.

O que ler onde

Fontes: