Code of Swapping Protocol

// Module for implementing a simple swap protocol for generating LP tokens, creating a pool, adding/removing liquidity, and swapping tokens.
module swap_account::Swap{
// Import required modules and libraries
use aptos_framework::coin::{Self, Coin, MintCapability, BurnCapability};
use std::string;
use std::string::String;
use std::option;
use swap_account::Math::{sqrt, min};
use std::signer::address_of;
use swap_account::Math;

// Constant for minimum liquidity required in LP tokens
const MINIMUM_LIQUIDITY: u64 = 1000;

// Define a phantom struct LP with generic types X and Y to represent LP tokens for token pair X-Y
struct LP<phantom X, phantom Y> {}

// Define a struct Pair with generic types X and Y representing a token pair with its related data
struct Pair<phantom X, phantom Y> has key {
    x_coin: Coin<X>,
    y_coin: Coin<Y>,
    lp_locked: Coin<LP<X, Y>>,
    lp_mint: MintCapability<LP<X, Y>>,
    lp_burn: BurnCapability<LP<X, Y>>,
}

// Function to generate LP token name symbol for a given token pair X-Y
public fun generate_lp_name_symbol<X, Y>(): String {
    let lp_name_symbol = string::utf8(b"");
    string::append_utf8(&mut lp_name_symbol, b"LP");
    string::append_utf8(&mut lp_name_symbol, b"-");
    string::append(&mut lp_name_symbol, coin::symbol<X>());
    string::append_utf8(&mut lp_name_symbol, b"-");
    string::append(&mut lp_name_symbol, coin::symbol<Y>());
    lp_name_symbol
}

// Function to create a new pool for token pair X-Y
public entry fun create_pool<X, Y>(sender: &signer) {
    // Check if the pair already exists (a pair for X-Y or Y-X)
    assert!(!pair_exist<X, Y>(@swap_account), 1000);

    // Generate LP token name and symbol for the token pair X-Y
    let lp_name_symbol = generate_lp_name_symbol<X, Y>();

    // Initialize LP token and get its mint and burn capabilities
    let (lp_burn, lp_freeze, lp_mint) = coin::initialize<LP<X, Y>>(
        sender,
        lp_name_symbol,
        lp_name_symbol,
        6, // Number of decimal places for the LP token
        true, // LP token is fungible
    );

    // Remove the freeze capability to prevent freezing LP tokens
    coin::destroy_freeze_cap(lp_freeze);

    // Create a new Pair for token pair X-Y and store it in the global storage
    move_to(
        sender,
        Pair<X, Y> {
            x_coin: coin::zero<X>(),
            y_coin: coin::zero<Y>(),
            lp_locked: coin::zero<LP<X, Y>>(),
            lp_mint,
            lp_burn,
        },
    );
}

// Function to add liquidity for a given token pair X-Y
public entry fun add_liquidity<X, Y>(sender: &signer, x_amount: u64, y_amount: u64)
    acquires Pair
{
    // Make sure the pair exists
    assert!(exists<Pair<X, Y>>(@swap_account), 1000);

    // Borrow the Pair data from global storage
    let pair = borrow_global_mut<Pair<X, Y>>(@swap_account);

    // Convert the amounts to u128 to prevent overflow during calculations
    let x_amount = (x_amount as u128);
    let y_amount = (y_amount as u128);

    // Get the current reserves for token X and Y
    let x_reserve = (coin::value(&pair.x_coin) as u128);
    let y_reserve = (coin::value(&pair.y_coin) as u128);

    // Calculate the optimal amount of Y to be added given the amount of X
    let y_amount_optimal = quote(x_amount, x_reserve, y_reserve);

    // Choose the smaller of the actual Y amount and the optimal Y amount
    if (y_amount_optimal <= y_amount) {
        y_amount = y_amount_optimal;
    }else{
        let x_amount_optimal = quote(y_amount,y_reserve,x_reserve);
        x_amount = x_amount_optimal;
    };

    // Withdraw X and Y tokens from the sender's account
    let x_amount_coin = coin::withdraw<X>(sender, (x_amount as u64));
    let y_amount_coin = coin::withdraw<Y>(sender, (y_amount as u64));

    // Deposit the withdrawn tokens into the Pair
    coin::merge(&mut pair.x_coin, x_amount_coin);
    coin::merge(&mut pair.y_coin, y_amount_coin);

    // Calculate the liquidity to be minted and mint LP tokens accordingly
    let liquidity;
    let total_supply = *option::borrow(&coin::supply<LP<X, Y>>());
    if (total_supply == 0){
        liquidity = sqrt(((x_amount * y_amount) as u128)) - MINIMUM_LIQUIDITY;
        let lp_locked = coin::mint(MINIMUM_LIQUIDITY, &pair.lp_mint);
        coin::merge(&mut pair.lp_locked, lp_locked);
    } else {
        liquidity = (min(
            Math::mul_div(x_amount, total_supply, x_reserve),
            Math::mul_div(y_amount, total_supply, y_reserve),
        ) as u64);
    };

    // Mint the liquidity and deposit it into the sender's account
    let lp_coin = coin::mint<LP<X, Y>>(liquidity, &pair.lp_mint);
    let addr = address_of(sender);
    if (!coin::is_account_registered<LP<X, Y>>(addr)) {
        coin::register<LP<X, Y>>(sender);
    };
    coin::deposit(addr, lp_coin);
}

// Function to remove liquidity for a given token pair X-Y
public entry fun remove_liquidity<X, Y>(sender: &signer, liquidity: u64) acquires Pair {
    // Make sure the pair exists
    assert!(exists<Pair<X, Y>>(@swap_account), 1000);

    // Borrow the Pair data from global storage
    let pair = borrow_global_mut<Pair<X, Y>>(@swap_account);

    // Withdraw LP tokens from the sender's account
    let liquidity_coin = coin::withdraw<LP<X, Y>>(sender, liquidity);
    coin::burn(liquidity_coin, &pair.lp_burn);

    // Get the total supply of LP tokens, and the current reserves for token X and Y
    let total_supply = *option::borrow(&coin::supply<LP<X,Y>>());
    let x_reserve = (coin::value(&pair.x_coin) as u128);
    let y_reserve = (coin::value(&pair.y_coin) as u128);

    // Calculate the amounts of X and Y to be removed based on the liquidity
    let x_amount = Math::mul_div((liquidity as u128), x_reserve, total_supply);
    let y_amount = Math::mul_div((liquidity as u128), y_reserve, total_supply);

    // Extract the amounts of X and Y tokens from the Pair
    let x_amount_coin = coin::extract<X>(&mut pair.x_coin,( x_amount as u64));
    let y_amount_coin = coin::extract<Y>(&mut pair.y_coin,( y_amount as u64));

    // Deposit the extracted tokens into the sender's account
    coin::deposit(address_of(sender), x_amount_coin);
    coin::deposit(address_of(sender), y_amount_coin);
}

// Function to swap token X for token Y
public entry fun swap_x_to_y<X, Y>(sender: &signer, amount_in: u64) acquires Pair {
    // Make sure the pair exists
    assert!(exists<Pair<X, Y>>(@swap_account), 1000);

    // Borrow the Pair data from global storage
    let pair = borrow_global_mut<Pair<X, Y>>(@swap_account);

    // Withdraw the input token X from the sender's account
    let coin_in = coin::withdraw<X>(sender, amount_in);

    // Register the sender's account for token Y if not already registered
    if (!coin::is_account_registered<Y>(address_of(sender))) {
        coin::register<Y>(sender);
    };

    // Get the current reserves for token X and Y
    let in_reserve = (coin::value(&pair.x_coin) as u128);
    let out_reserve = (coin::value(&pair.y_coin) as u128);

    // Calculate the amount of token Y to be received for the given amount of X
    let amount_out = get_amount_out((amount_in as u128), in_reserve, out_reserve);

    // Merge the input token X into the Pair and extract the output token Y from the Pair
    coin::merge(&mut pair.x_coin, coin_in);
    let amount_out_coin = coin::extract(&mut pair.y_coin, (amount_out as u64));

    // Deposit the received token Y into the sender's account
    coin::deposit(address_of(sender), amount_out_coin);
}

// Function to check if a Pair exists for the token pair X-Y or Y-X
public fun pair_exist<X, Y>(addr: address): bool {
    exists<Pair<X, Y>>(addr) || exists<Pair<Y, X>>(addr)
}

// Function to calculate the optimal amount of Y given X, X reserve, and Y reserve
public fun quote(x_amount:u128,x_reserve:u128,y_reserve:u128):u128{
    Math::mul_div(x_amount,y_reserve,x_reserve)
}

// Function to calculate the amount of output token for a given amount of input token
public fun get_amount_out(amount_in:u128,in_reserve:u128,out_reserve:u128):u128{
    let amount_in_with_fee = amount_in * 997;
    let numerator = amount_in_with_fee * out_reserve;
    let denominator = in_reserve * 1000 + amount_in_with_fee;
    numerator / denominator
}

// Function to get the current reserves of token X and Y in the Pair
public fun get_coin<X,Y>():(u64,u64) acquires Pair {
    let pair = borrow_global<Pair<X,Y>>(@swap_account);
    (coin::value(&pair.x_coin),coin::value(&pair.y_coin))
}
}

Last updated