Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

- perf: code change that improves performance & test with sepolia #1091

Merged
merged 1 commit into from
Apr 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 142 additions & 9 deletions basic/20-flash-loan/aavev3/README-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,156 @@
3. 如果欠款不可用(由于缺乏余额或批准或债务抵押品不足),则交易将被撤销。
4. 以上所有发生在 1 笔交易中(因此在单个以太坊区块中)。

## 闪电贷智能合约逻辑说明
### Flashloan 说明
来看下 `contracts/SimpleFlashLoan.sol` (aave v3版本)
```solidity

# 步骤
- 安装依赖项
import "https://github.com/aave/aave-v3-core/blob/master/contracts/flashloan/base/FlashLoanSimpleReceiverBase.sol";
import "https://github.com/aave/aave-v3-core/blob/master/contracts/interfaces/IPoolAddressesProvider.sol";
import "https://github.com/aave/aave-v3-core/blob/master/contracts/dependencies/openzeppelin/contracts/IERC20.sol";

constructor(address _addressProvider) FlashLoanSimpleReceiverBase(IPoolAddressesProvider(_addressProvider)) {

owner = payable(msg.sender);
}



```

这是导入所必要的依赖项,Flashloan 合约继承自`FlashLoanSimpleReceiverBase`,它是一个抽象合约,提供了一些有用的方法,比如如偿还闪电贷的方式。
SimpleFlashloan.sol 构造函数接受 Aave 的一借贷池的地址(deploy_aave_flashloan.js 脚本中配置对应的PoolAddressesProvider)。

创建地址类型的变量所有者(有些代码中并未设置此owner,意味着所有人都可以调用该合约),并将其设为可支付。

aave v3 对一些方法做了简化,因此有些方法名称后面加了Simple关键字

aave v3 合约名称相较v1有了变化(其实内部还是调用之前老的合约,可能是觉得之前的名字太长不好记忆,就修改了些), 具体变化参考:[AAVEf : supported-networks](https://docs.aave.com/developers/deployed-contracts/v3-testnet-addresses)
本文以Sepolia测试网络为例,选择Ethereum Sepolia,对于不同的测试网络,需要在deploy_aave_flashloan.js 脚本中配置对应的PoolAddressesProvider
<center><img src="./img/aave-v3-namechange.png?raw=true" /></center>
<center><img src="./img/sepolia-testnet.png?raw=true" /></center>



### flashloanSimple 方法
requestFlashLoan 方法中的核心是调用 POOL.flashLoanSimple(), POOL 变量被定义在FlashLoanSimpleReceiverBase中,是IPool 的实例
```solidity
function flashLoanSimple(
address receiverAddress,
address asset,
uint256 amount,
bytes calldata params,
uint16 referralCode
) external;

```

如下是 FlashLoanSimpleReceiverBase 合约中的定义(大致了解即可)
```solidity
abstract contract FlashLoanSimpleReceiverBase is IFlashLoanSimpleReceiver {
IPoolAddressesProvider public immutable override ADDRESSES_PROVIDER;
IPool public immutable override POOL;

constructor(IPoolAddressesProvider provider) {
ADDRESSES_PROVIDER = provider;
POOL = IPool(provider.getPool());
}

```

通过连接 Aave 的FlashLoanSimpleReceiverBase合约来启动SimpleFlashLoan合约

requestFlashLoan 的参数`_asset`是我们要用闪电贷借款的资产地址,比如 ETH, DAI 或者 USDC。
<center><img src="./img/token_address.png?raw=true" /></center>

`uint amount = 1 ether;`
amount 是我们将借用的代币数量,如果把 ETH 地址传过去,我们就会借到 1 个 ETH,即 10^18 wei。如果把 DAI 地址传给 `_asset`,我们就会借到 1 个 DAI。
还有两个我们不会使用但 Aave 合约需要的参数。

params - 是贷款的一些附加信息,例如消息或短语
ReferralCode - 暂时不明白这个参数的含义

FlashLoanSimpleReceiverBase 内部的 _addressProvider 变量将被传入到 IPoolAddressesProvider 接口中,之后将在那里调用 getPool() 函数,该函数将返回代理池(实现闪贷所有功能的合约)的地址。
而这一切都将保存在 POOL 变量中,该变量需要像我们的地址一样被包装在 IPOOL 接口中,以便访问合约的 Proxy Pool 功能。

### executeOperation 方法


```solidity
function executeOperation(
address asset,
uint256 amount,
uint256 premium,
address initiator,
bytes calldata params
) external override returns(bool){
str = "New Logic in FlashLoans";

//
// Your logic goes here.
// !! Ensure that *this contract* has enough of `_reserve` funds to payback the `_fee` !!
//
uint256 totalAmount = amount + premium;
IERC20(asset).approve(address(POOL),totalAmount);
return true;

}

}

```

在使用 `requestFlashLoan` 函数触发有效的闪电贷后,`executeOperation` 函数的所有参数都将被自动传递,

接下来,我们可以插入任何想要执行的逻辑。在这个步骤中,我们拥有了来自闪电贷的所有可用资金,因此,我们可以尝试套利机会。

我们在用完闪电贷后,就需要偿还资金了。

`uint totalAmount = amount + premium`

在这里,我们要计算还多少钱,也就是 **借款金额 + Aave借贷手续费(通常为金额的0.05%)** , premium 也可以固定设置 amount * 0.05%。

最后一步就是调用 `IERC20(asset).approve(address(POOL),totalAmount)` 来偿还闪电贷。

## AAVE FAUCET
我们的智能合约中需要一些 Testnet USDC(本文使用USD C作为贷款货币) 来支付闪电贷的利息。进入Aave的水龙头,选择Ethereum Market,连接MetaMask,点击USDC附近的 Faucet 即可获取USDC。
<center><img src="./img/aave_faucet.png?raw=true" /></center>


## 操作步骤
- 安装依赖
```shell
#Node 版本v20.11.0
npm init
npm install --save-dev hardhat
npx hardhat
npm install --save-dev "hardhat@^2.21.0" "@nomicfoundation/hardhat-toolbox@^2.0.0"
```

- 安装aav核心包
```shell
yarn
npm install @aave/core-v3
```

- 配置环境
```外壳
- 配置环境变量
```shell
cp .env.example .env
# 在 .env 中设置 INFURA_IDPRIVATE_KEY、ETHERSCAN_APIKEY
# 在 .env 中配置 INFURA_ID , PRIVATE_KEY
```

- 启动闪贷 && 借贷->铸币->还贷
```外壳
npx hardhat scripts/deploy_aavev3_flashloan.js --network goerli
- 部署合约
```shell
# 这里使用 sepolia 测试网进行测试
npx hardhat run scripts/deploy_aave_flashloan.js --network sepolia

```
交易完成后,根据打印的 Transaction hash 登录 [Etherscan](https://sepolia.etherscan.io/)查看交易详情

<center><img src="./img/etherscan_query.png?raw=true" /></center>
<center><img src="./img/transaction_detail.png?raw=true" /></center>

恭喜您执行闪贷;您借了一笔无抵押贷款。在本指南中,我们了解了闪电贷,创建并部署了 Aave V3 闪电贷合约,借入了一些无需抵押的代币,并收取利息费用返还

## 参考链接
- 闪贷文档:https://docs.aave.com/developers/guides/flash-loans
Expand Down
138 changes: 131 additions & 7 deletions basic/20-flash-loan/aavev3/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,148 @@
3. If the amount owing is not available (due to a lack of balance or approvaln or insufficient collateral for debt), then the transaction is reverted.
4. All of the above happens in 1 transaction (hence in a single ethereum block).

## explanatory of logical of flash loan smart contract
### Flashloan illustrate
let's get into `contracts/SimpleFlashLoan.sol` (aave version 3)
```solidity

## Steps
- Install dependencies
import "https://github.com/aave/aave-v3-core/blob/master/contracts/flashloan/base/FlashLoanSimpleReceiverBase.sol";
import "https://github.com/aave/aave-v3-core/blob/master/contracts/interfaces/IPoolAddressesProvider.sol";
import "https://github.com/aave/aave-v3-core/blob/master/contracts/dependencies/openzeppelin/contracts/IERC20.sol";

constructor(address _addressProvider) FlashLoanSimpleReceiverBase(IPoolAddressesProvider(_addressProvider)) {

owner = payable(msg.sender);
}


```

This is a necessary dependency for importing, and the Flashloan contract inherits from the `FlashLoanSimpleReceiverBase`. It is an abstract contract that provides some useful methods, such as repaying flash loans.

The SimpleFlashloan.sol constructor accepts the address of a loan pool from Aave (configured with the corresponding PoolAddressesProvider in the deploy.aave-flashloan.js script).

Create a variable owner for the address type (some codes do not set this owner, which means everyone can call the contract) and make it payable.

Aave v3 has simplified some methods, so some method names have the `Simple` keyword added after them

The name of the aave v3 contract has changed compared to v1 (in fact, it still calls the old contract internally, which may be because the previous name is too long and difficult to remember, so it has been modified). Please refer to [AAVEf: supported networks](https://docs.aave.com/developers/deployed-contracts/v3-testnet-addresses)

This article takes the Sepolia test network as an example and selects `Ethereum Sepolia`. For different test networks, the corresponding `PoolAddressesProvider` needs to be configured in the `deploy_1ave_flashloan.js` script
<center><img src="./img/aave-v3-namechange.png?raw=true" /></center>
<center><img src="./img/sepolia-testnet.png?raw=true" /></center>


### The flashloan function
The core of the requestFlashLoan method is to call POOL. flashLoanSimple(), where the POOL variable is defined in FlashLoanSimpleReceiverBase, it is an instance of IPool
```solidity
function flashLoanSimple(
address receiverAddress,
address asset,
uint256 amount,
bytes calldata params,
uint16 referralCode
) external;

```
The following is the definition in the FlashLoanSimpleReceiverBase contract (a rough understanding is sufficient)

```solidity
abstract contract FlashLoanSimpleReceiverBase is IFlashLoanSimpleReceiver {
IPoolAddressesProvider public immutable override ADDRESSES_PROVIDER;
IPool public immutable override POOL;

constructor(IPoolAddressesProvider provider) {
ADDRESSES_PROVIDER = provider;
POOL = IPool(provider.getPool());
}

```

Start the SimpleFlashLoan contract by connecting to Aave's FlashLoanSimpleReceiverBase contract

The parameter 'asset' of requestFlashLoan is the asset address where we want to borrow using Flash Loan, such as ETH, DAI, or USDC.
<center><img src="./img/token_address.png?raw=true" /></center>


`uint amount = 1 ether;`
In here, we define the loan count unit as `ether`, if we pass the ETH address in, we will take loan 1 ETH , thus 10^18 wei. And if we send DAI address to `_asset`, we will get loan 1 DAI!

There are two parameters that we will not use but are required by the Aave contract.

Params - Additional information about loans, such as messages or phrases
ReferralCode - I currently do not understand the meaning of this parameter

The addressProvider variable inside FlashLoanSimpleReceiverBase will be passed into the IPoolAddressesProvider interface, where the getPool() function will be called, which will return the address of the proxy pool (the contract that implements all functions of flash lending).

And all of this will be saved in the POOL variable, which needs to be wrapped in the IPOOL interface like our address in order to access the Proxy Pool function of the contract.

### The executeOperation function
```solidity
function executeOperation(
address asset,
uint256 amount,
uint256 premium,
address initiator,
bytes calldata params
) external override returns(bool){
str = "New Logic in FlashLoans";

//
// Your logic goes here.
// !! Ensure that *this contract* has enough of `_reserve` funds to payback the `_fee` !!
//
uint256 totalAmount = amount + premium;
IERC20(asset).approve(address(POOL),totalAmount);
return true;

}

}
```

When we triggered valid flashloan with `flashLoan` function, the all paramaters in `executeOperation` will be passed automatically

Then, we can insert any logical we want to excute. In this step, we had have all usable fund from flashloan, and we can use it to have a arbitrage.

After we used flashloan, it's repay time.

`uint totalAmount = amount + premium`

In here, we need caculate how much we need repay, which is **loan amount + 0.05% of loan amount (Borrowing charge of Aave flashloan)** , Premium can also be set to a fixed amount * 0.05%.

The final step is calling `IERC20(asset).approve(address(POOL),totalAmount)` to repay the flashloan.

## AAVE FAUCET
Our smart contract requires some Testnet USDC (using USD C as the loan currency in this article) to pay the interest on Flash Loan. Go to Aave's faucet, select Ethereum Market, connect to MetaMask, and click on Faucet near USDC to obtain USDC.
<center><img src="./img/aave_faucet.png?raw=true" /></center>

## Steps
- Install dependencies
```shell
yarn
# Node version v20.11.0
npm init
npm install --save-dev hardhat
npx hardhat
npm install --save-dev "hardhat@^2.21.0" "@nomicfoundation/hardhat-toolbox@^2.0.0"
```

- Config the envrioument
- Config the params of envrioument
```shell
cp .env.example .env
# set INFURA_ID , PRIVATE_KEY, ETHERSCAN_APIKEY in .env
# set INFURA_ID , PRIVATE_KEY in .env
```

- Start flashloan && mint flashLoan
- Deploy the contract
```shell
npx hardhat run scripts/deploy_aavev3_flashloan.js --network goerli
# we use test net sepolia to have test
npx hardhat run scripts/deploy_aave_flashloan.js --network sepolia
```
After the transaction is completed, log in to [Etherscan](https://sepolia.etherscan.io/) based on the printed Transaction hash view transaction details
<center><img src="./img/etherscan_query.png?raw=true" /></center>
<center><img src="./img/transaction_detail.png?raw=true" /></center>

Congratulations on executing the flash loan; You borrowed an unsecured loan. In this guide, we learned about Flash Loan, created and deployed the Aave V3 Flash Loan contract, borrowed some unsecured tokens, and charged interest fee refunds
## Reference link
- Detail of flash loan:https://docs.aave.com/developers/guides/flash-loans
- V3 Testnet Addresses:https://docs.aave.com/developers/deployed-contracts/v3-testnet-addresses
Expand Down
51 changes: 27 additions & 24 deletions basic/20-flash-loan/aavev3/contracts/MintFlashLoan.sol
Original file line number Diff line number Diff line change
@@ -1,31 +1,29 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.8.10;

import {IPoolAddressesProvider} from "@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol";
import {IPool} from "@aave/core-v3/contracts/interfaces/IPool.sol";
import {IFlashLoanSimpleReceiver} from "@aave/core-v3/contracts/flashloan/interfaces/IFlashLoanSimpleReceiver.sol";

import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";

interface IFaucet {
function mint(address _token, uint256 _amount) external;
}

abstract contract FlashLoanSimpleReceiverBase is IFlashLoanSimpleReceiver {
IPoolAddressesProvider public immutable override ADDRESSES_PROVIDER;
IPool public immutable override POOL;
IFaucet public immutable FAUCET;

constructor(IPoolAddressesProvider provider, IFaucet faucet) {
ADDRESSES_PROVIDER = provider;
POOL = IPool(provider.getPool());
FAUCET = faucet;
}
}
import "@aave/core-v3/contracts/flashloan/base/FlashLoanSimpleReceiverBase.sol";
import "@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol";
import "@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20.sol";

// interface IFaucet {
// function mint(address _token, uint256 _amount) external;
// }

// abstract contract FlashLoanSimpleReceiverBase is IFlashLoanSimpleReceiver {
// IPoolAddressesProvider public immutable override ADDRESSES_PROVIDER;
// IPool public immutable override POOL;
// IFaucet public immutable FAUCET;

// constructor(IPoolAddressesProvider provider, IFaucet faucet) {
// ADDRESSES_PROVIDER = provider;
// POOL = IPool(provider.getPool());
// FAUCET = faucet;
// }
// }

contract MintFlashLoan is FlashLoanSimpleReceiverBase {
constructor(IPoolAddressesProvider _addressProvider, IFaucet _faucet)
FlashLoanSimpleReceiverBase(_addressProvider, _faucet)
constructor(address _addressProvider)
FlashLoanSimpleReceiverBase(IPoolAddressesProvider(_addressProvider))
{}

function executeOperation(
Expand All @@ -39,7 +37,7 @@ contract MintFlashLoan is FlashLoanSimpleReceiverBase {

// Approve the LendingPool contract allowance to *pull* the owed amount
uint256 amountOwed = amount + premium;
FAUCET.mint(asset, premium);
// FAUCET.mint(asset, premium);
IERC20(asset).approve(address(POOL), amountOwed);

return true;
Expand All @@ -53,4 +51,9 @@ contract MintFlashLoan is FlashLoanSimpleReceiverBase {

POOL.flashLoanSimple(receiverAddress, asset, amount, params, referralCode);
}

function getBalance(address _tokenAddress) external view returns(uint256) {
return IERC20(_tokenAddress).balanceOf(address(this));
}

}
Loading
Loading