写给 Web3 小白:一文看懂 AMM 原理与极简代码实现

目录

[一、什么是 AMM?------ 不用 "中介" 的交易魔法](#一、什么是 AMM?—— 不用 “中介” 的交易魔法)

[二、AMM 的核心公式 ------x*y=k 背后的数学智慧](#二、AMM 的核心公式 ——x*y=k 背后的数学智慧)

用实例理解交易过程:

[场景 1:用 USDC 买 ETH](#场景 1:用 USDC 买 ETH)

[场景 2:用 ETH 卖 USDC](#场景 2:用 ETH 卖 USDC)

为什么这个公式能工作?

[三、流动性提供者(LP)------ 交易所的 "供应商"](#三、流动性提供者(LP)—— 交易所的 “供应商”)

[四、极简 AMM 合约实现 ------ 用 Solidity 写一个迷你 Uniswap](#四、极简 AMM 合约实现 —— 用 Solidity 写一个迷你 Uniswap)

五、代码核心逻辑解析

[1. 初始化资金池(constructor)](#1. 初始化资金池(constructor))

[2. 添加流动性(addLiquidity)](#2. 添加流动性(addLiquidity))

[3. 移除流动性(removeLiquidity)](#3. 移除流动性(removeLiquidity))

[4. 代币兑换(swapAForB/swapBForA)](#4. 代币兑换(swapAForB/swapBForA))

[六、AMM 的优缺点与真实世界的进化](#六、AMM 的优缺点与真实世界的进化)

优点:

缺点:

真实世界的优化:

[七、总结:AMM 是 DeFi 的 "心脏"](#七、总结:AMM 是 DeFi 的 “心脏”)


如果你刚接触去中心化交易所(DEX),可能会好奇:"没有中介撮合交易,数字资产是怎么成交的?" 答案就藏在 "自动做市商"(AMM)这个神奇的机制里。今天我们用大白话拆解 AMM 的工作原理,最后还会手把手实现一个简化版的 AMM 智能合约,让你从理论到实践彻底搞懂这个 DeFi 世界的核心引擎。

一、什么是 AMM?------ 不用 "中介" 的交易魔法

传统交易所(比如股票市场或中心化加密交易所)靠 "订单簿" 匹配买卖双方:买家挂 "我想花 100 元买 1 个 ETH",卖家挂 "我想以 100 元卖 1 个 ETH",系统撮合两者成交。但这种模式需要足够多的人同时挂单,否则会出现 "买不到" 或 "卖不出" 的情况。

AMM(自动做市商) 则完全不同:它不需要订单簿,而是通过一个 "资金池" 实现即时交易。任何人都可以向资金池注入两种代币(比如 ETH 和 USDC)成为 "流动性提供者",而交易者直接和资金池交易 ------ 想买 ETH 就往池子里塞 USDC,想卖 ETH 就从池子里换 USDC。

举个生活化的例子:

AMM 就像一个无人超市,货架上永远有 ETH 和 USDC 两种商品。超市的规则是 "两种商品的总价值乘积保持不变"。你买走 1 个 ETH,就得按规则补上相应的 USDC;你卖掉 1 个 ETH,超市会按规则找给你相应的 USDC。这个 "不变的乘积" 就是 AMM 的核心密码。

二、AMM 的核心公式 ------x*y=k 背后的数学智慧

目前主流的 AMM(如 Uniswap V2)都基于一个简单的数学公式:

x * y = k

其中:

  • x = 资金池中的代币 A 数量(比如 ETH)
  • y = 资金池中的代币 B 数量(比如 USDC)
  • k = 常数(x 和 y 的乘积永远不变)

这个公式保证了交易前后,资金池的 "总价值乘积" 恒定,从而自动计算出交易价格。

用实例理解交易过程:

假设资金池初始有:10 ETH 和 10000 USDC(此时 k=10*10000=100000)

场景 1:用 USDC 买 ETH

如果你想花 1000 USDC 买 ETH,交易后需要满足 x' * y' = k=100000。

  • 注入 1000 USDC 后,y 变为 10000+1000=11000
  • 此时 x' = k /y' = 100000 / 11000 ≈ 9.0909 ETH
  • 你能买到的 ETH = 初始 x - x' = 10 - 9.0909 ≈ 0.9091 ETH

交易后资金池变为:9.0909 ETH 和 11000 USDC,乘积依然是 100000。

场景 2:用 ETH 卖 USDC

如果你想卖 1 ETH 换 USDC,同样满足 x' * y' = 100000。

  • 注入 1 ETH 后,x 变为 10+1=11
  • 此时 y' = k /x' = 100000 / 11 ≈ 9090.91 USDC
  • 你能换到的 USDC = 初始 y - y' = 10000 - 9090.91 ≈ 909.09 USDC

交易后资金池变为:11 ETH 和 9090.91 USDC,乘积还是 100000。

为什么这个公式能工作?

  • 自动定价:交易价格由 x 和 y 的比例决定(比如初始 1 ETH = 1000 USDC,因为 10000/10=1000)。
  • 滑点现象:单笔交易越大,价格偏差越大(比如买 10 ETH 会比买 1 ETH 的单价高),这就是 "滑点"。
  • 流动性越深,滑点越小:如果资金池有 1000 ETH 和 100 万 USDC,买 1 ETH 的滑点几乎可以忽略。

三、流动性提供者(LP)------ 交易所的 "供应商"

资金池里的代币从哪里来?来自 "流动性提供者"(LP)。

  • 你可以向池子里同时注入 ETH 和 USDC(按当前比例),成为 LP。
  • 作为回报,你会获得 "流动性代币"(LP 代币),代表你在资金池中的份额。
  • 交易者每笔交易需要支付 0.3% 的手续费(不同 AMM 比例不同),这些手续费会按比例分给所有 LP。
  • 当你想退出时,用 LP 代币换回对应的 ETH 和 USDC(包括累计的手续费)。

四、极简 AMM 合约实现 ------ 用 Solidity 写一个迷你 Uniswap

理解了原理,我们来实现一个简化版的 AMM 合约。这个合约包含核心功能:添加流动性、移除流动性、代币兑换。

复制代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

// 引入ERC20接口,用于与代币交互
// interface IERC20 {
//     // 转账函数
//     function transfer(address to, uint256 amount) external returns (bool);
//     // 授权函数
//     function approve(address spender, uint256 amount) external returns (bool);
//     // 从授权地址转账
//     function transferFrom(address from, address to, uint256 amount) external returns (bool);
//     // 查询余额
//     function balanceOf(address account) external view returns (uint256);
// }

contract SimpleAMM {
    // 两种代币的地址
    address public tokenA;
    address public tokenB;
    
    // 记录资金池中的代币数量(储备量)
    uint256 public reserveA;
    uint256 public reserveB;
    
    // 流动性代币的总量(LP代币)
    uint256 public totalSupply;
    // 记录每个地址拥有的LP代币数量
    mapping(address => uint256) public balanceOf;

    // 部署合约时指定两种代币
    constructor(address _tokenA, address _tokenB) {
        require(_tokenA != address(0) && _tokenB != address(0), "invalid token address");
        tokenA = _tokenA;
        tokenB = _tokenB;
    }

    // 计算LP代币数量(简化版:按注入的代币比例分配)
    function _mintLPTokens(address to, uint256 amount) private {
        balanceOf[to] += amount;
        totalSupply += amount;
    }

    // 销毁LP代币
    function _burnLPTokens(address from, uint256 amount) private {
        balanceOf[from] -= amount;
        totalSupply -= amount;
    }

    // 更新资金池储备量(从合约余额读取)
    function _updateReserves() private {
        reserveA = IERC20(tokenA).balanceOf(address(this));
        reserveB = IERC20(tokenB).balanceOf(address(this));
    }

    // 1. 添加流动性
    function addLiquidity(uint256 amountA, uint256 amountB) external {
        // 转移用户的代币到合约
        IERC20(tokenA).transferFrom(msg.sender, address(this), amountA);
        IERC20(tokenB).transferFrom(msg.sender, address(this), amountB);

        // 更新储备量
        _updateReserves();

        // 计算LP代币数量:首次添加时,LP数量等于注入的代币A数量(简化处理)
        uint256 lpAmount;
        if (totalSupply == 0) {
            // 首次添加,按amountA计算LP数量
            lpAmount = amountA;
        } else {
            // 非首次添加,按注入比例计算(取较小值避免比例失衡)
            lpAmount = (amountA * totalSupply) / reserveA;
        }

        //  mint LP代币给用户
        _mintLPTokens(msg.sender, lpAmount);
    }

    // 2. 移除流动性
    function removeLiquidity(uint256 lpAmount) external {
        require(balanceOf[msg.sender] >= lpAmount, "LP token not enough");

        // 计算能取出的代币数量(按LP份额比例)
        uint256 amountA = (lpAmount * reserveA) / totalSupply;
        uint256 amountB = (lpAmount * reserveB) / totalSupply;

        // 销毁LP代币
        _burnLPTokens(msg.sender, lpAmount);
        // 转移代币给用户
        IERC20(tokenA).transfer(msg.sender, amountA);
        IERC20(tokenB).transfer(msg.sender, amountB);
        // 更新储备量
        _updateReserves();
    }

    // 3. 用tokenA兑换tokenB(核心交易逻辑)
    function swapAForB(uint256 amountAIn) external returns (uint256 amountBOut) {
        require(amountAIn > 0, "amountAIn = 0");

        // 收取0.3%的手续费(简化版:直接扣除)
        uint256 amountAInWithFee = amountAIn * 997; // 997/1000 = 0.3%手续费

        // 转移用户的tokenA到合约
        IERC20(tokenA).transferFrom(msg.sender, address(this), amountAIn);
        _updateReserves();

        // 计算能兑换的tokenB数量(基于x*y=k)
        // 新的reserveA = (reserveA * 1000 + amountAInWithFee) / 1000(因为手续费按比例计入池)
        uint256 numerator = amountAInWithFee * reserveB;
        uint256 denominator = (reserveA * 1000) + amountAInWithFee;
        amountBOut = numerator / denominator;

        require(amountBOut > 0, "amountBOut = 0");

        // 转移tokenB给用户
        IERC20(tokenB).transfer(msg.sender, amountBOut);
        // 更新储备量
        _updateReserves();
    }

    // 4. 用tokenB兑换tokenA(逻辑与swapAForB对称)
    function swapBForA(uint256 amountBIn) external returns (uint256 amountAOut) {
        require(amountBIn > 0, "amountBIn = 0");

        uint256 amountBInWithFee = amountBIn * 997;

        IERC20(tokenB).transferFrom(msg.sender, address(this), amountBIn);
        _updateReserves();

        uint256 numerator = amountBInWithFee * reserveA;
        uint256 denominator = (reserveB * 1000) + amountBInWithFee;
        amountAOut = numerator / denominator;

        require(amountAOut > 0, "amountAOut = 0");

        IERC20(tokenA).transfer(msg.sender, amountAOut);
        _updateReserves();
    }
}

五、代码核心逻辑解析

这个简化版合约实现了 AMM 的 4 个核心功能,我们逐一拆解:

1. 初始化资金池(constructor)

部署合约时指定两种代币(比如 ETH 和 USDC),合约会成为这两种代币的资金池载体。

2. 添加流动性(addLiquidity)

  • 用户需要按当前资金池的比例(reserveA:reserveB)注入两种代币(比如池里 1 ETH:1000 USDC,你就需要按 1:1000 的比例注入)。
  • 合约会发行 "LP 代币" 给用户,代表用户在资金池中的份额(类似 "股权证明")。
  • 首次添加流动性时,LP 代币数量直接等于注入的 tokenA 数量(简化处理);后续添加则按比例计算。

3. 移除流动性(removeLiquidity)

  • 用户销毁持有的 LP 代币,按份额比例取回对应的两种代币。
  • 比如你持有 10% 的 LP 代币,就能取回资金池中 10% 的 ETH 和 10% 的 USDC(包括累计的手续费)。

4. 代币兑换(swapAForB/swapBForA)

这是 AMM 的核心功能

  • 用户注入代币 A,合约根据公式计算能兑换的代币 B 数量。
  • 收取 0.3% 的手续费(代码中用 997/1000 简化处理),手续费会留在资金池,最终由 LP 按份额分配。
  • 交易后自动更新资金池的储备量,保证 x*y=k 始终成立。

六、AMM 的优缺点与真实世界的进化

优点:

  • 无需中介:任何人都能随时交易,不需要等待对手方。
  • 去中心化:没有中央机构控制,资金由智能合约托管。
  • 全民参与:任何人都能成为流动性提供者,赚取手续费。

缺点:

  • 滑点问题:大额交易可能导致价格大幅波动。
  • 无常损失:当两种代币的市场价格比例变化时,LP 可能会产生 "账面亏损"(相比单纯持有代币)。
  • 资金效率低:大部分资金闲置,只为应对极端行情。

真实世界的优化:

主流 AMM(如 Uniswap V3、Curve)都在解决这些问题:

  • Uniswap V3 引入 "集中流动性",允许 LP 将资金集中在特定价格区间,提高资金效率。
  • Curve 专注于稳定币交易,采用更复杂的公式减少滑点。
  • 部分 AMM 加入预言机价格喂价,防止极端价格操纵。

七、总结:AMM 是 DeFi 的 "心脏"

AMM 用一个简单的数学公式(x*y=k)解决了去中心化交易的核心问题,让任何人在任何时间都能自由交易数字资产。它不仅是 DEX 的核心引擎,还支撑了借贷、衍生品等整个 DeFi 生态的运转。

如果你是开发者,建议基于上面的简化合约动手实践,尝试添加手续费分配、无常损失计算等功能;如果你是用户,下次在 DEX 交易时,不妨看看资金池的深度和滑点,理解每一笔交易背后的数学逻辑。

相关推荐
+电报dapp1292 小时前
2025区块链革命:当乐高式公链遇见AI预言机,三大行业已被颠覆
人工智能·金融·web3·去中心化·区块链·哈希算法·零知识证明
测试人社区-浩辰2 小时前
AI与区块链结合的测试验证方法
大数据·人工智能·分布式·后端·opencv·自动化·区块链
程序员_大白2 小时前
区块链部署与运维,零基础入门到精通,收藏这篇就够了
运维·c语言·开发语言·区块链
0x派大星2 小时前
区块链中的数字签名:安全性与可信度的核心
区块链·密码学
Light602 小时前
区块链赋能档案管理革命:构建不可篡改的数字记忆基石
区块链·电子档案·真实性保障·单套制·协同管理
木头程序员2 小时前
去中心化AI数据共识难题破解:区块链、联邦学习与数据确权的协同之道
人工智能·去中心化·区块链
墨夶2 小时前
Java冷热钱包:不是所有钱包都叫“双保险“!用户资产安全的终极守护者
java·安全·区块链
老蒋每日coding4 小时前
FISCO BCOS 部署Solidity投票智能合约并基于Java SDK 调用智能合约详细指南
java·区块链·智能合约
傻小胖8 小时前
6.BTC-网络-北大肖臻老师客堂笔记
笔记·web3·区块链