Skip to main content
Esta página fue traducida automáticamente por IA. La versión en inglés es la fuente autorizada.Ver versión en inglés →
Esta página describe el diseño y rol de cada cuenta. Las semillas son canónicas y se listan en reference/program-addresses. Un pool CLMM requiere más cuentas que un pool CPMM porque la liquidez se almacena de forma dispersa en el rango de ticks; entender esa dispersión es la mayor parte de esta página.

Inventario de cuentas

Un pool CLMM activo se describe mediante las siguientes familias de cuentas. Todas son propiedad del programa CLMM excepto los dos mints y sus bóvedas.

PoolState

El estado activo del pool, leído en cada swap y cada cambio de posición.
Campos que realmente tocarás:
  • sqrt_price_x64 y tick_current son el estado de precio del pool. Se actualizan juntos en cada swap. tick_current es el piso de log_{1.0001}(price).
  • liquidity es la liquidez activa — la suma de valores L para todas las posiciones cuyo rango contiene tick_current. Cambia cada vez que un swap cruza un tick y cada vez que se abre/cierra/redimensiona una posición.
  • fee_growth_global_{0,1}_x64 son las comisiones acumuladas por unidad de liquidez en todo el historial del pool. Las posiciones leen esto para calcular lo que se les debe.
  • tick_spacing está bloqueado en AmmConfig en la inicialización y nunca cambia. Determina qué índices de tick se permiten como puntos finales de posición.
  • tick_array_bitmap es un mapa de bits en línea que cubre el rango comúnmente usado alrededor del precio spot. Para pools cuyas posiciones llegan lejos, el seguimiento de desbordamiento vive en la cuenta separada TickArrayBitmapExtension.
  • fee_on se fija en la creación del pool. 0 (FromInput) reproduce el comportamiento clásico de Uniswap-V3. 1 y 2 enrutan la comisión de swap a un lado del libro — ver products/clmm/fees para compensaciones.
  • seed_index es [0, 0] para cada pool creado a través de CreatePool / CreateCustomizablePool (un pool canónico por par). Un valor no cero significa que el pool fue creado vía CreatePermissionedPool y el índice es parte de las semillas PDA del pool, permitiendo que varios pools coexistan para el mismo (config, mint0, mint1). Para re-derivar la dirección de tal pool debes conocer su seed_index.
  • dynamic_fee_info lleva el estado de volatilidad para el recargo de comisión dinámica. Cuando está habilitado, cada swap recalcula un dynamic_fee_component además de AmmConfig.trade_fee_rate. El diseño se documenta bajo DynamicFeeInfo abajo; los pools sin comisión dinámica dejan toda la estructura en cero.

AmmConfig

Un conjunto típico publicado de niveles de tarifa CLMM (confirma contra GET https://api-v3.raydium.io/main/clmm-config): protocol_fee_rate y fund_fee_rate son fracciones de la comisión comercial; misma convención que CPMM. Ver products/clmm/fees.

TickArrayState

CLMM no almacena un registro único por tick. Eso serían miles de millones de cuentas. En su lugar agrupa TICK_ARRAY_SIZE ticks adyacentes inicializados o no (típicamente 60 u 88 dependiendo de la versión del programa) en un TickArrayState que se crea perezosamente en el primer uso.
Los cuatro campos de orden de límite son cero en cualquier tick que nunca haya sido usado para una orden de límite. Cuando se abren órdenes en un tick, el programa las rastrea como una secuencia de cohortes:
  • order_phase es el id de cohorte. Se incrementa cada vez que una cohorte transiciona de “completamente sin llenar” a “parcialmente llena”.
  • orders_amount es el total de token de entrada de la cohorte actual (más nueva).
  • part_filled_orders_remaining rastrea la cohorte anterior que actualmente está siendo llenada por swaps en curso.
  • unfilled_ratio_x64 es un multiplicador Q64.64 llevado en la cohorte: cuando un swap llena X% de la cohorte, la razón se multiplica por (1 − X). Cada orden abierta almacena su propia snapshot (order_phase, unfilled_ratio_x64) en el momento de apertura, así que la matemática de liquidación se reduce a comparar snapshots.
Reglas:
  • Un tick de punto final de posición t debe satisfacer t % tick_spacing == 0. El programa rechaza posiciones fuera de espaciado.
  • El array del tick se ubica en floor(t / (TICK_ARRAY_SIZE * tick_spacing)) * (TICK_ARRAY_SIZE * tick_spacing).
  • Un array de tick se inicializa perezosamente: la primera posición o swap que toca un array no inicializado lo crea, pagando la renta.
  • Un array de tick nunca se cierra por el programa. Una vez asignado persiste por la vida del pool, incluso después de que cada tick dentro de él vuelva a liquidity_gross == 0. Las posiciones y swaps posteriores reutilizan la cuenta existente sin renta adicional. No hay ruta de limpieza impulsada por ClosePosition para arrays de tick.

TickArrayBitmapExtension

PoolState.tick_array_bitmap (en línea) cubre el rango “cercano al spot” — ±1,024 arrays de tick. Fuera de ese rango (para valores de tick extremos), el programa mantiene una cuenta de extensión:
Si el rango de tu posición es “normal”, nunca piensas en la cuenta de extensión. Las posiciones de rango completo (p. ej., (MIN_TICK, MAX_TICK)) la requieren; el SDK la resuelve por ti.

Posiciones

Una posición CLMM es un paquete de tres cuentas más un mint:

NFT de posición mint

Un mint SPL Token o Token-2022 con suministro 1. El NFT de posición en la billetera del propietario es un ATA que contiene ese único token. El programa vincula la autorización al titular actual del saldo ATA del NFT, no a una Pubkey almacenada en estado. Los nuevos mints de NFT de posición establecen pool_state como su autoridad de congelación antes de acuñar el único token y eliminar la autoridad de mint. Establecer una autoridad de congelación no congela la cuenta del NFT en sí. La cuenta permanece descongelada y transferible a menos que ambas condiciones se cumplan: el llamador usa OpenPositionV2 u OpenPositionWithToken22Nft, y la autoridad de congelación de al menos un mint de bóveda subyacente aparece en la lista de emisor restringido de CLMM. Solo entonces CLMM congela la cuenta del NFT después de acuñar. Esto no cambia bytes de PersonalPositionState o PoolState.

PersonalPositionState

Uno por posición abierta. Clave basada en el mint del NFT.

ProtocolPositionState (deprecado)

Las versiones anteriores de CLMM almacenaban contabilidad agregada por (pool, tick_lower, tick_upper) en un PDA ProtocolPositionState. Las versiones más nuevas ya no crean ni leen esta cuenta. El slot aún aparece en las listas de cuentas OpenPosition / IncreaseLiquidity / DecreaseLiquidity como un UncheckedAccount para compatibilidad ABI, pero el programa no escribe en él. Las cuentas existentes en cadena son vestigiales; el admin puede llamar a CloseProtocolPosition para recuperar la renta para ellas.La contabilidad de rango agregada ahora se deriva directamente de los dos ticks de punto final (liquidity_gross, liquidity_net, y los fee_growth_outside_* / reward_growths_outside_x64 por tick) en TickArrayState. La fórmula de crecimiento de comisión dentro fee_growth_inside = global − outside_lower − outside_upper continúa funcionando sin una cuenta de posición agregada.

Observación

El buffer de observación de CLMM almacena un tick acumulativo, no un precio acumulativo. Los consumidores externos calculan el precio de media geométrica en un intervalo desde (tick_cumulative[t1] − tick_cumulative[t0]) / (t1 − t0) y luego price = 1.0001 ** tick. Ver algorithms/clmm-math.

DynamicFeeConfig y DynamicFeeInfo

Los parámetros de comisión dinámica viven en dos lugares. La plantilla reutilizable — DynamicFeeConfig — es administrada por admin y compartida entre pools que optan por participar. El estado de tiempo de ejecución por pool — DynamicFeeInfo — está incrustado en PoolState y se actualiza por cada swap.

DynamicFeeConfig

Semilla PDA: ["dynamic_fee_config", index.to_be_bytes()]. Creado vía create_dynamic_fee_config (protegido por admin) y modificado vía update_dynamic_fee_config. Un pool creado con enable_dynamic_fee = true captura los cinco parámetros de calibración de la config (filter_period, decay_period, reduction_factor, dynamic_fee_control, max_volatility_accumulator) en su propio DynamicFeeInfo en el momento de creación; ediciones posteriores a DynamicFeeConfig no afectan retroactivamente a pools existentes.

DynamicFeeInfo (incrustado en PoolState)

Los cuatro campos inferiores son estado; los cinco superiores son calibración copiada de DynamicFeeConfig. La matemática de comisión y las reglas de decaimiento se documentan bajo products/clmm/math y products/clmm/fees. Constantes usadas por la fórmula:

LimitOrderState

Una cuenta por orden de límite abierta.
Ciclo de vida:
  1. Abrir — el usuario llama a open_limit_order, deposita total_amount del token de entrada, la orden se vincula a una cohorte TickState.
  2. (opcional) Aumentar / Disminuirincrease_limit_order agrega a total_amount; decrease_limit_order devuelve tokens sin llenar (y cualquier salida liquidada hasta ese punto).
  3. Liquidar — cuando la cohorte está completamente o parcialmente llena, el propietario o el guardián operacional llama a settle_limit_order para enviar tokens de salida al ATA del propietario.
  4. Cerrar — una vez que unfilled_amount == 0, la cuenta es cerrable. La renta siempre vuelve a owner.
Semilla PDA: [owner.as_ref(), limit_order_nonce.key().as_ref(), limit_order_nonce.order_nonce.to_be_bytes().as_ref()]. El PDA de orden es por lo tanto único por (owner, nonce_index, order_nonce).

LimitOrderNonce

Contador por (wallet, nonce_index) que permite a un único usuario ejecutar múltiples pipelines paralelos de órdenes de límite sin colisionar en PDAs.
Semilla PDA: [user_wallet.as_ref(), &[nonce_index]]. La mayoría de clientes usan nonce_index = 0 y dejan que order_nonce lleve la cardinalidad.

Permission

Una cuenta de capacidad cuya existencia es la concesión: si se deriva un PDA Permission para una autoridad dada, esa autoridad puede llamar a CreatePermissionedPool. No almacena nada más allá de la autoridad para la que fue creada.
Semilla PDA: ["permission", authority.as_ref()]. Creado por un admin vía CreatePermissionPda y eliminado vía ClosePermissionPda (la renta se devuelve al llamador). Ambas instrucciones de admin aceptan ya sea el admin del programa o una clave dedicada permission_pda_admin. Cerrar el PDA revoca la concesión — la autoridad ya no puede crear pools adicionales, pero los pools que ya creó no se ven afectados.

Derivando las cuentas clave

Las cadenas de semilla exactas siempre deben verificarse contra el IDL en cadena y reference/program-addresses.

Referencia rápida del ciclo de vida

Las cuentas TickArrayState nunca se cierran por el programa — persisten por la vida del pool. Una vez que un array de tick ha sido inicializado permanece en cadena incluso cuando cada tick dentro de él vuelve a liquidity_gross == 0. Reutilizar un array de tick existente es gratis; solo la primera posición que toca un array nunca inicializado paga su renta.

Qué leer dónde

Fuentes: