本文将带你深入理解UniswapV1的添加流动性机制,从基础概念到代码实现,循序渐进。📚第一章:基础概念扫盲什么是DeFi和AMM?DeFi:去中心化金融,用智能合约替代传统银行AMM:自动做市商,用数学公式自动定价和交易的系统什么是流动性池?想象一个神奇的双格宝
本文将带你深入理解 Uniswap V1 的添加流动性机制,从基础概念到代码实现,循序渐进。
想象一个神奇的双格宝箱:
想象一个精密天平,这是理解 AMM 的最佳比喻:
初始状态:
场景:小明想用 2000 USDT 买 ETH
场景:小红想成为天平的合伙人,赚取手续费
如果添加流动性时价格变了会怎样?
灾难场景:
结论:添加流动性必须维持价格不变,这是协议安全的基石!
池子状态:
我的操作:想添加 1 ETH
计算过程:
池子状态:
我的操作:想添加 1 ETH
计算公式:
需要的USDT数量 = 我的ETH × (池里的USDT ÷ 池里的ETH)
需要的USDT数量 = 1 × (1000 ÷ 10) = 100 USDT
看!这就是代码中公式的来源:
tokenAmount = (msg.value * tokenReserve) / ethReserve
让我们做个思想实验,看看强行用 x × y = k 会发生什么灾难:
初始状态:
我的操作:加入 1 ETH
如果强行维持 k 不变:
这完全不合逻辑,会被套利机器人瞬间榨干。
添加流动性后,我能得到多少 LP 代币(股份凭证)?
核心原则:我的股份比例 = 我贡献的价值比例
公式:
我的LP代币数量 = (我存入的ETH ÷ 池里总ETH) × 总LP代币数量
代码表示:
liquidityMinted = (msg.value * totalLiquidity) / ethReserve
池子现状:
我的操作:存入 1 ETH + 100 Token
计算过程:
结论:我获得的所有权份额 = 我贡献的价值份额,公平合理!
💡 思考题:如果池子很大,我存入很少,会稀释我的股份吗?
function addLiquidity(uint256 min_liquidity, uint256 max_tokens, uint256 deadline)
external payable returns (uint256) {
require(deadline >= block.timestamp, "交易超时了");
uint256 totalLiquidity = totalSupply; // 当前总LP代币数量
if (totalLiquidity > 0) {
// 不是第一次添加流动性
uint256 ethReserve = address(this).balance - msg.value; // 池里原有的ETH
uint256 tokenReserve = IERC20(token).balanceOf(address(this)); // 池里原有的Token
// 🧮 计算我需要存多少Token(保持价格不变)
uint256 tokenAmount = (msg.value * tokenReserve) / ethReserve + 1;
// 需要的Token = (我的ETH × 池里Token) ÷ 池里ETH + 防精度损失
// 🏆 计算我能得到多少LP代币(我的股份)
uint256 liquidityMinted = (msg.value * totalLiquidity) / ethReserve;
// 我的LP = (我的ETH × 总LP) ÷ 池里ETH
// 🛡️ 安全检查
require(max_tokens >= tokenAmount, "Token数量超出你的预期");
require(liquidityMinted >= min_liquidity, "LP代币低于你的预期");
// ✨ 执行操作
_mint(msg.sender, liquidityMinted); // 给你铸造LP代币
require(IERC20(token).transferFrom(msg.sender, address(this), tokenAmount),
"Token转账失败");
return liquidityMinted;
} else {
// 🎉 第一次添加流动性(创建池子)
// 你存多少ETH,就给你多少LP代币作为初始基准
uint256 tokenAmount = max_tokens;
uint256 initialLiquidity = address(this).balance;
_mint(msg.sender, initialLiquidity);
require(IERC20(token).transferFrom(msg.sender, address(this), tokenAmount),
"Token转账失败");
return initialLiquidity;
}
}
你可能注意到这行代码:
uint256 tokenAmount = (msg.value * tokenReserve) / ethReserve + 1;
为什么要加 1?这涉及到 Solidity 的精度问题。
错误写法:
tokenAmount = msg.value * (tokenReserve / ethReserve); // ❌ 先除后乘
正确写法:
tokenAmount = (msg.value * tokenReserve) / ethReserve; // ✅ 先乘后除
场景设置:
错误方式(先除后乘):
正确方式(先乘后除):
💡 思考题:如果不加这个 +1,会发生什么问题?
特点:
为什么这样设计:
例子:
你存入:5 ETH + 500 USDT
你得到:5 LP 代币
初始价格:1 ETH = 100 USDT
特点:
为什么这样设计:
操作类型 | 添加流动性 | 交易(Swap) |
---|---|---|
核心规则 | 维持价格比例不变 | 维持乘积 k 不变 |
公式 | Δy/Δx = y/x | x × y = k |
目的 | 成为LP,赚手续费 | 用A换B |
对价格影响 | 价格不变 | 价格改变(滑点) |
对k的影响 | k 增大(池子变深) | k 不变 |
通过这篇文章,你应该已经深刻理解了:
这些知识是理解所有 AMM 协议的基础。掌握了 Uniswap V1,你就能更好地理解 V2、V3 以及其他 DeFi 协议的创新点。
记住:代码是冰冷的,但设计思想是温暖的。每一行代码背后,都有设计者对公平、效率和安全的深思熟虑。
继续探索 DeFi 的奇妙世界吧!🚀
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!