Integration
- The smart contracts’ source code can be found in the dexlyn swap interface repository.
- The current version of Dexlyn Swap is deployed on the address:
Mainnet:
0x0dc694898dff98a1b0447e0992d0413e123ea80da1021d464a4fbaf0265870d8Before we start, create a new Move project so that you can repeat the steps as described here.
Supported networks
The Supra Mainnet is currently supported.
Add as dependency
To integrate Dexlyn Swap into your project, you first need to add a dependency to your Move.toml
Mainnet
[dependencies.dexlyn_swap]
git = "https://github.com/DexlynLabs/dexlyn_swap_interface"
rev = "mainnet"Testnet
[dependencies.dexlyn_swap]
git = "https://github.com/DexlynLabs/dexlyn_swap_interface"
rev = "testnet"Add the LP coin module as a dependency too:
Mainnet
[dependencies.DexlynswapLP]
git = "https://github.com/DexlynLabs/dexlyn_swap_interface"
sub_dir = "dexlyn_swap_lp"
rev = "mainnet"Testnet
[dependencies.DexlynswapLP]
git = "https://github.com/DexlynLabs/dexlyn_swap_interface"
sub_dir = "dexlyn_swap_lp"
rev = "testnet"Next, try compiling; you shouldn’t get any errors.
Test Coins
This part works only for testnet, as on mainnet there are production ready coins that should be used!
During the integration, you probably would need test coins, so we prepared a repository containing several coins (USDC, BTC, ETH, SOL, DOGE) just for tests.
The existing pools on Dexlyn Swap use our test coins, so it’s a good idea to follow standard test coins during integration.
The following test coins are already deployed on the testnet and can be used in your project.
// Test USDC
0xe5ae87f5c4bbe2c324bba7c498e32aae3f314a455b5caa84677e1160a29e8fdc::coins::USDC
// Test BTC
0xe5ae87f5c4bbe2c324bba7c498e32aae3f314a455b5caa84677e1160a29e8fdc::coins::BTC
// Test ETH
0xe5ae87f5c4bbe2c324bba7c498e32aae3f314a455b5caa84677e1160a29e8fdc::coins::ETH
// Test SOL
0xe5ae87f5c4bbe2c324bba7c498e32aae3f314a455b5caa84677e1160a29e8fdc::coins::SOL
// Test DOGE
0xe5ae87f5c4bbe2c324bba7c498e32aae3f314a455b5caa84677e1160a29e8fdc::coins::DOGEAdd as dependency:
[dependencies.TestCoins]
git = "https://github.com/DexlynLabs/test_coins"
rev = "main"Use in your code:
use test_coins::coins::{USDC, BTC};List Your Token with Image in Dexlyn
dexlyn swap interface - This repository is used for adding token images to the Dexlyn coin listing. You can submit a pull request (PR) with that image in the tokens_image directory, and based on that, our team will review and add the token image.
Let’s Swap
Always practice solid risk management rules when experimenting with swaps: use the correct slippage values and trusted currency pairs, double-check the numbers before confirming, and compare external price sources.
Throughout this guide, we will use Router and try to swap USDC coins to test BTC.
module account::example {
use dexlyn_swap::router;
}Now let’s add the dependencies we will need for this experiment, as well as a basic entry function that we can call in a transaction later:
module account::example {
use std::signer;
use dexlyn_swap::router;
use dexlyn_swap::curves::Uncorrelated;
use test_coins::coins::{BTC,USDC};
use supra_framework::coin;
public entry fun buy_btc(account: &signer) {
}
}Let’s withdraw 1000 USDC coins from an account and see how many BTC we can get in exchange:
module account::example {
use std::signer;
use dexlyn_swap::router;
use dexlyn_swap::curves::Uncorrelated;
use test_coins::coins::{BTC,USDC};
use supra_framework::coin;
public entry fun buy_btc(account: &signer) {
let amount_to_swap = 1000 * 1000000; // convert into the decimal 10^6
let coins_to_swap = coin::withdraw<USDC>(account, amount_to_swap);
let btc_amount_to_get = router::get_amount_out<USDC, BTC, Uncorrelated>(amount_to_swap);
}
}The provided code above still won’t compile as we can’t drop USDC coins; at the same time, it already shows how much BTC we can get back for 1000 USDC coins because of the get_amount_out function.
Now let’s indeed do a swap and finally deposit the swapped coins on our account:
module account::example {
use std::signer;
use dexlyn_swap::router;
use dexlyn_swap::curves::Uncorrelated;
use test_coins::coins::{BTC,USDC};
use supra_framework::coin;
public entry fun buy_btc(account: &signer) {
let amount_to_swap = 1000 * 1000000; // convert into the decimal 10^6
let coins_to_swap = coin::withdraw<USDC>(account, amount_to_swap);
let btc_amount_to_get = router::get_amount_out<USDC, BTC, Uncorrelated>(
amount_to_swap,
);
let btc = router::swap_exact_coin_for_coin<USDC, BTC, Uncorrelated>(
coins_to_swap,
btc_amount_to_get
);
let account_addr = signer::address_of(account);
// Register BTC coin on account in case the account doesn't have it.
if (!coin::is_account_registered<BTC>(account_addr)) {
coin::register<BTC>(account);
};
// Deposit on account.
coin::deposit(account_addr, btc);
}
}Everything worked out well; using swap_exact_coin_for_coin, we have swapped USDC coins for BTC.
This looks simple, but what if someone decided to front-run us? Let’s add some basic slippage checks for the minimum amount of BTC we want to receive (afterwards we can remove get_amount_out).
module account::example {
use std::signer;
use dexlyn_swap::router;
use dexlyn_swap::curves::Uncorrelated;
use test_coins::coins::{BTC,USDC};
use supra_framework::coin;
public entry fun buy_btc(account: &signer) {
let amount_to_swap = 1000 * 1000000; // convert into the decimal 10^6
let coins_to_swap = coin::withdraw<USDC>(account, amount_to_swap);
let btc_amount_to_get = router::get_amount_out<USDC, BTC, Uncorrelated>(
amount_to_swap,
);
// Lets consider 0.5% slippage
let btc_min_value_to_get = btc_amount_to_get - (5 * btc_amount_to_get)/1000;
let btc = router::swap_exact_coin_for_coin<USDC, BTC, Uncorrelated>(
coins_to_swap,
btc_min_value_to_get
);
let account_addr = signer::address_of(account);
// Register BTC coin on account in case the account doesn't have it.
if (!coin::is_account_registered<BTC>(account_addr)) {
coin::register<BTC>(account);
};
// Deposit on account.
coin::deposit(account_addr, btc);
}
}All done. In the same way, you can try another function:
router::swap_coin_for_exact_coinAn important note regarding decentralized AMM securities and user transactions:
- Always use safety checks.
- Never swap tokens without a slippage value provided offline.
Create Pool
Now let’s create a new pool utilizing Router. First of all, deploy your new own coin just for test:
module account::my_coin {
use std::signer;
use std::string::utf8;
use supra_framework::coin;
struct MyCoin {}
public entry fun initialize(account: &signer) {
let (burn_cap, freeze_cap, mint_cap) = coin::initialize<MyCoin>(
account,
utf8(b"MyCoin"),
utf8(b"MC"),
6,
true,
);
let coins = coin::mint(1000000000000, &mint_cap);
coin::register<MyCoin>(account);
coin::deposit(signer::address_of(account), coins);
coin::destroy_burn_cap(burn_cap);
coin::destroy_freeze_cap(freeze_cap);
coin::destroy_mint_cap(mint_cap);
}
}Extend the example from the previous guide and add a new function:
module account::example {
use dexlyn_swap::router;
use dexlyn_swap::curves::Uncorrelated;
use dexlyn_swap::coin_helper::is_sorted;
use supra_framework::supra_coin::SupraCoin;
use account::my_coin::MyCoin;
public entry fun create_pool(account: &signer) {
// Check generics sorted.
assert!(is_sorted<MyCoin, SupraCoin>(), 0);
router::register_pool<MyCoin, SupraCoin, Uncorrelated>(account);
}
}
The function above creates a new pool for your coin: MyCoin/SupraCoin. As Uncorrelated generic provided, the formula for the pool would be standard one: x*y=k.
It’s very important to follow the rule about generic sorting as otherwise, the function would abort.
In the same way, you can create a stable pool by using AMM Curve generic.
Add liquidity to your pool
Let’s add liquidity to new pool:
module account::example {
use std::signer;
use dexlyn_swap::router;
use dexlyn_swap::curves::Uncorrelated;
use dexlyn_swap::coin_helper::is_sorted;
use dexlyn_swap_lp::lp_coin::LP;
use supra_framework::supra_coin::SupraCoin;
use supra_framework::coin;
use account::my_coin::MyCoin;
public entry fun create_pool(account: &signer) {
// Check generics sorted.
assert!(is_sorted<MyCoin, SupraCoin>(), 0);
router::register_pool<MyCoin, SupraCoin, Uncorrelated>(account);
}
public entry fun add_liquidity(account: &signer) {
assert!(is_sorted<MyCoin, SupraCoin>(), 0);
let account_addr = signer::address_of(account);
let (min_my_coin_liq, min_supra_coin_liq) = router::calc_optimal_coin_values<MyCoin, SupraCoin, Uncorrelated>(
100,
10,
95,
9
);
let my_coin_liq = coin::withdraw<MyCoin>(account, min_my_coin_liq);
let supra_liq = coin::withdraw<SupraCoin>(account, min_supra_coin_liq);
let (my_coin_remainder, supra_remainder, lp) = router::add_liquidity<MyCoin, SupraCoin, Uncorrelated>(
my_coin_liq,
min_my_coin_liq,
supra_liq,
min_supra_coin_liq,
);
coin::deposit(account_addr, my_coin_remainder);
coin::deposit(account_addr, supra_remainder);
if (!coin::is_account_registered<LP<MyCoin, SupraCoin, Uncorrelated>>(account_addr)) {
coin::register<LP<MyCoin, SupraCoin, Uncorrelated>>(account);
};
coin::deposit(account_addr, lp);
}
}As you can see above, add_liquidity allows you to add liquidity to the newly created pool and then deposit the remainder of both X and Y coins back in the account, together with the new LP coins.
Similarly, you can add liquidity to any other pool: replace MyCoin and SupraCoin with other coins.
When working with real-world examples, it’s better to deposit liquidity immediately during the same transaction with which you create a pool. We also recommend using slippage values on the amount of the Liquidity pair tokens that you are going to add into the pool.
Adding liquidity to an existing pool
Let’s say we want to add liquidity to an existing BTC/USDC pool created by the Dexlyn team:
module account::example {
use std::signer;
use dexlyn_swap::router;
use dexlyn_swap::curves::Uncorrelated;
use dexlyn_swap::coin_helper::is_sorted;
use dexlyn_swap_lp::lp_coin::LP;
use supra_framework::coin;
use test_coins::coins::BTC;
use test_coins::coins::USDC;
public entry fun add_liquidity(account: &signer) {
assert!(is_sorted<BTC, USDC>(), 0);
let account_addr = signer::address_of(account);
let (min_my_coin_liq, min_supar_coin_liq) = router::calc_optimal_coin_values<BTC, USDC, Uncorrelated>(
100,
10,
1,
1
);
let my_coin_liq = coin::withdraw<BTC>(account, min_my_coin_liq);
let supra_liq = coin::withdraw<USDC>(account, min_supra_coin_liq);
// For a safer side please consider a slippage for min_my_coin_liq and min_supar_coin_liq
let (my_coin_remainder, supra_remainder, lp) = router::add_liquidity<BTC, USDC, Uncorrelated>(
my_coin_liq,
min_my_coin_liq,
supra_liq,
min_supra_coin_liq,
);
coin::deposit(account_addr, my_coin_remainder);
coin::deposit(account_addr, supra_remainder);
if (!coin::is_account_registered<LP<BTC, USDC, Uncorrelated>>(account_addr)) {
coin::register<LP<BTC, USDC, Uncorrelated>>(account);
};
coin::deposit(account_addr, lp);
}
}When working with real-world examples, it’s better to deposit liquidity immediately during the same transaction with which you create a pool. We also recommend using slippage values on the amount of the Liquidity pair tokens that you are going to add into the pool.
Remove Liquidity
The following example shows how to burn liquidity in the BTC / USDC pool deployed by the Dexlyn team, where slippage can be provided as an argument:
module account::example {
use std::signer;
use dexlyn_swap::router;
use dexlyn_swap::curves::Uncorrelated;
use dexlyn_swap::coin_helper::is_sorted;
use dexlyn_swap_lp::lp_coin::LP;
use test_coins::coins::{BTC,USDC};
use supra_framework::coin;
public entry fun burn_liquidity(account: &signer, lp_to_burn: u64) {
assert!(is_sorted<BTC, USDC>(), 0);
let account_addr = signer::address_of(account);
let (min_x_amount,min_y_amount) = router::get_reserves_for_lp_coins<BTC, USDC, Uncorrelated>(lp_to_burn);
let lp = coin::withdraw<LP<BTC, USDC, Uncorrelated>>(account, lp_to_burn);
// For a safer side please consider a slippage for min_x_amount and min_y_amount
let (btc, usdc) = router::remove_liquidity<BTC, USDC, Uncorrelated>(lp, min_x_amount, min_y_amount);
if (!coin::is_account_registered<BTC>(account_addr)) {
coin::register<BTC>(account);
};
coin::deposit(account_addr, btc);
if (!coin::is_account_registered<USDC>(account_addr)) {
coin::register<USDC>(account);
};
coin::deposit(account_addr, usdc);
}
}