手把手教你实现代币 Swap！Move 语言与 Sui 链开发实战

Task5：move swap🚪
Task6：sdk ptb🚪

更多精彩内容，敬请期待！✌️

@TOC

前言

在上一篇文章《Task4：move game》中，我们探索了 Move 编程语言 在链上互动游戏中的应用，完成了一个简单但具有实用价值的剪刀石头布游戏的智能合约实现。通过这个任务，我们学习了资金池管理、链上公平性的保障，以及基于智能合约实现游戏交互的关键技术，进一步巩固了对 Move 的理解与实践。

本篇文章将聚焦于 Task5: move swap，挑战实现一个 基于 Move 的代币交换智能合约。通过这一任务，我们将构建一个代币交换的基础模型，使用户能够在链上进行两种代币的安全、快速交换。本任务将进一步展现 Move 在去中心化金融（DeFi）领域的潜力，帮助你掌握以下关键技术点：

如何设计和管理链上代币池；

如何通过智能合约实现代币交换的业务逻辑；

如何保证代币交换的安全性和效率。

在实现过程中，我们将采用两种在task2我们定义的代币 HUAHUAHUA1223_COIN 和 HUAHUAHUA1223_FAUCET_COIN，并通过设置兑换比例实现代币的互换。同时，我们还将深入探讨智能合约中的 资金验证与余额管理、公平性与鲁棒性设计 等核心问题。

让我们继续解锁 Move 的应用场景，一起构建去中心化金融的基础组件！

什么是 Sui 链？

Sui 是一个高性能的区块链平台，旨在为去中心化应用提供快速、安全且可扩展的基础设施。它由 Aptos Labs 团队开发，基于新型的共识协议 ——Narwhal & Tusk。Sui 的设计目标是解决区块链性能瓶颈，提供极高的交易吞吐量和低延迟，适应复杂应用场景的需求。

Sui 链的主要特点：

高吞吐量与低延迟： Sui 的共识机制允许并行处理大量交易，而无需等待整个网络的全局共识。这种并行化的设计能够实现每秒处理成千上万的交易，极大提高了区块链的吞吐量，并减少交易确认的延迟。

面向对象的资源管理： Sui 将区块链中的资源视为对象进行管理。这些资源（例如代币、NFT）有独立的标识符，能够被直接跟踪和操作。通过这种方式，Sui 可以在多个节点之间高效并行地处理资源，而不需要处理全局状态，进一步提升性能。

灵活的交易模型： Sui 提供了灵活且高效的交易模型，支持在多个资源对象之间并行执行交易。这意味着不同用户的交易可以独立且高效地进行，避免了传统区块链的性能瓶颈。

高效的账户和权限管理： Sui 提供了多样化的账户管理机制，可以应对去中心化应用中复杂的权限需求。无论是个人账户、智能合约账户，还是多签账户，都能灵活配置和管理。

什么是 Move 编程语言？

Move 是专为区块链开发设计的编程语言，最初由 MetaLibra（后来的 Diem）团队开发，后被 Sui 区块链采用。Move 的设计重点是资源的管理、所有权的控制以及类型安全，它特别适用于处理去中心化应用中的资产和数字资源。

Move 语言的主要特点：

资源类型系统： Move 语言将所有的资源（如代币、NFT、智能合约中的数据等）视为 “资源类型”。这些资源在系统中不能被复制或销毁，只能转移或借用。这确保了每个资源的唯一性和安全性，从根本上避免了传统智能合约中的资源丢失和重复转移问题。

所有权与借用机制： Move 通过严格的所有权和借用机制管理资源。每个资源都有一个唯一的所有者，资源的借用必须显式声明，这种机制避免了 “共享资源” 时的安全隐患。资源的借用可以确保开发者在不修改资源所有权的前提下共享和操作资源。

模块化编程： Move 支持模块化的编程结构，每个模块可以包含不同的资源类型和函数。模块化设计使得代码更加清晰、可复用，并有助于提高开发效率和降低代码出错的概率。

类型安全与可验证性： Move 是一门强类型语言，这意味着开发者必须在编译时明确地定义每个变量和资源的类型。Move 的类型系统能够确保合约中的大部分错误在编译阶段就被发现，从而避免了运行时错误，提高了智能合约的安全性。

Move 语言的示例代码：
以下是一个简单的 Move 合约示例，展示了如何创建和转移一个名为 Coin 的资源：

Copy
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 社区、HackQuest、OpenBuild、KeyMap 联合发起。该活动旨在为新手小白提供一个良好的学习平台，带领大家一步步熟悉 Move 语言，并了解如何将其应用到 Web3 开发中。

通过与 Move 领域的专业导师们合作，参与者可以快速掌握 Move 语言的基础知识，逐步向更复杂的应用开发进阶。无论是区块链初学者，还是有一定开发经验的工程师，都能从中获益。

资源链接：

sui 官方文档🚪：获取关于 Sui 链的详细文档，包括开发指南、API 参考等。

move 学习 B 站视频🚪：通过 B 站的视频教程，跟随导师学习 Move 编程语言的基础与进阶。

letsmove 仓库🚪：这是一个 Move 学习资源的 GitHub 仓库，包含了各种示例代码和教程，帮助开发者掌握 Move 语言。

一、创建项目

首先，我们需要创建一个新的 Move 项目，并导入之前在 Task2 中开发的代币合约

1.1 新建 Move 项目

运行以下命令创建名为 my_swap 的 Move 项目：

Copy
sui move new my_swap
cd .\my_swap\

1.2 导入依赖

假设我们的代币合约已经实现并存储在 my_coin 目录中。我们可以通过以下方式将其作为依赖项导入到新的项目中：

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

在此基础上，我们空编译项目，确保依赖项正确引入并且项目能顺利编译：sui move build

二、合约设计

在合约代码部分，其实和我们上个 task4 讲到的代码逻辑是异曲同工的，在 task4 中，我们对一种代币进行存取以及游戏输赢玩法，于是在 task5 中只需要再给 Pool 代币池中加一种代币，在代币池中自动进行两种代币的转换即可

以下是代币交换合约的完整实现代码，我们将逐步拆解其核心逻辑：

Copy
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 的逻辑：

Copy
// 存储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：

Copy
// 管理员提取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

Copy
// 将 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

Copy
// 将 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:1 或 1:2）在两种代币之间自由交换。

2.4 主网部署

如果未连接主网，请参考 这篇教程的第三部分🚪，之后在合约项目的根目录下（如 my_swap），执行以下命令部署合约：

Copy
sui client publish --skip-dependency-verification

部署之后会得到一个交易哈希，记录下该值并使用 suivision 区块链浏览器🚪查询合约详情

找到合约详情里的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 合约的各项功能验证通过：

存入代币时，余额正确减少，多余部分返还机制有效。

提取代币时，管理员权限验证无误，提取逻辑准确。

代币互换时，兑换比例正确，资金池和账户余额均更新正常。

至此，Task5：代币交换合约 圆满完成 🎉！

后续挑战：下一步我们可以尝试更复杂的逻辑，比如动态定价、流动性池设计等，进一步探索去中心化金融的无限可能！

总结

通过本篇文章的学习，你应该能够掌握如何利用 Move 语言在 Sui 链上构建一个简单的代币交换合约，熟悉代币存取、提取以及交换的基本逻辑设计。希望这篇文章为你在 DeFi 开发的入门阶段提供了实用的指导，同时加深了你对 Move 编程语言和 Sui 区块链的理解。

如果你对 DeFi 应用开发或 Move 语言感兴趣，欢迎继续关注后续的文章与项目分享！如有疑问或想法，欢迎在评论区与我互动交流🌹