Sui.

Publicación

Comparte tu conocimiento.

Evgeniy CRYPTOCOIN.
Jun 30, 2025
P&R expertos

How to Create a Liquidity Pool in Sui Move?

I'm building a DeFi protocol on Sui and need to implement a basic liquidity pool (like Uniswap-style AMM) in Move. I'm struggling with:

  1. Storing LP tokens – How to handle dynamic supply and balances?
  2. Deposits/Withdrawals – Ensuring atomic swaps and proper math.
  3. Fee mechanism – Where to deduct fees without breaking invariants?
  4. Frontrunning protection – Is there a built-in way to handle slippage?

What I've tried:

  • Basic two-token pool using Table for balances.
  • Manual LP mint/burn logic.
  • Fixed 0.3% fee on swaps.

Issues encountered:

  • "Arithmetic overflow" when calculating liquidity.
  • Reentrancy risks – Can Sui Move prevent this?
  • LP token accuracy – Decimals handling feels hacky.

Questions:

  1. What’s the correct architecture for a Sui liquidity pool?
  2. How to implement safe math for swaps/deposits?
  3. Are there Sui-specific optimizations (vs. EVM AMMs)?
  4. How to make the pool composable with other DeFi protocols?
  • Sui
5
1
Cuota
Comentarios
.

Respuestas

1
Benjamin XDV.
Jun 30 2025, 18:15

1. Estructura central El modelo centrado en objetos de Sui requiere:

Objeto de grupo: almacena las reservas, los datos del token LP y la configuración.

LP Token: un activo similar a una moneda que rastrea la propiedad.

move
module dex::amm {  
    use sui::balance;  
    use sui::coin;  
    use sui::math;  
    use sui::tx_context;  

    struct Pool<phantom X, phantom Y> has key {  
        id: UID,  
        reserve_x: Balance<X>,  
        reserve_y: Balance<Y>,  
        lp_supply: Balance<LP<X, Y>>,  
        fee_bps: u64, // e.g., 30 = 0.3%  
    }  

    struct LP<phantom X, phantom Y> has store {} // LP token type  

2. Lógica de depósitos (adición de liquidez) Pasos clave:

Toma los tokens de entrada.

Acuñe los tokens LP proporcionalmente.

Actualiza las reservas de forma atómica.

move
public fun add_liquidity<X, Y>(  
    pool: &mut Pool<X, Y>,  
    x: Coin<X>,  
    y: Coin<Y>,  
    ctx: &mut TxContext  
): Coin<LP<X, Y>> {  
    let x_val = coin::value(&x);  
    let y_val = coin::value(&y);  
    assert!(x_val > 0 && y_val > 0, EINVALID_AMOUNT);  

    // Calculate LP tokens to mint (geometric mean)  
    let lp_amount = if (balance::supply(&pool.lp_supply) == 0) {  
        math::sqrt(x_val * y_val) // Initial mint  
    } else {  
        min(  
            (x_val * balance::supply(&pool.lp_supply)) / balance::value(&pool.reserve_x),  
            (y_val * balance::supply(&pool.lp_supply)) / balance::value(&pool.reserve_y)  
        )  
    };  

    // Update reserves  
    balance::join(&mut pool.reserve_x, coin::into_balance(x));  
    balance::join(&mut pool.reserve_y, coin::into_balance(y));  

    // Mint LP tokens  
    balance::increase_supply(&mut pool.lp_supply, lp_amount)  
}  

3. Swap Logic (con comisiones)

move
public fun swap_x_to_y<X, Y>(  
    pool: &mut Pool<X, Y>,  
    dx: Coin<X>,  
    min_dy: u64,  
    ctx: &mut TxContext  
): Coin<Y> {  
    let dx_val = coin::value(&dx);  
    let fee = dx_val * pool.fee_bps / 10_000;  
    let dx_after_fee = dx_val - fee;  

    // Constant product formula  
    let dy_val = (dx_after_fee * balance::value(&pool.reserve_y)) /  
                 (balance::value(&pool.reserve_x) + dx_after_fee);  
    assert!(dy_val >= min_dy, EINSUFFICIENT_OUTPUT);  

    // Update reserves  
    balance::join(&mut pool.reserve_x, coin::into_balance(dx));  
    balance::withdraw(&mut pool.reserve_y, dy_val)  
}  

4. Optimizaciones clave Ventajas de Sui:

Sin reingreso: el modelo de propiedad de Move lo impide.

Transacciones por lotes: utilice las funciones de registro para operaciones de varios pasos.

Campos dinámicos: almacena datos adicionales (por ejemplo, oráculos TWAP) en el objeto del grupo.

5. Errores y soluciones comunes Overflow: usa las funciones math: :checked_* para la aritmética.

Redondeo LP: almacena internamente cantidades fraccionarias mediante u128.

Primeros pasos:

Requiere min_dy en los swaps (establecido por la interfaz).

Utilice TxContext: :epoch () para comprobar los plazos.

2
Mejor Respuesta
Comentarios
.

Sabes la respuesta?

Inicie sesión y compártalo.