开始我的第一个闪电贷

该案例最简单的闪电贷合约,不提供任何策略,只是贷款还款操作,往合约转代币即可成功完成一笔闪电贷。

第一步,添加所需要的接口

根据自己需求添加:

import { ILendingPoolAddressesProvider } from "./ILendingPoolAddressesProvider.sol"; // 通过该接口获取LendingPool地址
import { FlashLoanReceiverBase } from "./FlashLoanReceiverBase.sol";   // 接收闪电贷金额的合约必须通过IFlashLoanReceiver 接口实现的 executeOperation() 
import { ILendingPool } from "./ILendingPool.sol";        // 通过该接口调用flashloan
import { IERC20 } from "./IERC20.sol";                    // 涉及到ERC20代币所需

请注意闪电贷将扣取一定量的手续费,平台手续费为0.09%

第二步,设置FlashLoanReceiverBase

FlashLoanReceiverBase.sol

abstract contract FlashLoanReceiverBase is IFlashLoanReceiver {
  using SafeERC20 for IERC20;
  using SafeMath for uint256;

  ILendingPoolAddressesProvider public immutable override ADDRESSES_PROVIDER;
  ILendingPool public immutable override LENDING_POOL;

  constructor(ILendingPoolAddressesProvider provider) public {
    ADDRESSES_PROVIDER = provider;
    LENDING_POOL = ILendingPool(provider.getLendingPool());
  }
}

另请注意,由于欠款将从你的策略同中提取,您的合同必须为 LendingPool 提供补贴以提取这些资金以偿还闪贷债务 + 保费。

第三步,完善策略合约

接收闪贷金额的合约必须通过实现相关的 executeOperation() 函数。 在下面的示例中,我们策略将继承FlashLoanReceiverBase。

以下代码未提供策略代码,仅贷款还款操作,往合约转代币才可成功完成一笔闪电贷。


contract MyfirstFlashLoan is FlashLoanReceiverBase {

    /**
        在你的策略合约收到闪电贷金额后将调用此函数
     */
    function executeOperation(
        address[] calldata assets,
        uint256[] calldata amounts,
        uint256[] calldata premiums,
        address initiator,
        bytes calldata params
    )
        external
        override
        returns (bool)
    {

        // 该策略合约此时已获得所需资金
        // 策略逻辑可以写在这里
        
        // 在上述逻辑的最后,该合约欠闪电贷额度 + 手续费。
        // 因此,请确保您的合同有足够的资金来偿还这些金额。
        
        // 授权 LendingPool 合约提前欠款额度
        for (uint i = 0; i < assets.length; i++) {
            uint amountOwing = amounts[i].add(premiums[i]);
            IERC20(assets[i]).approve(address(LENDING_POOL), amountOwing);
        }
        
        return true;
    }
    
    function myfirstFlashLoanCall() public {
        // 接收贷款地址
        address receiverAddress = address(this);

        // 贷款币币种,仅贷一个币种
        address[] memory assets = new address[](1);
        assets[0] = address(INSERT_ASSET_ONE_ADDRESS);

        // 贷款币数量
        uint256[] memory amounts = new uint256[](1);
        amounts[0] = INSERT_ASSET_ONE_AMOUNT;

        // 贷款模式
        uint256[] memory modes = new uint256[](1);
        modes[0] = INSERT_ASSET_ONE_MODE;

        address onBehalfOf = address(this);
        bytes memory params = "";
        uint16 referralCode = 0;

        LENDING_POOL.flashLoan(
            receiverAddress,
            assets,
            amounts,
            modes,
            onBehalfOf,
            params,
            referralCode
        );
    }
}

第四步,合约部署

部署工具可自由选择,remix,truffle,hardhat

部署参数初始值设置为LendingPoolAddressesProvider合约地址:

0xF7CeaE542b00d8CBa6D7bcb3a5AbcA7A2E19829a

MyfirstFlashLoan合约已部署在heco链上并在浏览器验证,任何人都可以调用,准备好手续费即可

第五步,调用闪电贷

调用myfirstFlashLoanCall(address asset, uint256 num, uint256 mode)函数:

参考交易:

编码和解码参数

如果您想将参数传递到您的闪电贷函数中,您首先需要对它们进行编码,然后在您的 executeOperation() 中对其进行解码。

编码

如果您要进行编码,则可以使用内置的abi.encode():

// Encoding an address and a uint
bytes memory params = abi.encode(address(this), 1234);

如果是链下操作,例如 web3.js 可以调用abi.encodeParameters():

const params = web3.eth.abi.encodeParameters(
	["bytes32"],
	[
		web3.utils.utf8ToHex("some_value")
	]
)

解码

在你的 executeOperation() 中解码时,你需要使用 abi.decode():

(bytes32 someValue) = abi.decode(params, (bytes32));

用法可参考闪电贷清算合约代码

预留提币接口

function rescueTokens(IERC20 token) external onlyOwner {
    token.transfer(owner(), token.balanceOf(address(this)));
  }

Last updated