从技术角度理解闪电贷最基本的方法和原理,并学会如何代码实现。

原文标题:《实现你的第一笔闪电贷合约》
撰文:谈国鹏,Ownbit 创始人

闪电贷(Flash loan)是 DeFi 中一个独特的产品,在传统金融活动中没有与之对应的事物。会编写和使用闪电贷也是一项很重要的技能。

概念

闪电贷是一种无抵押、借贷金额无上限(可以借光池中所有的钱)的贷款。它要求借贷人快借快还,快到还款和借款在同一个交易内(同一个以太坊 transaction)。

闪电贷要求每笔借款在归还时,除了归还本金,还要支付一定利息。该利息(或称手续费)由系统动态计算。

实现

很多人对理解闪电贷有一定的难度。因为这需要了解智能合约执行原理和以太坊状态变化的机制。我们通过自己编写一个闪电贷合约,来理解其背后的运行原理。

以使用 Aave 提供的闪电贷为例,编写一个你自己的合约非常简单,你只需要实现一个叫 executeOperation 的方 法。

1. executeOperation

executeOperation 方法参数中 _reserve 是所借代币的合约地址,如果是 ETH 则是一个特殊的地址(在 Aave 中用 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE 来表示 ETH),_amount 是借款的金额,_fee 是本次借款要偿还的利息。_params 暂时可忽略。

function executeOperation(

    address_reserve,

    uint256_amount,

    uint256_fee,

    bytes calldata_params

    )

    external

{

    require(_amount <= getBalanceInternal(address(this),_reserve), " 借款失败 ");



    //

    // 用借来的 ETH 去赚取更多的 ETH

    //



    // 还款

    uint totalDebt =_amount.add(_fee);

    transferFundsBackToPoolInternal(_reserve, totalDebt);

}

当代码执行到 executeOperation 时,你已经拿到了借来的钱,你主要是写如何利用这些钱赚取更多钱的逻辑。例如你发现同一个交易对 A/ETH 在 DEX1 (价格低) 和 DEX2 (价格高) 的价格差较大,那么你的赚钱逻辑为:

  1. 已借得 100 ETH;
  2. 在价格低的 DEX1 中买入 A;
  3. 在价格高的 DEX2 中卖出 A;
  4. 卖得 110 ETH。

2. 开始借款

executeOperation 是借款成功后要执行的逻辑。而开始借款的逻辑需要用户自己触发(通过发送交易调用合约),或者直接写在合约的构造函数里。例如:

function flashloan() public onlyOwner {

    bytes memory data = "";

    uint amount = 100 ether;

    address asset = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); // 借 ETH



    ILendingPool lendingPool = ILendingPool(addressesProvider.getLendingPool());

    lendingPool.flashLoan(address(this), asset, amount, data);

}

flashLoan() 是真正的借款函数,例子中是借入 100 ETH。闪电贷由此方法触发(开始),也由此方法结束。要完全理解它,我们需要看看 flashLoan() 的内部逻辑。我们摘取最重要的部分。

3. flashLoan() 内部逻辑

flashLoan() 中首先进行各项数据验证,然后便借款给你,紧接着调用你的 executeOperation 的赚钱逻辑,最后验证你是否归还了本金加利息。主要逻辑如下所示:

// 各种数据验证,池中是否还有足够的钱可以贷出?计算手续费等等



        // 将借款转给你

        core.transferToUser(_reserve, userPayable,_amount);



        // 执行你的合约的赚钱逻辑(步骤 1 中所写的函数)

        receiver.executeOperation(_reserve,_amount, amountFee,_params);



        // 检查目前池中余额

        uint256 availableLiquidityAfter =_reserve == EthAddressLib.ethAddress()

            ? address(core).balance

            : IERC20(_reserve).balanceOf(address(core));



        // 通过池中余额变化来确认你是否已经归还了借款+利息

        require(

            availableLiquidityAfter == availableLiquidityBefore.add(amountFee),

            "The actual balance of the protocol is inconsistent"

        );

如果 require() 验证失败,它将 revert 整个交易,让借款无效,从而保证借出方总是安全的。

结语

以上解释了实现第一笔闪电贷最基本的方法和原理。编译源码,以及部署到线上,大家可以参考 Aave 相关官方文档,这里就不一一介绍了。

编写闪电贷合约总体并不复杂。使用闪电贷风险也比较小。如果赚钱逻辑失败,变成了亏钱逻辑,不能完成还款,那么也不用担心借来的巨款(如 1 万 ETH)无力偿还。整个交易将会执行失败,就如同借款从来没有发生过一样。