Aptos

2025年09月02日更新 4 人订阅
原价: ¥ 2.2 限时优惠
专栏简介 Aptos 开发实战:从环境搭建到第一个 Hello World Aptos 开发指南:在 JetBrains 编辑器中配置运行、编译、测试与发布部署,实现更高效开发 Aptos 区块链智能合约入门:使用 Move 实现消息存储与检索 Aptos Move 语言中的变量管理与内存所有权机制详解 Aptos Move 编程语言中的四大基础类型解析:UINT、STRING、BOOL 与 ADDRESS 深入解读 APTOS-MOVE 中的 Vector 向量核心特性与操作 深入理解APTOS-MOVE中的函数修饰符:核心概念与应用 深入解读 Aptos Move 的 Struct 特性与四大能力 Aptos Move 控制流解析:IF、WHILE与LOOP的深入解读 Aptos Move 模块的特性与实操指南:模块引用与作用域管理 Aptos Move 模块的发布与交互:完整指南 深入理解 Aptos Move 中的 Object 创建与管理 深入探索 Aptos Move:Object 配置与实操指南 使用 Aptos Move 实现随机数生成:从 AIP-41 到实战操作 Aptos Move 实践指南:构建并部署同质化代币水龙头 (FA Faucet) Aptos Move NFT 项目实操指南:从开发到部署全流程解析 Aptos Move 开发入门:从环境搭建到合约部署全流程实录 Aptos Move 入门:从零到一的合约开发与测试实战 Move 语言核心:布尔逻辑与地址类型的实战精解 深入 Aptos Move:从public到friend,函数可见性详解 Aptos Move 编程:for、while 与 loop 循环的实战详解 Aptos Move 安全编程:abort 与 assert! 错误处理实战 Aptos Move 实战:基础运算与比较逻辑的实现与测试 Aptos Move 性能优化:位运算与移位操作实战 Aptos Move 实战:as 关键字与整数类型转换技巧 Aptos Move DeFi 实战:从零构建流动性池兑换逻辑

Aptos Move DeFi 实战:从零构建流动性池兑换逻辑

AptosMoveDeFi实战:从零构建流动性池兑换逻辑去中心化交易所(DEX)是DeFi生态的基石,而其核心正是流动性池背后的自动做市商(AMM)算法。对于有志于在Aptos上构建DeFi应用的开发者来说,亲手实现并验证一个兑换(Swap)功能,是理解其底层机制的最佳路径。本

Aptos Move DeFi 实战:从零构建流动性池兑换逻辑

去中心化交易所(DEX)是 DeFi 生态的基石,而其核心正是流动性池背后的自动做市商(AMM)算法。对于有志于在 Aptos 上构建 DeFi 应用的开发者来说,亲手实现并验证一个兑换(Swap)功能,是理解其底层机制的最佳路径。

本文将摒弃复杂的理论,以最“实事求是”的方式,带你深入 Aptos Move 的开发实践。我们将从一个最基础的恒定乘积公式入手,一步步构建一个包含手续费的兑换计算函数,然后将其扩展为一个可管理多个交易对的模块化结构,并最终分析交易对价格产生的冲击。每一步,我们都将通过严格的单元测试来验证代码的准确性。

实操

Aptos Move Liquidity Pool Calculator

示例一

module net2dev_addr::Sample9 {
    /// Error code indicating that the provided amount is not enough for the swap.
    const E_NOT_ENOUGH: u64 = 0;

    const Pool1_n2dr: u64 = 312;
    const Pool1_usdt: u64 = 3201;

    fun calculate_swap(coin1: u64, coin2: u64, coin1_amount: u64): u64 {
        assert!(coin1_amount > 0, E_NOT_ENOUGH);

        let fee = coin1_amount * 5 / 100;
        let mix_supply = coin1 * coin2;
        let new_usdt = coin1 + coin1_amount;
        let new_n2dr = mix_supply / (new_usdt - fee);
        let receive = coin2 - new_n2dr;
        receive
    }

    #[test_only]
    use std::debug::print;

    #[test]
    fun test_function() {
        let swap_amount = 495; // USDT to swap
        let receive = calculate_swap(Pool1_usdt, Pool1_n2dr, swap_amount);
        print(&receive);
        assert!(receive == 41, 1);
    }

    /*

    Liquidity Pool

    Coin1 = 3201 USDT
    Coin2 = 312 N2DR

    495 USDT -> N2DR

    FORMULA with 5% fee

    Value1 = Apply a 5% fee to the USDT amount to be swapped.
    Fee: 495 * 5 / 100 = 24.75

    Value2 = Multiply both USDT and N2DR Supply.
    MixSupply: Coin1 * Coin2 = 998,712

    Value3 = Determine the new supply of USDT after the swap.
    NewUSDT = Coin1 + 495 = 3201 + 495 = 3696

    Value4 = Determine the new supply of N2DR after the swap.
    NewN2DR = MixSupply / (NewUSDT - fee) = 998,712 / (3696 - 24.75) = 271.5879458

    Value5 = Determine the amount of N2DRs to transfer to the user.
    Transfer = Coin2 - NewN2DR = 312 - 271.5879458 = 40.4120542

    */
}

这段 Aptos Move 代码通过一个名为 Sample9 的模块,演示了一个简化的去中心化交易所(DEX)中流动性池的兑换计算逻辑。其核心功能 calculate_swap 函数实现了一种自动做市商(AMM)的定价公式:它采用恒定乘积模型(x * y = k),在计算中扣除了 5% 的手续费,并最终算出用户用一种代币能够换取另一种代币的数量。该模块使用常量来模拟一个 USDT/N2DR 交易对的初始流动性状态,并通过一个单元测试来验证整个兑换算法,该测试模拟了一笔 495 USDT 的交易,并用 assert! 断言其兑换结果是否等于预期的 41 N2DR。

测试

 ➜ aptos move test
INCLUDING DEPENDENCY AptosFramework
INCLUDING DEPENDENCY AptosStdlib
INCLUDING DEPENDENCY MoveStdlib
BUILDING my-dapp
Running Move unit tests
[debug] 41
[ PASS    ] 0x48daaa7d16f1a59929ece03157b8f3e4625bc0a201988ac6fea9b38f50db5ef3::Sample9::test_function
Test result: OK. Total tests: 1; passed: 1; failed: 0
{
  "Result": "Success"
}

这个测试结果表明,你的 my-dapp 项目已成功通过了其单元测试。在成功编译项目后,测试框架执行了 Sample9 模块中定义的唯一一个测试函数 test_function。日志中的 [debug] 41calculate_swap 函数在接收到测试参数后实际计算并返回的最终兑换数量。紧随其后的 [ PASS ] 状态确认了该测试用例顺利完成,这意味着代码中的 assert! 断言成功地验证了计算结果 41 与预期值完全一致。最后的 Test result: OK 总结陈述为这次测试画上了圆满的句号,证明了你合约中的兑换算法逻辑根据该测试场景是正确无误的。

示例二

module net2dev_addr::Sample9 {

    /// Error code indicating that the provided amount is not enough for the swap.
    const E_NOT_ENOUGH: u64 = 0;

    const N2DR: u64 = 1;
    const APT: u64 = 2;
    const WETH: u64 = 3;

    const Pool1_n2dr: u64 = 312;
    const Pool1_usdt: u64 = 3201;
    const N2DR_name: vector<u8> = b"N2DR Rewards";

    const Pool2_apt: u64 = 21500;
    const Pool2_usdt: u64 = 124700;
    const APT_name: vector<u8> = b"Aptos";

    const Pool3_weth: u64 = 1310;
    const Pool3_usdt: u64 = 2750000;
    const WETH_name: vector<u8> = b"Wrapped Ether";

    fun get_supply(coin_symbol: u64): (u64, u64, vector<u8>) {
        if (coin_symbol == N2DR) {
            (Pool1_usdt, Pool1_n2dr, N2DR_name)
        } else if (coin_symbol == APT) {
            (Pool2_usdt, Pool2_apt, APT_name)
        } else {
            (Pool3_usdt, Pool3_weth, WETH_name)
        }
    }

    // This applies a 5% fee to each swap tx.
    fun calculate_swap(coin1: u64, coin2: u64, coin1_amount: u64): u64 {
        assert!(coin1_amount > 0, E_NOT_ENOUGH);

        let fee = coin1_amount * 5 / 100;
        let mix_supply = coin1 * coin2;
        let new_usdt = coin1 + coin1_amount;
        let new_n2dr = mix_supply / (new_usdt - fee);
        let receive = coin2 - new_n2dr;
        receive
    }

    #[test_only]
    use std::debug::print;
    use std::string::utf8;

    #[test]
    fun test_function() {
        let swap_amount = 495; // USDT to swap
        let receive = calculate_swap(Pool1_usdt, Pool1_n2dr, swap_amount);
        print(&receive);
        assert!(receive == 41, 1);

        let (coin1, coin2, name) = get_supply(N2DR);
        print(&coin1);
        print(&coin2);

        assert!(coin1 == Pool1_usdt, 2);
        assert!(coin2 == Pool1_n2dr, 3);
        assert!(name == N2DR_name, 4);
        print(&utf8(b"Swap USDT for:"));
        print(&utf8(name));
        let result = calculate_swap(coin1, coin2, swap_amount);
        print(&result);
        assert!(result == receive, 5);

        let (coin1, coin2, name) = get_supply(APT);
        print(&coin1);
        print(&coin2);

        assert!(coin1 == Pool2_usdt, 6);
        assert!(coin2 == Pool2_apt, 7);
        assert!(name == APT_name, 8);
        print(&utf8(b"Swap USDT for:"));
        print(&utf8(name));
        let result = calculate_swap(coin1, coin2, swap_amount);
        print(&result);
        assert!(result == 81, 9);

        let (coin1, coin2, name) = get_supply(WETH);
        print(&coin1);
        print(&coin2);

        assert!(coin1 == Pool3_usdt, 10);
        assert!(coin2 == Pool3_weth, 11);
        assert!(name == WETH_name, 12);
        print(&utf8(b"Swap USDT for:"));
        print(&utf8(name));
        let result = calculate_swap(coin1, coin2, swap_amount);
        print(&result);
        assert!(result == 1, 13);
    }

    /*

    Liquidity Pool

    Coin1 = 3201 USDT
    Coin2 = 312 N2DR

    495 USDT -> N2DR

    FORMULA with 5% fee

    Value1 = Apply a 5% fee to the USDT amount to be swapped.
    Fee: 495 * 5 / 100 = 24.75

    Value2 = Multiply both USDT and N2DR Supply.
    MixSupply: Coin1 * Coin2 = 998,712

    Value3 = Determine the new supply of USDT after the swap.
    NewUSDT = Coin1 + 495 = 3201 + 495 = 3696

    Value4 = Determine the new supply of N2DR after the swap.
    NewN2DR = MixSupply / (NewUSDT - fee) = 998,712 / (3696 - 24.75) = 271.5879458

    Value5 = Determine the amount of N2DRs to transfer to the user.
    Transfer = Coin2 - NewN2DR = 312 - 271.5879458 = 40.4120542

    */
}

这段 Aptos Move 代码在前一个兑换计算示例的基础上进行了扩展,用于模拟一个拥有多个流动性池的去中心化交易所 (DEX)。该模块通过常量定义了三个独立的流动性池(分别是 USDT 与 N2DR、APT 和 WETH 的交易对),并引入了一个新的分派函数 get_supply,该函数能根据传入的代币标识返回对应池子的储备金数据。原有的 calculate_swap 函数被复用,以处理所有这些池子的兑-换计算。整个系统的正确性由一个全面的单元测试来验证,该测试依次获取并验证每个池子的数据,然后对每个池子执行兑换计算并断言其输出结果的正确性,从而展示了一种更模块化和可扩展的多交易对处理方法。

测试

➜ aptos move test
INCLUDING DEPENDENCY AptosFramework
INCLUDING DEPENDENCY AptosStdlib
INCLUDING DEPENDENCY MoveStdlib
BUILDING my-dapp
Running Move unit tests
[debug] 41
[debug] 3201
[debug] 312
[debug] "Swap USDT for:"
[debug] "N2DR Rewards"
[debug] 41
[debug] 124700
[debug] 21500
[debug] "Swap USDT for:"
[debug] "Aptos"
[debug] 81
[debug] 2750000
[debug] 1310
[debug] "Swap USDT for:"
[debug] "Wrapped Ether"
[debug] 1
[ PASS    ] 0x48daaa7d16f1a59929ece03157b8f3e4625bc0a201988ac6fea9b38f50db5ef3::Sample9::test_function
Test result: OK. Total tests: 1; passed: 1; failed: 0
{
  "Result": "Success"
}

这个测试结果表明,你的 my-dapp 项目已成功通过了其全面的单元测试。在成功编译项目后,测试框架执行了 Sample9 模块中定义的唯一但包含了多个步骤的测试函数 test_function。日志中详细的 [debug] 输出,依次展示了针对 N2DR、Aptos 和 Wrapped Ether 这三个不同流动性池的验证过程:它首先打印出池子的储备金数据,然后打印出模拟兑换后计算出的结果(分别为 41811)。最终的 [ PASS ] 状态确认了该测试函数顺利完成,意味着其内部所有 assert! 断言均已满足,既验证了 get_supply 函数能为每个池子返回正确数据,也验证了 calculate_swap 函数对每个池子的计算结果都准确无误。

示例三

module net2dev_addr::Sample9 {

    /// Error code indicating that the provided amount is not enough for the swap.
    const E_NOT_ENOUGH: u64 = 0;

    const N2DR: u64 = 1;
    const APT: u64 = 2;
    const WETH: u64 = 3;

    const Pool1_n2dr: u64 = 312;
    const Pool1_usdt: u64 = 3201;
    const N2DR_name: vector<u8> = b"N2DR Rewards";

    const Pool2_apt: u64 = 21500;
    const Pool2_usdt: u64 = 124700;
    const APT_name: vector<u8> = b"Aptos";

    const Pool3_weth: u64 = 1310;
    const Pool3_usdt: u64 = 2750000;
    const WETH_name: vector<u8> = b"Wrapped Ether";

    fun get_supply(coin_symbol: u64): (u64, u64, vector<u8>) {
        if (coin_symbol == N2DR) {
            (Pool1_usdt, Pool1_n2dr, N2DR_name)
        } else if (coin_symbol == APT) {
            (Pool2_usdt, Pool2_apt, APT_name)
        } else {
            (Pool3_usdt, Pool3_weth, WETH_name)
        }
    }

    fun token_price(coin1: u64, coin2: u64): u64 {
        assert!(coin1 > 0, E_NOT_ENOUGH);
        assert!(coin2 > 0, E_NOT_ENOUGH);
        coin1 / coin2
    }

    // This applies a 5% fee to each swap tx.
    fun calculate_swap(coin1: u64, coin2: u64, coin1_amount: u64): u64 {
        assert!(coin1_amount > 0, E_NOT_ENOUGH);

        let fee = coin1_amount * 5 / 100;
        let mix_supply = coin1 * coin2;
        let new_usdt = coin1 + coin1_amount;
        let new_n2dr = mix_supply / (new_usdt - fee);
        let receive = coin2 - new_n2dr;
        receive
    }

    #[test_only]
    use std::debug::print;
    #[test_only]
    use std::string::utf8;

    #[test]
    fun test_function() {
        let swap_amount = 495; // USDT to swap
        let receive = calculate_swap(Pool1_usdt, Pool1_n2dr, swap_amount);
        print(&receive);
        assert!(receive == 41, 1);

        let (coin1, coin2, name) = get_supply(N2DR);
        print(&coin1);
        print(&coin2);

        assert!(coin1 == Pool1_usdt, 2);
        assert!(coin2 == Pool1_n2dr, 3);
        assert!(name == N2DR_name, 4);
        print(&utf8(b"Swap USDT for:"));
        print(&utf8(name));
        let result = calculate_swap(coin1, coin2, swap_amount);
        print(&result);
        assert!(result == receive, 5);

        let (coin1, coin2, name) = get_supply(APT);
        print(&coin1);
        print(&coin2);

        assert!(coin1 == Pool2_usdt, 6);
        assert!(coin2 == Pool2_apt, 7);
        assert!(name == APT_name, 8);
        print(&utf8(b"Swap USDT for:"));
        print(&utf8(name));
        let result = calculate_swap(coin1, coin2, swap_amount);
        print(&result);
        assert!(result == 81, 9);

        let (coin1, coin2, name) = get_supply(WETH);
        print(&coin1);
        print(&coin2);

        assert!(coin1 == Pool3_usdt, 10);
        assert!(coin2 == Pool3_weth, 11);
        assert!(name == WETH_name, 12);
        print(&utf8(b"Swap USDT for:"));
        print(&utf8(name));
        let result = calculate_swap(coin1, coin2, swap_amount);
        print(&result);
        assert!(result == 1, 13);
    }

    #[test]
    fun test_function2() {
        let (coin1, coin2, name) = get_supply(N2DR);
        let swap_amount = 512; // USDT to swap
        print(&utf8(b"Swap USDT for:"));
        print(&utf8(name));

        print(&utf8(b"Token Price Before Swap:"));
        let price_before = token_price(coin1, coin2);
        print(&price_before);

        let result = calculate_swap(coin1, coin2, swap_amount);
        print(&result);

        print(&utf8(b"Token Price After Swap:"));
        let coin1_after = coin1 + swap_amount;
        let coin2_after = coin2 - result;
        let price_after = token_price(coin1_after, coin2_after);
        print(&price_after);

        assert!(price_after > price_before, 13);

        let (coin1, coin2, name) = get_supply(APT);
        let swap_amount = 2340; // USDT to swap
        print(&utf8(b"Swap USDT for:"));
        print(&utf8(name));

        print(&utf8(b"Token Price Before Swap:"));
        let price_before = token_price(coin1, coin2);
        print(&price_before);

        let result = calculate_swap(coin1, coin2, swap_amount);
        print(&utf8(b"Swap Result:"));
        print(&result);

        print(&utf8(b"Token Price After Swap:"));
        let coin1_after = coin1 + swap_amount;
        let coin2_after = coin2 - result;
        let price_after = token_price(coin1_after, coin2_after);
        print(&price_after);
    }

    /*

    Liquidity Pool

    Coin1 = 3201 USDT
    Coin2 = 312 N2DR

    495 USDT -> N2DR

    FORMULA with 5% fee

    Value1 = Apply a 5% fee to the USDT amount to be swapped.
    Fee: 495 * 5 / 100 = 24.75

    Value2 = Multiply both USDT and N2DR Supply.
    MixSupply: Coin1 * Coin2 = 998,712

    Value3 = Determine the new supply of USDT after the swap.
    NewUSDT = Coin1 + 495 = 3201 + 495 = 3696

    Value4 = Determine the new supply of N2DR after the swap.
    NewN2DR = MixSupply / (NewUSDT - fee) = 998,712 / (3696 - 24.75) = 271.5879458

    Value5 = Determine the amount of N2DRs to transfer to the user.
    Transfer = Coin2 - NewN2DR = 312 - 271.5879458 = 40.4120542

    */
}

这段 Aptos Move 代码定义了一个名为 Sample9 的模块,它模拟了一个功能有所增强的多池去中心化交易所(DEX)。在原有的 calculate_swapget_supply 函数基础上,该模块新增了一个简单的 token_price 价格计算函数,用于通过基础除法来估算池中两种代币的相对价格。其单元测试也扩展为了两个独立的函数:第一个 test_function 负责验证在所有三个预定义流动性池(USDT 分别与 N2DR、APT 和 WETH 配对)中的兑换计算是否准确;而新增的 test_function2 则演示了更完整的兑换分析,它通过在模拟交易前后分别调用 token_price 函数,来展示单笔交易对流动性池价格产生的冲击,并断言价格如预期般发生了变化。

测试

➜ aptos move test
INCLUDING DEPENDENCY AptosFramework
INCLUDING DEPENDENCY AptosStdlib
INCLUDING DEPENDENCY MoveStdlib
BUILDING my-dapp
Running Move unit tests
[debug] 41
[debug] 3201
[debug] 312
[debug] "Swap USDT for:"
[debug] "N2DR Rewards"
[debug] 41
[debug] 124700
[debug] 21500
[debug] "Swap USDT for:"
[debug] "Aptos"
[debug] 81
[debug] 2750000
[debug] 1310
[debug] "Swap USDT for:"
[debug] "Wrapped Ether"
[debug] 1
[ PASS    ] 0x48daaa7d16f1a59929ece03157b8f3e4625bc0a201988ac6fea9b38f50db5ef3::Sample9::test_function
[debug] "Swap USDT for:"
[debug] "N2DR Rewards"
[debug] "Token Price Before Swap:"
[debug] 10
[debug] 42
[debug] "Token Price After Swap:"
[debug] 13
[debug] "Swap USDT for:"
[debug] "Aptos"
[debug] "Token Price Before Swap:"
[debug] 5
[debug] "Swap Result:"
[debug] 377
[debug] "Token Price After Swap:"
[debug] 6
[ PASS    ] 0x48daaa7d16f1a59929ece03157b8f3e4625bc0a201988ac6fea9b38f50db5ef3::Sample9::test_function2
Test result: OK. Total tests: 2; passed: 2; failed: 0
{
  "Result": "Success"
}

这个测试结果表明,你的 my-dapp 项目完美地通过了其全部单元测试。在成功编译项目后,测试框架执行了 Sample9 模块中定义的 test_functiontest_function2 两个测试函数。日志中的 [debug] 输出清晰地展示了两个测试的详细过程:第一个 test_function 的输出验证了在三个不同流动性池中的基础兑换计算结果(41, 81, 1)均准确无误;而第二个 test_function2 的输出则更进一步,它打印出了在两次不同交易中,代币价格交易前(如 105)和交易后(如 136)的变化,其 [ PASS ] 状态证明了 assert! 断言成功确认了交易对池子价格产生了预期的冲击。最终 Test result: OK 的总结陈述,证明了你合约中所有的兑换和价格影响逻辑都已通过验证。

总结

恭喜你!通过本篇详尽的实战教程,你不仅成功地在 Aptos Move 中实现了一个功能完备的流动性池兑换逻辑,更重要的是,你掌握了从单一功能到模块化、可扩展系统演进的设计思路。

我们从一个基础的 AMM 公式出发,逐步构建了一个能够处理多个交易对的灵活模块,并学会了如何分析和测试交易行为对池子价格产生的具体影响。最终,测试日志中每一条 [ PASS ] 记录,都是对我们严谨逻辑的最好肯定。

现在,你已经具备了构建 DEX 核心算法的能力,并为在 Aptos DeFi 生态中创造更复杂的金融应用打下了坚实的基础。

参考

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论