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主要包含三个核心合约:
- UniswapV2Factory: 创建和管理交易对
- UniswapV2Pair: 交易对合约(流动性池)
- 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的特色功能
- CAKE代币激励
- Syrup池(单币质押)
- 彩票系统
- 预测市场
- 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);
}
}
}