├── requirements.txt ├── .gitattributes ├── .vscode └── settings.json ├── .gitignore ├── README.md ├── scripts ├── pullTokens.py ├── helpful_scripts.py ├── deploy_flashloanContract.py └── run_flashloan.py ├── interfaces ├── IUniswapV2Factory.sol ├── IFlashLoanReceiverV2.sol ├── IUniswapV2Router02.sol ├── ILendingPoolAddressesProviderV2.sol ├── IUniswapV2Pair.sol ├── IUniswapV2Router01.sol └── ILendingPoolV2.sol ├── contracts ├── utils │ ├── FlashLoanReceiverBaseV2.sol │ └── Withdrawable.sol └── FlashloanV2.sol ├── tests ├── test_flashloan_v1.py ├── test_flashloan_v2.py └── conftest.py ├── libraries └── DataTypes.sol └── brownie-config.yaml /requirements.txt: -------------------------------------------------------------------------------- 1 | eth-brownie>=1.12.0,<2.0.0 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity 2 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "solidity.defaultCompiler": "localFile" 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | .history 3 | .hypothesis/ 4 | build/ 5 | reports/ 6 | .env 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Aave => Sushiswap or Uniswap => Aave 2 | 3 | 1. Get a flashloan from Aave. 4 | 2. Check which is profitable, then sell on either Uniswap or Sushiswap. 5 | 3. Pay back Aave and keep the profit. -------------------------------------------------------------------------------- /scripts/pullTokens.py: -------------------------------------------------------------------------------- 1 | from brownie import FlashloanV2, config, network 2 | from scripts.helpful_scripts import get_account 3 | 4 | 5 | asset = config["networks"][network.show_active()]["dai_token"] 6 | 7 | 8 | def main(): 9 | acct = get_account() 10 | flashloan_contract = FlashloanV2[len(FlashloanV2) - 1] 11 | 12 | flashloan_contract.pullTokens(asset, {"from": acct}) 13 | print("pull successful!") 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /scripts/helpful_scripts.py: -------------------------------------------------------------------------------- 1 | from brownie import accounts, config, network 2 | 3 | LOCAL_BLOCKCHAIN_ENVIRONMENTS = [ 4 | "mainnet-fork", 5 | "mainnet-fork-dev" 6 | ] 7 | 8 | 9 | def get_account(index=None, id=None): 10 | if index: 11 | return accounts[index] 12 | if network.show_active() in LOCAL_BLOCKCHAIN_ENVIRONMENTS: 13 | return accounts[0] 14 | if id: 15 | return accounts.load(id) 16 | if network.show_active() in config["networks"]: 17 | return accounts.add(config["wallets"]["from_key"]) 18 | return None -------------------------------------------------------------------------------- /interfaces/IUniswapV2Factory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | interface IUniswapV2Factory { 5 | event PairCreated(address indexed token0, address indexed token1, address pair, uint); 6 | function getPair(address tokenA, address tokenB) external view returns (address pair); 7 | function allPairs(uint) external view returns (address pair); 8 | function allPairsLength() external view returns (uint); 9 | function feeTo() external view returns (address); 10 | function feeToSetter() external view returns (address); 11 | function createPair(address tokenA, address tokenB) external returns (address pair); 12 | } 13 | -------------------------------------------------------------------------------- /interfaces/IFlashLoanReceiverV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: agpl-3.0 2 | pragma solidity ^0.8.0; 3 | 4 | import { ILendingPoolAddressesProviderV2 } from './ILendingPoolAddressesProviderV2.sol'; 5 | import { ILendingPoolV2 } from './ILendingPoolV2.sol'; 6 | 7 | /** 8 | * @title IFlashLoanReceiverV2 interface 9 | * @notice Interface for the Aave fee IFlashLoanReceiver. 10 | * @author Aave 11 | * @dev implement this interface to develop a flashloan-compatible flashLoanReceiver contract 12 | **/ 13 | interface IFlashLoanReceiverV2 { 14 | function executeOperation( 15 | address[] calldata assets, 16 | uint256[] calldata amounts, 17 | uint256[] calldata premiums, 18 | address initiator, 19 | bytes calldata params 20 | ) external returns (bool); 21 | 22 | function ADDRESSES_PROVIDER() external view returns (ILendingPoolAddressesProviderV2); 23 | 24 | function LENDING_POOL() external view returns (ILendingPoolV2); 25 | } -------------------------------------------------------------------------------- /scripts/deploy_flashloanContract.py: -------------------------------------------------------------------------------- 1 | from brownie import FlashloanV2, config, network 2 | from scripts.helpful_scripts import get_account 3 | 4 | # AAVE_LENDING_POOL_ADDRESS_PROVIDER = "0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5" 5 | ETHERSCAN_ADDRESS_URL = "https://kovan.etherscan.io/address/{}" 6 | 7 | 8 | def main(): 9 | """ 10 | Deploy the `FlashloanV2` contract with `accounts[0]`. 11 | """ 12 | 13 | aave_lending_pool_v2 = config["networks"][network.show_active()]["aave_lending_pool_v2"] 14 | uniswap_router = config["networks"][network.show_active()]["uniswap_router"] 15 | sushiswap_router = config["networks"][network.show_active()]["sushiswap_router"] 16 | 17 | 18 | acct = get_account() 19 | 20 | flashloan = FlashloanV2.deploy( 21 | aave_lending_pool_v2, 22 | uniswap_router, 23 | sushiswap_router, 24 | {"from": acct}, 25 | ) 26 | print("You did it! View your deployed contract at here: " + ETHERSCAN_ADDRESS_URL.format(flashloan.address)) 27 | # print("flashloan Contract deployed at: ", flashloan.address) 28 | return flashloan 29 | -------------------------------------------------------------------------------- /contracts/utils/FlashLoanReceiverBaseV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: agpl-3.0 2 | pragma solidity ^0.8.0; 3 | 4 | import { IERC20 } from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; 5 | import { IFlashLoanReceiverV2 } from './../../interfaces/IFlashLoanReceiverV2.sol'; 6 | import { ILendingPoolAddressesProviderV2 } from './../../interfaces/ILendingPoolAddressesProviderV2.sol'; 7 | import { ILendingPoolV2 } from './../../interfaces/ILendingPoolV2.sol'; 8 | 9 | /** 10 | !!! 11 | Never keep funds permanently on your FlashLoanReceiverBase contract as they could be 12 | exposed to a 'griefing' attack, where the stored funds are used by an attacker. 13 | !!! 14 | */ 15 | 16 | abstract contract FlashLoanReceiverBaseV2 is IFlashLoanReceiverV2 { 17 | 18 | ILendingPoolAddressesProviderV2 public immutable override ADDRESSES_PROVIDER; 19 | ILendingPoolV2 public immutable override LENDING_POOL; 20 | 21 | constructor(address provider) { 22 | ADDRESSES_PROVIDER = ILendingPoolAddressesProviderV2(provider); 23 | LENDING_POOL = ILendingPoolV2(ILendingPoolAddressesProviderV2(provider).getLendingPool()); 24 | } 25 | 26 | receive() payable external {} 27 | } -------------------------------------------------------------------------------- /tests/test_flashloan_v1.py: -------------------------------------------------------------------------------- 1 | # NOTE: The following tests begin by transferring assets to the deployed flashloan 2 | # contract. this ensures that the tests pass with the base Flashloan implementation, 3 | # i.e. one that does not implement any custom logic. 4 | 5 | # The initial transfer should be removed prior to testing your final implementation. 6 | 7 | 8 | def test_eth_flashloan(accounts, ETH, flashloan_v1): 9 | """ 10 | Test a flashloan that borrows Ethereum. 11 | """ 12 | 13 | # transfer ether to the flashloan contract 14 | accounts[0].transfer(flashloan_v1, "2 ether") 15 | 16 | flashloan_v1.flashloan(ETH, {"from": accounts[0]}) 17 | 18 | def test_dai_flashloan(Contract, accounts, DAI, flashloan_v1): 19 | """ 20 | Test a flashloan that borrows DAI. 21 | 22 | To use a different asset, swap DAI with any of the fixture names in `tests/conftest.py` 23 | """ 24 | 25 | # purchase DAI on uniswap 26 | uniswap_dai = Contract.from_explorer("0x2a1530C4C41db0B0b2bB646CB5Eb1A67b7158667") 27 | uniswap_dai.ethToTokenSwapInput( 28 | 1, 10000000000, {"from": accounts[0], "value": "2 ether"} 29 | ) 30 | 31 | # transfer DAI to the flashloan contract 32 | balance = DAI.balanceOf(accounts[0]) 33 | DAI.transfer(flashloan_v1, balance, {"from": accounts[0]}) 34 | 35 | flashloan_v1.flashloan(DAI, {"from": accounts[0]}) 36 | -------------------------------------------------------------------------------- /scripts/run_flashloan.py: -------------------------------------------------------------------------------- 1 | from brownie import FlashloanV2, config, network, web3 2 | from scripts.helpful_scripts import get_account 3 | 4 | ETHERSCAN_TX_URL = "https://kovan.etherscan.io/tx/{}" 5 | 6 | # Factory addresses 7 | uniswap_factory = config["networks"][network.show_active()]["uniswap_factory"] 8 | sushiswap_factory = config["networks"][network.show_active()]["sushiswap_factory"] 9 | 10 | 11 | #flash_Asset is the Aave borrowed token 12 | flash_Asset = config["networks"][network.show_active()]["dai_token"] 13 | flash_amount = web3.toWei(50, "ether") #50,000,000,000,000,000,000 wei 14 | 15 | #swapping_pair is the asset you wish to swap with. This is not the aave borrowed token 16 | swapping_pair = config["networks"][network.show_active()]["weth"] 17 | 18 | def main(): 19 | acct = get_account() 20 | flashloan_contract = FlashloanV2[len(FlashloanV2) - 1] 21 | 22 | 23 | flashloan_tx = flashloan_contract.startTransaction( 24 | flash_Asset, 25 | flash_amount, 26 | swapping_pair, 27 | uniswap_factory, 28 | {"from": acct, "gas_limit": 12000000, "allow_revert": True, "gas_price": 20000000000} 29 | ) 30 | 31 | if network.show_active() == "kovan": 32 | print("You did it! View your tx here: " + ETHERSCAN_TX_URL.format(flashloan_tx.txid)) 33 | print("Flashloan success!") 34 | return flashloan_contract 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /contracts/utils/Withdrawable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.0 <=0.8.13; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 6 | import "@openzeppelin/contracts/access/Ownable.sol"; 7 | 8 | /** 9 | Ensures that any contract that inherits from this contract is able to 10 | withdraw funds that are accidentally received or stuck. 11 | */ 12 | 13 | contract Withdrawable is Ownable { 14 | using SafeERC20 for ERC20; 15 | address constant ETHER = address(0); 16 | 17 | event LogWithdraw( 18 | address indexed _from, 19 | address indexed _assetAddress, 20 | uint amount 21 | ); 22 | 23 | /** 24 | * @dev Withdraw asset. 25 | * @param _assetAddress Asset to be withdrawn. 26 | */ 27 | function withdraw(address _assetAddress) public onlyOwner { 28 | uint assetBalance; 29 | if (_assetAddress == ETHER) { 30 | address self = address(this); // workaround for a possible solidity bug 31 | assetBalance = self.balance; 32 | payable(msg.sender).transfer(assetBalance); 33 | } else { 34 | assetBalance = ERC20(_assetAddress).balanceOf(address(this)); 35 | ERC20(_assetAddress).safeTransfer(msg.sender, assetBalance); 36 | } 37 | emit LogWithdraw(msg.sender, _assetAddress, assetBalance); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /interfaces/IUniswapV2Router02.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import './IUniswapV2Router01.sol'; 6 | 7 | interface IUniswapV2Router02 is IUniswapV2Router01 { 8 | function removeLiquidityETHSupportingFeeOnTransferTokens( 9 | address token, 10 | uint liquidity, 11 | uint amountTokenMin, 12 | uint amountETHMin, 13 | address to, 14 | uint deadline 15 | ) external returns (uint amountETH); 16 | function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens( 17 | address token, 18 | uint liquidity, 19 | uint amountTokenMin, 20 | uint amountETHMin, 21 | address to, 22 | uint deadline, 23 | bool approveMax, uint8 v, bytes32 r, bytes32 s 24 | ) external returns (uint amountETH); 25 | 26 | function swapExactTokensForTokensSupportingFeeOnTransferTokens( 27 | uint amountIn, 28 | uint amountOutMin, 29 | address[] calldata path, 30 | address to, 31 | uint deadline 32 | ) external; 33 | function swapExactETHForTokensSupportingFeeOnTransferTokens( 34 | uint amountOutMin, 35 | address[] calldata path, 36 | address to, 37 | uint deadline 38 | ) external payable; 39 | function swapExactTokensForETHSupportingFeeOnTransferTokens( 40 | uint amountIn, 41 | uint amountOutMin, 42 | address[] calldata path, 43 | address to, 44 | uint deadline 45 | ) external; 46 | } 47 | -------------------------------------------------------------------------------- /libraries/DataTypes.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: agpl-3.0 2 | pragma solidity ^0.8.0; 3 | 4 | /** 5 | * @dev This is the Aave V2 DataTypes library. 6 | */ 7 | library DataTypes { 8 | // refer to the whitepaper, section 1.1 basic concepts for a formal description of these properties. 9 | struct ReserveData { 10 | //stores the reserve configuration 11 | ReserveConfigurationMap configuration; 12 | //the liquidity index. Expressed in ray 13 | uint128 liquidityIndex; 14 | //variable borrow index. Expressed in ray 15 | uint128 variableBorrowIndex; 16 | //the current supply rate. Expressed in ray 17 | uint128 currentLiquidityRate; 18 | //the current variable borrow rate. Expressed in ray 19 | uint128 currentVariableBorrowRate; 20 | //the current stable borrow rate. Expressed in ray 21 | uint128 currentStableBorrowRate; 22 | uint40 lastUpdateTimestamp; 23 | //tokens addresses 24 | address aTokenAddress; 25 | address stableDebtTokenAddress; 26 | address variableDebtTokenAddress; 27 | //address of the interest rate strategy 28 | address interestRateStrategyAddress; 29 | //the id of the reserve. Represents the position in the list of the active reserves 30 | uint8 id; 31 | } 32 | 33 | struct ReserveConfigurationMap { 34 | //bit 0-15: LTV 35 | //bit 16-31: Liq. threshold 36 | //bit 32-47: Liq. bonus 37 | //bit 48-55: Decimals 38 | //bit 56: Reserve is active 39 | //bit 57: reserve is frozen 40 | //bit 58: borrowing is enabled 41 | //bit 59: stable rate borrowing enabled 42 | //bit 60-63: reserved 43 | //bit 64-79: reserve factor 44 | uint256 data; 45 | } 46 | 47 | struct UserConfigurationMap { 48 | uint256 data; 49 | } 50 | 51 | enum InterestRateMode {NONE, STABLE, VARIABLE} 52 | } -------------------------------------------------------------------------------- /tests/test_flashloan_v2.py: -------------------------------------------------------------------------------- 1 | # NOTE: The following tests begin by transferring assets to the deployed flashloan 2 | # contract. this ensures that the tests pass with the base Flashloan implementation, 3 | # i.e. one that does not implement any custom logic. 4 | 5 | # The initial transfer should be removed prior to testing your final implementation. 6 | 7 | 8 | def test_eth_flashloan(accounts, WETH, flashloan_v2): 9 | """ 10 | Test a flashloan that borrows Ethereum. 11 | """ 12 | 13 | # transfer ether to the flashloan contract 14 | accounts[0].transfer(WETH, "2 ether") 15 | WETH.transfer(flashloan_v2, "2 ether", {"from": accounts[0]}) 16 | 17 | flashloan_v2.flashloan(WETH, {"from": accounts[0]}) 18 | 19 | 20 | def test_dai_flashloan(Contract, accounts, DAI, flashloan_v2): 21 | """ 22 | Test a flashloan that borrows DAI. 23 | 24 | To use a different asset, swap DAI with any of the fixture names in `tests/conftest.py` 25 | """ 26 | 27 | # purchase DAI on uniswap 28 | uniswap_dai = Contract.from_explorer("0x2a1530C4C41db0B0b2bB646CB5Eb1A67b7158667") 29 | uniswap_dai.ethToTokenSwapInput( 30 | 1, 10000000000, {"from": accounts[0], "value": "2 ether"} 31 | ) 32 | 33 | # transfer DAI to the flashloan contract 34 | balance = DAI.balanceOf(accounts[0]) 35 | DAI.transfer(flashloan_v2, balance, {"from": accounts[0]}) 36 | 37 | flashloan_v2.flashloan(DAI, {"from": accounts[0]}) 38 | 39 | 40 | def test_batch_eth_dai_flashloan(Contract, accounts, DAI, WETH, flashloan_v2): 41 | """ 42 | Test a flashloan that borrows WETH and DAI. 43 | """ 44 | 45 | # purchase DAI on uniswap 46 | uniswap_dai = Contract.from_explorer("0x2a1530C4C41db0B0b2bB646CB5Eb1A67b7158667") 47 | uniswap_dai.ethToTokenSwapInput( 48 | 1, 10000000000, {"from": accounts[0], "value": "2 ether"} 49 | ) 50 | 51 | # transfer DAI to the flashloan contract 52 | balance = DAI.balanceOf(accounts[0]) 53 | DAI.transfer(flashloan_v2, balance, {"from": accounts[0]}) 54 | 55 | # transfer ether to the flashloan contract 56 | accounts[0].transfer(WETH, "2 ether") 57 | WETH.transfer(flashloan_v2, "2 ether", {"from": accounts[0]}) 58 | 59 | flashloan_v2.flashloan([WETH, DAI], ["1 ether", "1 ether"], {"from": accounts[0]}) 60 | -------------------------------------------------------------------------------- /interfaces/ILendingPoolAddressesProviderV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: agpl-3.0 2 | pragma solidity ^0.8.0; 3 | 4 | /** 5 | * @title LendingPoolAddressesProvider contract 6 | * @dev Main registry of addresses part of or connected to the protocol, including permissioned roles 7 | * - Acting also as factory of proxies and admin of those, so with right to change its implementations 8 | * - Owned by the Aave Governance 9 | * @author Aave 10 | **/ 11 | interface ILendingPoolAddressesProviderV2 { 12 | event MarketIdSet(string newMarketId); 13 | event LendingPoolUpdated(address indexed newAddress); 14 | event ConfigurationAdminUpdated(address indexed newAddress); 15 | event EmergencyAdminUpdated(address indexed newAddress); 16 | event LendingPoolConfiguratorUpdated(address indexed newAddress); 17 | event LendingPoolCollateralManagerUpdated(address indexed newAddress); 18 | event PriceOracleUpdated(address indexed newAddress); 19 | event LendingRateOracleUpdated(address indexed newAddress); 20 | event ProxyCreated(bytes32 id, address indexed newAddress); 21 | event AddressSet(bytes32 id, address indexed newAddress, bool hasProxy); 22 | 23 | function getMarketId() external view returns (string memory); 24 | 25 | function setMarketId(string calldata marketId) external; 26 | 27 | function setAddress(bytes32 id, address newAddress) external; 28 | 29 | function setAddressAsProxy(bytes32 id, address impl) external; 30 | 31 | function getAddress(bytes32 id) external view returns (address); 32 | 33 | function getLendingPool() external view returns (address); 34 | 35 | function setLendingPoolImpl(address pool) external; 36 | 37 | function getLendingPoolConfigurator() external view returns (address); 38 | 39 | function setLendingPoolConfiguratorImpl(address configurator) external; 40 | 41 | function getLendingPoolCollateralManager() external view returns (address); 42 | 43 | function setLendingPoolCollateralManager(address manager) external; 44 | 45 | function getPoolAdmin() external view returns (address); 46 | 47 | function setPoolAdmin(address admin) external; 48 | 49 | function getEmergencyAdmin() external view returns (address); 50 | 51 | function setEmergencyAdmin(address admin) external; 52 | 53 | function getPriceOracle() external view returns (address); 54 | 55 | function setPriceOracle(address priceOracle) external; 56 | 57 | function getLendingRateOracle() external view returns (address); 58 | 59 | function setLendingRateOracle(address lendingRateOracle) external; 60 | } -------------------------------------------------------------------------------- /interfaces/IUniswapV2Pair.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.6; 3 | 4 | interface IUniswapV2Pair { 5 | event Approval(address indexed owner, address indexed spender, uint value); 6 | event Transfer(address indexed from, address indexed to, uint value); 7 | 8 | function name() external pure returns (string memory); 9 | function symbol() external pure returns (string memory); 10 | function decimals() external pure returns (uint8); 11 | function totalSupply() external view returns (uint); 12 | function balanceOf(address owner) external view returns (uint); 13 | function allowance(address owner, address spender) external view returns (uint); 14 | 15 | function approve(address spender, uint value) external returns (bool); 16 | function transfer(address to, uint value) external returns (bool); 17 | function transferFrom(address from, address to, uint value) external returns (bool); 18 | 19 | function DOMAIN_SEPARATOR() external view returns (bytes32); 20 | function PERMIT_TYPEHASH() external pure returns (bytes32); 21 | function nonces(address owner) external view returns (uint); 22 | 23 | function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external; 24 | 25 | event Mint(address indexed sender, uint amount0, uint amount1); 26 | event Burn(address indexed sender, uint amount0, uint amount1, address indexed to); 27 | event Swap( 28 | address indexed sender, 29 | uint amount0In, 30 | uint amount1In, 31 | uint amount0Out, 32 | uint amount1Out, 33 | address indexed to 34 | ); 35 | event Sync(uint112 reserve0, uint112 reserve1); 36 | 37 | function MINIMUM_LIQUIDITY() external pure returns (uint); 38 | function factory() external view returns (address); 39 | function token0() external view returns (address); 40 | function token1() external view returns (address); 41 | function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); 42 | function price0CumulativeLast() external view returns (uint); 43 | function price1CumulativeLast() external view returns (uint); 44 | function kLast() external view returns (uint); 45 | 46 | function mint(address to) external returns (uint liquidity); 47 | function burn(address to) external returns (uint amount0, uint amount1); 48 | function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external; 49 | function skim(address to) external; 50 | function sync() external; 51 | 52 | function initialize(address, address) external; 53 | } 54 | -------------------------------------------------------------------------------- /brownie-config.yaml: -------------------------------------------------------------------------------- 1 | dotenv: .env 2 | 3 | # automatically fetch contract sources from Etherscan 4 | autofetch_sources: True 5 | 6 | # require OpenZepplin Contracts v3.4.0 7 | dependencies: 8 | - OpenZeppelin/openzeppelin-contracts@4.6.0 9 | 10 | # path remapping to support OpenZepplin imports with NPM-style path 11 | compiler: 12 | solc: 13 | remappings: 14 | - '@openzeppelin=OpenZeppelin/openzeppelin-contracts@4.6.0' 15 | networks: 16 | # use Ganache's forked mainnet mode as the default network 17 | default: kovan 18 | mainnet-fork: 19 | aave_lending_pool_v2: "0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5" 20 | uniswap_factory: "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f" 21 | sushiswap_factory: "0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac" 22 | uniswap_router: "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D" 23 | sushiswap_router: "0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F" 24 | weth: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" 25 | dai_token: "0x6B175474E89094C44Da98b954EedeAC495271d0F" 26 | mainnet-fork-dev: 27 | aave_lending_pool_v2: "0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5" 28 | uniswap_factory: "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f" 29 | sushiswap_factory: "0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac" 30 | uniswap_router: "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D" 31 | sushiswap_router: "0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F" 32 | weth: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" 33 | dai_token: "0x6B175474E89094C44Da98b954EedeAC495271d0F" 34 | #Aave is available only on kovan testnet for now 35 | kovan: 36 | aave_lending_pool_v2: "0x88757f2f99175387ab4c6a4b3067c77a695b0349" #Aave lending pool addresses provider 37 | uniswap_factory: "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f" 38 | # https://dev.sushi.com/docs/Developers/Deployment%20Addresses Note: All the testnet adresses are same. Mainnet is different though 39 | sushiswap_factory: "0xc35DADB65012eC5796536bD9864eD8773aBc74C4" 40 | uniswap_router: "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D" 41 | sushiswap_router: "0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506" 42 | weth: "0xd0a1e359811322d97991e03f863a0c30c2cf029c" 43 | dai_token: "0xFf795577d9AC8bD7D90Ee22b6C1703490b6512FD" 44 | mainnet: 45 | aave_lending_pool_v2: "0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5" 46 | uniswap_factory: "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f" 47 | sushiswap_factory: "0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac" 48 | uniswap_router: "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D" 49 | sushiswap_router: "0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F" 50 | weth: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" 51 | dai_token: "0x6B175474E89094C44Da98b954EedeAC495271d0F" 52 | wallets: 53 | from_key: ${PRIVATE_KEY} 54 | from_mnemonic: ${MNEMONIC} 55 | -------------------------------------------------------------------------------- /interfaces/IUniswapV2Router01.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | interface IUniswapV2Router01 { 6 | function factory() external pure returns (address); 7 | function WETH() external pure returns (address); 8 | 9 | function addLiquidity( 10 | address tokenA, 11 | address tokenB, 12 | uint amountADesired, 13 | uint amountBDesired, 14 | uint amountAMin, 15 | uint amountBMin, 16 | address to, 17 | uint deadline 18 | ) external returns (uint amountA, uint amountB, uint liquidity); 19 | function addLiquidityETH( 20 | address token, 21 | uint amountTokenDesired, 22 | uint amountTokenMin, 23 | uint amountETHMin, 24 | address to, 25 | uint deadline 26 | ) external payable returns (uint amountToken, uint amountETH, uint liquidity); 27 | function removeLiquidity( 28 | address tokenA, 29 | address tokenB, 30 | uint liquidity, 31 | uint amountAMin, 32 | uint amountBMin, 33 | address to, 34 | uint deadline 35 | ) external returns (uint amountA, uint amountB); 36 | function removeLiquidityETH( 37 | address token, 38 | uint liquidity, 39 | uint amountTokenMin, 40 | uint amountETHMin, 41 | address to, 42 | uint deadline 43 | ) external returns (uint amountToken, uint amountETH); 44 | function removeLiquidityWithPermit( 45 | address tokenA, 46 | address tokenB, 47 | uint liquidity, 48 | uint amountAMin, 49 | uint amountBMin, 50 | address to, 51 | uint deadline, 52 | bool approveMax, uint8 v, bytes32 r, bytes32 s 53 | ) external returns (uint amountA, uint amountB); 54 | function removeLiquidityETHWithPermit( 55 | address token, 56 | uint liquidity, 57 | uint amountTokenMin, 58 | uint amountETHMin, 59 | address to, 60 | uint deadline, 61 | bool approveMax, uint8 v, bytes32 r, bytes32 s 62 | ) external returns (uint amountToken, uint amountETH); 63 | function swapExactTokensForTokens( 64 | uint amountIn, 65 | uint amountOutMin, 66 | address[] calldata path, 67 | address to, 68 | uint deadline 69 | ) external returns (uint[] memory amounts); 70 | function swapTokensForExactTokens( 71 | uint amountOut, 72 | uint amountInMax, 73 | address[] calldata path, 74 | address to, 75 | uint deadline 76 | ) external returns (uint[] memory amounts); 77 | function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline) 78 | external 79 | payable 80 | returns (uint[] memory amounts); 81 | function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline) 82 | external 83 | returns (uint[] memory amounts); 84 | function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) 85 | external 86 | returns (uint[] memory amounts); 87 | function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline) 88 | external 89 | payable 90 | returns (uint[] memory amounts); 91 | 92 | function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB); 93 | function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut); 94 | function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn); 95 | function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts); 96 | function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts); 97 | } 98 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | @pytest.fixture(autouse=True) 5 | def setup(fn_isolation): 6 | """ 7 | Isolation setup fixture. 8 | 9 | This ensures that each test runs against the same base environment. 10 | """ 11 | pass 12 | 13 | 14 | @pytest.fixture(scope="module") 15 | def aave_lending_pool_v1(Contract): 16 | """ 17 | Yield a `Contract` object for the Aave lending pool address provider. 18 | """ 19 | yield Contract("0x24a42fD28C976A61Df5D00D0599C34c4f90748c8") 20 | 21 | 22 | @pytest.fixture(scope="module") 23 | def flashloan_v1(FlashloanV1, aave_lending_pool_v1, accounts): 24 | """ 25 | Deploy a `Flashloan` contract from `web3.eth.accounts[0]` and yields the 26 | generated object. 27 | """ 28 | yield FlashloanV1.deploy(aave_lending_pool_v1, {"from": accounts[0]}) 29 | 30 | 31 | @pytest.fixture(scope="module") 32 | def aave_lending_pool_v2(Contract): 33 | """ 34 | Yield a `Contract` object for the Aave lending pool address provider. 35 | """ 36 | yield Contract("0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5") 37 | 38 | 39 | @pytest.fixture(scope="module") 40 | def flashloan_v2(FlashloanV2, aave_lending_pool_v2, accounts): 41 | """ 42 | Deploy a `Flashloan` contract from `web3.eth.accounts[0]` and yields the 43 | generated object. 44 | """ 45 | yield FlashloanV2.deploy(aave_lending_pool_v2, {"from": accounts[0]}) 46 | 47 | 48 | # Mainnet reserve token fixtures - addresses are taken from 49 | # https://docs.aave.com/developers/v/1.0/deployed-contracts/deployed-contract-instances#reserves-assets 50 | 51 | 52 | @pytest.fixture(scope="module") 53 | def WETH(Contract): 54 | yield Contract("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2") 55 | 56 | 57 | @pytest.fixture(scope="module") 58 | def ETH(): 59 | yield "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" 60 | 61 | 62 | @pytest.fixture(scope="module") 63 | def DAI(Contract): 64 | yield Contract("0x6B175474E89094C44Da98b954EedeAC495271d0F") 65 | 66 | 67 | @pytest.fixture(scope="module") 68 | def USDC(Contract): 69 | yield Contract("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48") 70 | 71 | 72 | @pytest.fixture(scope="module") 73 | def SUSD(Contract): 74 | yield Contract("0x57Ab1ec28D129707052df4dF418D58a2D46d5f51") 75 | 76 | 77 | @pytest.fixture(scope="module") 78 | def TUSD(Contract): 79 | yield Contract("0x0000000000085d4780B73119b644AE5ecd22b376") 80 | 81 | 82 | @pytest.fixture(scope="module") 83 | def USDT(Contract): 84 | yield Contract("0xdAC17F958D2ee523a2206206994597C13D831ec7") 85 | 86 | 87 | @pytest.fixture(scope="module") 88 | def BUSD(Contract): 89 | yield Contract("0x4Fabb145d64652a948d72533023f6E7A623C7C53") 90 | 91 | 92 | @pytest.fixture(scope="module") 93 | def BAT(Contract): 94 | yield Contract("0x0D8775F648430679A709E98d2b0Cb6250d2887EF") 95 | 96 | 97 | @pytest.fixture(scope="module") 98 | def KNC(Contract): 99 | yield Contract("0xdd974D5C2e2928deA5F71b9825b8b646686BD200") 100 | 101 | 102 | @pytest.fixture(scope="module") 103 | def LEND(Contract): 104 | yield Contract("0x80fB784B7eD66730e8b1DBd9820aFD29931aab03") 105 | 106 | 107 | @pytest.fixture(scope="module") 108 | def LINK(Contract): 109 | yield Contract("0x514910771AF9Ca656af840dff83E8264EcF986CA") 110 | 111 | 112 | @pytest.fixture(scope="module") 113 | def MANA(Contract): 114 | yield Contract("0x0F5D2fB29fb7d3CFeE444a200298f468908cC942") 115 | 116 | 117 | @pytest.fixture(scope="module") 118 | def MKR(Contract): 119 | yield Contract("0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2") 120 | 121 | 122 | @pytest.fixture(scope="module") 123 | def REP(Contract): 124 | yield Contract("0x1985365e9f78359a9B6AD760e32412f4a445E862") 125 | 126 | 127 | @pytest.fixture(scope="module") 128 | def SNX(Contract): 129 | yield Contract("0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F") 130 | 131 | 132 | @pytest.fixture(scope="module") 133 | def WBTC(Contract): 134 | yield Contract("0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599") 135 | 136 | 137 | @pytest.fixture(scope="module") 138 | def ZRX(Contract): 139 | yield Contract("0xE41d2489571d322189246DaFA5ebDe1F4699F498") 140 | -------------------------------------------------------------------------------- /contracts/FlashloanV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | 5 | import "./utils/FlashLoanReceiverBaseV2.sol"; 6 | import "./utils/Withdrawable.sol"; 7 | import "./../interfaces/IUniswapV2Pair.sol"; 8 | import "./../interfaces/IUniswapV2Router02.sol"; 9 | import "./../interfaces/IUniswapV2Factory.sol"; 10 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 11 | 12 | 13 | contract FlashloanV2 is FlashLoanReceiverBaseV2, Withdrawable { 14 | 15 | 16 | address immutable uniswapRouterAddress; 17 | address immutable sushiswapRouterAddress; 18 | 19 | constructor( 20 | address _addressProvider, 21 | address _uniswapRouterAddress, 22 | address _sushiswapRouterAddress 23 | ) FlashLoanReceiverBaseV2(_addressProvider) { 24 | uniswapRouterAddress = _uniswapRouterAddress; 25 | sushiswapRouterAddress = _sushiswapRouterAddress; 26 | } 27 | 28 | enum Exchange { 29 | UNISWAP, 30 | SUSHI, 31 | NONE 32 | } 33 | 34 | function pullTokens(address _asset) public onlyOwner { 35 | IERC20 tokenToPull = IERC20(_asset); 36 | tokenToPull.approve(msg.sender , address(this).balance); 37 | tokenToPull.transferFrom(address(this), msg.sender, address(this).balance); 38 | } 39 | 40 | function executeOperation( 41 | address[] calldata assets, 42 | uint256[] calldata amounts, 43 | uint256[] calldata premiums, 44 | address /* initiator */, 45 | bytes calldata params 46 | ) 47 | external 48 | override 49 | returns (bool) 50 | { 51 | address borrowedAsset = assets[0]; 52 | uint borrowedAmount = amounts[0]; 53 | uint premiumAmount = premiums[0]; 54 | 55 | // This contract now has the funds requested. 56 | // Your logic goes here. 57 | require(msg.sender == address(LENDING_POOL), "Not pool"); 58 | 59 | (address swappingPair) = abi.decode(params, (address)); 60 | 61 | makeArbitrage(borrowedAsset, borrowedAmount, swappingPair); 62 | 63 | // At the end of your logic above, this contract owes 64 | // the flashloaned amounts + premiums. 65 | // Therefore ensure your contract has enough to repay 66 | // these amounts. 67 | 68 | // Approve the LendingPool contract allowance to *pull* the owed amount 69 | //Use this logic when you have multiple assets 70 | // for (uint i = 0; i < assets.length; i++) { 71 | // uint amountOwing = amounts[i].add(premiums[i]); 72 | // IERC20(assets[i]).approve(address(LENDING_POOL), amountOwing); 73 | // } 74 | 75 | uint amountOwing = borrowedAmount + premiumAmount; 76 | IERC20(borrowedAsset).approve(address(LENDING_POOL), amountOwing); 77 | return true; 78 | } 79 | 80 | 81 | function startTransaction(address _borrowAsset, uint256 _borrowAmount, address _swappingPair, address _factoryAddress) public onlyOwner{ 82 | 83 | // Get pool address and check if it exists 84 | address poolAddress = IUniswapV2Factory(_factoryAddress).getPair( 85 | _borrowAsset, 86 | _swappingPair 87 | ); 88 | 89 | require(poolAddress != address(0), "PairAddress does not exist!"); 90 | bytes memory params = abi.encode(_swappingPair); 91 | 92 | _getFlashloan(_borrowAsset, _borrowAmount, params); 93 | } 94 | 95 | 96 | // Flash multiple assets 97 | // function flashloan(address[] memory assets, uint256[] memory amounts) public onlyOwner { 98 | // _flashloan(assets, amounts); 99 | // } 100 | 101 | /* 102 | * Flash loan 1,000,000,000,000,000,000 wei (1 ether) worth of `_asset` 103 | */ 104 | function _getFlashloan(address _asset, uint256 _amount, bytes memory _params) internal { 105 | // bytes memory data = ""; 106 | 107 | address[] memory assets = new address[](1); 108 | assets[0] = _asset; 109 | 110 | uint256[] memory amounts = new uint256[](1); 111 | amounts[0] = _amount; 112 | 113 | _flashloan(assets, amounts, _params); 114 | } 115 | 116 | 117 | function _flashloan(address[] memory assets, uint256[] memory amounts, bytes memory params) internal { 118 | //We send the flashloan amount to this contract (receiverAddress) so we can make the arbitrage trade 119 | 120 | address receiverAddress = address(this); 121 | 122 | address onBehalfOf = address(this); 123 | // bytes memory params = ""; 124 | uint16 referralCode = 0; 125 | 126 | uint256[] memory modes = new uint256[](assets.length); 127 | 128 | // 0 = no debt (flash), 1 = stable, 2 = variable 129 | for (uint256 i = 0; i < assets.length; i++) { 130 | modes[i] = 0; 131 | } 132 | 133 | LENDING_POOL.flashLoan( 134 | receiverAddress, 135 | assets, 136 | amounts, 137 | modes, 138 | onBehalfOf, 139 | params, 140 | referralCode 141 | ); 142 | } 143 | 144 | 145 | function makeArbitrage(address _borrowedAsset, uint _borrowedAmount, address _swappingPair) internal returns(uint256){ 146 | 147 | //Write a better comparePrice function 148 | Exchange result = _comparePrice(_borrowedAmount, _borrowedAsset, _swappingPair); 149 | uint amountFinal; 150 | if (result == Exchange.UNISWAP) { 151 | 152 | // e.g sell WETH in uniswap for DAI with high price and buy WETH from sushiswap with lower price 153 | uint256 amountOut = _swapTokens( 154 | _borrowedAmount, 155 | uniswapRouterAddress, 156 | _borrowedAsset, 157 | _swappingPair 158 | ); 159 | 160 | amountFinal = _swapTokens( 161 | amountOut, 162 | sushiswapRouterAddress, 163 | _swappingPair, 164 | _borrowedAsset 165 | ); 166 | } else if (result == Exchange.SUSHI) { 167 | 168 | // e.g sell WETH in sushiswap for DAI with high price and buy WETH from uniswap with lower price 169 | uint256 amountOut = _swapTokens( 170 | _borrowedAmount, 171 | sushiswapRouterAddress, 172 | _borrowedAsset, 173 | _swappingPair 174 | ); 175 | 176 | amountFinal = _swapTokens( 177 | amountOut, 178 | uniswapRouterAddress, 179 | _swappingPair, 180 | _borrowedAsset 181 | ); 182 | }else{ 183 | revert(); 184 | } 185 | 186 | return amountFinal; 187 | } 188 | 189 | 190 | //This compares the prices of the assets on the individual exchanges i.e Uniswap and Sushiswap 191 | function _comparePrice(uint256 _amount, address _firstToken, address _secondToken) internal view returns (Exchange) { 192 | uint256 uniswapPrice = _getPrice( 193 | uniswapRouterAddress, 194 | _firstToken, //sell token 195 | _secondToken, //buy token 196 | _amount 197 | ); 198 | 199 | uint256 sushiswapPrice = _getPrice( 200 | sushiswapRouterAddress, 201 | _firstToken, //sell token 202 | _secondToken, //buy token 203 | _amount 204 | ); 205 | 206 | // we try to sell ETH with higher price and buy it back with low price to make profit 207 | if (uniswapPrice > sushiswapPrice) { 208 | require( 209 | _checkIfArbitrageIsProfitable( 210 | _amount, 211 | uniswapPrice, 212 | sushiswapPrice 213 | ), 214 | "Arbitrage not profitable" 215 | ); 216 | return Exchange.UNISWAP; 217 | } else if (uniswapPrice < sushiswapPrice) { 218 | require( 219 | _checkIfArbitrageIsProfitable( 220 | _amount, 221 | sushiswapPrice, 222 | uniswapPrice 223 | ), 224 | "Arbitrage not profitable" 225 | ); 226 | return Exchange.SUSHI; 227 | } else { 228 | return Exchange.NONE; 229 | } 230 | } 231 | 232 | 233 | function _swapTokens( 234 | uint256 amountIn, 235 | address routerAddress, 236 | address sell_token, 237 | address buy_token 238 | ) internal returns (uint256) { 239 | IERC20(sell_token).approve(routerAddress, amountIn); 240 | 241 | uint256 amountOutMin = (_getPrice( 242 | routerAddress, 243 | sell_token, 244 | buy_token, 245 | amountIn 246 | ) * 95) / 100; //Meaning I am expecting to receive at least 95% of the price out. 247 | 248 | address[] memory path = new address[](2); 249 | path[0] = sell_token; 250 | path[1] = buy_token; 251 | 252 | uint256 amountReceived = IUniswapV2Router02(routerAddress) 253 | .swapExactTokensForTokens( 254 | amountIn, /**+-Amount of Tokens we are going to Sell.*/ 255 | amountOutMin, /**+-Minimum Amount of Tokens that we expect to receive in exchange for our Tokens.*/ 256 | path, /**+-We tell SushiSwap what token to sell and what token to Buy.*/ 257 | address(this), /**+-Address of where the Output Tokens are going to be received. i.e this contract address(this) */ 258 | block.timestamp + 300 /**+-Time Limit after which an order will be rejected by SushiSwap(It is mainly useful if you send an Order directly from your wallet).*/ 259 | )[1]; 260 | return amountReceived; 261 | } 262 | 263 | 264 | function _checkIfArbitrageIsProfitable( 265 | uint256 amountIn, 266 | uint256 higherPrice, 267 | uint256 lowerPrice 268 | ) internal pure returns (bool) { 269 | // Uniswap & Sushiswap have 0.3% fee for every exchange 270 | // so gain made must be greater than 2 * 0.3% * arbitrage_amount 271 | //This means 0.3% for Uniswap and another 0.3% for Sushiswap 272 | // 0.3 percent means 0.003 or 3/1000 273 | 274 | // difference in ETH 275 | //Also, here implies that you are getting the value in wei amount. 276 | //After that, then dividing a wei value by an eth value *higherPrice*, means that we are essentially getting (How many eth is in that wei value) 277 | // put simply, Wei/eth = how any eth is in the wei 278 | uint256 difference = ((higherPrice - lowerPrice) * 10**18) / 279 | higherPrice; 280 | 281 | //Remember, Solidity does not deal with decimals so that is why we are dividing by 1000 282 | // 0.3 percent means 0.003 or 3/1000 283 | uint256 paid_fee = (2 * (amountIn * 3)) / 1000; 284 | 285 | //Eth amount minus another Eth 286 | if (difference > paid_fee) { 287 | return true; 288 | } else { 289 | return false; 290 | } 291 | } 292 | 293 | function _getPrice( 294 | address routerAddress, 295 | address sell_token, 296 | address buy_token, 297 | uint256 amount 298 | ) internal view returns (uint256) { 299 | address[] memory pairs = new address[](2); 300 | pairs[0] = sell_token; 301 | pairs[1] = buy_token; 302 | uint256 price = IUniswapV2Router02(routerAddress).getAmountsOut( 303 | amount, 304 | pairs 305 | )[1]; 306 | 307 | //The return price is in Eth...So you can always multiply by 10**18 to convert to wei 308 | return price; 309 | } 310 | 311 | } -------------------------------------------------------------------------------- /interfaces/ILendingPoolV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: agpl-3.0 2 | pragma solidity ^0.8.0; 3 | pragma experimental ABIEncoderV2; 4 | 5 | import {ILendingPoolAddressesProviderV2} from './ILendingPoolAddressesProviderV2.sol'; 6 | import {DataTypes} from '../libraries/DataTypes.sol'; 7 | 8 | interface ILendingPoolV2 { 9 | /** 10 | * @dev Emitted on deposit() 11 | * @param reserve The address of the underlying asset of the reserve 12 | * @param user The address initiating the deposit 13 | * @param onBehalfOf The beneficiary of the deposit, receiving the aTokens 14 | * @param amount The amount deposited 15 | * @param referral The referral code used 16 | **/ 17 | event Deposit( 18 | address indexed reserve, 19 | address user, 20 | address indexed onBehalfOf, 21 | uint256 amount, 22 | uint16 indexed referral 23 | ); 24 | 25 | /** 26 | * @dev Emitted on withdraw() 27 | * @param reserve The address of the underlyng asset being withdrawn 28 | * @param user The address initiating the withdrawal, owner of aTokens 29 | * @param to Address that will receive the underlying 30 | * @param amount The amount to be withdrawn 31 | **/ 32 | event Withdraw(address indexed reserve, address indexed user, address indexed to, uint256 amount); 33 | 34 | /** 35 | * @dev Emitted on borrow() and flashLoan() when debt needs to be opened 36 | * @param reserve The address of the underlying asset being borrowed 37 | * @param user The address of the user initiating the borrow(), receiving the funds on borrow() or just 38 | * initiator of the transaction on flashLoan() 39 | * @param onBehalfOf The address that will be getting the debt 40 | * @param amount The amount borrowed out 41 | * @param borrowRateMode The rate mode: 1 for Stable, 2 for Variable 42 | * @param borrowRate The numeric rate at which the user has borrowed 43 | * @param referral The referral code used 44 | **/ 45 | event Borrow( 46 | address indexed reserve, 47 | address user, 48 | address indexed onBehalfOf, 49 | uint256 amount, 50 | uint256 borrowRateMode, 51 | uint256 borrowRate, 52 | uint16 indexed referral 53 | ); 54 | 55 | /** 56 | * @dev Emitted on repay() 57 | * @param reserve The address of the underlying asset of the reserve 58 | * @param user The beneficiary of the repayment, getting his debt reduced 59 | * @param repayer The address of the user initiating the repay(), providing the funds 60 | * @param amount The amount repaid 61 | **/ 62 | event Repay( 63 | address indexed reserve, 64 | address indexed user, 65 | address indexed repayer, 66 | uint256 amount 67 | ); 68 | 69 | /** 70 | * @dev Emitted on swapBorrowRateMode() 71 | * @param reserve The address of the underlying asset of the reserve 72 | * @param user The address of the user swapping his rate mode 73 | * @param rateMode The rate mode that the user wants to swap to 74 | **/ 75 | event Swap(address indexed reserve, address indexed user, uint256 rateMode); 76 | 77 | /** 78 | * @dev Emitted on setUserUseReserveAsCollateral() 79 | * @param reserve The address of the underlying asset of the reserve 80 | * @param user The address of the user enabling the usage as collateral 81 | **/ 82 | event ReserveUsedAsCollateralEnabled(address indexed reserve, address indexed user); 83 | 84 | /** 85 | * @dev Emitted on setUserUseReserveAsCollateral() 86 | * @param reserve The address of the underlying asset of the reserve 87 | * @param user The address of the user enabling the usage as collateral 88 | **/ 89 | event ReserveUsedAsCollateralDisabled(address indexed reserve, address indexed user); 90 | 91 | /** 92 | * @dev Emitted on rebalanceStableBorrowRate() 93 | * @param reserve The address of the underlying asset of the reserve 94 | * @param user The address of the user for which the rebalance has been executed 95 | **/ 96 | event RebalanceStableBorrowRate(address indexed reserve, address indexed user); 97 | 98 | /** 99 | * @dev Emitted on flashLoan() 100 | * @param target The address of the flash loan receiver contract 101 | * @param initiator The address initiating the flash loan 102 | * @param asset The address of the asset being flash borrowed 103 | * @param amount The amount flash borrowed 104 | * @param premium The fee flash borrowed 105 | * @param referralCode The referral code used 106 | **/ 107 | event FlashLoan( 108 | address indexed target, 109 | address indexed initiator, 110 | address indexed asset, 111 | uint256 amount, 112 | uint256 premium, 113 | uint16 referralCode 114 | ); 115 | 116 | /** 117 | * @dev Emitted when the pause is triggered. 118 | */ 119 | event Paused(); 120 | 121 | /** 122 | * @dev Emitted when the pause is lifted. 123 | */ 124 | event Unpaused(); 125 | 126 | /** 127 | * @dev Emitted when a borrower is liquidated. This event is emitted by the LendingPool via 128 | * LendingPoolCollateral manager using a DELEGATECALL 129 | * This allows to have the events in the generated ABI for LendingPool. 130 | * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation 131 | * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation 132 | * @param user The address of the borrower getting liquidated 133 | * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover 134 | * @param liquidatedCollateralAmount The amount of collateral received by the liiquidator 135 | * @param liquidator The address of the liquidator 136 | * @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants 137 | * to receive the underlying collateral asset directly 138 | **/ 139 | event LiquidationCall( 140 | address indexed collateralAsset, 141 | address indexed debtAsset, 142 | address indexed user, 143 | uint256 debtToCover, 144 | uint256 liquidatedCollateralAmount, 145 | address liquidator, 146 | bool receiveAToken 147 | ); 148 | 149 | /** 150 | * @dev Emitted when the state of a reserve is updated. NOTE: This event is actually declared 151 | * in the ReserveLogic library and emitted in the updateInterestRates() function. Since the function is internal, 152 | * the event will actually be fired by the LendingPool contract. The event is therefore replicated here so it 153 | * gets added to the LendingPool ABI 154 | * @param reserve The address of the underlying asset of the reserve 155 | * @param liquidityRate The new liquidity rate 156 | * @param stableBorrowRate The new stable borrow rate 157 | * @param variableBorrowRate The new variable borrow rate 158 | * @param liquidityIndex The new liquidity index 159 | * @param variableBorrowIndex The new variable borrow index 160 | **/ 161 | event ReserveDataUpdated( 162 | address indexed reserve, 163 | uint256 liquidityRate, 164 | uint256 stableBorrowRate, 165 | uint256 variableBorrowRate, 166 | uint256 liquidityIndex, 167 | uint256 variableBorrowIndex 168 | ); 169 | 170 | /** 171 | * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens. 172 | * - E.g. User deposits 100 USDC and gets in return 100 aUSDC 173 | * @param asset The address of the underlying asset to deposit 174 | * @param amount The amount to be deposited 175 | * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user 176 | * wants to receive them on his own wallet, or a different address if the beneficiary of aTokens 177 | * is a different wallet 178 | * @param referralCode Code used to register the integrator originating the operation, for potential rewards. 179 | * 0 if the action is executed directly by the user, without any middle-man 180 | **/ 181 | function deposit( 182 | address asset, 183 | uint256 amount, 184 | address onBehalfOf, 185 | uint16 referralCode 186 | ) external; 187 | 188 | /** 189 | * @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned 190 | * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC 191 | * @param asset The address of the underlying asset to withdraw 192 | * @param amount The underlying amount to be withdrawn 193 | * - Send the value type(uint256).max in order to withdraw the whole aToken balance 194 | * @param to Address that will receive the underlying, same as msg.sender if the user 195 | * wants to receive it on his own wallet, or a different address if the beneficiary is a 196 | * different wallet 197 | * @return The final amount withdrawn 198 | **/ 199 | function withdraw( 200 | address asset, 201 | uint256 amount, 202 | address to 203 | ) external returns (uint256); 204 | 205 | /** 206 | * @dev Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower 207 | * already deposited enough collateral, or he was given enough allowance by a credit delegator on the 208 | * corresponding debt token (StableDebtToken or VariableDebtToken) 209 | * - E.g. User borrows 100 USDC passing as `onBehalfOf` his own address, receiving the 100 USDC in his wallet 210 | * and 100 stable/variable debt tokens, depending on the `interestRateMode` 211 | * @param asset The address of the underlying asset to borrow 212 | * @param amount The amount to be borrowed 213 | * @param interestRateMode The interest rate mode at which the user wants to borrow: 1 for Stable, 2 for Variable 214 | * @param referralCode Code used to register the integrator originating the operation, for potential rewards. 215 | * 0 if the action is executed directly by the user, without any middle-man 216 | * @param onBehalfOf Address of the user who will receive the debt. Should be the address of the borrower itself 217 | * calling the function if he wants to borrow against his own collateral, or the address of the credit delegator 218 | * if he has been given credit delegation allowance 219 | **/ 220 | function borrow( 221 | address asset, 222 | uint256 amount, 223 | uint256 interestRateMode, 224 | uint16 referralCode, 225 | address onBehalfOf 226 | ) external; 227 | 228 | /** 229 | * @notice Repays a borrowed `amount` on a specific reserve, burning the equivalent debt tokens owned 230 | * - E.g. User repays 100 USDC, burning 100 variable/stable debt tokens of the `onBehalfOf` address 231 | * @param asset The address of the borrowed underlying asset previously borrowed 232 | * @param amount The amount to repay 233 | * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode` 234 | * @param rateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable 235 | * @param onBehalfOf Address of the user who will get his debt reduced/removed. Should be the address of the 236 | * user calling the function if he wants to reduce/remove his own debt, or the address of any other 237 | * other borrower whose debt should be removed 238 | * @return The final amount repaid 239 | **/ 240 | function repay( 241 | address asset, 242 | uint256 amount, 243 | uint256 rateMode, 244 | address onBehalfOf 245 | ) external returns (uint256); 246 | 247 | /** 248 | * @dev Allows a borrower to swap his debt between stable and variable mode, or viceversa 249 | * @param asset The address of the underlying asset borrowed 250 | * @param rateMode The rate mode that the user wants to swap to 251 | **/ 252 | function swapBorrowRateMode(address asset, uint256 rateMode) external; 253 | 254 | /** 255 | * @dev Rebalances the stable interest rate of a user to the current stable rate defined on the reserve. 256 | * - Users can be rebalanced if the following conditions are satisfied: 257 | * 1. Usage ratio is above 95% 258 | * 2. the current deposit APY is below REBALANCE_UP_THRESHOLD * maxVariableBorrowRate, which means that too much has been 259 | * borrowed at a stable rate and depositors are not earning enough 260 | * @param asset The address of the underlying asset borrowed 261 | * @param user The address of the user to be rebalanced 262 | **/ 263 | function rebalanceStableBorrowRate(address asset, address user) external; 264 | 265 | /** 266 | * @dev Allows depositors to enable/disable a specific deposited asset as collateral 267 | * @param asset The address of the underlying asset deposited 268 | * @param useAsCollateral `true` if the user wants to use the deposit as collateral, `false` otherwise 269 | **/ 270 | function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external; 271 | 272 | /** 273 | * @dev Function to liquidate a non-healthy position collateral-wise, with Health Factor below 1 274 | * - The caller (liquidator) covers `debtToCover` amount of debt of the user getting liquidated, and receives 275 | * a proportionally amount of the `collateralAsset` plus a bonus to cover market risk 276 | * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation 277 | * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation 278 | * @param user The address of the borrower getting liquidated 279 | * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover 280 | * @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants 281 | * to receive the underlying collateral asset directly 282 | **/ 283 | function liquidationCall( 284 | address collateralAsset, 285 | address debtAsset, 286 | address user, 287 | uint256 debtToCover, 288 | bool receiveAToken 289 | ) external; 290 | 291 | /** 292 | * @dev Allows smartcontracts to access the liquidity of the pool within one transaction, 293 | * as long as the amount taken plus a fee is returned. 294 | * IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept into consideration. 295 | * For further details please visit https://developers.aave.com 296 | * @param receiverAddress The address of the contract receiving the funds, implementing the IFlashLoanReceiver interface 297 | * @param assets The addresses of the assets being flash-borrowed 298 | * @param amounts The amounts amounts being flash-borrowed 299 | * @param modes Types of the debt to open if the flash loan is not returned: 300 | * 0 -> Don't open any debt, just revert if funds can't be transferred from the receiver 301 | * 1 -> Open debt at stable rate for the value of the amount flash-borrowed to the `onBehalfOf` address 302 | * 2 -> Open debt at variable rate for the value of the amount flash-borrowed to the `onBehalfOf` address 303 | * @param onBehalfOf The address that will receive the debt in the case of using on `modes` 1 or 2 304 | * @param params Variadic packed params to pass to the receiver as extra information 305 | * @param referralCode Code used to register the integrator originating the operation, for potential rewards. 306 | * 0 if the action is executed directly by the user, without any middle-man 307 | **/ 308 | function flashLoan( 309 | address receiverAddress, 310 | address[] calldata assets, 311 | uint256[] calldata amounts, 312 | uint256[] calldata modes, 313 | address onBehalfOf, 314 | bytes calldata params, 315 | uint16 referralCode 316 | ) external; 317 | 318 | /** 319 | * @dev Returns the user account data across all the reserves 320 | * @param user The address of the user 321 | * @return totalCollateralETH the total collateral in ETH of the user 322 | * @return totalDebtETH the total debt in ETH of the user 323 | * @return availableBorrowsETH the borrowing power left of the user 324 | * @return currentLiquidationThreshold the liquidation threshold of the user 325 | * @return ltv the loan to value of the user 326 | * @return healthFactor the current health factor of the user 327 | **/ 328 | function getUserAccountData(address user) 329 | external 330 | view 331 | returns ( 332 | uint256 totalCollateralETH, 333 | uint256 totalDebtETH, 334 | uint256 availableBorrowsETH, 335 | uint256 currentLiquidationThreshold, 336 | uint256 ltv, 337 | uint256 healthFactor 338 | ); 339 | 340 | function initReserve( 341 | address reserve, 342 | address aTokenAddress, 343 | address stableDebtAddress, 344 | address variableDebtAddress, 345 | address interestRateStrategyAddress 346 | ) external; 347 | 348 | function setReserveInterestRateStrategyAddress(address reserve, address rateStrategyAddress) 349 | external; 350 | 351 | function setConfiguration(address reserve, uint256 configuration) external; 352 | 353 | /** 354 | * @dev Returns the configuration of the reserve 355 | * @param asset The address of the underlying asset of the reserve 356 | * @return The configuration of the reserve 357 | **/ 358 | function getConfiguration(address asset) 359 | external 360 | view 361 | returns (DataTypes.ReserveConfigurationMap memory); 362 | 363 | /** 364 | * @dev Returns the configuration of the user across all the reserves 365 | * @param user The user address 366 | * @return The configuration of the user 367 | **/ 368 | function getUserConfiguration(address user) 369 | external 370 | view 371 | returns (DataTypes.UserConfigurationMap memory); 372 | 373 | /** 374 | * @dev Returns the normalized income normalized income of the reserve 375 | * @param asset The address of the underlying asset of the reserve 376 | * @return The reserve's normalized income 377 | */ 378 | function getReserveNormalizedIncome(address asset) external view returns (uint256); 379 | 380 | /** 381 | * @dev Returns the normalized variable debt per unit of asset 382 | * @param asset The address of the underlying asset of the reserve 383 | * @return The reserve normalized variable debt 384 | */ 385 | function getReserveNormalizedVariableDebt(address asset) external view returns (uint256); 386 | 387 | /** 388 | * @dev Returns the state and configuration of the reserve 389 | * @param asset The address of the underlying asset of the reserve 390 | * @return The state of the reserve 391 | **/ 392 | function getReserveData(address asset) external view returns (DataTypes.ReserveData memory); 393 | 394 | function finalizeTransfer( 395 | address asset, 396 | address from, 397 | address to, 398 | uint256 amount, 399 | uint256 balanceFromAfter, 400 | uint256 balanceToBefore 401 | ) external; 402 | 403 | function getReservesList() external view returns (address[] memory); 404 | 405 | function getAddressesProvider() external view returns (ILendingPoolAddressesProviderV2); 406 | 407 | function setPause(bool val) external; 408 | 409 | function paused() external view returns (bool); 410 | } 411 | --------------------------------------------------------------------------------