去中心化交易所 #

一、DEX概述 #

1.1 AMM机制 #

自动做市商(AMM)使用数学公式确定价格,无需订单簿。

text
恒定乘积公式:x * y = k

x: 代币A数量
y: 代币B数量
k: 恒定乘积

1.2 核心概念 #

概念 说明
流动性池 代币对的储备池
LP代币 流动性提供者凭证
手续费 交易手续费
价格滑点 价格变化幅度

二、简单DEX实现 #

2.1 基础合约 #

solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

contract SimpleDEX is ERC20 {
    IERC20 public token0;
    IERC20 public token1;
    
    uint256 public constant FEE_NUMERATOR = 997;
    uint256 public constant FEE_DENOMINATOR = 1000;
    
    constructor(address _token0, address _token1) ERC20("LP Token", "LP") {
        token0 = IERC20(_token0);
        token1 = IERC20(_token1);
    }
    
    function addLiquidity(
        uint256 amount0,
        uint256 amount1
    ) public returns (uint256 liquidity) {
        token0.transferFrom(msg.sender, address(this), amount0);
        token1.transferFrom(msg.sender, address(this), amount1);
        
        uint256 totalSupply_ = totalSupply();
        
        if (totalSupply_ == 0) {
            liquidity = sqrt(amount0 * amount1);
        } else {
            liquidity = min(
                (amount0 * totalSupply_) / token0.balanceOf(address(this)),
                (amount1 * totalSupply_) / token1.balanceOf(address(this))
            );
        }
        
        require(liquidity > 0, "Insufficient liquidity");
        _mint(msg.sender, liquidity);
    }
    
    function removeLiquidity(uint256 liquidity) public returns (uint256 amount0, uint256 amount1) {
        uint256 totalSupply_ = totalSupply();
        
        amount0 = (liquidity * token0.balanceOf(address(this))) / totalSupply_;
        amount1 = (liquidity * token1.balanceOf(address(this))) / totalSupply_;
        
        _burn(msg.sender, liquidity);
        
        token0.transfer(msg.sender, amount0);
        token1.transfer(msg.sender, amount1);
    }
    
    function swap(
        address tokenIn,
        uint256 amountIn,
        uint256 amountOutMin
    ) public returns (uint256 amountOut) {
        require(tokenIn == address(token0) || tokenIn == address(token1), "Invalid token");
        
        IERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn);
        
        bool isToken0 = tokenIn == address(token0);
        
        (IERC20 tokenIn_, IERC20 tokenOut_) = isToken0 
            ? (token0, token1) 
            : (token1, token0);
        
        uint256 reserveIn = tokenIn_.balanceOf(address(this));
        uint256 reserveOut = tokenOut_.balanceOf(address(this));
        
        amountOut = getAmountOut(amountIn, reserveIn, reserveOut);
        
        require(amountOut >= amountOutMin, "Slippage too high");
        
        tokenOut_.transfer(msg.sender, amountOut);
    }
    
    function getAmountOut(
        uint256 amountIn,
        uint256 reserveIn,
        uint256 reserveOut
    ) public pure returns (uint256) {
        uint256 amountInWithFee = (amountIn * FEE_NUMERATOR) / FEE_DENOMINATOR;
        return (amountInWithFee * reserveOut) / (reserveIn + amountInWithFee);
    }
    
    function sqrt(uint256 y) internal pure returns (uint256 z) {
        if (y > 3) {
            z = y;
            uint256 x = y / 2 + 1;
            while (x < z) {
                z = x;
                x = (y / x + x) / 2;
            }
        } else if (y != 0) {
            z = 1;
        }
    }
    
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }
}

三、价格计算 #

3.1 恒定乘积公式 #

solidity
// x * y = k
// 价格 = y / x

function getPrice(
    uint256 reserveIn,
    uint256 reserveOut
) public pure returns (uint256) {
    return (reserveOut * 1e18) / reserveIn;
}

3.2 滑点计算 #

solidity
function calculateSlippage(
    uint256 amountIn,
    uint256 reserveIn,
    uint256 reserveOut
) public pure returns (uint256) {
    uint256 amountOut = getAmountOut(amountIn, reserveIn, reserveOut);
    uint256 spotPrice = reserveOut / reserveIn;
    uint256 effectivePrice = amountOut / amountIn;
    
    return ((spotPrice - effectivePrice) * 100) / spotPrice;
}

四、最佳实践 #

4.1 安全考虑 #

考虑点 说明
重入保护 使用ReentrancyGuard
滑点保护 设置最小输出
价格操纵 使用TWAP预言机
闪电贷攻击 实施保护措施

4.2 Gas优化 #

solidity
// 使用缓存减少存储读取
function swapOptimized(
    address tokenIn,
    uint256 amountIn,
    uint256 amountOutMin
) public returns (uint256 amountOut) {
    uint256 reserve0 = token0.balanceOf(address(this));
    uint256 reserve1 = token1.balanceOf(address(this));
    
    // 使用缓存的值进行计算
    // ...
}

五、总结 #

DEX要点:

功能 说明
AMM 自动做市商机制
流动性 添加和移除流动性
交易 代币交换
价格 恒定乘积公式

恭喜你完成了Solidity智能合约完全指南的学习!

最后更新:2026-03-27