温馨提示:这篇文章已超过854天没有更新,请注意相关的内容是否还可用!
从技术角度理解闪电贷最基本的方法和原理,并学会如何代码实现。
原文标题:《实现你的第一笔闪电贷合约》
撰文:谈国鹏,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 (价格高) 的价格差较大,那么你的赚钱逻辑为:
- 已借得 100 ETH;
- 在价格低的 DEX1 中买入 A;
- 在价格高的 DEX2 中卖出 A;
- 卖得 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)无力偿还。整个交易将会执行失败,就如同借款从来没有发生过一样。
来源链接:mp.weixin.qq.com