网速在线测速,优化游戏性能的软件,商洛微网站建设,怎样免费建公司网站交换意味着使用一定数量的TokenA来换取Tokenb。但我们需要一些额外的辅助服务: 1.提供实际汇率。 2.保证所有的交易都是在正确的汇率下进行的。 GitHub - XuHugo/solidityproject: DApp go go go #xff01;#xff01;#xff01; 我们在研究流动性供应时学到了一些关于 D… 交换意味着使用一定数量的TokenA来换取Tokenb。但我们需要一些额外的辅助服务: 1.提供实际汇率。 2.保证所有的交易都是在正确的汇率下进行的。 GitHub - XuHugo/solidityproject: DApp go go go 我们在研究流动性供应时学到了一些关于 DEX 定价的知识决定汇率的是池中的流动性数量。成功互换的主要条件互换后的reserve乘积必须等于或大于互换前的reserve乘积。无论Pool中的reserve数量是多少恒等乘积都必须保持不变。这基本上是我们必须保证的唯一条件而且令人惊讶的是这个条件让我们无需计算互换价格。 正如我在介绍中提到的Pair合约是一个核心合约这意味着它的功能必须尽可能底层化和最小化。这也影响到我们如何向合约发送代币。有两种方法可以将代币转移给他人 1、调用Token合约的transfer方法并传递接收者的地址和要发送的金额。 2、调用 approve 方法将一定数量的代币授权允许其他用户或合约使用。其他人必须调用 transferFrom 才能转移你的Token。你只需为批准一定数量的Token支付费用而对方则需为实际转账支付费用。 调用 approve 模式在以太坊应用中非常常见dapp 要求用户批准最大金额的消费这样用户就不需要一次又一次地调用批准。这可以改善用户体验。而这并不是我们目前需要考虑的因此我们将采用手动转入Pair合约的方式。 函数入参需要两个输出金额每个token一个。这些是调用者希望用token换取的金额。为什么要这样做呢——需要两个token因为我们不想强制执行交换的方向调用者可以指定任一个金额或两个金额我们只需执行必要的检查。
function swap(uint256 amount0Out,uint256 amount1Out,address to) public {if (amount0Out 0 amount1Out 0)revert InsufficientOutputAmount();...
接下来我们需要确保有足够的reserve发送给用户。 ...(uint112 reserve0_, uint112 reserve1_, ) getReserves();if (amount0Out reserve0_ || amount1Out reserve1_)revert InsufficientLiquidity();... 在获得reserve并进行预检查后我们要做的第一件事就是将token转移给用户。有趣的是我们可以提前做这件事反而是未了更安全后边我们会介绍原因也许你现在就知道了原因。转账完成后我们再计算输入金额
if (amount0Out 0) _safeTransfer(token0, to, amount0Out);if (amount1Out 0) _safeTransfer(token1, to, amount1Out);uint256 balance0 IERC20(token0).balanceOf(address(this));uint256 balance1 IERC20(token1).balanceOf(address(this));uint256 amount0In balance0 reserve0 - amount0Out? balance0 - (reserve0 - amount0Out): 0;uint256 amount1In balance1 reserve1 - amount1Out? balance1 - (reserve1 - amount1Out): 0;if (amount0In 0 amount1In 0) revert InsufficientInputAmount(); 为了让这部分的逻辑更清晰我们可以把 reserve0 和 reserve1 看作 旧余额即交换开始前合约的余额。 在交换token时我们通常会提供 amount0Out 或 amount1Out。因此通常会有 amount0In 或 amount1In。但这里允许我们同时设置 amount0Out 和 amount1Out因此 amount0In 和 amount1In 也有可能都大于零。但如果这两个值都为零用户就没有向合约发送任何Token这是不允许的。 因此在这几行中我们发现了新的余额它们不包括输出金额但包括输入金额。 然后就是我们之前讨论过的常数乘积检验。我们预计这个合约token的余额与其reserve不同我们需要确保它们的乘积等于或大于当前储备的乘积。如果满足此要求则: 1.调用方正确计算了汇率(包括滑点)。 2.输出量正确。 3.转到合约中的金额也是正确的。
uint256 balance0Adjusted (balance0 * 1000) - (amount0In * 3);uint256 balance1Adjusted (balance1 * 1000) - (amount1In * 3);if (balance0Adjusted * balance1Adjusted uint256(reserve0_) * uint256(reserve1_) * (1000**2)) revert InvalidK(); 首先我们计算调整后的余额即当前余额减去swap fee后者适用于输入金额。同样由于整除的原因我们必须将余额乘以 1000金额乘以 3以 模拟 输入金额乘以 0.0030.3%。 接下来我们要为调整后的余额计算一个新的 K并将其与当前的 K 进行比较。为了补偿调整后余额乘以 1000 的结果我们将旧储备金乘以 1000 * 1000。 基本上我们是用新余额减去掉期费来计算新的 K 值。这个新 K 必须大于或等于旧 K。 让我们测试一下当我们试图获取过多的输出代币时是否会出现 InvalidK 错误
function testSwapUnpaidFee() public {token0.transfer(address(pair), 1 ether);token1.transfer(address(pair), 2 ether);pair.mint(address(this));token0.transfer(address(pair), 0.1 ether);vm.expectRevert(encodeError(InvalidK()));pair.swap(0, 0.181322178776029827 ether, address(this), );} 在这里我们试图用 0.181322178776029827 ether 的 token1 交换 0.1 ether 的 token0但失败了。如果将代币 1 的金额减少 1测试就会通过。我使用 getAmountOut 计算了这个数额