Swap and Preswap
1. Construct Your Own Swap Method.
1. 1 Swap
Notes
You can construct your own swap method using this approach, but it is not a ready-to-use swap method.
In the Cetus CLMM contract, both the flash swap and the repay flash swap functions do not care for slippage. If you integrate these functions into your contract, you must implement your own slippage checks.
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
Parameter simplification
This method simplifies the parameters required when calling it to accommodate the most common fixed-input swap scenarios for users. Below are the details of the simplifications:
1. This method default set swap fix amount in, it will use all balance of input coin. 2. Must pass partner, if you didn't apply one partner, you can set default partner: https://github.com/CetusProtocol/aggregator/blob/396d1a0bd77b185beda6ef4d6579ba6fe249c99a/src/transaction/cetus.ts#L24
Mainnet: 0x639b5e433da31739e800cd085f356e64cae222966d0f1b11bd9dc76b322ff58b
Testnet: 0x1f5fa5c820f40d43fc47815ad06d95e40a1942ff72a732a92e8ef4aa8cde70a5
This method will return one coin b object.
This example uses a2b; if you want to perform b2a, please use a different method
swap_b2a
.
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
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