Solidity Uniswap V2 Flash loans

        Flash loans是一种非常强大的金融工具,在传统金融中没有类似的工具。它是一种无限制、无抵押的贷款,必须在接受贷款的同一交易中偿还。Uniswap 就是能提供闪电贷款的平台之一。让我们把它们添加到合同中,看看它们是如何工作的。

GitHub - XuHugo/solidityproject: DApp go go go !!!

        关于闪电贷款的实现,你需要知道的第一件事就是它们只能由智能合约使用。下面是闪贷的借款和还款方式:

        1一个智能合约从另一个合约借入闪贷。

        2出借合约向借款合约发送代币,并调用该合约中的一个特殊函数。

        3在特殊函数中,借款合约对贷款执行一些操作,然后将贷款转回。

        4贷方合约确保全额还款。如果有费用,它也会确保费用已支付。

        5控制流返回借款合同。

        要在 Zuniswap 中添加闪贷,我们需要做一些改动。首先,更新 swap,增加一个参数:

function swap(

    uint256 amount0Out,

    uint256 amount1Out,

    address to,

    bytes calldata data

) public {

        新参数是一个字节数组数据。它可以包含任何文学内容。

        下一步是实际发放贷款。请记住,我们在交换函数中进行了乐观的转账:

...

if (amount0Out > 0) _safeTransfer(token0, to, amount0Out);

if (amount1Out > 0) _safeTransfer(token1, to, amount1Out);

...

        这意味着我们已经在不要求抵押的情况下提供了任意数量(输出量由用户指定)的代币!我们唯一需要做的就是让调用者偿还贷款。为此,我们需要调用调用者合约中的一个特殊函数:

...

if (amount0Out > 0) _safeTransfer(token0, to, amount0Out);

if (amount1Out > 0) _safeTransfer(token1, to, amount1Out);

if (data.length > 0) IZuniswapV2Callee(to).zuniswapV2Call(msg.sender, amount0Out, amount1Out, data);

...

        根据我们定义的约定,我们希望调用者合约实现 zuniswapV2Call 函数,该函数接收:发送者地址、第一输出量、第二输出量和新数据参数。合同中的其他内容保持不变!同样,这也是一个非常优雅和简单的解决方案。

        基本上就是这样!事实证明,我们已经实现了检查贷款是否偿还的逻辑--这与检查新 K 是否有效的逻辑相同!

        现在,让我们来测试一下 Flash 贷款!我希望在添加测试后,整个流程会更加清晰。

        如上所述,要使用闪电贷款,我们需要一个智能合约。为了测试闪电贷款,我们需要一个单独的合约--让我们称它为 Flashloaner:

contract Flashloaner {

    error InsufficientFlashLoanAmount();



    uint256 expectedLoanAmount;



    ...

}

        合同将实现两个功能:

        第一个函数将从 Zuniswap 借入闪存贷款。

        第二个函数 zuniswapV2Call 将处理贷款并偿还贷款。

        获取闪贷和swap一样简单:

function flashloan(

    address pairAddress,

    uint256 amount0Out,

    uint256 amount1Out,

    address tokenAddress

) public {

    if (amount0Out > 0) {

        expectedLoanAmount = amount0Out;

    }

    if (amount1Out > 0) {

        expectedLoanAmount = amount1Out;

    }



    ZuniswapV2Pair(pairAddress).swap(

        amount0Out,

        amount1Out,

        address(this),

        abi.encode(tokenAddress)

    );

}

        在进行swap之前,我们要设置预期贷款额,以便日后检查所请求的token数额是否确实已发放给我们。

        在swap函数中,请注意我们传递了 tokenAddress 作为数据参数--我们稍后将使用它来偿还贷款。或者,我们也可以将该地址存储在状态变量中。由于数据是字节数组,我们需要一种将地址转换为字节的方法,而 abi.encode 是一种常用的解决方案。

        现在是闪存贷款处理程序:

function zuniswapV2Call(

    address sender,

    uint256 amount0Out,

    uint256 amount1Out,

    bytes calldata data

) public {

    address tokenAddress = abi.decode(data, (address));

    uint256 balance = ERC20(tokenAddress).balanceOf(address(this));



    if (balance < expectedLoanAmount) revert InsufficientFlashLoanAmount();



    ERC20(tokenAddress).transfer(msg.sender, balance);

}

        这是我们在 flashloan 中调用的交换函数中的配对合约将调用的函数。pair合约还会把我们输入的任何数据传给swap。

        在处理函数中,我们要确保我们确实获得了所请求的贷款,而且我们只是在还贷。与其偿还贷款,我们还可以将其用于杠杆、套利或利用智能合约中的漏洞等用途。Flash 贷款是一个非常强大的工具,可以用来做好事,也可以用来做坏事。

        最后,让我们添加一个测试,获取贷款并确保偿还正确的金额:

function testFlashloan() public {

    token0.transfer(address(pair), 1 ether);

    token1.transfer(address(pair), 2 ether);

    pair.mint(address(this));



    uint256 flashloanAmount = 0.1 ether;

    uint256 flashloanFee = (flashloanAmount * 1000) / 997 - flashloanAmount + 1;



    Flashloaner fl = new Flashloaner();



    token1.transfer(address(fl), flashloanFee);



    fl.flashloan(address(pair), 0, flashloanAmount, address(token1));



    assertEq(token1.balanceOf(address(fl)), 0);

    assertEq(token1.balanceOf(address(pair)), 2 ether + flashloanFee);

}

        问题是,Uniswap V2 对闪贷收取了费用:我们必须支付闪贷的交换费。回想一下,我们并没有对闪贷是否偿还进行额外检查,我们只是使用了新的 k 计算方法。这种计算方法会从余额中减去掉期费用!因此,在归还闪贷时,我们必须支付所借金额 + 0.3%(实际略高于 0.3009027%)。

        为了让 Flashloaner 全额还款,我们要计算 flashloanFee 并将其发送到合同中。闪贷还清后,Flashloaner 的余额为 0,而pair合约将获得手续费。

  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

0xweb3q

有钱的捧个钱场,没钱的捧个人场

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值