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 key technologies for implementing game interactions based on smart contracts, further consolidating our understanding and practice of Move.
This article will focus on Task5: move swap, challenging us to implement a token exchange smart contract based on Move. Through this task, we will build a basic model for token exchange, allowing users to securely and quickly swap two types of tokens on-chain. This task will further showcase Move's potential 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 exchange through smart contracts;
- How to ensure the security and efficiency of token exchanges.
In the implementation process, we will use two tokens defined in task2
, HUAHUAHUA1223_COIN
and HUAHUAHUA1223_FAUCET_COIN
, and achieve token swapping by setting exchange ratios. We will also delve into core issues such as fund validation and balance management and fairness and robustness design in smart contracts.
Let’s continue to unlock the application scenarios of Move and build the foundational components of decentralized finance together!
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 and is based on a new consensus protocol—Narwhal & Tusk. The design goal of Sui is to solve 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:
-
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 thousands of transactions to be processed per second, greatly increasing blockchain throughput and reducing transaction confirmation latency.
-
Object-oriented resource management: Sui treats resources in the blockchain as objects for management. These resources (such as tokens and NFTs) have independent identifiers that can be directly tracked and manipulated. This approach allows Sui to efficiently and parallelly process resources across multiple nodes without handling global state, further enhancing performance.
-
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 be conducted independently and efficiently, avoiding performance bottlenecks typical of traditional blockchains.
-
Efficient account and permission management: Sui offers a diverse account management mechanism that can handle complex permission requirements in decentralized applications. Whether it is 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:
-
Resource type system: The Move language treats all resources (such as tokens, NFTs, and data in smart contracts) as "resource types." These resources cannot be copied or destroyed in the system; they can only be transferred or borrowed. This ensures the uniqueness and security of each resource, fundamentally avoiding issues of resource loss and duplicate transfer found in traditional smart contracts.
-
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 altering ownership.
-
Modular programming: Move supports a modular programming structure, where each module can contain different resource types and functions. Modular design makes the code clearer, more reusable, and helps improve development efficiency while reducing the likelihood of coding errors.
-
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 can ensure that most errors in contracts are caught during the compilation phase, thus avoiding runtime errors and enhancing the security of smart contracts.
Example code in Move language:
Here is a simple example of a Move contract 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 };
// Actual transfer operation can be executed here
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:
- Sui Official 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.
- Let’s Move 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\
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" }
Based on this, we will compile the project to ensure that the dependencies are correctly introduced and that the project compiles smoothly: sui move build
II. Contract Design#
In the contract code section, the logic is actually similar to what we discussed in task4. In task4, we handled one type of token for storage and game win/loss gameplay, so in task5, we just need to add another token to the Pool
token pool and automatically perform the conversion between the two tokens in the pool.
Below is the complete implementation code for the token exchange 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 has more tokens 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),
);
// Return 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 administrator permissions 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 withdrawal failures do not occur.
2.4 Token Exchange#
The core functionality of the token pool is to implement the exchange between the two types of tokens. Below are the exchange logics for both types:
- Exchanging
faucet_coin
formy_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 if 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());
}
- Exchanging
my_coin
forfaucet_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 if 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 (such as 2:1
or 1:2
).
2.5 Mainnet Deployment#
- If not connected to the mainnet, please refer to the third part of this tutorial🚪, then in the root directory of the contract project (such as
my_swap
), execute the following command to deploy the contract:
sui client publish --skip-dependency-verification
-
After deployment, you will receive a transaction hash. Record this value and use the Suivision blockchain explorer🚪 to query contract details.
-
Find the
Package ID
in the contract details, and call the contract functions in the frontend orSui CLI
testing tool.
III. Testing and Verification#
After the contract is deployed, 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 exchange, recording the results of key operations and verifications.
Environment Preparation#
Before starting the tests, please ensure that there are sufficient task2
tokens in the account. For convenience, we used Sui CLI to re-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🚪.
The initial status of the account is as follows:
coin
token quantity: 100faucet_coin
token quantity: 100
3.1 Deposit Tokens#
First, call the deposit
function to deposit some tokens into the pool.
Operation 1: Deposit 20 faucet_coin
Operation 2: Deposit 20 coin
Result verification:
Remaining coin
quantity in the account: 80
Remaining faucet_coin
quantity in the account: 80
3.2 Withdraw Tokens#
Next, call the withdraw
function to withdraw tokens from the liquidity pool using administrator permissions.
Operation 1: Withdraw 6 faucet_coin
The administrator calls the following command to withdraw 6 faucet_coin
(note that the token precision is 8, so 600000000 represents 6 tokens):
Operation 2: Withdraw 6 coin
Result verification:
The administrator's account increased by 6 coin
The administrator's account increased by 6 faucet_coin
3.3 Token Exchange#
Finally, call the swap
function to verify the exchange functionality between the two types of tokens, ensuring the exchange ratio is correct.
Operation 1: Use 6 faucet_coin
to exchange for 3 coin
, with an exchange ratio of 2:1
Result verification:
User's account increased by 3 coin
, reaching 89
User's account decreased by 6 faucet_coin
, reaching 80
Operation 2: Use 5 faucet_coin
to exchange for 10 coin
, with an exchange ratio of 1:2
Result verification:
User's account decreased by 5 coin
, reaching 84
User's account increased by 10 faucet_coin
, reaching 90
Through the above tests, the functionalities of the my_swap
contract have been verified:
- When depositing tokens, the balance decreases correctly, and the excess return mechanism is effective.
- When withdrawing tokens, the administrator permission verification is accurate, and the withdrawal logic is correct.
- During token exchanges, the exchange ratio is correct, and both the liquidity pool and account balances are updated normally.
Thus, Task5: Token Exchange Contract is successfully completed 🎉!
Future Challenges: Next, we can attempt more complex logic, such as dynamic pricing and liquidity pool design, 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 exchange contract on the Sui chain, becoming familiar with the basic logic design of token deposit, withdrawal, and exchange. I hope this article provides practical guidance for you in the introductory phase of 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 subsequent 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 road of exploring Move, see you next time! 🎉