huahua

huahua

手把手教你實現代幣 Swap!Move 語言與 Sui 鏈開發實戰

系列文章目錄#

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

更多精彩內容,敬請期待!✌️


@TOC


前言#

在上一篇文章《Task4:move game》中,我們探索了 Move 程式語言 在鏈上互動遊戲中的應用,完成了一個簡單但具有實用價值的剪刀石頭布遊戲的智能合約實現。通過這個任務,我們學習了資金池管理、鏈上公平性的保障,以及基於智能合約實現遊戲互動的關鍵技術,進一步鞏固了對 Move 的理解與實踐。

本篇文章將聚焦於 Task5: move swap,挑戰實現一個 基於 Move 的代幣交換智能合約。通過這一任務,我們將構建一個代幣交換的基礎模型,使使用者能夠在鏈上進行兩種代幣的安全、快速交換。本任務將進一步展現 Move 在去中心化金融(DeFi)領域的潛力,幫助你掌握以下關鍵技術點:

  • 如何設計和管理鏈上代幣池;
  • 如何通過智能合約實現代幣交換的業務邏輯;
  • 如何保證代幣交換的安全性和效率。

在實現過程中,我們將採用兩種在task2我們定義的代幣 HUAHUAHUA1223_COINHUAHUAHUA1223_FAUCET_COIN,並通過設置兌換比例實現代幣的互換。同時,我們還將深入探討智能合約中的 資金驗證與餘額管理公平性與魯棒性設計 等核心問題。

讓我們繼續解鎖 Move 的應用場景,一起構建去中心化金融的基礎組件!

HOH 社區


什麼是 Sui 鏈?#

Sui 是一個高性能的區塊鏈平台,旨在為去中心化應用提供快速、安全且可擴展的基礎設施。它由 Aptos Labs 團隊開發,基於新型的共識協議 ——Narwhal & Tusk。Sui 的設計目標是解決區塊鏈性能瓶頸,提供極高的交易吞吐量和低延遲,適應複雜應用場景的需求。

Sui 鏈的主要特點:

  1. 高吞吐量與低延遲: Sui 的共識機制允許並行處理大量交易,而無需等待整個網絡的全局共識。這種並行化的設計能夠實現每秒處理成千上萬的交易,極大提高了區塊鏈的吞吐量,並減少交易確認的延遲。

  2. 面向對象的資源管理: Sui 將區塊鏈中的資源視為對象進行管理。這些資源(例如代幣、NFT)有獨立的標識符,能夠被直接跟蹤和操作。通過這種方式,Sui 可以在多個節點之間高效並行地處理資源,而不需要處理全局狀態,進一步提升性能。

  3. 靈活的交易模型: Sui 提供了靈活且高效的交易模型,支持在多個資源對象之間並行執行交易。這意味著不同使用者的交易可以獨立且高效地進行,避免了傳統區塊鏈的性能瓶頸。

  4. 高效的賬戶和權限管理: Sui 提供了多樣化的賬戶管理機制,可以應對去中心化應用中複雜的權限需求。無論是個人賬戶、智能合約賬戶,還是多簽賬戶,都能靈活配置和管理。


什麼是 Move 程式語言?#

Move 是專為區塊鏈開發設計的程式語言,最初由 MetaLibra(後來的 Diem)團隊開發,後被 Sui 區塊鏈採用。Move 的設計重點是資源的管理、所有權的控制以及類型安全,它特別適用於處理去中心化應用中的資產和數字資源。

Move 語言的主要特點:

  1. 資源類型系統: Move 語言將所有的資源(如代幣、NFT、智能合約中的數據等)視為 “資源類型”。這些資源在系統中不能被複製或銷毀,只能轉移或借用。這確保了每個資源的唯一性和安全性,從根本上避免了傳統智能合約中的資源丟失和重複轉移問題。

  2. 所有權與借用機制: Move 通過嚴格的所有權和借用機制管理資源。每個資源都有一個唯一的所有者,資源的借用必須明確聲明,這種機制避免了 “共享資源” 時的安全隱患。資源的借用可以確保開發者在不修改資源所有權的前提下共享和操作資源。

  3. 模組化編程: Move 支持模組化的編程結構,每個模組可以包含不同的資源類型和函數。模組化設計使得代碼更加清晰、可重用,並有助於提高開發效率和降低代碼出錯的概率。

  4. 類型安全與可驗證性: Move 是一門強類型語言,這意味著開發者必須在編譯時明確地定義每個變量和資源的類型。Move 的類型系統能夠確保合約中的大部分錯誤在編譯階段就被發現,從而避免了運行時錯誤,提高了智能合約的安全性。

Move 語言的示例代碼:
以下是一個簡單的 Move 合約示例,展示了如何創建和轉移一個名為 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 };
            // 這裡可以執行實際的轉帳操作
            return new_coin;
        }
    }
}

在這個示例中,Coin 是一個資源類型,包含一個 value 字段,表示代幣的值。create_coin 函數用來創建新的 Coin 資源,而 transfer_coin 函數則用於將 Coin 資源轉移到指定的賬戶。


Move 共學活動:快速上手 Move 開發#

為了幫助更多開發者快速了解和掌握 Move 程式語言,Move 共學活動由 HOH 社區HackQuestOpenBuildKeyMap 聯合發起。該活動旨在為新手小白提供一個良好的學習平台,帶領大家一步步熟悉 Move 語言,並了解如何將其應用到 Web3 開發中。

通過與 Move 領域的專業導師們合作,參與者可以快速掌握 Move 語言的基礎知識,逐步向更複雜的應用開發進階。無論是區塊鏈初學者,還是有一定開發經驗的工程師,都能從中獲益。

資源鏈接:

  • sui 官方文檔🚪:獲取關於 Sui 鏈的詳細文檔,包括開發指南、API 參考等。
  • move 學習 B 站視頻🚪:通過 B 站的視頻教程,跟隨導師學習 Move 程式語言的基礎與進階。
  • letsmove 倉庫🚪:這是一個 Move 學習資源的 GitHub 倉庫,包含了各種示例代碼和教程,幫助開發者掌握 Move 語言。

一、創建項目#

首先,我們需要創建一個新的 Move 項目,並導入之前在 Task2 中開發的代幣合約

1.1 新建 Move 項目#

運行以下命令創建名為 my_swap 的 Move 項目:

sui move new my_swap
cd .\my_swap\

在這裡插入圖片描述

1.2 導入依賴#

假設我們的代幣合約已經實現並存儲在 my_coin 目錄中。我們可以通過以下方式將其作為依賴項導入到新的項目中:

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

在這裡插入圖片描述

在此基礎上,我們空編譯項目,確保依賴項正確引入並且項目能順利編譯:sui move build
在這裡插入圖片描述

二、合約設計#

在合約代碼部分,其實和我們上個 task4 講到的代碼邏輯是異曲同工的,在 task4 中,我們對一種代幣進行存取以及遊戲輸贏玩法,因此在 task5 中只需要再給 Pool 代幣池中加一種代幣,在代幣池中自動進行兩種代幣的轉換即可。

以下是代幣交換合約的完整實現代碼,我們將逐步拆解其核心邏輯:

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) };

    // 公開swap池
    share_object(pool);
    // 管理員權限給合約部署者
    transfer(admin, ctx.sender());
}

2.1 代幣池與管理員權限#

我們通過 Pool 結構維護兩種代幣的餘額,同時設置一個 AdminCap 結構,確保管理員擁有權限來提取或管理代幣。init 函數初始化了代幣池,並將管理員權限授予合約部署者。

2.2. 存儲代幣#

使用者可以向代幣池存入兩種代幣之一。以下是存儲 HUAHUAHUA1223_COIN 的邏輯:

// 存儲my_coin代幣
public entry fun deposit_my_coin(
    pool: &mut Pool,
    user_coin: Coin<HUAHUAHUA1223_COIN>,
    amount: u64,
    ctx: &mut TxContext,
) {
    // 驗證錢包代幣是否比輸入金額多
    let coin_value = user_coin.value();
    assert!(coin_value >= amount, EInputNotEnough);

    // 把Coin轉換為Balance
    let mut input_balance = into_balance(user_coin);
    if (coin_value == amount) {
        // 輸入的amount就是所有的代幣
        balance::join(&mut pool.huahuahua1223_coin, input_balance);
    } else {
        balance::join(
            &mut pool.huahuahua1223_coin,
            balance::split(&mut input_balance, amount),
        );
        // 退回多餘的代幣
        let surplus_coin = from_balance(input_balance, ctx);
        public_transfer(surplus_coin, ctx.sender());
    };
}

這裡的關鍵點包括:

  • 驗證輸入代幣數量是否足夠。
  • 處理多餘代幣,將剩餘部分返還給使用者。

類似邏輯也適用於存儲 HUAHUAHUA1223_FAUCET_COIN

2.3 提取代幣#

管理員可以提取池內的任意一種代幣。這需要管理員權限 AdminCap

// 管理員提取my_coin代幣
public entry fun withdraw_coin(
    _: &AdminCap,
    pool: &mut Pool,
    amount: u64,
    ctx: &mut TxContext,
) {
    assert!(pool.huahuahua1223_coin.value() >= amount, EPoolNotEnough );

    // 用 from_balance 將balance轉換為coin類型
    let withdrawn_balance = balance::split(&mut pool.huahuahua1223_coin, amount);
    let withdrawn_coin = from_balance(withdrawn_balance, ctx);
    public_transfer(withdrawn_coin, ctx.sender());
}

// 管理員提取faucet_coin代幣
public entry fun withdraw_faucet_coin(
    _: &AdminCap,
    pool: &mut Pool,
    amount: u64,
    ctx: &mut TxContext,
) {
    assert!(pool.huahuahua1223_faucet_coin.value() >= amount, EPoolNotEnough );

    // 用 from_balance 將balance轉換為coin類型
    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());
}

此部分邏輯嚴格驗證了池內餘額是否足夠,確保不會出現提取失敗的情況。

2.4 代幣交換#

代幣池的核心功能是實現兩種代幣的互換,下面分別展示兩種交換邏輯:

  • faucet_coin 換取 my_coin
// 將 2個 faucet_coin 轉換成 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,
) {
    // 驗證swap池子是否能兌換出這麼多的huahuahua1223_coin
    let output_value = amount * 1000 / 2000;
    assert!(pool.huahuahua1223_coin.value() >= output_value, EPoolNotEnough);

    // 將 faucet_coin 存入到swap池子裡等待交換
    deposit_faucet_coin(pool, user_coin, amount, ctx);

    // 交換一半數量的 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());
}
  • my_coin 換取 faucet_coin
// 將 1個 my_coin 轉換成 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,
) {
    // 驗證swap池子是否能兌換出這麼多的huahuahua1223_faucet_coin
    let output_value = amount * 2000 / 1000;
    assert!(pool.huahuahua1223_faucet_coin.value() >= output_value, EPoolNotEnough);

    // 將 my_coin 存入到swap池子裡等待交換
    deposit_my_coin(pool, user_coin, amount, ctx);

    // 交換兩倍的 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());
}

通過上述邏輯,使用者可以根據預定義的比例(如 2:11:2)在兩種代幣之間自由交換。

2.5 主網部署#

  1. 如果未連接主網,請參考 這篇教程的第三部分🚪,之後在合約項目的根目錄下(如 my_swap),執行以下命令部署合約:
sui client publish --skip-dependency-verification
  1. 部署之後會得到一個交易哈希,記錄下該值並使用 suivision 區塊鏈瀏覽器🚪查詢合約詳情
    在這裡插入圖片描述
  2. 找到合約詳情裡的Package ID,在前端或Sui CLI測試工具中調用合約函數。
    在這裡插入圖片描述

三、測試與驗證#

合約部署完成後,通過對關鍵功能的調用進行測試,確保 my_swap 合約的正確性和穩定性。以下測試包括 代幣存入提取和交換 的完整流程,並記錄了關鍵操作的結果及驗證。
在這裡插入圖片描述

環境準備#

在測試開始前,請確保賬戶內已有足夠的 task2 代幣。為方便測試,我們使用 Sui CLI 為賬戶重新鑄造了 100 個 coin 和 100 個 faucet_coin。如果對鑄造命令不熟悉,可以參考之前的 Task2 教程🚪
在這裡插入圖片描述

以下是賬戶的初始狀態:

  • coin 代幣數量:100
  • faucet_coin 代幣數量:100

在這裡插入圖片描述

3.1 存入代幣#

首先調用 deposit 函數,將一部分代幣存入池中。
操作 1:存入 20 個 faucet_coin
在這裡插入圖片描述

操作 2:存入 20 個 coin
在這裡插入圖片描述

結果驗證:
賬戶剩餘 coin 數量:80
賬戶剩餘 faucet_coin 數量:80
在這裡插入圖片描述

3.2 提取代幣#

接下來,調用 withdraw 函數,通過管理員權限從資金池中提取代幣。

操作 1:提取 6 個faucet_coin
管理員調用以下命令提取 6 個 faucet_coin(注意代幣的精度為 8,因此 600000000 表示 6 個代幣):
在這裡插入圖片描述

操作 2:提取 6 個coin
在這裡插入圖片描述

結果驗證:
管理員賬戶增加了 6 個 coin
管理員賬戶增加了 6 個 faucet_coin
在這裡插入圖片描述

3.3 代幣交換#

最後,調用 swap 函數驗證兩種代幣的互換功能,確保兌換比例正確。

操作 1:使用 6 個faucet_coin換取 3 個coin,兌換比例為2:1
在這裡插入圖片描述
結果驗證:
使用者賬戶增加 3 個 coin,達到 89 個
使用者賬戶減少 6 個 faucet_coin,達到 80 個
在這裡插入圖片描述

操作 2:使用 5 個faucet_coin換取 10 個coin,兌換比例為1:2
在這裡插入圖片描述
結果驗證:
使用者賬戶減少 5 個 coin,達到 84 個
使用者賬戶增加 10 個 faucet_coin,達到 90 個
在這裡插入圖片描述

通過上述測試,my_swap 合約的各項功能驗證通過:

  1. 存入代幣時,餘額正確減少,多餘部分返還機制有效。
  2. 提取代幣時,管理員權限驗證無誤,提取邏輯準確。
  3. 代幣互換時,兌換比例正確,資金池和賬戶餘額均更新正常。

至此,Task5:代幣交換合約 圓滿完成 🎉!

後續挑戰:下一步我們可以嘗試更複雜的邏輯,比如動態定價、流動性池設計等,進一步探索去中心化金融的無限可能!


總結#

通過本篇文章的學習,你應該能夠掌握如何利用 Move 語言在 Sui 鏈上構建一個簡單的代幣交換合約,熟悉代幣存取、提取以及交換的基本邏輯設計。希望這篇文章為你在 DeFi 開發的入門階段提供了實用的指導,同時加深了你對 Move 程式語言和 Sui 區塊鏈的理解。

如果你對 DeFi 應用開發或 Move 語言感興趣,歡迎繼續關注後續的文章與項目分享!如有疑問或想法,歡迎在評論區與我互動交流🌹


更多精彩內容,歡迎關注系列文章目錄!
我們在探索 Move 的道路上共同成長,不見不散! 🎉

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。