Swap and Preswap

1. Construct Your Own Swap Method.

1. 1 Swap

Function params (click to open)

config: the reference of clmm globalconfig object. you can see details at Global config id

pool : one mutable reference of the pool.

coin_a: mutable coin a object. this type is the same as the coin a of pool.

coin_b: mutable coin b object. this type is the same as the coin b of pool.

a2b: the swap direction, a2b equals true means sold coin a then get coin b.

by_amount_in: when it equal true means want to fix the amount of input coin. when it equal false means want to fix the amount of output coin.

amount: when by_amount_in equals true, amount means the quantity of input coin, when by_amount_in equals false, amount means the quantity of output coin.

amount_limit: the threshold value of coin. when by_amount_in equals true, it means the minimum amount about received coin, when by_amount_in equals false, it means the maximum amount about sold.

sqrt_price_limit: two constant of sqrt price(x64 fixed-point number). When a2b equals true, it equals 4295048016, when a2b equals false, it equals 79226673515401279992447579055. Just use the default values.

clock: the sui clock object.

You can implement swaps like the following examples. This is divided into two instances: one with a partner and the other without. For a detailed introduction to the Cetus partner, you can refer to cetus partner.

fun swap<CoinTypeA, CoinTypeB>(
    config: &GlobalConfig,
    pool: &mut Pool<CoinTypeA, CoinTypeB>,
    coin_a: &mut Coin<CoinTypeA>,
    coin_b: &mut Coin<CoinTypeB>,
    a2b: bool,
    by_amount_in: bool,
    amount: u64,
    amount_limit: u64,
    sqrt_price_limit: u128,
    clock: &Clock,
    ctx: &mut TxContext
) {
    // flash swap first
    let (receive_a, receive_b, flash_receipt) = pool::flash_swap<CoinTypeA, CoinTypeB>(
        config,
        pool,
        a2b,
        by_amount_in,
        amount,
        sqrt_price_limit,
        clock
    );
    let (in_amount, out_amount) = (
        pool::swap_pay_amount(&flash_receipt),
        if (a2b) balance::value(&receive_b) else balance::value(&receive_a)
    );

    // pay for flash swap
    let (pay_coin_a, pay_coin_b) = if (a2b) {
        (coin::into_balance(coin::split(&mut coin_a, in_amount, ctx)), balance::zero<CoinTypeB>())
    } else {
        (balance::zero<CoinTypeA>(), coin::into_balance(coin::split(&mut coin_b, in_amount, ctx)))
    };

    coin::join(&mut coin_b, coin::from_balance(receive_b, ctx));
    coin::join(&mut coin_a, coin::from_balance(receive_a, ctx));

    pool::repay_flash_swap<CoinTypeA, CoinTypeB>(
        config,
        pool,
        pay_coin_a,
        pay_coin_b,
        flash_receipt
    );
}

/// Swap with partner.
fun swap_with_partner<CoinTypeA, CoinTypeB>(
    config: &GlobalConfig,
    pool: &mut Pool<CoinTypeA, CoinTypeB>,
    partner: &mut Partner,
    coin_a: &mut Coin<CoinTypeA>,
    coin_b: &mut Coin<CoinTypeB>,
    a2b: bool,
    by_amount_in: bool,
    amount: u64,
    amount_limit: u64,
    sqrt_price_limit: u128,
    clock: &Clock,
    ctx: &mut TxContext
) {
    let (receive_a, receive_b, flash_receipt) = pool::flash_swap_with_partner<CoinTypeA, CoinTypeB>(
        config,
        pool,
        partner,
        a2b,
        by_amount_in,
        amount,
        sqrt_price_limit,
        clock
    );
    let (in_amount, out_amount) = (
        pool::swap_pay_amount(&flash_receipt),
        if (a2b) balance::value(&receive_b) else balance::value(&receive_a)
    );

    // pay for flash swap
    let (pay_coin_a, pay_coin_b) = if (a2b) {
        (coin::into_balance(coin::split(&mut coin_a, in_amount, ctx)), balance::zero<CoinTypeB>())
    } else {
        (balance::zero<CoinTypeA>(), coin::into_balance(coin::split(&mut coin_b, in_amount, ctx)))
    };

    coin::join(&mut coin_b, coin::from_balance(receive_b, ctx));
    coin::join(&mut coin_a, coin::from_balance(receive_a, ctx));

    pool::repay_flash_swap_with_partner<CoinTypeA, CoinTypeB>(
        config,
        pool,
        partner,
        pay_coin_a,
        pay_coin_b,
        flash_receipt
    );
}

2. Using Existing Swap Methods

2.1 Aggregator contract (recommend)

If you just want to call the existing Cetus swap method using a Move call, we can use the swap method of the Cetus aggregator that has already been deployed on the sui mainnet.

Contract Code https://suivision.xyz/package/0x11451575c775a3e633437b827ecbc1eb51a5964b0302210b28f5b89880be21a2?tab=Code

Function params (click to open)

config: the reference of clmm globalconfig object. you can see details at Global config id

pool : one mutable reference of the pool.

partner: cetus partner object.

coin_a: coin a object. this type is the same as the coin b of pool. This method will use all balance of this coin, it you want to swap 1000 a to b, you need to split 1000 a before swap.

clock: the sui clock object.

public fun swap_a2b<CoinA, CoinB>(
    config: &GlobalConfig,
    pool: &mut Pool<CoinA, CoinB>,
    partner: &mut Partner,
    coin_a: Coin<CoinA>,
    clock: &Clock,
    ctx: &mut TxContext
): Coin<CoinB> {
    let amount_in = coin::value(&coin_a);
    let (receive_a, receive_b, flash_receipt, pay_amount) = flash_swap<CoinA, CoinB>(
        config,
        pool,
        partner,
        amount_in,
        true,
        true,
        tick_math::min_sqrt_price(),
        clock,
        ctx
    );

    assert!(pay_amount == amount_in, 0);
    let remainer_a = repay_flash_swap_a2b<CoinA, CoinB>(config, pool, partner, coin_a, flash_receipt, ctx);
    transfer_or_destroy_coin(remainer_a, ctx);
    coin::destroy_zero(receive_a);
    receive_b
}

Call this method by ts sdk

// https://github.com/CetusProtocol/aggregator/blob/396d1a0bd77b185beda6ef4d6579ba6fe249c99a/src/transaction/cetus.ts#L88
import {
  Transaction,
  TransactionArgument,
  TransactionObjectArgument,
} from "@mysten/sui/transactions"

const txb = new Transaction();

let inputCoin: TransactionObjectArgument

// build input coin by split coins.

const args = [
  // 0xdaa46292632c3c4d8f31f23ea0f9b36a28ff3677e9684980e4438403a67a3d8f
  txb.object(globalConfig),
  txb.object(pool),
  // 0x639b5e433da31739e800cd085f356e64cae222966d0f1b11bd9dc76b322ff58b
  txb.object(partner),
  inputCoin,
  txb.object(CLOCK_ADDRESS),
]
const res = txb.moveCall({
  target: `${client.publishedAt()}::cetus::${func}`,
  typeArguments: [coinAType, coinBType],
  arguments: args,
}) as TransactionObjectArgument

2.2 integrate contract

Contract Code https://suivision.xyz/package/0x3a5aa90ffa33d09100d7b6941ea1c0ffe6ab66e77062ddd26320c1b073aabb10?tab=Code

0x3a5aa90ffa33d09100d7b6941ea1c0ffe6ab66e77062ddd26320c1b073aabb10::router::swap

Function params (click to open)

config: the reference of clmm globalconfig object. you can see details at Global config id

pool : one mutable reference of the pool.

coins_a: coins a object vector. this type is the same as the coin a of pool.Future it will be taken as Coin<CoinTypeA> and Coin<CoinTypeB>.

coins_b: coins b object vector. this type is the same as the coin b of pool.

a2b: the swap direction, a2b equals true means sold coin a then get coin b.

by_amount_in: when it equal true means want to fix the amount of input coin. when it equal false means want to fix the amount of output coin.

amount: when by_amount_in equals true, amount means the quantity of input coin, when by_amount_in equals false, amount means the quantity of output coin.

sqrt_price_limit: two constant of sqrt price(x64 fixed-point number). When a2b equals true, it equals 4295048016, when a2b equals false, it equals 79226673515401279992447579055. Just use the default values.

use_coin_value:This parameter determines whether to directly use the input coin value as the amount in. If so (i.e. true), it will ignore the passed-in amount and use the coin value as the amount instead; otherwise, it will use the input amount (i.e. amount of the 'input' coin). This parameter is only effective with fixed input (by_amount_in = true). Of course, this method is designed for our legacy router service, which is why this additional parameter is included.

clock: the sui clock object.

fun swap<CoinTypeA, CoinTypeB>(
    config: &GlobalConfig,
    pool: &mut Pool<CoinTypeA, CoinTypeB>,
    coins_a: vector<Coin<CoinTypeA>>,
    coins_b: vector<Coin<CoinTypeB>>,
    a2b: bool,
    by_amount_in: bool,
    amount: u64,
    amount_limit: u64,
    sqrt_price_limit: u128,
    clock: &Clock,
    ctx: &mut TxContext
) { ... }

3. Pre swap clmmpool/sources/pool

Users can use this method to pre-calculate the corresponding amount out for a swap on-chain.

/// The step swap result
struct SwapStepResult has copy, drop, store {
    current_sqrt_price: u128,
    target_sqrt_price: u128,
    current_liquidity: u128,
    amount_in: u64,
    amount_out: u64,
    fee_amount: u64,
    remainer_amount: u64
}

/// The calculated swap result
struct CalculatedSwapResult has copy, drop, store {
    amount_in: u64,
    amount_out: u64,
    fee_amount: u64,
    fee_rate: u64,
    after_sqrt_price: u128,
    is_exceed: bool,
    step_results: vector<SwapStepResult>
}
// Calculate Swap Result

public fun calculate_swap_result<CoinTypeA, CoinTypeB>(
    pool: &Pool<CoinTypeA, CoinTypeB>,
    a2b: bool,
    by_amount_in: bool,
    amount: u64,
): CalculatedSwapResult {}

Last updated