> ## Documentation Index
> Fetch the complete documentation index at: https://docs.powfi.alephium.org/llms.txt
> Use this file to discover all available pages before exploring further.

# CLMM

> Pools à liquidité concentrée : paliers de frais, état du pool, simulation de swap, swaps, positions et récompenses de farming.

export const VersionBanner = ({network = 'mainnet', products = [], lang = 'en'}) => {
  const SDK_VERSION = '1.0.2';
  const ADDRESSES = {
    mainnet: {
      cpmm: {
        Router: 'ze15rnAwTyVCPnDhN7J8An3SvNeWBsZHAVMeBx1ehXeP',
        TokenPairFactory: '24CXRLvj1QiDH6riwr1EPERG7BcduHKZtktygbq97MfYb'
      },
      clmm: {
        PoolFactory: '24gqd4DMQXVzjm5QDAGxUtYupx3WM2PSu3vZUseuBCV9h',
        PositionManager: 'vFyKSqnJHS9MicrojBcLgztyRFqhS4hB349Ci3LJCELw'
      },
      staking: {
        XAlphToken: '225WevmFp5ZgzPsyVJTvyp2v2uyKrvp329HfrVmzffnWj',
        RewardFeeCollector: '22CE7vG6u64zjjsLZLxeJqQuPDydjPvbP9iG1d861j2G7'
      }
    },
    testnet: {
      cpmm: {
        Router: 'xaueT8kPMvpnaFJEfHv6CJLgqsSPpoNmHjF1QgTxoF9R',
        TokenPairFactory: '29hdd9b9Gp7oXuamwHKfUqBaRXP487HoeTcutS3RhZJEj'
      },
      clmm: {
        PoolFactory: 'z2QFT7qBQrfm8UwAYG3VHWoHs7B56ZDFNdSCcDu5j53m',
        PositionManager: '21p6egW8d6FEyVVuH17WqvHCARsCsD61GcBmeCr75XdGT'
      },
      staking: {
        XAlphToken: '23psDicimC5VdpM56wrgqgCzQzcyXWGCkQGSYWbtZAPRq',
        RewardFeeCollector: '29qQ1LPJ5uqawRviXLVDMeo19JUygRrNNTQy5RUBh817D'
      }
    }
  };
  const EXPLORERS = {
    mainnet: 'https://explorer.alephium.org/addresses/',
    testnet: 'https://testnet.alephium.org/addresses/'
  };
  const LABELS = {
    en: {
      sdk: 'SDK',
      network: 'Network',
      contracts: 'Contracts',
      all: 'All addresses',
      allNetworks: 'mainnet · testnet · devnet',
      devnet: 'Local deployment (addresses vary per devnet)'
    },
    zh: {
      sdk: 'SDK 版本',
      network: '网络',
      contracts: '合约',
      all: '全部地址',
      allNetworks: 'mainnet · testnet · devnet',
      devnet: '本地部署（每个 devnet 的地址不同）'
    },
    fr: {
      sdk: 'SDK',
      network: 'Réseau',
      contracts: 'Contrats',
      all: 'Toutes les adresses',
      allNetworks: 'mainnet · testnet · devnet',
      devnet: 'Déploiement local (adresses propres à chaque devnet)'
    }
  };
  const t = LABELS[lang] ?? LABELS.en;
  const contractsPage = lang === 'en' ? '/reference/contracts' : `/${lang}/reference/contracts`;
  const short = a => `${a.slice(0, 6)}…${a.slice(-4)}`;
  const entries = [];
  const addrs = ADDRESSES[network];
  if (addrs) {
    for (const p of products) {
      for (const [name, address] of Object.entries(addrs[p] ?? ({}))) entries.push({
        name,
        address
      });
    }
  }
  let contractsCell;
  if (network === 'devnet' && products.length > 0) {
    contractsCell = <span>{t.devnet}</span>;
  } else if (entries.length > 0) {
    contractsCell = entries.map(({name, address}, i) => <span key={name}>
        {i > 0 && <span className="mx-1 opacity-50">·</span>}
        {name}{' '}
        <a href={EXPLORERS[network] + address} target="_blank" rel="noreferrer" title={address}>
          <code>{short(address)}</code>
        </a>
      </span>);
  }
  const row = (label, value) => <div className="flex flex-wrap gap-x-2">
      <span className="font-semibold min-w-[5.5rem]">{label}</span>
      <span className="flex-1 min-w-0 break-words">{value}</span>
    </div>;
  return <div className="not-prose mb-6 rounded-xl border border-zinc-950/10 dark:border-white/10 bg-zinc-50 dark:bg-white/5 px-4 py-3 text-sm leading-6 text-zinc-700 dark:text-zinc-300">
      {row(t.sdk, <code>@alephium/powfi-sdk@{SDK_VERSION}</code>)}
      {row(t.network, network === 'all' ? t.allNetworks : network)}
      {contractsCell && row(t.contracts, <span>
            {contractsCell}
            <span className="mx-1 opacity-50">·</span>
            <a href={contractsPage}>{t.all} →</a>
          </span>)}
    </div>;
};

<VersionBanner network="mainnet" products={['clmm']} lang="fr" />

```ts theme={null}
powfi.clmm // ClmmModule
```

Les pools CLMM sont de style Uniswap V3 : les fournisseurs de liquidité choisissent une plage de prix (`tickLower`–`tickUpper`) et ne gagnent des frais que lorsque le prix se trouve dans cette plage. Chaque pool est identifié par une **paire de jetons et un palier de frais** (`configIndex`). Les prix sont stockés sous la forme `sqrtPriceX96 = sqrt(token1/token0) × 2^96`.

<CardGroup cols={2}>
  <Card title="Guide : swapper sur CLMM" icon="arrow-right-arrow-left" href="/fr/guides/clmm-swap" />

  <Card title="Guide : ouvrir et gérer une position" icon="chart-area" href="/fr/guides/clmm-position" />
</CardGroup>

## Paliers de frais (configurations de pool)

Un palier de frais est un contrat `PoolConfig` enregistré dans la factory, indexé à partir de `0n`. Chaque pool en référence exactement un.

```ts theme={null}
interface ClmmPoolConfig {
  configIndex: bigint
  tickSpacing: bigint  // positions' ticks must be multiples of this
  tradingFee: bigint   // pips (MAX_PIPS = 1_000_000n): 3000n = 0.3%
  protocolFee: bigint  // protocol share of trading fees
}
```

| Méthode | Renvoie |
| - | - |
| `getAllPoolConfigs()` | `Promise<ClmmPoolConfig[]>` : tous les paliers (mis en cache par index) |
| `getPoolConfig(configIndex)` | `Promise<ClmmPoolConfig \| undefined>` : `undefined` si l'index n'existe pas |
| `getPoolConfigId(configIndex)` | Identifiant du contrat `PoolConfig` du palier |

## Recherche de pools

| Méthode | Renvoie |
| - | - |
| `getPoolId(tokenA, tokenB, configIndex)` | Identifiant du contrat du pool (jetons triés en interne) |
| `getPoolAddress(tokenA, tokenB, configIndex)` | Adresse du pool |
| `getPool(tokenA, tokenB, configIndex)` | `PoolInstance` typée pour les appels directs au contrat |
| `poolExists(tokenA, tokenB, configIndex)` | `Promise<boolean>` |
| `findBestRoute(tokenA, tokenB)` | `Promise<bigint>` : le `configIndex` dont le pool a le plus de liquidité active. Lève `PoolNotFoundError` s'il n'en existe aucun. |

### `getPoolState(poolId)`

```ts theme={null}
getPoolState(poolId: string): Promise<ClmmPoolContractState>
```

<ResponseField name="ClmmPoolContractState" type="object">
  <Expandable title="champs" defaultOpen>
    <ResponseField name="poolId" type="string" />

    <ResponseField name="token0Info / token1Info" type="TokenInfo">Métadonnées de la paire triée (les deux jetons doivent être dans la liste de jetons).</ResponseField>
    <ResponseField name="sqrtPriceX96" type="bigint">Prix actuel. Convertissez avec `TickUtils.sqrtPriceX96ToPrice`.</ResponseField>
    <ResponseField name="tick" type="bigint">Tick actuel.</ResponseField>
    <ResponseField name="liquidity" type="bigint">Liquidité active dans la plage courante.</ResponseField>
    <ResponseField name="configIndex / tickSpacing / tradingFee / protocolFee" type="bigint">Champs du palier de frais.</ResponseField>
  </Expandable>
</ResponseField>

Lève `PoolNotFoundError` si le pool n'existe pas.

### Autres lectures de pool

| Méthode | Renvoie |
| - | - |
| `getPoolTokenBalances(poolId)` | `{ token0Balance, token1Balance }` : soldes réels détenus par le contrat du pool |
| `getPoolProtocolFees(poolId)` | `{ token0, token1 }` : frais de protocole non collectés |
| `getPoolRewardState(poolId)` | `{ token2Info?, rewardInfos: { amount, openTime, endTime }[] }` : programmes de récompenses de farming. `token2` est le jeton de récompense supplémentaire du pool. `token2Info` vaut `undefined` pour les pools qui n'en ont pas. |

## Swaps

### `simulateSwap(params)`

```ts theme={null}
simulateSwap(p: ClmmSimulateSwapParams): Promise<ClmmSimulateSwapQuote>
```

Exécute la méthode de vue `simulateSwap` du pool sur le nœud (aucune transaction) et renvoie la courbe de liquidité parcourue par le swap. Passez le résultat à [`PoolUtils.offlineSwap`](/fr/utilities/liquidity-utils) pour calculer localement le montant de sortie.

<ParamField path="configIndex" type="bigint" required />

<ParamField path="token0 / token1" type="string" required>Les jetons du pool (dans n'importe quel ordre, utilisés pour localiser le pool).</ParamField>
<ParamField path="zeroForOne" type="boolean" required>`true` vend le `token0` trié contre du `token1` (le prix baisse).</ParamField>
<ParamField path="amount" type="bigint" required>**Positif = entrée exacte**, **négatif = sortie exacte**.</ParamField>
<ParamField path="data" type="string">Chemin multi-sauts issu de `buildSwapPath` (optionnel).</ParamField>
<ParamField path="interestedContracts" type="string[]">Adresses des pools supplémentaires traversés par un chemin multi-sauts.</ParamField>

<ResponseField name="ClmmSimulateSwapQuote" type="object">
  <Expandable title="champs" defaultOpen>
    <ResponseField name="baseSqrtPriceX96" type="bigint">Sqrt price de référence à une limite de tick trouvée dans le sens opposé au swap, utilisé pour ancrer la courbe de liquidité hors ligne.</ResponseField>
    <ResponseField name="sqrtPriceX96" type="bigint">Sqrt price du pool au début du swap simulé.</ResponseField>
    <ResponseField name="liquidity" type="bigint">Liquidité active dans la plage au début du swap simulé.</ResponseField>
    <ResponseField name="fee" type="bigint">Frais de trading en pips.</ResponseField>
    <ResponseField name="rows" type="{ sqrtPriceX96, liquidity }[]">Prix et liquidité enregistrés aux bornes des plages des LP, aux limites des groupes de recherche de ticks et au point final atteint par la simulation.</ResponseField>
  </Expandable>
</ResponseField>

### `swap(params)`

```ts theme={null}
swap(p: ClmmSwapRequest): Promise<SignExecuteScriptTxResult>
```

Exécute un swap sur un seul pool via le script `SwapWithoutAccount` (ou `SwapWithoutAccountWithFee` si des frais d'intégrateur sont définis). Le signataire est à la fois payeur et destinataire.

<ParamField path="token0" type="string" required>Le jeton **d'entrée** (pas forcément le token0 trié du pool).</ParamField>
<ParamField path="token1" type="string" required>Le jeton **de sortie**.</ParamField>
<ParamField path="amount" type="bigint" required>Montant spécifié : **positif = entrée exacte**, **négatif = sortie exacte** (l'opposé de la sortie souhaitée).</ParamField>
<ParamField path="amountIn" type="bigint" required>Quantité de jeton d'entrée à joindre. En entrée exacte : identique à `amount`. En sortie exacte : votre entrée maximale (simulez, puis ajoutez le slippage).</ParamField>
<ParamField path="slippage" type="bigint" required>Points de base, convertis en `sqrtPriceLimitX96`.</ParamField>
<ParamField path="routePlan" type="bigint[]" required>Paliers de frais à parcourir. Seul `routePlan[0]` est utilisé aujourd'hui.</ParamField>
<ParamField path="fee / feeRecipient" type="bigint / string">[Frais d'intégrateur](/fr/guides/integrator-fees) optionnels, dans le jeton d'entrée.</ParamField>

<Warning>
  `swap()` envoie toujours un chemin multi-sauts vide : il ne swappe donc que dans un seul pool. Pour du multi-sauts, construisez le chemin avec `buildSwapPath` et appelez directement le script `SwapWithoutAccount` de `ClmmScripts`.
</Warning>

### `swapTo(params)`

```ts theme={null}
swapTo(p: ClmmSwapToRequest): Promise<SignExecuteScriptTxResult>
// { tokenIn, tokenOut, configIndex, targetSqrtPriceX96, amountInMax }
```

Swappe jusqu'à ce que le pool atteigne `targetSqrtPriceX96`, en dépensant au plus `amountInMax` de `tokenIn`. Échoue on-chain (code d'erreur 106, `InvalidSqrtPriceLimit`) si la cible est du mauvais côté du prix actuel.

### `buildSwapPath(tokenId, configIndex)`

Encode un saut supplémentaire pour `simulateSwap({ data })` : `tokenId + configIndex` sur 4 caractères hexadécimaux.

## Positions de liquidité

Une position est identifiée par **pool + propriétaire + tickLower + tickUpper**. L'identifiant du contrat de position sert aussi d'**identifiant de jeton de position** : le propriétaire en détient une unité, qui est jointe lors de la modification de la position.

### `getPositionId(poolId, owner, tickLower, tickUpper)`

Dérive l'identifiant de position hors ligne. Pour les adresses sans groupe, passez `owner` sous forme normalisée (voir [Groupes et adresses](/fr/concepts/networks)). `PoolUtils.getPositionId(poolAddress, owner, ...)` normalise pour vous.

### `createPool(...)`

```ts theme={null}
createPool(
  configIndex: bigint,
  token0: string, token1: string,
  tick: bigint,                    // initial price as a tick
  amount0: bigint, amount1: bigint,
  tickLower: bigint, tickUpper: bigint,
  dustAmount?: bigint
): Promise<{ poolId: string; result: ExecuteScriptResult }>
```

Crée un pool au prix `tick` et minte la première position dans `[tickLower, tickUpper]`. Les jetons, leurs montants et les ticks sont triés en interne. Joint `6 × MINIMAL_CONTRACT_DEPOSIT` d'ALPH pour les nouveaux contrats.

### `addLiquidity(params)`

```ts theme={null}
addLiquidity(p: ClmmAddLiquidityRequest): Promise<{ positionId: string; result: SignExecuteScriptTxResult }>
```

<ParamField path="token0 / token1" type="string" required>Jetons du pool. Passez-les **triés** (`sortTokens`) et faites correspondre `amount0` / `amount1` à cet ordre.</ParamField>

<ParamField path="configIndex" type="bigint" required />

<ParamField path="tickLower / tickUpper" type="bigint" required>Multiples du `tickSpacing` du palier. Utilisez `TickUtils.getAlignedTick`.</ParamField>
<ParamField path="amount0 / amount1" type="bigint" required>Montants souhaités. Le contrat dépose les montants équilibrés maximaux dans cette limite.</ParamField>
<ParamField path="slippage" type="bigint" required>Points de base sur le prix. Les montants minimaux sont déduits des bornes de prix.</ParamField>
<ParamField path="owner" type="string">Propriétaire de la position. Par défaut, le signataire.</ParamField>
<ParamField path="existingPosition" type="boolean">Mettez `true` pour ajouter à une position que vous détenez déjà (joint le jeton de position). Le signataire doit détenir le NFT de la position, que le SDK joint automatiquement.</ParamField>

Le dépôt de la position (`dustAmount`) est lu on-chain via `PositionManager.getSqrtPricesX96`.

**Variante en deux étapes :** `getAddLiquidityParams(p)` renvoie `[positionId, positionManager, params]` sans rien envoyer, et `addLiquidityFromParams(positionId, positionManager, params)` les exécute. Utile pour afficher un écran de confirmation avec les montants minimaux exacts.

### `removeLiquidity(params)`

```ts theme={null}
removeLiquidity(p: ClmmRemoveLiquidityRequest): Promise<{ positionId: string; result: SignExecuteScriptTxResult }>
```

Réduit la liquidité de la position et, dans la même transaction, collecte tous les frais et récompenses accumulés. Les jetons sont envoyés au signataire.

<ParamField path="token0 / token1 / configIndex" required>Identifient le pool.</ParamField>
<ParamField path="owner / tickLower / tickUpper" required>Identifient la position.</ParamField>
<ParamField path="liquidity" type="bigint" required>Unités de liquidité à retirer (lisez la `liquidity` actuelle de la position, puis prenez-en une fraction).</ParamField>
<ParamField path="base" type="'token0' | 'token1'" required>Côté auquel se rapporte `baseAmount`.</ParamField>
<ParamField path="baseAmount" type="bigint" required>Montant minimal du jeton `base` à recevoir.</ParamField>
<ParamField path="otherAmountMax" type="bigint" required>Montant minimal de l'autre jeton à recevoir. Malgré son nom, il est transmis on-chain comme **minimum**.</ParamField>

Passez `0n` pour les deux minimums afin de désactiver le contrôle.

### `collectTokens(params)`

```ts theme={null}
collectTokens(p: ClmmCollectTokensRequest): Promise<{ positionId: string; result: SignExecuteScriptTxResult }>
```

Collecte les frais et récompenses accumulés vers `recipient`. Ne retire pas de liquidité ; utilisez [`removeLiquidity`](#removeliquidity-params) pour cela.

<ParamField path="token0 / token1 / configIndex / owner / tickLower / tickUpper" required>Identifient la position.</ParamField>

<ParamField path="recipient" type="string" required />

<ParamField path="amount0Max / amount1Max" type="bigint" required>Plafonds de collecte. Utilisez des valeurs de l'ordre de `U128_MAX` (par exemple `UNLIMITED_AMOUNT`) pour tout collecter.</ParamField>

### `positionInfo(params)`

```ts theme={null}
positionInfo(p: ClmmPositionInfoRequest): Promise<ClmmPositionInfo>
// => { amount0, amount1, fees: [bigint, bigint, bigint], avgValue, avgFees, avgTime }
```

Appelle la vue `positionInfo` du pool et renvoie son résultat tel quel. `fees` contient trois entrées : token0, token1 et le jeton de récompense du pool. Le SDK transmet directement au contrat les arguments d'accumulateur (`acc`, `iacc0`, `iacc1`, `t0`, `acct0`). Consultez le code source du contrat `Pool` pour leur signification.

Pour lire la liquidité brute d'une position, récupérez l'état du contrat de position :

```ts theme={null}
import { ClmmContracts } from '@alephium/powfi-sdk'
import { addressFromContractId } from '@alephium/web3'

const pos = await ClmmContracts.Position.at(addressFromContractId(positionId)).fetchState()
pos.fields.liquidity // bigint
pos.fields.tokensOwed // [token0, token1, reward]
```

## Récompenses de farming

Un pool peut avoir jusqu'à trois programmes de récompenses (`MAX_REWARDS = 3`), versées en token0, en token1 ou dans le jeton de récompense supplémentaire du pool (`token2`). Les programmes sont mis en place et financés par l'équipe Powfi.

* `getPoolRewardState(poolId)` renvoie le jeton de récompense ainsi que l'`amount`, l'`openTime` et l'`endTime` (horodatages en millisecondes) de chaque programme.
* Les récompenses accumulées par une position apparaissent dans `positionInfo(...).fees` : les récompenses en token0 dans `fees[0]`, en token1 dans `fees[1]`, et dans tout autre token dans `fees[2]`. Elles sont versées via `collectTokens`.

## Comptes de parrainage (DEX)

Powfi suit les parrainages grâce à des contrats `DexAccount` par utilisateur, sous un `DexAccountRoot`.

| Méthode | Description |
| - | - |
| `createDexAccount(referrer)` | Crée le compte du signataire avec `referrer` (coûte `MINIMAL_CONTRACT_DEPOSIT`). |
| `getDexAccountId(owner)` | Dérive l'identifiant du contrat de compte d'un propriétaire. |
| `getDexAccountState(owner)` | `{ address, id, state }` du compte. |
| `getDexAccountRoot()` | `DexAccountRootInstance` typée. |

## Configuration

```ts theme={null}
interface ClmmConfig {
  groupIndex: number
  factoryId: string
  positionManagerId: string
  defaultConfigIndex: bigint // 0n
  accountRoot: string
}
```

Lisez la configuration active avec `getClmmConfig()` et surchargez-la avec `setConfig()`. `getConfig()` relit le déploiement inclus.

## Constantes

| Nom | Valeur |
| - | - |
| `MIN_TICK` / `MAX_TICK` | `-887272n` / `887272n` |
| `MAX_PIPS` | `1_000_000n` |
| `U256_MAX` | `2n ** 256n - 1n` |
| `UNLIMITED_AMOUNT` | `2n ** 128n - 1n` |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.