Creating Trading Pairs in a DEX

Key points covered:

  • Understanding the create_pair function and its parameters
  • Performing initial checks and setup for pair creation
  • Generating a unique name for the LP (Liquidity Provider) token
  • Initializing the LP token with specific attributes
  • Setting up pool state data structures
  • Configuring event tracking for liquidity and swap actions
  • Emitting a PairCreatedEvent to notify listeners
  • Registering the ability to hold LP tokens

Introduction to Creating Trading Pairs in a DEX

Welcome to this beginner-friendly tutorial on creating trading pairs in a Decentralized Exchange (DEX). We'll explore the create_pair function, which is the foundation of setting up liquidity pools for token trading. Don't worry if some terms are unfamiliar - we'll break everything down step-by-step!

1. Understanding the create_pair Function

Let's start by looking at the basic structure of our create_pair function:

public fun create_pair<X, Y>(sender: &signer) acquires SwapInfo {
    // Implementation will go here
}

Here's what each part means:

  • public fun: This function can be called by anyone
  • <X, Y>: These are generic type parameters representing our two tokens
  • sender: &signer: The user creating the pair must sign the transaction
  • acquires SwapInfo: This function will access global state information

2. Initial Checks and Setup

Before creating a new pair, we need to perform some checks and setup:

assert!(!is_pair_created<X, Y>(), ERROR_ALREADY_INITIALIZED);

let sender_addr = signer::address_of(sender);
let swap_info = borrow_global_mut<SwapInfo>(DEFAULT_ADMIN);
let resource_signer = account::create_signer_with_capability(&swap_info.signer_cap);

Let's break this down:

  • We first check if the pair already exists. If it does, we stop the process.
  • We get the address of the sender for later use.
  • We borrow the global SwapInfo to access shared state.
  • We create a special "resource signer" that allows the contract to manage resources.

3. Creating the LP Token Name

LP stands for "Liquidity Provider". We need to create a unique name for our LP token:

let lp_name: string::String = string::utf8(b"movement-");
let name_x = coin::symbol<X>();
let name_y = coin::symbol<Y>();
string::append(&mut lp_name, name_x);
string::append_utf8(&mut lp_name, b"-");
string::append(&mut lp_name, name_y);
string::append_utf8(&mut lp_name, b"-LP");

if (string::length(&lp_name) > MAX_COIN_NAME_LENGTH) {
    lp_name = string::utf8(b"movement LPs");
};

This code does the following:

  • Starts with "movement-"
  • Adds the symbols of both tokens
  • Ends with "-LP"
  • If the name is too long, it uses a generic "movement LPs" instead

4. Initializing the LP Token

Now we create the actual LP token:

let (burn_cap, freeze_cap, mint_cap) = coin::initialize<LPToken<X, Y>>(
    &resource_signer,
    lp_name,
    string::utf8(b"Move-LP"),
    8,
    true
);

This function call does several things:

  • Creates a new token type LPToken<X, Y>
  • Sets its name to our generated lp_name
  • Sets its symbol to "Move-LP"
  • Sets 8 decimal places
  • Enables supply tracking
  • Returns capabilities for burning, freezing, and minting these tokens

5. Setting Up the Pool State

We need to initialize some data structures to keep track of our trading pair:

move_to<TokenPairReserve<X, Y>>(
    &resource_signer,
    TokenPairReserve {
        reserve_x: 0,
        reserve_y: 0,
        block_timestamp_last: 0
    }
);

move_to<TokenPairMetadata<X, Y>>(
    &resource_signer,
    TokenPairMetadata {
        creator: sender_addr,
        fee_amount: coin::zero<LPToken<X, Y>>(),
        k_last: 0,
        balance_x: coin::zero<X>(),
        balance_y: coin::zero<Y>(),
        mint_cap,
        burn_cap,
        freeze_cap,
    }
);

This initializes two important structures:

  • TokenPairReserve: Keeps track of the current amounts of each token in the pool
  • TokenPairMetadata: Stores various pieces of information about the pair, including who created it, accumulated fees, and token balances

6. Setting Up Events and Finalizing

Lastly, we set up event tracking and finalize the pair creation:

move_to<PairEventHolder<X, Y>>(
    &resource_signer,
    PairEventHolder {
        add_liquidity: account::new_event_handle<AddLiquidityEvent<X, Y>>(&resource_signer),
        remove_liquidity: account::new_event_handle<RemoveLiquidityEvent<X, Y>>(&resource_signer),
        swap: account::new_event_handle<SwapEvent<X, Y>>(&resource_signer)
    }
);

let token_x = type_info::type_name<X>();
let token_y = type_info::type_name<Y>();

event::emit_event<PairCreatedEvent>(
    &mut swap_info.pair_created,
    PairCreatedEvent {
        user: sender_addr,
        token_x,
        token_y
    }
);

register_lp<X, Y>(&resource_signer);

This final part:

  • Sets up event handlers for adding liquidity, removing liquidity, and swapping
  • Emits a "PairCreatedEvent" to notify listeners that a new pair has been created
  • Registers the ability to hold LP tokens

Conclusion

Congratulations! You've just learned about the intricate process of creating a trading pair in a DEX. This function sets up the foundation for users to provide liquidity and trade tokens. In the next part of this tutorial, we'll explore how to add liquidity to our newly created pair. Stay tuned!