HiHuo
首页
博客
手册
工具
关于
首页
博客
手册
工具
关于
  • Web3 完整技术体系

    • Web3 完整技术体系
    • 区块链基础与密码学
    • 第一章:比特币原理与实现
    • 第二章:以太坊架构与核心概念
    • Solidity智能合约开发基础
    • 04-Solidity进阶与安全
    • 05-ERC标准详解
    • 06-DeFi核心协议-去中心化交易所
    • 07-DeFi核心协议-借贷协议
    • 08-DeFi核心协议-稳定币
    • NFT与元宇宙
    • Layer2扩容方案
    • 跨链技术
    • Web3前端开发
    • 链下数据与预言机
    • 智能合约测试与部署
    • MEV与交易优化
    • DAO治理
    • 项目实战:完整DeFi借贷协议

06-DeFi核心协议-去中心化交易所

第一部分:AMM自动做市商原理

1.1 什么是AMM

自动做市商(Automated Market Maker, AMM)是一种去中心化交易所的核心机制,通过智能合约和数学公式自动提供流动性和定价,无需传统的订单簿。

1.1.1 传统订单簿 vs AMM

传统订单簿:

  • 买方和卖方下单
  • 通过撮合引擎匹配订单
  • 需要足够的买卖双方
  • 流动性由做市商提供

AMM模式:

  • 流动性池提供资金
  • 通过数学公式自动定价
  • 任何人都可以成为流动性提供者
  • 无需撮合,即时交易

1.2 恒定乘积做市商(Constant Product Market Maker)

Uniswap v2采用的核心公式:

x * y = k

其中:

  • x: 代币A的数量
  • y: 代币B的数量
  • k: 常数(在没有流动性变化时保持不变)

1.2.1 价格计算

// 假设池中有 1000 ETH 和 2,000,000 USDT
// x = 1000, y = 2,000,000, k = 2,000,000,000

// 价格 = y / x = 2,000,000 / 1000 = 2000 USDT/ETH

1.2.2 交易过程

用户用100 ETH购买USDT:

// 交易前: x = 1000, y = 2,000,000
// 交易后: x = 1100 (增加100 ETH)

// 根据 x * y = k
// 1100 * y_new = 2,000,000,000
// y_new = 1,818,181.82 USDT

// 用户获得: 2,000,000 - 1,818,181.82 = 181,818.18 USDT
// 平均价格: 181,818.18 / 100 = 1818.18 USDT/ETH

1.3 滑点(Slippage)

滑点是指实际成交价格与预期价格之间的差异。

// 滑点 = (预期价格 - 实际价格) / 预期价格 * 100%

// 上例中:
// 预期价格: 2000 USDT/ETH
// 实际价格: 1818.18 USDT/ETH
// 滑点: (2000 - 1818.18) / 2000 = 9.09%

交易量越大,滑点越大。

1.4 无常损失(Impermanent Loss)

流动性提供者面临的主要风险。

示例:

初始状态:
- 1 ETH = 2000 USDT
- 提供 10 ETH + 20,000 USDT
- 总价值 = 40,000 USDT

价格变化后:
- 1 ETH = 4000 USDT
- 池中: 7.07 ETH + 28,284 USDT (k保持不变)
- 总价值 = 56,568 USDT

如果持有:
- 10 ETH + 20,000 USDT = 60,000 USDT

无常损失 = (60,000 - 56,568) / 60,000 = 5.72%

第二部分:Uniswap v2实现

2.1 核心合约架构

Uniswap v2主要包含三个核心合约:

  1. UniswapV2Factory: 创建和管理交易对
  2. UniswapV2Pair: 交易对合约(流动性池)
  3. UniswapV2Router: 路由合约(处理用户交互)

2.2 UniswapV2Pair合约

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

/**
 * @title UniswapV2Pair
 * @dev Uniswap V2交易对合约的简化实现
 */
contract UniswapV2Pair {
    // ERC20代币接口
    IERC20 public token0;
    IERC20 public token1;

    // 储备量
    uint112 private reserve0;
    uint112 private reserve1;
    uint32 private blockTimestampLast;

    // 累计价格(用于预言机)
    uint256 public price0CumulativeLast;
    uint256 public price1CumulativeLast;

    // LP代币
    uint256 public totalSupply;
    mapping(address => uint256) public balanceOf;

    // 最小流动性(锁定在零地址)
    uint256 public constant MINIMUM_LIQUIDITY = 10**3;

    // 事件
    event Mint(address indexed sender, uint256 amount0, uint256 amount1);
    event Burn(address indexed sender, uint256 amount0, uint256 amount1, address indexed to);
    event Swap(
        address indexed sender,
        uint256 amount0In,
        uint256 amount1In,
        uint256 amount0Out,
        uint256 amount1Out,
        address indexed to
    );
    event Sync(uint112 reserve0, uint112 reserve1);

    // 重入锁
    uint256 private unlocked = 1;
    modifier lock() {
        require(unlocked == 1, "LOCKED");
        unlocked = 0;
        _;
        unlocked = 1;
    }

    constructor(address _token0, address _token1) {
        token0 = IERC20(_token0);
        token1 = IERC20(_token1);
    }

    /**
     * @dev 获取储备量
     */
    function getReserves() public view returns (
        uint112 _reserve0,
        uint112 _reserve1,
        uint32 _blockTimestampLast
    ) {
        _reserve0 = reserve0;
        _reserve1 = reserve1;
        _blockTimestampLast = blockTimestampLast;
    }

    /**
     * @dev 更新储备量和价格累计值
     */
    function _update(uint256 balance0, uint256 balance1) private {
        require(balance0 <= type(uint112).max && balance1 <= type(uint112).max, "OVERFLOW");

        uint32 blockTimestamp = uint32(block.timestamp % 2**32);
        uint32 timeElapsed;
        unchecked {
            timeElapsed = blockTimestamp - blockTimestampLast;
        }

        if (timeElapsed > 0 && reserve0 != 0 && reserve1 != 0) {
            // 更新价格累计值
            unchecked {
                price0CumulativeLast += uint256(reserve1 / reserve0) * timeElapsed;
                price1CumulativeLast += uint256(reserve0 / reserve1) * timeElapsed;
            }
        }

        reserve0 = uint112(balance0);
        reserve1 = uint112(balance1);
        blockTimestampLast = blockTimestamp;

        emit Sync(reserve0, reserve1);
    }

    /**
     * @dev 添加流动性
     */
    function mint(address to) external lock returns (uint256 liquidity) {
        (uint112 _reserve0, uint112 _reserve1, ) = getReserves();
        uint256 balance0 = token0.balanceOf(address(this));
        uint256 balance1 = token1.balanceOf(address(this));
        uint256 amount0 = balance0 - _reserve0;
        uint256 amount1 = balance1 - _reserve1;

        if (totalSupply == 0) {
            // 首次添加流动性
            liquidity = sqrt(amount0 * amount1) - MINIMUM_LIQUIDITY;
            _mint(address(0), MINIMUM_LIQUIDITY); // 锁定最小流动性
        } else {
            // 后续添加流动性
            liquidity = min(
                (amount0 * totalSupply) / _reserve0,
                (amount1 * totalSupply) / _reserve1
            );
        }

        require(liquidity > 0, "INSUFFICIENT_LIQUIDITY_MINTED");
        _mint(to, liquidity);

        _update(balance0, balance1);
        emit Mint(msg.sender, amount0, amount1);
    }

    /**
     * @dev 移除流动性
     */
    function burn(address to) external lock returns (uint256 amount0, uint256 amount1) {
        uint256 balance0 = token0.balanceOf(address(this));
        uint256 balance1 = token1.balanceOf(address(this));
        uint256 liquidity = balanceOf[address(this)];

        // 计算可提取的代币数量
        amount0 = (liquidity * balance0) / totalSupply;
        amount1 = (liquidity * balance1) / totalSupply;

        require(amount0 > 0 && amount1 > 0, "INSUFFICIENT_LIQUIDITY_BURNED");

        _burn(address(this), liquidity);

        // 转出代币
        _safeTransfer(address(token0), to, amount0);
        _safeTransfer(address(token1), to, amount1);

        balance0 = token0.balanceOf(address(this));
        balance1 = token1.balanceOf(address(this));

        _update(balance0, balance1);
        emit Burn(msg.sender, amount0, amount1, to);
    }

    /**
     * @dev 交换代币
     */
    function swap(
        uint256 amount0Out,
        uint256 amount1Out,
        address to
    ) external lock {
        require(amount0Out > 0 || amount1Out > 0, "INSUFFICIENT_OUTPUT_AMOUNT");
        (uint112 _reserve0, uint112 _reserve1, ) = getReserves();
        require(amount0Out < _reserve0 && amount1Out < _reserve1, "INSUFFICIENT_LIQUIDITY");

        // 转出代币
        if (amount0Out > 0) _safeTransfer(address(token0), to, amount0Out);
        if (amount1Out > 0) _safeTransfer(address(token1), to, amount1Out);

        // 获取新的余额
        uint256 balance0 = token0.balanceOf(address(this));
        uint256 balance1 = token1.balanceOf(address(this));

        uint256 amount0In = balance0 > _reserve0 - amount0Out
            ? balance0 - (_reserve0 - amount0Out)
            : 0;
        uint256 amount1In = balance1 > _reserve1 - amount1Out
            ? balance1 - (_reserve1 - amount1Out)
            : 0;

        require(amount0In > 0 || amount1In > 0, "INSUFFICIENT_INPUT_AMOUNT");

        // 验证恒定乘积公式(扣除0.3%手续费)
        {
            uint256 balance0Adjusted = (balance0 * 1000) - (amount0In * 3);
            uint256 balance1Adjusted = (balance1 * 1000) - (amount1In * 3);
            require(
                balance0Adjusted * balance1Adjusted >= uint256(_reserve0) * uint256(_reserve1) * (1000**2),
                "K"
            );
        }

        _update(balance0, balance1);
        emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);
    }

    /**
     * @dev 强制同步储备量
     */
    function sync() external lock {
        _update(token0.balanceOf(address(this)), token1.balanceOf(address(this)));
    }

    // LP代币相关函数
    function _mint(address to, uint256 value) private {
        totalSupply += value;
        balanceOf[to] += value;
    }

    function _burn(address from, uint256 value) private {
        balanceOf[from] -= value;
        totalSupply -= value;
    }

    function transfer(address to, uint256 value) external returns (bool) {
        balanceOf[msg.sender] -= value;
        balanceOf[to] += value;
        return true;
    }

    // 辅助函数
    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 x, uint256 y) internal pure returns (uint256) {
        return x < y ? x : y;
    }

    function _safeTransfer(address token, address to, uint256 value) private {
        (bool success, bytes memory data) = token.call(
            abi.encodeWithSignature("transfer(address,uint256)", to, value)
        );
        require(success && (data.length == 0 || abi.decode(data, (bool))), "TRANSFER_FAILED");
    }
}

2.3 UniswapV2Factory合约

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

/**
 * @title UniswapV2Factory
 * @dev 工厂合约,用于创建和管理交易对
 */
contract UniswapV2Factory {
    // 手续费接收地址
    address public feeTo;
    address public feeToSetter;

    // 交易对映射
    mapping(address => mapping(address => address)) public getPair;
    address[] public allPairs;

    event PairCreated(address indexed token0, address indexed token1, address pair, uint256);

    constructor(address _feeToSetter) {
        feeToSetter = _feeToSetter;
    }

    /**
     * @dev 创建交易对
     */
    function createPair(address tokenA, address tokenB) external returns (address pair) {
        require(tokenA != tokenB, "IDENTICAL_ADDRESSES");
        (address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
        require(token0 != address(0), "ZERO_ADDRESS");
        require(getPair[token0][token1] == address(0), "PAIR_EXISTS");

        // 创建新的交易对合约
        bytes memory bytecode = type(UniswapV2Pair).creationCode;
        bytes32 salt = keccak256(abi.encodePacked(token0, token1));
        assembly {
            pair := create2(0, add(bytecode, 32), mload(bytecode), salt)
        }

        // 初始化交易对
        UniswapV2Pair(pair).initialize(token0, token1);

        // 保存交易对地址
        getPair[token0][token1] = pair;
        getPair[token1][token0] = pair;
        allPairs.push(pair);

        emit PairCreated(token0, token1, pair, allPairs.length);
    }

    /**
     * @dev 设置手续费接收地址
     */
    function setFeeTo(address _feeTo) external {
        require(msg.sender == feeToSetter, "FORBIDDEN");
        feeTo = _feeTo;
    }

    /**
     * @dev 设置手续费管理员
     */
    function setFeeToSetter(address _feeToSetter) external {
        require(msg.sender == feeToSetter, "FORBIDDEN");
        feeToSetter = _feeToSetter;
    }

    /**
     * @dev 返回所有交易对数量
     */
    function allPairsLength() external view returns (uint256) {
        return allPairs.length;
    }
}

2.4 UniswapV2Router合约

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

/**
 * @title UniswapV2Router
 * @dev 路由合约,处理用户交互
 */
contract UniswapV2Router {
    address public immutable factory;
    address public immutable WETH;

    modifier ensure(uint256 deadline) {
        require(deadline >= block.timestamp, "EXPIRED");
        _;
    }

    constructor(address _factory, address _WETH) {
        factory = _factory;
        WETH = _WETH;
    }

    /**
     * @dev 添加流动性
     */
    function addLiquidity(
        address tokenA,
        address tokenB,
        uint256 amountADesired,
        uint256 amountBDesired,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    )
        external
        ensure(deadline)
        returns (
            uint256 amountA,
            uint256 amountB,
            uint256 liquidity
        )
    {
        // 创建交易对(如果不存在)
        if (IUniswapV2Factory(factory).getPair(tokenA, tokenB) == address(0)) {
            IUniswapV2Factory(factory).createPair(tokenA, tokenB);
        }

        // 计算最优添加数量
        (amountA, amountB) = _calculateLiquidityAmounts(
            tokenA,
            tokenB,
            amountADesired,
            amountBDesired,
            amountAMin,
            amountBMin
        );

        // 转入代币
        address pair = pairFor(tokenA, tokenB);
        _safeTransferFrom(tokenA, msg.sender, pair, amountA);
        _safeTransferFrom(tokenB, msg.sender, pair, amountB);

        // 铸造LP代币
        liquidity = IUniswapV2Pair(pair).mint(to);
    }

    /**
     * @dev 移除流动性
     */
    function removeLiquidity(
        address tokenA,
        address tokenB,
        uint256 liquidity,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    ) external ensure(deadline) returns (uint256 amountA, uint256 amountB) {
        address pair = pairFor(tokenA, tokenB);

        // 转入LP代币到交易对
        IUniswapV2Pair(pair).transferFrom(msg.sender, pair, liquidity);

        // 销毁LP代币,获得代币
        (uint256 amount0, uint256 amount1) = IUniswapV2Pair(pair).burn(to);

        // 排序代币
        (address token0, ) = sortTokens(tokenA, tokenB);
        (amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0);

        // 检查最小数量
        require(amountA >= amountAMin, "INSUFFICIENT_A_AMOUNT");
        require(amountB >= amountBMin, "INSUFFICIENT_B_AMOUNT");
    }

    /**
     * @dev 精确输入交换
     */
    function swapExactTokensForTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external ensure(deadline) returns (uint256[] memory amounts) {
        // 计算输出数量
        amounts = getAmountsOut(amountIn, path);
        require(amounts[amounts.length - 1] >= amountOutMin, "INSUFFICIENT_OUTPUT_AMOUNT");

        // 转入第一个代币
        _safeTransferFrom(
            path[0],
            msg.sender,
            pairFor(path[0], path[1]),
            amounts[0]
        );

        // 执行交换
        _swap(amounts, path, to);
    }

    /**
     * @dev 精确输出交换
     */
    function swapTokensForExactTokens(
        uint256 amountOut,
        uint256 amountInMax,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external ensure(deadline) returns (uint256[] memory amounts) {
        // 计算输入数量
        amounts = getAmountsIn(amountOut, path);
        require(amounts[0] <= amountInMax, "EXCESSIVE_INPUT_AMOUNT");

        // 转入第一个代币
        _safeTransferFrom(
            path[0],
            msg.sender,
            pairFor(path[0], path[1]),
            amounts[0]
        );

        // 执行交换
        _swap(amounts, path, to);
    }

    /**
     * @dev 执行交换
     */
    function _swap(
        uint256[] memory amounts,
        address[] memory path,
        address _to
    ) internal {
        for (uint256 i; i < path.length - 1; i++) {
            (address input, address output) = (path[i], path[i + 1]);
            (address token0, ) = sortTokens(input, output);
            uint256 amountOut = amounts[i + 1];

            (uint256 amount0Out, uint256 amount1Out) = input == token0
                ? (uint256(0), amountOut)
                : (amountOut, uint256(0));

            address to = i < path.length - 2 ? pairFor(output, path[i + 2]) : _to;

            IUniswapV2Pair(pairFor(input, output)).swap(
                amount0Out,
                amount1Out,
                to,
                new bytes(0)
            );
        }
    }

    /**
     * @dev 计算添加流动性的数量
     */
    function _calculateLiquidityAmounts(
        address tokenA,
        address tokenB,
        uint256 amountADesired,
        uint256 amountBDesired,
        uint256 amountAMin,
        uint256 amountBMin
    ) internal view returns (uint256 amountA, uint256 amountB) {
        (uint256 reserveA, uint256 reserveB) = getReserves(tokenA, tokenB);

        if (reserveA == 0 && reserveB == 0) {
            (amountA, amountB) = (amountADesired, amountBDesired);
        } else {
            uint256 amountBOptimal = quote(amountADesired, reserveA, reserveB);
            if (amountBOptimal <= amountBDesired) {
                require(amountBOptimal >= amountBMin, "INSUFFICIENT_B_AMOUNT");
                (amountA, amountB) = (amountADesired, amountBOptimal);
            } else {
                uint256 amountAOptimal = quote(amountBDesired, reserveB, reserveA);
                assert(amountAOptimal <= amountADesired);
                require(amountAOptimal >= amountAMin, "INSUFFICIENT_A_AMOUNT");
                (amountA, amountB) = (amountAOptimal, amountBDesired);
            }
        }
    }

    /**
     * @dev 计算输出数量
     */
    function getAmountsOut(uint256 amountIn, address[] memory path)
        public
        view
        returns (uint256[] memory amounts)
    {
        require(path.length >= 2, "INVALID_PATH");
        amounts = new uint256[](path.length);
        amounts[0] = amountIn;

        for (uint256 i; i < path.length - 1; i++) {
            (uint256 reserveIn, uint256 reserveOut) = getReserves(path[i], path[i + 1]);
            amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut);
        }
    }

    /**
     * @dev 计算输入数量
     */
    function getAmountsIn(uint256 amountOut, address[] memory path)
        public
        view
        returns (uint256[] memory amounts)
    {
        require(path.length >= 2, "INVALID_PATH");
        amounts = new uint256[](path.length);
        amounts[amounts.length - 1] = amountOut;

        for (uint256 i = path.length - 1; i > 0; i--) {
            (uint256 reserveIn, uint256 reserveOut) = getReserves(path[i - 1], path[i]);
            amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut);
        }
    }

    /**
     * @dev 根据输入计算输出
     */
    function getAmountOut(
        uint256 amountIn,
        uint256 reserveIn,
        uint256 reserveOut
    ) public pure returns (uint256 amountOut) {
        require(amountIn > 0, "INSUFFICIENT_INPUT_AMOUNT");
        require(reserveIn > 0 && reserveOut > 0, "INSUFFICIENT_LIQUIDITY");

        uint256 amountInWithFee = amountIn * 997; // 0.3% 手续费
        uint256 numerator = amountInWithFee * reserveOut;
        uint256 denominator = (reserveIn * 1000) + amountInWithFee;
        amountOut = numerator / denominator;
    }

    /**
     * @dev 根据输出计算输入
     */
    function getAmountIn(
        uint256 amountOut,
        uint256 reserveIn,
        uint256 reserveOut
    ) public pure returns (uint256 amountIn) {
        require(amountOut > 0, "INSUFFICIENT_OUTPUT_AMOUNT");
        require(reserveIn > 0 && reserveOut > 0, "INSUFFICIENT_LIQUIDITY");

        uint256 numerator = reserveIn * amountOut * 1000;
        uint256 denominator = (reserveOut - amountOut) * 997;
        amountIn = (numerator / denominator) + 1;
    }

    /**
     * @dev 报价函数
     */
    function quote(
        uint256 amountA,
        uint256 reserveA,
        uint256 reserveB
    ) public pure returns (uint256 amountB) {
        require(amountA > 0, "INSUFFICIENT_AMOUNT");
        require(reserveA > 0 && reserveB > 0, "INSUFFICIENT_LIQUIDITY");
        amountB = (amountA * reserveB) / reserveA;
    }

    // 辅助函数
    function pairFor(address tokenA, address tokenB) internal view returns (address pair) {
        (address token0, address token1) = sortTokens(tokenA, tokenB);
        pair = address(
            uint160(
                uint256(
                    keccak256(
                        abi.encodePacked(
                            hex"ff",
                            factory,
                            keccak256(abi.encodePacked(token0, token1)),
                            hex"96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f" // init code hash
                        )
                    )
                )
            )
        );
    }

    function sortTokens(address tokenA, address tokenB)
        internal
        pure
        returns (address token0, address token1)
    {
        require(tokenA != tokenB, "IDENTICAL_ADDRESSES");
        (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
        require(token0 != address(0), "ZERO_ADDRESS");
    }

    function getReserves(address tokenA, address tokenB)
        internal
        view
        returns (uint256 reserveA, uint256 reserveB)
    {
        (address token0, ) = sortTokens(tokenA, tokenB);
        (uint256 reserve0, uint256 reserve1, ) = IUniswapV2Pair(pairFor(tokenA, tokenB))
            .getReserves();
        (reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
    }

    function _safeTransferFrom(
        address token,
        address from,
        address to,
        uint256 value
    ) private {
        (bool success, bytes memory data) = token.call(
            abi.encodeWithSignature("transferFrom(address,address,uint256)", from, to, value)
        );
        require(success && (data.length == 0 || abi.decode(data, (bool))), "TRANSFER_FROM_FAILED");
    }
}

// 接口定义
interface IUniswapV2Factory {
    function getPair(address tokenA, address tokenB) external view returns (address pair);
    function createPair(address tokenA, address tokenB) external returns (address pair);
}

interface IUniswapV2Pair {
    function getReserves()
        external
        view
        returns (
            uint112 reserve0,
            uint112 reserve1,
            uint32 blockTimestampLast
        );
    function mint(address to) external returns (uint256 liquidity);
    function burn(address to) external returns (uint256 amount0, uint256 amount1);
    function swap(
        uint256 amount0Out,
        uint256 amount1Out,
        address to,
        bytes calldata data
    ) external;
    function transferFrom(
        address from,
        address to,
        uint256 value
    ) external returns (bool);
}

第三部分:Uniswap v3集中流动性

3.1 Uniswap v3核心创新

Uniswap v3引入了集中流动性(Concentrated Liquidity),允许流动性提供者在特定价格区间内提供流动性。

3.1.1 价格区间

// v2: 流动性分布在 [0, ∞]
// v3: 流动性集中在 [priceLower, priceUpper]

// 示例:
// ETH价格在 1800-2200 USDT时提供流动性
// 资本效率提升: 可能达到 4000倍

3.2 Uniswap v3 Position(简化版)

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

/**
 * @title UniswapV3Pool (简化版)
 * @dev Uniswap V3的核心概念演示
 */
contract UniswapV3Pool {
    // 代币
    IERC20 public immutable token0;
    IERC20 public immutable token1;

    // 手续费等级
    uint24 public immutable fee;

    // 当前价格(sqrtPriceX96)
    uint160 public sqrtPriceX96;

    // 当前tick
    int24 public tick;

    // 流动性
    uint128 public liquidity;

    // Position信息
    struct Position {
        uint128 liquidity;
        uint256 feeGrowthInside0LastX128;
        uint256 feeGrowthInside1LastX128;
        uint128 tokensOwed0;
        uint128 tokensOwed1;
    }

    // position key => Position
    mapping(bytes32 => Position) public positions;

    // Tick信息
    struct TickInfo {
        uint128 liquidityGross;
        int128 liquidityNet;
        uint256 feeGrowthOutside0X128;
        uint256 feeGrowthOutside1X128;
        bool initialized;
    }

    mapping(int24 => TickInfo) public ticks;

    event Mint(
        address sender,
        address indexed owner,
        int24 indexed tickLower,
        int24 indexed tickUpper,
        uint128 amount,
        uint256 amount0,
        uint256 amount1
    );

    event Burn(
        address indexed owner,
        int24 indexed tickLower,
        int24 indexed tickUpper,
        uint128 amount,
        uint256 amount0,
        uint256 amount1
    );

    event Swap(
        address indexed sender,
        address indexed recipient,
        int256 amount0,
        int256 amount1,
        uint160 sqrtPriceX96,
        uint128 liquidity,
        int24 tick
    );

    constructor(
        address _token0,
        address _token1,
        uint24 _fee,
        uint160 _sqrtPriceX96
    ) {
        token0 = IERC20(_token0);
        token1 = IERC20(_token1);
        fee = _fee;
        sqrtPriceX96 = _sqrtPriceX96;
        tick = getTickAtSqrtRatio(_sqrtPriceX96);
    }

    /**
     * @dev 添加流动性
     */
    function mint(
        address recipient,
        int24 tickLower,
        int24 tickUpper,
        uint128 amount
    ) external returns (uint256 amount0, uint256 amount1) {
        require(amount > 0, "Amount must be positive");
        require(tickLower < tickUpper, "Invalid tick range");

        // 获取position key
        bytes32 positionKey = keccak256(abi.encodePacked(recipient, tickLower, tickUpper));

        // 更新position
        Position storage position = positions[positionKey];
        position.liquidity += amount;

        // 更新tick
        _updateTick(tickLower, int128(amount), false);
        _updateTick(tickUpper, int128(amount), true);

        // 计算需要的代币数量
        (amount0, amount1) = _getAmountsForLiquidity(
            sqrtPriceX96,
            getSqrtRatioAtTick(tickLower),
            getSqrtRatioAtTick(tickUpper),
            amount
        );

        // 转入代币
        if (amount0 > 0) token0.transferFrom(msg.sender, address(this), amount0);
        if (amount1 > 0) token1.transferFrom(msg.sender, address(this), amount1);

        // 如果当前价格在区间内,更新全局流动性
        if (tick >= tickLower && tick < tickUpper) {
            liquidity += amount;
        }

        emit Mint(msg.sender, recipient, tickLower, tickUpper, amount, amount0, amount1);
    }

    /**
     * @dev 移除流动性
     */
    function burn(
        int24 tickLower,
        int24 tickUpper,
        uint128 amount
    ) external returns (uint256 amount0, uint256 amount1) {
        require(amount > 0, "Amount must be positive");

        bytes32 positionKey = keccak256(abi.encodePacked(msg.sender, tickLower, tickUpper));
        Position storage position = positions[positionKey];
        require(position.liquidity >= amount, "Insufficient liquidity");

        // 更新position
        position.liquidity -= amount;

        // 更新tick
        _updateTick(tickLower, -int128(amount), false);
        _updateTick(tickUpper, -int128(amount), true);

        // 计算可提取的代币数量
        (amount0, amount1) = _getAmountsForLiquidity(
            sqrtPriceX96,
            getSqrtRatioAtTick(tickLower),
            getSqrtRatioAtTick(tickUpper),
            amount
        );

        // 转出代币
        if (amount0 > 0) token0.transfer(msg.sender, amount0);
        if (amount1 > 0) token1.transfer(msg.sender, amount1);

        // 如果当前价格在区间内,更新全局流动性
        if (tick >= tickLower && tick < tickUpper) {
            liquidity -= amount;
        }

        emit Burn(msg.sender, tickLower, tickUpper, amount, amount0, amount1);
    }

    /**
     * @dev 交换(简化版)
     */
    function swap(
        address recipient,
        bool zeroForOne,
        int256 amountSpecified,
        uint160 sqrtPriceLimitX96
    ) external returns (int256 amount0, int256 amount1) {
        require(amountSpecified != 0, "Amount cannot be zero");

        // 简化实现:假设不跨tick
        uint256 amountIn = uint256(amountSpecified);
        uint256 amountOut;

        if (zeroForOne) {
            // 卖token0,买token1
            amountOut = getAmountOut(amountIn, liquidity, sqrtPriceX96, zeroForOne);
            token0.transferFrom(msg.sender, address(this), amountIn);
            token1.transfer(recipient, amountOut);
            amount0 = int256(amountIn);
            amount1 = -int256(amountOut);
        } else {
            // 卖token1,买token0
            amountOut = getAmountOut(amountIn, liquidity, sqrtPriceX96, zeroForOne);
            token1.transferFrom(msg.sender, address(this), amountIn);
            token0.transfer(recipient, amountOut);
            amount0 = -int256(amountOut);
            amount1 = int256(amountIn);
        }

        emit Swap(msg.sender, recipient, amount0, amount1, sqrtPriceX96, liquidity, tick);
    }

    /**
     * @dev 更新tick信息
     */
    function _updateTick(
        int24 _tick,
        int128 liquidityDelta,
        bool upper
    ) private {
        TickInfo storage tickInfo = ticks[_tick];

        uint128 liquidityGrossBefore = tickInfo.liquidityGross;
        uint128 liquidityGrossAfter = liquidityDelta < 0
            ? liquidityGrossBefore - uint128(-liquidityDelta)
            : liquidityGrossBefore + uint128(liquidityDelta);

        tickInfo.liquidityGross = liquidityGrossAfter;

        if (upper) {
            tickInfo.liquidityNet -= liquidityDelta;
        } else {
            tickInfo.liquidityNet += liquidityDelta;
        }

        if (!tickInfo.initialized) {
            tickInfo.initialized = true;
        }
    }

    // 数学辅助函数(简化)
    function getSqrtRatioAtTick(int24 _tick) public pure returns (uint160) {
        // 简化实现
        return uint160(uint256(1) << 96);
    }

    function getTickAtSqrtRatio(uint160 sqrtRatio) public pure returns (int24) {
        // 简化实现
        return 0;
    }

    function _getAmountsForLiquidity(
        uint160 sqrtRatioX96Current,
        uint160 sqrtRatioAX96,
        uint160 sqrtRatioBX96,
        uint128 liquidityAmount
    ) private pure returns (uint256 amount0, uint256 amount1) {
        // 简化实现
        amount0 = uint256(liquidityAmount);
        amount1 = uint256(liquidityAmount);
    }

    function getAmountOut(
        uint256 amountIn,
        uint128 _liquidity,
        uint160 _sqrtPriceX96,
        bool _zeroForOne
    ) public pure returns (uint256) {
        // 简化实现
        return amountIn;
    }
}

第四部分:PancakeSwap(BSC上的Uniswap分叉)

PancakeSwap是Binance Smart Chain上最大的DEX,基本是Uniswap v2的分叉,但添加了一些特色功能。

4.1 PancakeSwap的特色功能

  1. CAKE代币激励
  2. Syrup池(单币质押)
  3. 彩票系统
  4. 预测市场
  5. NFT市场

4.2 Syrup池(单币质押)实现

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

/**
 * @title SyrupPool
 * @dev PancakeSwap的单币质押池
 */
contract SyrupPool {
    IERC20 public immutable stakingToken;
    IERC20 public immutable rewardToken;

    // 每秒奖励
    uint256 public rewardPerSecond;

    // 最后更新时间
    uint256 public lastRewardTime;

    // 累计每股奖励
    uint256 public accRewardPerShare;

    // 总质押量
    uint256 public totalStaked;

    // 用户信息
    struct UserInfo {
        uint256 amount; // 质押数量
        uint256 rewardDebt; // 已领取的奖励
    }

    mapping(address => UserInfo) public userInfo;

    event Deposit(address indexed user, uint256 amount);
    event Withdraw(address indexed user, uint256 amount);
    event Harvest(address indexed user, uint256 amount);
    event EmergencyWithdraw(address indexed user, uint256 amount);

    constructor(
        address _stakingToken,
        address _rewardToken,
        uint256 _rewardPerSecond
    ) {
        stakingToken = IERC20(_stakingToken);
        rewardToken = IERC20(_rewardToken);
        rewardPerSecond = _rewardPerSecond;
        lastRewardTime = block.timestamp;
    }

    /**
     * @dev 更新奖励
     */
    function updatePool() public {
        if (block.timestamp <= lastRewardTime) {
            return;
        }

        if (totalStaked == 0) {
            lastRewardTime = block.timestamp;
            return;
        }

        uint256 timeElapsed = block.timestamp - lastRewardTime;
        uint256 reward = timeElapsed * rewardPerSecond;
        accRewardPerShare += (reward * 1e12) / totalStaked;
        lastRewardTime = block.timestamp;
    }

    /**
     * @dev 质押
     */
    function deposit(uint256 amount) external {
        UserInfo storage user = userInfo[msg.sender];

        updatePool();

        if (user.amount > 0) {
            uint256 pending = (user.amount * accRewardPerShare) / 1e12 - user.rewardDebt;
            if (pending > 0) {
                safeRewardTransfer(msg.sender, pending);
            }
        }

        if (amount > 0) {
            stakingToken.transferFrom(msg.sender, address(this), amount);
            user.amount += amount;
            totalStaked += amount;
        }

        user.rewardDebt = (user.amount * accRewardPerShare) / 1e12;

        emit Deposit(msg.sender, amount);
    }

    /**
     * @dev 提取
     */
    function withdraw(uint256 amount) external {
        UserInfo storage user = userInfo[msg.sender];
        require(user.amount >= amount, "Insufficient balance");

        updatePool();

        uint256 pending = (user.amount * accRewardPerShare) / 1e12 - user.rewardDebt;
        if (pending > 0) {
            safeRewardTransfer(msg.sender, pending);
        }

        if (amount > 0) {
            user.amount -= amount;
            totalStaked -= amount;
            stakingToken.transfer(msg.sender, amount);
        }

        user.rewardDebt = (user.amount * accRewardPerShare) / 1e12;

        emit Withdraw(msg.sender, amount);
    }

    /**
     * @dev 收获奖励
     */
    function harvest() external {
        UserInfo storage user = userInfo[msg.sender];

        updatePool();

        uint256 pending = (user.amount * accRewardPerShare) / 1e12 - user.rewardDebt;
        if (pending > 0) {
            safeRewardTransfer(msg.sender, pending);
        }

        user.rewardDebt = (user.amount * accRewardPerShare) / 1e12;

        emit Harvest(msg.sender, pending);
    }

    /**
     * @dev 紧急提取(不要奖励)
     */
    function emergencyWithdraw() external {
        UserInfo storage user = userInfo[msg.sender];
        uint256 amount = user.amount;

        user.amount = 0;
        user.rewardDebt = 0;
        totalStaked -= amount;

        stakingToken.transfer(msg.sender, amount);

        emit EmergencyWithdraw(msg.sender, amount);
    }

    /**
     * @dev 查看待领取奖励
     */
    function pendingReward(address _user) external view returns (uint256) {
        UserInfo storage user = userInfo[_user];
        uint256 _accRewardPerShare = accRewardPerShare;

        if (block.timestamp > lastRewardTime && totalStaked != 0) {
            uint256 timeElapsed = block.timestamp - lastRewardTime;
            uint256 reward = timeElapsed * rewardPerSecond;
            _accRewardPerShare += (reward * 1e12) / totalStaked;
        }

        return (user.amount * _accRewardPerShare) / 1e12 - user.rewardDebt;
    }

    /**
     * @dev 安全转账奖励代币
     */
    function safeRewardTransfer(address to, uint256 amount) internal {
        uint256 rewardBalance = rewardToken.balanceOf(address(this));
        if (amount > rewardBalance) {
            rewardToken.transfer(to, rewardBalance);
        } else {
            rewardToken.transfer(to, amount);
        }
    }
}

第五部分:流动性挖矿(Yield Farming)

5.1 MasterChef合约

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

/**
 * @title MasterChef
 * @dev 流动性挖矿主合约
 */
contract MasterChef {
    // 治理代币
    IERC20 public immutable rewardToken;

    // 每个区块的奖励
    uint256 public rewardPerBlock;

    // 开始区块
    uint256 public startBlock;

    // 池子信息
    struct PoolInfo {
        IERC20 lpToken; // LP代币地址
        uint256 allocPoint; // 分配点数
        uint256 lastRewardBlock; // 最后奖励区块
        uint256 accRewardPerShare; // 累计每股奖励
    }

    // 用户信息
    struct UserInfo {
        uint256 amount; // 用户提供的LP数量
        uint256 rewardDebt; // 已领取的奖励
    }

    // 池子数组
    PoolInfo[] public poolInfo;

    // 池子ID => 用户地址 => 用户信息
    mapping(uint256 => mapping(address => UserInfo)) public userInfo;

    // 总分配点数
    uint256 public totalAllocPoint;

    event Deposit(address indexed user, uint256 indexed pid, uint256 amount);
    event Withdraw(address indexed user, uint256 indexed pid, uint256 amount);
    event EmergencyWithdraw(address indexed user, uint256 indexed pid, uint256 amount);

    constructor(
        address _rewardToken,
        uint256 _rewardPerBlock,
        uint256 _startBlock
    ) {
        rewardToken = IERC20(_rewardToken);
        rewardPerBlock = _rewardPerBlock;
        startBlock = _startBlock;
    }

    /**
     * @dev 添加池子
     */
    function add(
        uint256 _allocPoint,
        address _lpToken,
        bool _withUpdate
    ) external {
        if (_withUpdate) {
            massUpdatePools();
        }

        uint256 lastRewardBlock = block.number > startBlock ? block.number : startBlock;
        totalAllocPoint += _allocPoint;

        poolInfo.push(
            PoolInfo({
                lpToken: IERC20(_lpToken),
                allocPoint: _allocPoint,
                lastRewardBlock: lastRewardBlock,
                accRewardPerShare: 0
            })
        );
    }

    /**
     * @dev 更新池子分配点数
     */
    function set(
        uint256 _pid,
        uint256 _allocPoint,
        bool _withUpdate
    ) external {
        if (_withUpdate) {
            massUpdatePools();
        }

        totalAllocPoint = totalAllocPoint - poolInfo[_pid].allocPoint + _allocPoint;
        poolInfo[_pid].allocPoint = _allocPoint;
    }

    /**
     * @dev 更新所有池子
     */
    function massUpdatePools() public {
        uint256 length = poolInfo.length;
        for (uint256 pid = 0; pid < length; ++pid) {
            updatePool(pid);
        }
    }

    /**
     * @dev 更新单个池子
     */
    function updatePool(uint256 _pid) public {
        PoolInfo storage pool = poolInfo[_pid];

        if (block.number <= pool.lastRewardBlock) {
            return;
        }

        uint256 lpSupply = pool.lpToken.balanceOf(address(this));
        if (lpSupply == 0) {
            pool.lastRewardBlock = block.number;
            return;
        }

        uint256 multiplier = block.number - pool.lastRewardBlock;
        uint256 reward = (multiplier * rewardPerBlock * pool.allocPoint) / totalAllocPoint;

        pool.accRewardPerShare += (reward * 1e12) / lpSupply;
        pool.lastRewardBlock = block.number;
    }

    /**
     * @dev 存入LP代币
     */
    function deposit(uint256 _pid, uint256 _amount) external {
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][msg.sender];

        updatePool(_pid);

        if (user.amount > 0) {
            uint256 pending = (user.amount * pool.accRewardPerShare) / 1e12 - user.rewardDebt;
            if (pending > 0) {
                safeRewardTransfer(msg.sender, pending);
            }
        }

        if (_amount > 0) {
            pool.lpToken.transferFrom(msg.sender, address(this), _amount);
            user.amount += _amount;
        }

        user.rewardDebt = (user.amount * pool.accRewardPerShare) / 1e12;

        emit Deposit(msg.sender, _pid, _amount);
    }

    /**
     * @dev 提取LP代币
     */
    function withdraw(uint256 _pid, uint256 _amount) external {
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][msg.sender];

        require(user.amount >= _amount, "Insufficient balance");

        updatePool(_pid);

        uint256 pending = (user.amount * pool.accRewardPerShare) / 1e12 - user.rewardDebt;
        if (pending > 0) {
            safeRewardTransfer(msg.sender, pending);
        }

        if (_amount > 0) {
            user.amount -= _amount;
            pool.lpToken.transfer(msg.sender, _amount);
        }

        user.rewardDebt = (user.amount * pool.accRewardPerShare) / 1e12;

        emit Withdraw(msg.sender, _pid, _amount);
    }

    /**
     * @dev 紧急提取
     */
    function emergencyWithdraw(uint256 _pid) external {
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][msg.sender];

        uint256 amount = user.amount;
        user.amount = 0;
        user.rewardDebt = 0;

        pool.lpToken.transfer(msg.sender, amount);

        emit EmergencyWithdraw(msg.sender, _pid, amount);
    }

    /**
     * @dev 查看待领取奖励
     */
    function pendingReward(uint256 _pid, address _user) external view returns (uint256) {
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][_user];

        uint256 accRewardPerShare = pool.accRewardPerShare;
        uint256 lpSupply = pool.lpToken.balanceOf(address(this));

        if (block.number > pool.lastRewardBlock && lpSupply != 0) {
            uint256 multiplier = block.number - pool.lastRewardBlock;
            uint256 reward = (multiplier * rewardPerBlock * pool.allocPoint) / totalAllocPoint;
            accRewardPerShare += (reward * 1e12) / lpSupply;
        }

        return (user.amount * accRewardPerShare) / 1e12 - user.rewardDebt;
    }

    /**
     * @dev 安全转账奖励
     */
    function safeRewardTransfer(address _to, uint256 _amount) internal {
        uint256 balance = rewardToken.balanceOf(address(this));
        if (_amount > balance) {
            rewardToken.transfer(_to, balance);
        } else {
            rewardToken.transfer(_to, _amount);
        }
    }

    /**
     * @dev 池子数量
     */
    function poolLength() external view returns (uint256) {
        return poolInfo.length;
    }
}

第六部分:闪电贷(Flash Loan)

6.1 闪电贷原理

闪电贷允许用户在单个交易中无抵押借款,但必须在同一交易中归还。

6.2 闪电贷实现

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

interface IFlashLoanReceiver {
    function executeOperation(
        address token,
        uint256 amount,
        uint256 fee,
        bytes calldata params
    ) external returns (bool);
}

/**
 * @title FlashLoan
 * @dev 闪电贷合约
 */
contract FlashLoan {
    IERC20 public immutable token;

    // 手续费率(基点)
    uint256 public constant FEE_RATE = 9; // 0.09%

    event FlashLoan(address indexed receiver, uint256 amount, uint256 fee);

    constructor(address _token) {
        token = IERC20(_token);
    }

    /**
     * @dev 闪电贷
     */
    function flashLoan(
        address receiver,
        uint256 amount,
        bytes calldata params
    ) external {
        uint256 balanceBefore = token.balanceOf(address(this));
        require(balanceBefore >= amount, "Insufficient liquidity");

        uint256 fee = (amount * FEE_RATE) / 10000;

        // 转账给接收者
        token.transfer(receiver, amount);

        // 调用接收者的回调函数
        require(
            IFlashLoanReceiver(receiver).executeOperation(
                address(token),
                amount,
                fee,
                params
            ),
            "Flash loan failed"
        );

        // 检查归还
        uint256 balanceAfter = token.balanceOf(address(this));
        require(
            balanceAfter >= balanceBefore + fee,
            "Flash loan not repaid"
        );

        emit FlashLoan(receiver, amount, fee);
    }

    /**
     * @dev 存入流动性
     */
    function deposit(uint256 amount) external {
        token.transferFrom(msg.sender, address(this), amount);
    }

    /**
     * @dev 提取流动性
     */
    function withdraw(uint256 amount) external {
        token.transfer(msg.sender, amount);
    }
}

/**
 * @title FlashLoanArbitrage
 * @dev 闪电贷套利示例
 */
contract FlashLoanArbitrage is IFlashLoanReceiver {
    address public owner;

    constructor() {
        owner = msg.sender;
    }

    /**
     * @dev 执行闪电贷回调
     */
    function executeOperation(
        address token,
        uint256 amount,
        uint256 fee,
        bytes calldata params
    ) external override returns (bool) {
        // 解码参数
        (address dex1, address dex2) = abi.decode(params, (address, address));

        // 1. 在DEX1卖出token,获得token2
        // 2. 在DEX2买入token,使用token2
        // 3. 确保最终获利

        // 简化示例:假设已经完成套利
        uint256 profit = 100 * 10**18; // 假设获利100代币

        // 归还闪电贷 + 手续费
        uint256 repayAmount = amount + fee;
        require(IERC20(token).transfer(msg.sender, repayAmount), "Repay failed");

        // 转移利润
        require(IERC20(token).transfer(owner, profit), "Profit transfer failed");

        return true;
    }

    /**
     * @dev 发起闪电贷套利
     */
    function initiateArbitrage(
        address flashLoanContract,
        uint256 amount,
        address dex1,
        address dex2
    ) external {
        require(msg.sender == owner, "Not owner");

        bytes memory params = abi.encode(dex1, dex2);
        FlashLoan(flashLoanContract).flashLoan(address(this), amount, params);
    }
}

第七部分:价格预言机

7.1 TWAP(时间加权平均价格)

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

/**
 * @title TWAPOracle
 * @dev 基于Uniswap v2的TWAP价格预言机
 */
contract TWAPOracle {
    address public immutable pair;
    address public immutable token0;
    address public immutable token1;

    uint256 public price0CumulativeLast;
    uint256 public price1CumulativeLast;
    uint32 public blockTimestampLast;

    uint256 public price0Average;
    uint256 public price1Average;

    uint256 public constant PERIOD = 24 hours;

    constructor(address _pair, address _token0, address _token1) {
        pair = _pair;
        token0 = _token0;
        token1 = _token1;

        price0CumulativeLast = IUniswapV2Pair(_pair).price0CumulativeLast();
        price1CumulativeLast = IUniswapV2Pair(_pair).price1CumulativeLast();

        (, , blockTimestampLast) = IUniswapV2Pair(_pair).getReserves();
    }

    /**
     * @dev 更新价格
     */
    function update() external {
        (uint256 price0Cumulative, uint256 price1Cumulative, uint32 blockTimestamp) =
            currentCumulativePrices(pair);

        uint32 timeElapsed;
        unchecked {
            timeElapsed = blockTimestamp - blockTimestampLast;
        }

        require(timeElapsed >= PERIOD, "Period not elapsed");

        // 计算平均价格
        unchecked {
            price0Average = (price0Cumulative - price0CumulativeLast) / timeElapsed;
            price1Average = (price1Cumulative - price1CumulativeLast) / timeElapsed;
        }

        price0CumulativeLast = price0Cumulative;
        price1CumulativeLast = price1Cumulative;
        blockTimestampLast = blockTimestamp;
    }

    /**
     * @dev 获取当前累计价格
     */
    function currentCumulativePrices(address _pair)
        internal
        view
        returns (
            uint256 price0Cumulative,
            uint256 price1Cumulative,
            uint32 blockTimestamp
        )
    {
        blockTimestamp = uint32(block.timestamp % 2**32);
        price0Cumulative = IUniswapV2Pair(_pair).price0CumulativeLast();
        price1Cumulative = IUniswapV2Pair(_pair).price1CumulativeLast();

        // 如果时间已过去,累加当前时间段的价格
        (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast_) =
            IUniswapV2Pair(_pair).getReserves();

        if (blockTimestampLast_ != blockTimestamp) {
            uint32 timeElapsed;
            unchecked {
                timeElapsed = blockTimestamp - blockTimestampLast_;
                price0Cumulative += uint256(reserve1 / reserve0) * timeElapsed;
                price1Cumulative += uint256(reserve0 / reserve1) * timeElapsed;
            }
        }
    }

    /**
     * @dev 查询价格
     */
    function consult(address token, uint256 amountIn) external view returns (uint256 amountOut) {
        if (token == token0) {
            amountOut = (amountIn * price0Average) / (2**112);
        } else {
            require(token == token1, "Invalid token");
            amountOut = (amountIn * price1Average) / (2**112);
        }
    }
}
Prev
05-ERC标准详解
Next
07-DeFi核心协议-借贷协议