从零开始了解 Uniswap
本文通过大概100行代码演示了Uniswap 原理
> * 原文:[Uniswap from Scratch](https://monokh.com/posts/uniswap-from-scratch) > * 译文出自:[登链翻译计划](https://github.com/lbc-team/Pioneer) > * 译者:[翻译小组](https://learnblockchain.cn/people/412) > * 校对:[Tiny 熊](https://learnblockchain.cn/people/15) > * 本文永久链接:[learnblockchain.cn/article…](https://learnblockchain.cn/article/2747) 你一定听说过Uniswap,甚至非常了解它的运作方式。 Uniswap是一个神奇的产品,没有订单簿?没有服务器?没有预言机?WTF!!!! **X\* Y = K** 是其魔法所在。 这是一个永远执行、**去中心化的AMM的执行引擎**。 让我们从头开始实现它,了解它到底是如何工作的。 本文的目的不是要进行一个完美和安全的Uniswap实现。而是为了以最简化的方式展示其机制,你可以在文末查看到本文的完整代码。 我们假定你对以太坊有基本了解,例如了解 [Solidity](https://learnblockchain.cn/docs/solidity/)、智能合约、交易和代币。 > 如果还不了解,订阅[全面掌握Solidity智能合约开发](https://learnblockchain.cn/column/1)专栏全面学习。 ## Pool(流动性资金池) Uniswap的故事从Pool(以下简称:流动池)开始。流动池是一个智能合约,它储备了两个代币的`token0`和`token1`。 ```javascript contract LooneySwapPool is ERC20 { address public token0; address public token1; // Reserve of token 0 uint public reserve0; // Reserve of token 1 uint public reserve1; ... } ``` 创建一个流动池很简单:只需要指定这个流动池可以存储的2个代币: ```javascript constructor(address _token0, address _token1) ERC20("LiquidityProvider", "LP") { token0 = _token0; token1 = _token1; } ``` 合约创建后,它将在状态变量`reserve0`和`reserve1`中分别记录`token0`和`token1`的数量余额。 请注意,该合约也扩展了[ERC20](https://learnblockchain.cn/tags/ERC20)。这是因为,除了计算每个代币的储备量之外,它本身也是一个代币,正如你从名字中可以看出,该代币代表了流动性提供者的余额。 ## 增加流动性资金 为了将流动性添加到我们的资金池中,必须调用`add`函数,指定我们要存入的每个代币的数量: ``` function add(uint amount0, uint amount1) ``` 首先我们把代币转移到流动池(本身)里: ``` assert(IERC20(token0).transferFrom(msg.sender, address(this), amount0)); assert(IERC20(token1).transferFrom(msg.sender, address(this), amount1)); ``` 由于用户不再拥有这些代币的控制权,我们需要一种方法来给他们一个对象,代表他们在流动池中的所占的份额。这就是流动池代币的作用! 我们按照用户在资金池中的份额,铸造新的流动池代币。 如果这是第一个提供流动资金的用户,我们会铸造一个初始金额,有效地给他们一个100%的份额。 ``` _mint(msg.sender, INITIAL_SUPPLY); ``` 否则,我们将计算出按比例的份额,并铸造出等值的LP代币: ``` uint reserve0After = reserve0 + amount0; uint reserve1After = reserve1 + amount1; ... uint currentSupply = totalSupply(); // Current supply of LP tokens uint newSupplyGivenReserve0Ratio = reserve0After * currentSupply / reserve0; uint newSupplyGivenReserv...
- 原文:Uniswap from Scratch
- 译文出自:登链翻译计划
- 译者:翻译小组
- 校对:Tiny 熊
- 本文永久链接:learnblockchain.cn/article…
你一定听说过Uniswap,甚至非常了解它的运作方式。
Uniswap是一个神奇的产品,没有订单簿?没有服务器?没有预言机?WTF!!!!
X* Y = K 是其魔法所在。
这是一个永远执行、去中心化的AMM的执行引擎。
让我们从头开始实现它,了解它到底是如何工作的。
本文的目的不是要进行一个完美和安全的Uniswap实现。而是为了以最简化的方式展示其机制,你可以在文末查看到本文的完整代码。
我们假定你对以太坊有基本了解,例如了解 Solidity、智能合约、交易和代币。
如果还不了解,订阅全面掌握Solidity智能合约开发专栏全面学习。
Pool(流动性资金池)
Uniswap的故事从Pool(以下简称:流动池)开始。流动池是一个智能合约,它储备了两个代币的token0
和token1
。
contract LooneySwapPool is ERC20 {
address public token0;
address public token1;
// Reserve of token 0
uint public reserve0;
// Reserve of token 1
uint public reserve1;
...
}
创建一个流动池很简单:只需要指定这个流动池可以存储的2个代币:
constructor(address _token0, address _token1) ERC20("LiquidityProvider", "LP") {
token0 = _token0;
token1 = _token1;
}
合约创建后,它将在状态变量reserve0
和reserve1
中分别记录token0
和token1
的数量余额。
请注意,该合约也扩展了ERC20。这是因为,除了计算每个代币的储备量之外,它本身也是一个代币,正如你从名字中可以看出,该代币代表了流动性提供者的余额。
增加流动性资金
为了将流动性添加到我们的资金池中,必须调用add
函数,指定我们要存入的每个代币的数量:
function add(uint amount0, uint amount1)
首先我们把代币转移到流动池(本身)里:
assert(IERC20(token0).transferFrom(msg.sender, address(this), amount0));
assert(IERC20(token1).transferFrom(msg.sender, address(this), amount1));
由于用户不再拥有这些代币的控制权,我们需要一种方法来给他们一个对象,代表他们在流动池中的所占的份额。这就是流动池代币的作用!
我们按照用户在资金池中的份额,铸造新的流动池代币。
如果这是第一个提供流动资金的用户,我们会铸造一个初始金额,有效地给他们一个100%的份额。
_mint(msg.sender, INITIAL_SUPPLY);
否则,我们将计算出按比例的份额,并铸造出等值的LP代币:
uint reserve0After = reserve0 + amount0;
uint reserve1After = reserve1 + amount1;
...
uint currentSupply = totalSupply(); // Current supply of LP tokens
uint newSupplyGivenReserve0Ratio = reserve0After * currentSupply / reserve0;
uint newSupplyGivenReserv...
剩余50%的内容订阅专栏后可查看
- 单篇购买 8学分
- 永久订阅专栏 (30学分)
- 发表于 2021-07-15 18:33
- 阅读 ( 2661 )
- 学分 ( 9 )
- 分类:Uniswap
- 专栏:热门 DEFI 分析
评论