Skip to main content
Aviso de versión. Todas las demos en TypeScript usan @raydium-io/raydium-sdk-v2@0.2.64-alpha. Se ejecutaron por última vez con 0.2.42-alpha (2026-04) y sus firmas de llamada se revisaron de nuevo frente al código fuente de 0.2.64-alpha el 2026-09-09, en Solana mainnet-beta. El esqueleto de CPI en Rust que aparece al final apunta a raydium-clmm en master, con Anchor 1.0.2, la misma versión fijada que en la página de CPMM, de modo que ambas pueden convivir en un mismo crate. Los IDs de programa se obtienen del SDK a partir de reference/program-addresses.

Configuración

Cada demo de esta página replica un archivo de raydium-sdk-V2-demo/src/clmm; el enlace a GitHub aparece junto a cada sección. La inicialización sigue el config.ts.template del repositorio de demos (fuente). Para cualquier integración no trivial, se recomienda disableFeatureCheck: true:

Crear un pool de CLMM

Fuente: src/clmm/createPool.ts
El SDK:
  • Ordena mint1/mint2 por orden de bytes antes de la derivación.
  • Calcula sqrt_price_x64 = floor(sqrt(initialPrice × 10^(dB−dA)) × 2^64).
  • Crea las cuentas observation y tick_array_bitmap_extension.
  • Paga la comisión de creación del pool definida en ammConfig.

Abrir una posición en un rango elegido

Fuente: src/clmm/createPosition.ts
El SDK calcula qué tick arrays toca el rango y los pasa como cuentas. No necesita incluir ninguna instrucción de inicialización: no existe una instrucción de inicialización de tick arrays, ya que OpenPosition* crea por sí mismo el tick array que falte, con cargo al pagador.

Aumentar la liquidez de una posición existente

Fuente: src/clmm/increaseLiquidity.ts

Reducir la liquidez (y recolectar comisiones al mismo tiempo)

Fuente: src/clmm/decreaseLiquidity.ts y src/clmm/closePosition.ts
Para recolectar solo comisiones y recompensas, llama a decreaseLiquidity con liquidity = new BN(0). El efecto secundario de la instrucción es liquidar token_fees_owed_{0,1} y reward_amount_owed y transferirlos fuera del pool. Es la única forma de recolectar cualquiera de los dos. Para cerrar la posición por completo, una vez que la liquidez y las comisiones estén a cero, pasa ownerInfo: { closePosition: true } en la última llamada a decreaseLiquidity. El SDK añade ClosePosition y quema el NFT.
Las posiciones de emisor restringido requieren un constructor de cierre compatible. Estas posiciones tienen una cuenta de token NFT congelada. ClosePosition debe añadir el ID del pool de la posición como primera cuenta restante, para que CLMM pueda descongelar la cuenta antes de quemarla. La rama del código fuente del programa no incluye ningún cambio en el SDK. Antes de habilitar la creación de posiciones de emisor restringido, confirma que tu versión del SDK admite explícitamente la ruta de cierre con cuenta congelada.
Para un cliente Anchor directo, mantén sin cambios las cuentas declaradas y añade el pool al final:
Puedes pasar poolId en todos los cierres. CLMM solo lo lee cuando positionNftAccount está congelada, lo que permite que una misma ruta de cliente sea compatible con posiciones antiguas y nuevas.

Recolectar recompensas

Fuente: src/clmm/harvestAllRewards.ts
harvestAllRewards recorre todas las posiciones de todos los pools que se le pasan, agrupa las llamadas DecreaseLiquidity de liquidez cero que liquidan comisiones y recompensas (además de cualquier UpdateRewardInfos) y, si hace falta, las reparte en varias transacciones.

Swap

Fuente: src/clmm/swap.ts Esto replica src/clmm/swap.ts, que simula con swapInternal en lugar de PoolUtils.computeAmountOutFormat.
La simulación recorre el mapa de ticks fuera de la cadena (off-chain) con la misma lógica que el programa on-chain y devuelve la cantidad de salida (amountCalculated) junto con la lista exacta de cuentas que tocará el swap (accounts). Pase siempre los remainingAccounts que devuelve la simulación: si faltan, el swap revierte a mitad del recorrido con NotEnoughTickArrayAccount; si sobran o están desactualizados, solo desperdician cómputo.
PoolUtils.computeAmountOutFormat sigue existiendo, pero necesita un ComputeClmmPoolInfo (el computePoolInfo devuelto por getPoolInfoFromRpc, no un objeto de pool de la API) y dos argumentos obligatorios más: tickarrayBitmapExtension y blockTimestamp. Además, no existe el método raydium.clmm.fetchTickArrays (fetchTickArrays es una función independiente; los helpers a nivel de módulo son PoolUtils.fetchMultiplePoolTickArrays y los tickData / tickArrays que devuelve getPoolInfoFromRpc).

Crear un pool CLMM personalizable

createCustomizablePool es el punto de entrada que expone los interruptores de comisión dinámica y de comisión de un solo lado en el momento de crear el pool. Recibe la misma forma que createPool más dos parámetros adicionales:
No existen los parámetros enableDynamicFee, dynamicFeeConfigId ni startTime. Pasar dynamicFeeConfig es lo que activa las comisiones dinámicas: si lo omite, obtendrá un pool con comisión estática, sin ningún error. Tenga en cuenta además que los miembros del enum del SDK son TokenOnlyA / TokenOnlyB, mientras que el enum de Rust on-chain los nombra Token0Only / Token1Only; los valores numéricos coinciden (FromInput = 0).
createPool sigue funcionando para el camino de comisión por defecto, sin comisión dinámica. Use createCustomizablePool cuando necesite cualquiera de estos dos ajustes. Consulte products/clmm/instructions para ver la lista de cuentas on-chain.

Órdenes límite

Una orden límite deposita el importe del usuario en un único tick y se ejecuta por orden FIFO cuando un swap cruza ese tick. Los importes resultantes se envían al ATA del propietario en el momento de la liquidación; el propietario no necesita estar conectado para que se ejecute.

Abrir una orden límite

No existe una cuenta de configuración de órdenes límite por pool ni un helper getClmmLimitOrderConfigs(): las órdenes se identifican por tick.
El SDK deriva la PDA de LimitOrderState a partir de (owner, nonce PDA, order nonce), incrementa el LimitOrderNonce de la wallet e inserta la orden en la cohorte FIFO de ese tick.

Aumentar / reducir una orden abierta

decreaseLimitOrder solo puede retirar importe de la parte no ejecutada de la orden; la parte ejecutada queda bloqueada hasta la liquidación. Ambas instrucciones revierten con InvalidOrderPhase si la orden ya se ha ejecutado por completo.

Liquidar una orden ejecutada

settleLimitOrder lee unfilled_ratio_x64 de la orden frente al rastreador de la cohorte, calcula el importe ejecutado y lo transfiere al ATA del propietario. El propietario puede invocarlo él mismo; limit_order_admin (un keeper operativo off-chain) también puede hacerlo en nombre del propietario, y el importe seguirá yendo al propietario. Para cerrar órdenes totalmente liquidadas y recuperar la renta, use closeLimitOrder (individual) o closeAllLimitOrder (por lotes). Para liquidar muchas a la vez, settleAllLimitOrder agrupa en una transacción v0 todas las llamadas SettleLimitOrder que quepan.

Listar las órdenes activas de una wallet (off-chain)

El endpoint de órdenes activas devuelve en una misma respuesta las órdenes sin ejecutar y las parcialmente ejecutadas (totalAmount / filledAmount / pendingSettle distinguen las fases). Para el historial de órdenes cerradas use /limit-order/history/order/list-by-user?wallet=… (por wallet, paginado con nextPageId); para el registro completo de eventos de una orden concreta use /limit-order/history/event/list-by-pda?pda=….

Esqueleto de CPI en Rust

Orden de las cuentas restantes para SwapV2:
Si el swap nunca necesita la extensión, omítala; en caso contrario, es la primera cuenta restante.

Errores comunes

  • Extremos de tick que no coinciden con el espaciado → TickAndSpacingNotMatch. Ajuste siempre los valores con TickUtil.getPriceAndTick (TickUtil en singular).
  • No se proporcionan suficientes tick arrays en SwapV2 → NotEnoughTickArrayAccount. Tome la lista de swapInternal(...).accounts.
  • Posición de rango completo sin la extensión de bitmap → la PDA de la extensión debe ser escribible. El SDK se encarga de esto automáticamente.
  • Confundir sqrt_price_x64 con price → una confusión de factor 2 en este punto resulta especialmente costosa. En caso de duda, deje que el SDK calcule el valor a partir de un precio legible.
  • Recolectar recompensas con demasiada frecuencia → cada recolección es un DecreaseLiquidity con liquidez cero y cuesta una transacción. Agrúpelas con harvestAllRewards para varias posiciones, y recuerde que su execute necesita { sequentially: true }.
  • Cerrar por su cuenta las cuentas de NFT → ClosePosition quema el NFT y cierra su ATA. También cierra el mint de un NFT de Token-2022; un mint de SPL Token clásico permanece con supply cero, porque ese programa no puede cerrar mints. No cierre por separado las cuentas admitidas, o la instrucción fallará.
  • Abrir una orden límite en un tick que no respeta el espaciado → TickAndSpacingNotMatch. Cuantifique siempre el valor con el helper exportado getOrderTick.
  • Llamar a decreaseLimitOrder sobre una orden totalmente ejecutada → InvalidOrderPhase. Use settleLimitOrder y después closeLimitOrder.
  • Esperar un flag enableDynamicFee → no existe. Si omite dynamicFeeConfig, simplemente se crea un pool de comisión estática, sin aviso ni error. Si quiere comisiones dinámicas, pase el PublicKey de la cuenta de configuración, tomado de /main/clmm-dynamic-config.

Próximos pasos

Fuentes: