> ## 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.

# CPMM

> Pools à produit constant (x·y=k) : état du pool, cotations, swaps, liquidité et création de pool.

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={['cpmm']} lang="fr" />

```ts theme={null}
powfi.cpmm              // CpmmModule instance (network-bound)
import { CpmmModule } from '@alephium/powfi-sdk' // static quote helpers
```

Les pools CPMM détiennent deux jetons et les valorisent selon l'invariant à produit constant `reserve0 × reserve1 = k`. Chaque swap verse des frais de **0,3 %** aux fournisseurs de liquidité. La liquidité est représentée par un **jeton LP** fongible **dont l'identifiant est celui du pool**.

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

  <Card title="Guide : fournir de la liquidité CPMM" icon="droplet" href="/fr/guides/cpmm-liquidity" />
</CardGroup>

## Recherche de pools

### `getPoolId(tokenA, tokenB)`

```ts theme={null}
getPoolId(tokenA: string, tokenB: string): string
```

Dérive hors ligne l'identifiant de contrat de la paire (les jetons sont triés d'abord, l'ordre n'a donc pas d'importance). C'est aussi l'**identifiant du jeton LP**.

### `getPoolAddress(tokenA, tokenB)`

Renvoie l'adresse du contrat de la paire (`addressFromContractId(getPoolId(...))`).

### `poolExists(tokenA, tokenB)`

```ts theme={null}
poolExists(tokenA: string, tokenB: string): Promise<boolean>
```

### `getPoolState(tokenA, tokenB)`

```ts theme={null}
getPoolState(tokenA: string, tokenB: string): Promise<CpmmPoolContractState>
```

Récupère les réserves et l'offre de LP, et résout les deux jetons via la liste de jetons. Lève `PoolNotFoundError` si la paire n'existe pas. Les deux jetons doivent figurer dans la liste de jetons.

<ResponseField name="CpmmPoolContractState" type="object">
  <Expandable title="champs" defaultOpen>
    <ResponseField name="poolId" type="string">Identifiant du contrat de la paire (= identifiant du jeton LP).</ResponseField>
    <ResponseField name="reserve0" type="bigint">Réserve de `token0` (ordre trié).</ResponseField>
    <ResponseField name="reserve1" type="bigint">Réserve de `token1`.</ResponseField>
    <ResponseField name="token0Info" type="TokenInfo">Métadonnées du plus petit identifiant de jeton (ordre lexicographique).</ResponseField>
    <ResponseField name="token1Info" type="TokenInfo">Métadonnées du plus grand identifiant.</ResponseField>
    <ResponseField name="totalSupply" type="bigint">Total des jetons LP en circulation.</ResponseField>
    <ResponseField name="dexRoot" type="string">Identifiant du contrat racine du DEX enregistré dans la paire.</ResponseField>
  </Expandable>
</ResponseField>

### `getPoolProtocolFees(poolAddress)`

```ts theme={null}
getPoolProtocolFees(poolAddress: string): Promise<bigint>
```

Frais de protocole accumulés dans la paire et pas encore collectés. Prend l'**adresse** du pool, pas les identifiants des jetons.

## Cotations (statiques, hors ligne)

Toutes les fonctions de cotation sont **statiques** et pures. Passez un `CpmmPoolContractState` déjà récupéré.

### `CpmmModule.computeSwapAmount(params)`

```ts theme={null}
static computeSwapAmount(params: CpmmSwapQuoteParams): CpmmSwapQuote
```

<ParamField path="state" type="CpmmPoolContractState" required />

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

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

<ParamField path="amountIn" type="bigint">À renseigner pour une cotation en **entrée exacte**.</ParamField>
<ParamField path="amountOut" type="bigint">À renseigner pour une cotation en **sortie exacte** (utilisé seulement si `amountIn` est indéfini).</ParamField>
<ParamField path="slippageBps" type="bigint" required>Tolérance de slippage en points de base.</ParamField>

<ResponseField name="CpmmSwapQuote" type="object">
  <Expandable title="champs" defaultOpen>
    <ResponseField name="swapType" type="'ExactIn' | 'ExactOut'" />

    <ResponseField name="tokenInInfo / tokenOutInfo" type="TokenInfo" />

    <ResponseField name="tokenInAmount" type="bigint">Montant d'entrée (donné, ou calculé en sortie exacte).</ResponseField>
    <ResponseField name="tokenOutAmount" type="bigint">Montant de sortie (calculé, ou donné en sortie exacte).</ResponseField>
    <ResponseField name="minimalTokenOutAmount" type="bigint | undefined">Entrée exacte uniquement : sortie après slippage.</ResponseField>
    <ResponseField name="maximalTokenInAmount" type="bigint | undefined">Sortie exacte uniquement : entrée après slippage.</ResponseField>
    <ResponseField name="priceImpact" type="number">Pourcentage, par exemple `0.42` = 0,42 %.</ResponseField>
    <ResponseField name="state" type="CpmmPoolContractState">L'état à partir duquel la cotation a été calculée.</ResponseField>
  </Expandable>
</ResponseField>

En sortie exacte, lève `InsufficientLiquidityError` si `amountOut >= reserveOut`.

### `CpmmModule.getAmountOut(state, tokenInId, amountIn)` / `getAmountIn(state, tokenOutId, amountOut)`

Les formules x·y=k brutes avec les frais de 0,3 % :

```ts theme={null}
amountOut = (amountIn * 997 * reserveOut) / (reserveIn * 1000 + amountIn * 997)
amountIn  = (reserveIn * amountOut * 1000) / ((reserveOut - amountOut) * 997) + 1
```

### `CpmmModule.calcPriceImpact(reserve0, reserve1, tokenInId, token0Id, amountIn, amountOut)`

Renvoie l'impact de prix en pourcentage.

### `CpmmModule.computeLiquidityAmounts(params)`

```ts theme={null}
static computeLiquidityAmounts(params: CpmmAddLiquidityQuoteParams): CpmmAddLiquidityQuote
```

* **Pool existant** (`poolState` renseigné) : indiquez le montant d'un côté (`inputType: 'TokenA'` utilise `amountA`, `'TokenB'` utilise `amountB`). La fonction en déduit l'autre côté à partir des réserves, ainsi que les jetons LP que vous recevrez.
* **Nouveau pool** (`poolState` indéfini) : `amountA` et `amountB` sont tous deux requis. LP mintés = `sqrt(amountA × amountB) − MINIMUM_LIQUIDITY` (1000 unités sont verrouillées pour toujours). Lève `InsufficientLiquidityError` si `sqrt(amountA × amountB) <= 1000`.

Renvoie `{ tokenAId, tokenBId, amountA, amountB, shareAmount, sharePercentage, state? }`, où `sharePercentage` est votre part du pool après le dépôt, en pourcentage.

### `CpmmModule.computeRemoveLiquidityAmounts(state, totalLiquidity, liquidityToRemove)`

Montants de jetons rendus pour la destruction de `liquidityToRemove` jetons LP, plus votre part restante. `totalLiquidity` est **votre** solde de LP. Lève une erreur si `liquidityToRemove > totalLiquidity`.

### `CpmmModule.computeClaimableAmounts(state, liquidityBalance)`

La valeur sous-jacente complète d'un solde de LP : `{ token0, amount0, token1, amount1, ... }`.

### `CpmmModule.minimalAmount(amount, slippage)` / `maximalAmount(amount, slippage)`

Helpers de slippage. Voir [Montants et slippage](/fr/concepts/amounts-and-slippage).

## Swaps

### `swap(params, balances?)`

```ts theme={null}
swap(params: CpmmSwapRequest, balances?: Map<string, bigint>): Promise<ExecuteScriptResult>
```

Récupère un état de pool frais, calcule la cotation, vérifie l'impact de prix et exécute `SwapMinOut` (entrée exacte) ou `SwapMaxIn` (sortie exacte) via le routeur. Nécessite un signataire.

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

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

<ParamField path="amountIn" type="bigint">Montant en entrée exacte.</ParamField>
<ParamField path="amountOut" type="bigint">Montant en sortie exacte (utilisé si `amountIn` est indéfini).</ParamField>
<ParamField path="slippageBps" type="bigint" required>Points de base.</ParamField>
<ParamField path="sender" type="string" required>Adresse qui paie et reçoit, généralement celle du signataire.</ParamField>
<ParamField path="ttlMinutes" type="number" default="60">Échéance.</ParamField>
<ParamField path="fee" type="bigint">[Frais d'intégrateur](/fr/guides/integrator-fees) optionnels, dans le jeton d'entrée.</ParamField>
<ParamField path="feeRecipient" type="string">Requis avec `fee`.</ParamField>

<ParamField path="balances" type="Map<tokenId, bigint>">
  Vérification préalable optionnelle : si elle est fournie et que `balances.get(tokenIn) < entrée + frais`, lève `InsufficientBalanceError` avant la signature. Pour l'ALPH, utilisez `ALPH_TOKEN_ID` comme clé.
</ParamField>

Lève `PriceImpactTooHighError` si `priceImpact >= 5`.

### `simSwap(params)`

```ts theme={null}
simSwap(params: CpmmSwapRequest): Promise<CpmmSwapQuote>
```

Récupère l'état du pool et renvoie la cotation que `swap` utiliserait, sans rien envoyer. `slippageBps` vaut `100n` par défaut s'il est nul.

### `swapTo(params)`

```ts theme={null}
swapTo(params: {
  tokenA: string
  tokenB: string
  targetPrice: number | BigNumber // human price: token1 per token0 (sorted order)
  sender: string
  slippageBps?: bigint            // default 50n
}): Promise<ExecuteScriptResult>
```

Calcule le trade en entrée exacte qui amène le prix du pool à `targetPrice`, puis l'exécute. La direction est déduite automatiquement. **Ignore le contrôle d'impact de prix.** Lève une erreur si la cible est égale au prix actuel. Utile pour l'arbitrage et pour recaler le prix de pools de test.

## Liquidité

### `addLiquidity(params, balances?)`

```ts theme={null}
addLiquidity(params: CpmmAddLiquidityRequest, balances?: Map<string, bigint>): Promise<ExecuteScriptResult>
```

<ParamField path="poolState" type="CpmmPoolContractState" required>État frais issu de `getPoolState`.</ParamField>
<ParamField path="tokenAId / tokenBId" type="string" required>Doivent tous deux être des jetons du pool (dans n'importe quel ordre).</ParamField>
<ParamField path="amountA / amountB" type="bigint" required>Montants souhaités, tous deux supérieurs à 0. Utilisez `computeLiquidityAmounts` pour obtenir une paire équilibrée.</ParamField>
<ParamField path="slippageBps" type="bigint" required>Appliqué aux deux montants comme minimums. Ignoré si le pool est vide.</ParamField>

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

<ParamField path="ttlMinutes" type="number" default="60" />

Le routeur dépose selon le ratio optimal et minte les jetons LP pour `sender`.

### `removeLiquidity(params)`

```ts theme={null}
removeLiquidity(params: CpmmRemoveLiquidityRequest): Promise<ExecuteScriptResult>
```

<ParamField path="poolState" type="CpmmPoolContractState" required />

<ParamField path="liquidity" type="bigint" required>Jetons LP à détruire.</ParamField>
<ParamField path="totalLiquidityAmount" type="bigint">Votre solde de LP (utilisé pour le calcul de la part). Par défaut `poolState.totalSupply`.</ParamField>
<ParamField path="slippageBps" type="bigint" required>Appliqué aux deux montants de sortie comme minimums.</ParamField>

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

<ParamField path="ttlMinutes" type="number" default="60" />

### `computeClaimableAmounts(tokenAId, tokenBId, liquidityBalance)`

Version d'instance : récupère l'état du pool puis renvoie `{ token0, amount0, token1, amount1 }` pour un solde de LP.

## Création de pool

### `createPool(params)`

```ts theme={null}
createPool(params: CpmmCreatePoolRequest): Promise<{ poolId: string; result: ExecuteScriptResult }>
```

<ParamField path="tokenAId / tokenBId" type="string" required />

<ParamField path="sender" type="string" required>Paie le dépôt de contrat de 1 ALPH.</ParamField>

<ParamField path="initialLiquidity" type="{ tokenAAmount: bigint; tokenBAmount: bigint }">
  Si renseigné, crée la paire et dépose la liquidité initiale en une seule transaction (`CreatePairAndAddLiquidity`). Le ratio fixe le prix de départ. S'il est omis, seule la paire est créée (`CreatePair`, qui joint 1 unité de base de chaque jeton).
</ParamField>

## Configuration

`getConfig()` renvoie la `CpmmConfig` active (`groupIndex`, `factoryId`, `routerId`). `setConfig(config)` la surcharge. `getCpmmConfig()` relit le déploiement inclus.

## Constantes

| Nom | Valeur | Signification |
| - | - | - |
| `MAX_PRICE_IMPACT` | `5` | Pourcentage. `swap` rejette les cotations égales ou supérieures. |
| `MINIMUM_LIQUIDITY` | `1000n` | Unités de LP verrouillées lors du premier dépôt. |
| `BPS` | `10_000n` | Dénominateur des points de base. |


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