去中心化金融(DeFi)是区块链技术最具革命性的应用之一。本文将带你从零开始构建一个简化版的去中心化交易所,理解AMM(自动做市商)的核心机制。
什么是AMM?
AMM(Automated Market Maker)自动做市商,通过算法自动为交易对提供流动性。不同于传统订单簿模式,AMM使用流动性池和定价公式来实现代币交换。
核心概念
- 流动性池(Liquidity Pool): 存放两种代币的智能合约
- 恒定乘积公式:
x * y = k - 流动性提供者(LP): 向池子提供代币的用户
- 滑点(Slippage): 交易对价格的影响程度
构建基础DEX
1. 核心合约结构
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IERC20 {
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
function transfer(address recipient, uint256 amount) external returns (bool);
function balanceOf(address account) external view returns (uint256);
}
contract SimpleDEX {
IERC20 public token0;
IERC20 public token1;
uint256 public reserve0; // 代币0的储备量
uint256 public reserve1; // 代币1的储备量
uint256 public totalSupply; // LP代币总供应量
mapping(address => uint256) public balanceOf; // 用户LP代币余额
event Swap(address indexed sender, uint amount0In, uint amount1In, uint amount0Out, uint amount1Out, address indexed to);
event LiquidityAdded(address indexed provider, uint amount0, uint amount1, uint liquidity);
event LiquidityRemoved(address indexed provider, uint amount0, uint amount1, uint liquidity);
constructor(address _token0, address _token1) {
token0 = IERC20(_token0);
token1 = IERC20(_token1);
}
// 获取流动性份额
function _mint(address _to, uint _amount) private {
balanceOf[_to] += _amount;
totalSupply += _amount;
}
// 销毁流动性份额
function _burn(address _from, uint _amount) private {
balanceOf[_from] -= _amount;
totalSupply -= _amount;
}
// 更新储备量
function _update(uint _reserve0, uint _reserve1) private {
reserve0 = _reserve0;
reserve1 = _reserve1;
}
}
2. 添加流动性
function addLiquidity(uint _amount0, uint _amount1) external returns (uint liquidity) {
// 转账代币到合约
token0.transferFrom(msg.sender, address(this), _amount0);
token1.transferFrom(msg.sender, address(this), _amount1);
uint _reserve0 = reserve0;
uint _reserve1 = reserve1;
if (_reserve0 == 0 && _reserve1 == 0) {
// 首次添加流动性
liquidity = sqrt(_amount0 * _amount1);
} else {
// 按比例添加
require(_amount0 * _reserve1 == _amount1 * _reserve0, "Invalid ratio");
liquidity = min((_amount0 * totalSupply) / _reserve0, (_amount1 * totalSupply) / _reserve1);
}
require(liquidity > 0, "Insufficient liquidity");
_mint(msg.sender, liquidity);
_update(_reserve0 + _amount0, _reserve1 + _amount1);
emit LiquidityAdded(msg.sender, _amount0, _amount1, liquidity);
}
3. 移除流动性
function removeLiquidity(uint _liquidity) external returns (uint amount0, uint amount1) {
require(balanceOf[msg.sender] >= _liquidity, "Insufficient balance");
uint _reserve0 = reserve0;
uint _reserve1 = reserve1;
// 计算可提取的代币数量
amount0 = (_liquidity * _reserve0) / totalSupply;
amount1 = (_liquidity * _reserve1) / totalSupply;
require(amount0 > 0 && amount1 > 0, "Invalid amounts");
_burn(msg.sender, _liquidity);
// 转账代币给用户
token0.transfer(msg.sender, amount0);
token1.transfer(msg.sender, amount1);
_update(_reserve0 - amount0, _reserve1 - amount1);
emit LiquidityRemoved(msg.sender, amount0, amount1, _liquidity);
}
4. 代币交换
function swap(uint _amount0Out, uint _amount1Out, address _to) external {
require(_amount0Out > 0 || _amount1Out > 0, "Invalid output");
require(_amount0Out == 0 || _amount1Out == 0, "Only one output allowed");
uint _reserve0 = reserve0;
uint _reserve1 = reserve1;
require(_amount0Out < _reserve0 && _amount1Out < _reserve1, "Insufficient liquidity");
uint amount0In = 0;
uint amount1In = 0;
if (_amount0Out > 0) {
token0.transfer(_to, _amount0Out);
amount1In = token1.balanceOf(address(this)) - _reserve1;
} else {
token1.transfer(_to, _amount1Out);
amount0In = token0.balanceOf(address(this)) - _reserve0;
}
require(amount0In > 0 || amount1In > 0, "Invalid input");
// 恒定乘积公式验证
uint balance0 = token0.balanceOf(address(this));
uint balance1 = token1.balanceOf(address(this));
require(balance0 * balance1 >= _reserve0 * _reserve1, "K invariant violated");
_update(balance0, balance1);
emit Swap(msg.sender, amount0In, amount1In, _amount0Out, _amount1Out, _to);
}
5. 价格计算
function getAmountOut(uint _amountIn, uint _reserveIn, uint _reserveOut) public pure returns (uint amountOut) {
require(_amountIn > 0, "Invalid amount");
require(_reserveIn > 0 && _reserveOut > 0, "Invalid reserves");
// 简化版,实际应该考虑手续费
uint amountInWithFee = _amountIn * 997; // 0.3% 手续费
uint numerator = amountInWithFee * _reserveOut;
uint denominator = (_reserveIn * 1000) + amountInWithFee;
amountOut = numerator / denominator;
}
function getAmountsOut(uint _amountIn, address[] calldata _path) public view returns (uint[] memory amounts) {
require(_path.length >= 2, "Invalid path");
amounts = new uint[](_path.length);
amounts[0] = _amountIn;
for (uint i; i < _path.length - 1; i++) {
(uint reserveIn, uint reserveOut) = getReserves(_path[i], _path[i + 1]);
amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut);
}
}
高级功能
1. 价格预言机
contract PriceOracle {
struct Observation {
uint timestamp;
uint price0Cumulative;
uint price1Cumulative;
}
Observation[] public observations;
uint public constant PERIOD = 3600; // 1小时
function update() external {
uint timeElapsed = block.timestamp - observations[observations.length - 1].timestamp;
require(timeElapsed >= PERIOD, "Period not elapsed");
uint price0Cumulative = reserve1 * 2**112 / reserve0;
uint price1Cumulative = reserve0 * 2**112 / reserve1;
observations.push(Observation(block.timestamp, price0Cumulative, price1Cumulative));
}
function getPrice() external view returns (uint price) {
require(observations.length >= 2, "Insufficient data");
Observation memory first = observations[observations.length - 2];
Observation memory last = observations[observations.length - 1];
uint timeElapsed = last.timestamp - first.timestamp;
require(timeElapsed >= PERIOD, "Period too short");
price = (last.price0Cumulative - first.price0Cumulative) / timeElapsed;
}
}
2. 闪电贷
interface IFlashLoanReceiver {
function executeOperation(address token, uint amount, uint fee, bytes calldata data) external;
}
contract FlashLoan {
function flashLoan(address _token, uint _amount, bytes calldata _data) external {
uint balanceBefore = IERC20(_token).balanceOf(address(this));
require(balanceBefore >= _amount, "Insufficient liquidity");
// 转账给借款人
IERC20(_token).transfer(msg.sender, _amount);
// 执行借款人的操作
IFlashLoanReceiver(msg.sender).executeOperation(_token, _amount, _fee, _data);
// 检查还款
uint balanceAfter = IERC20(_token).balanceOf(address(this));
require(balanceAfter >= balanceBefore + _fee, "Flash loan not repaid");
}
}
部署和测试
使用Hardhat进行测试
const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("SimpleDEX", function () {
let dex, token0, token1, owner, user1, user2;
beforeEach(async () => {
[owner, user1, user2] = await ethers.getSigners();
// 部署测试代币
const Token = await ethers.getContractFactory("Token");
token0 = await Token.deploy("Token0", "TK0", ethers.utils.parseEther("1000"));
token1 = await Token.deploy("Token1", "TK1", ethers.utils.parseEther("1000"));
// 部署DEX
const SimpleDEX = await ethers.getContractFactory("SimpleDEX");
dex = await SimpleDEX.deploy(token0.address, token1.address);
// 授权DEX使用代币
await token0.approve(dex.address, ethers.constants.MaxUint256);
await token1.approve(dex.address, ethers.constants.MaxUint256);
});
it("Should add liquidity", async () => {
const amount0 = ethers.utils.parseEther("100");
const amount1 = ethers.utils.parseEther("100");
await dex.addLiquidity(amount0, amount1);
expect(await dex.reserve0()).to.equal(amount0);
expect(await dex.reserve1()).to.equal(amount1);
expect(await dex.balanceOf(owner.address)).to.equal(ethers.utils.parseEther("100"));
});
it("Should perform swap", async () => {
// 添加流动性
await dex.addLiquidity(
ethers.utils.parseEther("100"),
ethers.utils.parseEther("100")
);
// 执行交换
const amountIn = ethers.utils.parseEther("10");
await token0.connect(user1).approve(dex.address, amountIn);
await token0.connect(user1).transfer(dex.address, amountIn);
const amountOut = await dex.getAmountOut(
amountIn,
await dex.reserve0(),
await dex.reserve1()
);
await dex.swap(0, amountOut, user1.address);
expect(await token1.balanceOf(user1.address)).to.equal(amountOut);
});
});
总结
通过构建这个DEX,我们学习了:
- 恒定乘积公式:
x * y = k是AMM的核心 - 流动性提供: 如何添加和移除流动性
- 代币交换: 基于定价算法的交换机制
- 价格计算: 滑点和价格影响的计算
- 高级功能: 价格预言机和闪电贷
这只是一个基础实现,实际的DEX如Uniswap还包含更多复杂功能:
- 多跳路由
- 价格影响保护
- 闪电交换
- 协议费用
- 治理代币
继续探索,构建更强大的DeFi协议!
下一步
- 实现更复杂的AMM算法(如Curve的稳定币交换)
- 添加治理机制
- 集成跨链功能
- 构建用户界面
- 进行安全审计