huahua

huahua

A step-by-step guide to implementing token Swap! Practical development with Move language and Sui chain

Series Article Directory#

Task1: hello move🚪
Task2: move coin🚪
Task3: move nft🚪
Task4: move game🚪
Task5: move swap🚪
Task6: sdk ptb🚪

More exciting content, stay tuned!✌️


@[TOC](Article Directory)


Preface#

In the previous article "Task4: move game", we explored the application of the Move programming language in on-chain interactive games, completing a simple yet practical implementation of a rock-paper-scissors game smart contract. Through this task, we learned about liquidity pool management, ensuring on-chain fairness, and the key technologies for implementing game interactions based on smart contracts, further solidifying our understanding and practice of Move.

This article will focus on Task5: move swap, challenging the implementation of a Move-based token swap smart contract. Through this task, we will build a basic model for token swapping, enabling users to securely and quickly exchange two types of tokens on-chain. This task will further showcase the potential of Move in the decentralized finance (DeFi) field, helping you master the following key technical points:

  • How to design and manage on-chain token pools;
  • How to implement the business logic of token swapping through smart contracts;
  • How to ensure the security and efficiency of token swaps.

In the implementation process, we will use the two tokens defined in task2, HUAHUAHUA1223_COIN and HUAHUAHUA1223_FAUCET_COIN, and achieve token interchange by setting exchange rates. At the same time, we will delve into core issues such as fund validation and balance management and fairness and robustness design in smart contracts.

Let us continue to unlock the application scenarios of Move and build the foundational components of decentralized finance together!

HOH Community


What is the Sui Chain?#

Sui is a high-performance blockchain platform designed to provide fast, secure, and scalable infrastructure for decentralized applications. It was developed by the Aptos Labs team, based on a new consensus protocol—Narwhal & Tusk. The design goal of Sui is to address blockchain performance bottlenecks, providing extremely high transaction throughput and low latency to meet the needs of complex application scenarios.

Main features of the Sui chain:

  1. High throughput and low latency: Sui's consensus mechanism allows for the parallel processing of a large number of transactions without waiting for global consensus across the entire network. This parallel design enables the processing of thousands of transactions per second, greatly increasing the throughput of the blockchain and reducing transaction confirmation delays.

  2. Object-oriented resource management: Sui treats resources in the blockchain as objects for management. These resources (such as tokens, NFTs) have independent identifiers that can be directly tracked and manipulated. This approach allows Sui to efficiently and parallelly handle resources across multiple nodes without managing global state, further enhancing performance.

  3. Flexible transaction model: Sui provides a flexible and efficient transaction model that supports parallel execution of transactions across multiple resource objects. This means that transactions from different users can proceed independently and efficiently, avoiding performance bottlenecks typical in traditional blockchains.

  4. Efficient account and permission management: Sui offers a diverse account management mechanism that can address complex permission requirements in decentralized applications. Whether it's personal accounts, smart contract accounts, or multi-signature accounts, they can be flexibly configured and managed.


What is the Move Programming Language?#

Move is a programming language designed specifically for blockchain development, initially developed by the MetaLibra (later Diem) team, and later adopted by the Sui blockchain. The design focus of Move is on resource management, ownership control, and type safety, making it particularly suitable for handling assets and digital resources in decentralized applications.

Main features of the Move language:

  1. Resource type system: The Move language treats all resources (such as tokens, NFTs, data in smart contracts, etc.) as "resource types". These resources cannot be copied or destroyed in the system, only transferred or borrowed. This ensures the uniqueness and security of each resource, fundamentally avoiding issues of resource loss and duplicate transfers common in traditional smart contracts.

  2. Ownership and borrowing mechanism: Move manages resources through strict ownership and borrowing mechanisms. Each resource has a unique owner, and borrowing of resources must be explicitly declared, which avoids security risks when "sharing resources". Borrowing resources ensures that developers can share and manipulate resources without modifying ownership.

  3. Modular programming: Move supports a modular programming structure, where each module can contain different resource types and functions. The modular design makes the code clearer, more reusable, and helps improve development efficiency while reducing the likelihood of coding errors.

  4. Type safety and verifiability: Move is a strongly typed language, meaning developers must explicitly define the type of each variable and resource at compile time. Move's type system ensures that most errors in contracts are caught during the compilation stage, thus avoiding runtime errors and enhancing the security of smart contracts.

Example code in Move language:
Here is a simple Move contract example that demonstrates how to create and transfer a resource called Coin:

address 0x1 {
    module CoinModule {
        resource struct Coin has store {
            value: u64,
        }

        public fun create_coin(value: u64): Coin {
            Coin { value }
        }

        public fun transfer_coin(coin: Coin, recipient: address): Coin {
            let new_coin = Coin { value: coin.value };
            // Here we can perform the actual transfer operation
            return new_coin;
        }
    }
}

In this example, Coin is a resource type that contains a value field representing the value of the token. The create_coin function is used to create a new Coin resource, while the transfer_coin function is used to transfer the Coin resource to a specified account.


Move Collaborative Learning Activity: Quick Start with Move Development#

To help more developers quickly understand and master the Move programming language, the Move Collaborative Learning activity is jointly initiated by the HOH Community, HackQuest, OpenBuild, and KeyMap. This activity aims to provide a good learning platform for beginners, guiding everyone step by step to become familiar with the Move language and understand how to apply it to Web3 development.

By collaborating with professional mentors in the Move field, participants can quickly grasp the basics of the Move language and gradually advance to more complex application development. Whether you are a blockchain beginner or an engineer with some development experience, you can benefit from this.

Resource Links:

  • Official Sui Documentation🚪: Get detailed documentation about the Sui chain, including development guides, API references, etc.
  • Move Learning Bilibili Video🚪: Follow along with the video tutorials on Bilibili to learn the basics and advanced topics of the Move programming language.
  • letsmove Repository🚪: This is a GitHub repository of Move learning resources, containing various example codes and tutorials to help developers master the Move language.

I. Create Project#

First, we need to create a new Move project and import the token contract developed in Task2.

1.1 Create a New Move Project#

Run the following command to create a Move project named my_swap:

sui move new my_swap
cd .\my_swap\

Insert image description here

1.2 Import Dependencies#

Assuming our token contract has been implemented and stored in the my_coin directory. We can import it as a dependency into the new project as follows:

my_coin = { local = "../../task2/my_coin" }

Insert image description here

On this basis, we will compile the project to ensure that the dependencies are correctly introduced and the project can compile smoothly: sui move build
Insert image description here

II. Contract Design#

In the contract code section, the logic is actually similar to what we discussed in task4. In task4, we dealt with one type of token for storage and game win/loss gameplay, so in task5, we only need to add another token to the Pool token pool and automatically perform the conversion between the two tokens in the token pool.

Below is the complete implementation code of the token swap contract, and we will gradually break down its core logic:

module my_swap::my_swap;

use my_coin::huahuahua1223_coin::HUAHUAHUA1223_COIN;
use my_coin::huahuahua1223_faucet_coin::HUAHUAHUA1223_FAUCET_COIN;
use sui::balance::{Self, Balance};
use sui::coin::{Self, Coin, from_balance, into_balance};
use sui::transfer::{share_object, transfer, public_transfer};

const EInputNotEnough: u64 = 1000;
const EPoolNotEnough: u64 = 1001;

public struct AdminCap has key {
    id: UID
}

public struct Pool has key {
    id: UID,
    huahuahua1223_faucet_coin: Balance<HUAHUAHUA1223_FAUCET_COIN>,
    huahuahua1223_coin: Balance<HUAHUAHUA1223_COIN>,
}

fun init(ctx: &mut TxContext) {
    let pool = Pool {
        id: object::new(ctx),
        huahuahua1223_faucet_coin: balance::zero<HUAHUAHUA1223_FAUCET_COIN>(),
        huahuahua1223_coin: balance::zero<HUAHUAHUA1223_COIN>(),
    };

    let admin = AdminCap { id: object::new(ctx) };

    // Share the swap pool publicly
    share_object(pool);
    // Grant admin permissions to the contract deployer
    transfer(admin, ctx.sender());
}

2.1 Token Pool and Admin Permissions#

We maintain the balances of two types of tokens through the Pool structure while setting up an AdminCap structure to ensure that the administrator has the authority to withdraw or manage tokens. The init function initializes the token pool and grants admin permissions to the contract deployer.

2.2 Storing Tokens#

Users can deposit one of the two types of tokens into the token pool. Below is the logic for storing HUAHUAHUA1223_COIN:

// Store my_coin token
public entry fun deposit_my_coin(
    pool: &mut Pool,
    user_coin: Coin<HUAHUAHUA1223_COIN>,
    amount: u64,
    ctx: &mut TxContext,
) {
    // Verify that the wallet token is greater than the input amount
    let coin_value = user_coin.value();
    assert!(coin_value >= amount, EInputNotEnough);

    // Convert Coin to Balance
    let mut input_balance = into_balance(user_coin);
    if (coin_value == amount) {
        // The input amount is all the tokens
        balance::join(&mut pool.huahuahua1223_coin, input_balance);
    } else {
        balance::join(
            &mut pool.huahuahua1223_coin,
            balance::split(&mut input_balance, amount),
        );
        // Refund the excess tokens
        let surplus_coin = from_balance(input_balance, ctx);
        public_transfer(surplus_coin, ctx.sender());
    };
}

Key points here include:

  • Verifying that the input token quantity is sufficient.
  • Handling excess tokens and returning the remaining portion to the user.

Similar logic also applies to storing HUAHUAHUA1223_FAUCET_COIN.

2.3 Withdrawing Tokens#

Administrators can withdraw any type of token from the pool. This requires admin permission AdminCap:

// Admin withdraws my_coin token
public entry fun withdraw_coin(
    _: &AdminCap,
    pool: &mut Pool,
    amount: u64,
    ctx: &mut TxContext,
) {
    assert!(pool.huahuahua1223_coin.value() >= amount, EPoolNotEnough );

    // Convert balance to coin type using from_balance
    let withdrawn_balance = balance::split(&mut pool.huahuahua1223_coin, amount);
    let withdrawn_coin = from_balance(withdrawn_balance, ctx);
    public_transfer(withdrawn_coin, ctx.sender());
}

// Admin withdraws faucet_coin token
public entry fun withdraw_faucet_coin(
    _: &AdminCap,
    pool: &mut Pool,
    amount: u64,
    ctx: &mut TxContext,
) {
    assert!(pool.huahuahua1223_faucet_coin.value() >= amount, EPoolNotEnough );

    // Convert balance to coin type using from_balance
    let withdrawn_balance = balance::split(&mut pool.huahuahua1223_faucet_coin, amount);
    let withdrawn_coin = from_balance(withdrawn_balance, ctx);
    public_transfer(withdrawn_coin, ctx.sender());
}

This part of the logic strictly verifies that the pool balance is sufficient, ensuring that there will be no withdrawal failures.

2.4 Token Swapping#

The core functionality of the token pool is to implement the interchange of the two types of tokens. Below are the two swapping logics:

  • Swap faucet_coin for my_coin
// Convert 2 faucet_coin to 1 my_coin
public entry fun swap_faucet_coin_to_my_coin(
    pool: &mut Pool,
    user_coin: Coin<HUAHUAHUA1223_FAUCET_COIN>,
    amount: u64,
    ctx: &mut TxContext,
) {
    // Verify that the swap pool can exchange this much huahuahua1223_coin
    let output_value = amount * 1000 / 2000;
    assert!(pool.huahuahua1223_coin.value() >= output_value, EPoolNotEnough);

    // Deposit faucet_coin into the swap pool for exchange
    deposit_faucet_coin(pool, user_coin, amount, ctx);

    // Exchange half the amount of huahuahua1223_coin
    let output_balance = balance::split(&mut pool.huahuahua1223_coin, output_value);
    let output_coin = from_balance(output_balance, ctx);
    public_transfer(output_coin, ctx.sender());
}
  • Swap my_coin for faucet_coin
// Convert 1 my_coin to 2 faucet_coin
public entry fun swap_my_coin_to_faucet_coin(
    pool: &mut Pool,
    user_coin: Coin<HUAHUAHUA1223_COIN>,
    amount: u64,
    ctx: &mut TxContext,
) {
    // Verify that the swap pool can exchange this much huahuahua1223_faucet_coin
    let output_value = amount * 2000 / 1000;
    assert!(pool.huahuahua1223_faucet_coin.value() >= output_value, EPoolNotEnough);

    // Deposit my_coin into the swap pool for exchange
    deposit_my_coin(pool, user_coin, amount, ctx);

    // Exchange double the amount of huahuahua1223_faucet_coin
    let output_balance = balance::split(&mut pool.huahuahua1223_faucet_coin, output_value);
    let output = from_balance(output_balance, ctx);
    public_transfer(output, ctx.sender());
}

Through the above logic, users can freely exchange between the two types of tokens based on predefined ratios (e.g., 2:1 or 1:2).

2.5 Mainnet Deployment#

  1. If you are not connected to the mainnet, please refer to the third part of this tutorial🚪, then in the root directory of the contract project (e.g., my_swap), execute the following command to deploy the contract:
sui client publish --skip-dependency-verification
  1. After deployment, you will receive a transaction hash. Record this value and use the suivision blockchain explorer🚪 to query contract details.
    Insert image description here
  2. Find the Package ID in the contract details and call the contract functions in the frontend or Sui CLI testing tool.
    Insert image description here

III. Testing and Verification#

After deploying the contract, test the key functionalities to ensure the correctness and stability of the my_swap contract. The following tests include the complete process of token deposit, withdrawal, and swap, recording the results of key operations and verifications.
Insert image description here

Environment Preparation#

Before starting the tests, please ensure that there are sufficient task2 tokens in the account. For convenience, we used Sui CLI to mint 100 coin and 100 faucet_coin for the account. If you are unfamiliar with the minting command, you can refer to the previous Task2 tutorial🚪.
Insert image description here

The initial status of the account is as follows:

  • coin token quantity: 100
  • faucet_coin token quantity: 100

Insert image description here

3.1 Deposit Tokens#

First, call the deposit function to deposit some tokens into the pool.
Operation 1: Deposit 20 faucet_coin
Insert image description here

Operation 2: Deposit 20 coin
Insert image description here

Result verification:
Remaining coin quantity in the account: 80
Remaining faucet_coin quantity in the account: 80
Insert image description here

3.2 Withdraw Tokens#

Next, call the withdraw function to withdraw tokens from the liquidity pool using admin permissions.

Operation 1: Withdraw 6 faucet_coin
The admin calls the following command to withdraw 6 faucet_coin (note that the token precision is 8, so 600000000 represents 6 tokens):
Insert image description here

Operation 2: Withdraw 6 coin
Insert image description here

Result verification:
The admin account increased by 6 coin
The admin account increased by 6 faucet_coin
Insert image description here

3.3 Token Swapping#

Finally, call the swap function to verify the interchange functionality of the two types of tokens, ensuring that the exchange ratios are correct.

Operation 1: Use 6 faucet_coin to swap for 3 coin, with an exchange ratio of 2:1
Insert image description here
Result verification:
User account increased by 3 coin, reaching 89
User account decreased by 6 faucet_coin, reaching 80
Insert image description here

Operation 2: Use 5 faucet_coin to swap for 10 coin, with an exchange ratio of 1:2
Insert image description here
Result verification:
User account decreased by 5 coin, reaching 84
User account increased by 10 faucet_coin, reaching 90
Insert image description here

Through the above tests, the functionalities of the my_swap contract have been validated:

  1. When depositing tokens, the balance decreases correctly, and the excess refund mechanism is effective.
  2. When withdrawing tokens, the admin permission verification is accurate, and the withdrawal logic is correct.
  3. During token swapping, the exchange ratios are correct, and both the liquidity pool and account balances are updated normally.

Thus, Task5: Token Swap Contract is successfully completed 🎉!

Future Challenges: Next, we can try more complex logic, such as dynamic pricing, liquidity pool design, etc., to further explore the infinite possibilities of decentralized finance!


Summary#

Through this article, you should be able to master how to use the Move language to build a simple token swap contract on the Sui chain, becoming familiar with the basic logic design of token deposit, withdrawal, and swapping. I hope this article provides practical guidance for your entry into DeFi development while deepening your understanding of the Move programming language and the Sui blockchain.

If you are interested in DeFi application development or the Move language, feel free to continue following the upcoming articles and project shares! If you have any questions or thoughts, welcome to interact and communicate with me in the comments section🌹


For more exciting content, please follow the series article directory!
We grow together on the journey of exploring Move, see you next time! 🎉

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.