Error: execution reverted: SafeERC20: low-level call failed with AAVE - solidity

Don't know if here is the best place but i'm in despair. I have this flashloan code:
pragma solidity ^0.6.6;
import "#aave/protocol-v2/contracts/flashloan/base/FlashLoanReceiverBase.sol";
import "#aave/protocol-v2/contracts/interfaces/ILendingPoolAddressesProvider.sol";
import "#aave/protocol-v2/contracts/interfaces/ILendingPool.sol";
import "#uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
import {SafeERC20} from "#aave/protocol-v2/contracts/dependencies/openzeppelin/contracts/SafeERC20.sol";
//import "#openzeppelin/contracts/interfaces/IERC20.sol";
contract FlashloanV2 is FlashLoanReceiverBase {
using SafeERC20 for IERC20;
constructor(
ILendingPoolAddressesProvider _addressProvider,
address _routerA,
address _routerB,
address _token,
address _WETH
) public FlashLoanReceiverBase(_addressProvider)
{
owner = msg.sender;
routerA = _routerA;
routerB = _routerB;
token = _token;
WETH = _WETH;
}
address owner;
address routerA;
address routerB;
address token;
address WETH;
modifier onlyOwner{
require(msg.sender == owner, "Hey hey hey you can't use this function");
_;
}
/**
* #dev This function must be called only be the LENDING_POOL and takes care of repaying
* active debt positions, migrating collateral and incurring new V2 debt token debt.
*
* #param assets The array of flash loaned assets used to repay debts.
* #param amounts The array of flash loaned asset amounts used to repay debts.
* #param premiums The array of premiums incurred as additional debts.
* #param initiator The address that initiated the flash loan, unused.
* #param params The byte array containing, in this case, the arrays of aTokens and aTokenAmounts.
*/
function executeOperation(
address[] calldata assets,
uint256[] calldata amounts,
uint256[] calldata premiums,
address initiator,
bytes calldata params
) external override returns (bool) {
//
// This contract now has the funds requested.
// Your logic goes here.
//
address[] memory path = new address[](2);
path[0] = token;
path[1] = WETH;
address[] memory path2 = new address[](2);
path2[0] = WETH;
path2[1] = token;
uint balance = address(this).balance;
IERC20(WETH).approve(routerA, balance);
IUniswapV2Router02(routerA).swapExactTokensForTokensSupportingFeeOnTransferTokens(
balance,
0,
path2,
address(this),
block.timestamp + 1200
);
uint tokenBalance = IERC20(token).balanceOf(address(this));
IERC20(token).approve(routerB, tokenBalance);
IUniswapV2Router02(routerB).swapExactTokensForTokensSupportingFeeOnTransferTokens(
tokenBalance,
0,
path,
address(this),
block.timestamp + 1200
);
//payable(owner).transfer(address(this).balance - (amounts[0] + premiums[0]));
// At the end of your logic above, this contract owes
// the flashloaned amounts + premiums.
// Therefore ensure your contract has enough to repay
// these amounts.
// Approve the LendingPool contract allowance to *pull* the owed amount
for (uint256 i = 0; i < assets.length; i++) {
uint256 amountOwing = amounts[i].add(premiums[i]);
IERC20(assets[i]).approve(address(LENDING_POOL), amountOwing);
}
return true;
}
function _flashloan(address[] memory assets, uint256[] memory amounts)
internal
{
address receiverAddress = address(this);
address onBehalfOf = address(this);
bytes memory params = "";
uint16 referralCode = 0;
uint256[] memory modes = new uint256[](assets.length);
// 0 = no debt (flash), 1 = stable, 2 = variable
for (uint256 i = 0; i < assets.length; i++) {
modes[i] = 0;
}
LENDING_POOL.flashLoan(
receiverAddress,
assets,
amounts,
modes,
onBehalfOf,
params,
referralCode
);
}
/*
* Flash multiple assets
*/
function flashloan(address[] memory assets, uint256[] memory amounts)
public
onlyOwner
{
_flashloan(assets, amounts);
}
/*
* Flash loan 100000000000000000 wei (0.1 ether) worth of `_asset`
*/
function flashloan(address _asset) public onlyOwner {
bytes memory data = "";
uint256 amount = 50 ether;
address[] memory assets = new address[](1);
assets[0] = _asset;
uint256[] memory amounts = new uint256[](1);
amounts[0] = amount;
_flashloan(assets, amounts);
}
event LogWithdraw(
address indexed _from,
address indexed _assetAddress,
uint amount
);
/**
* #dev Withdraw asset.
* #param _assetAddress Asset to be withdrawn.
*/
function withdraw(address _assetAddress) public onlyOwner {
uint assetBalance;
if (_assetAddress == WETH) {
address self = address(this); // workaround for a possible solidity bug
assetBalance = self.balance;
payable(msg.sender).transfer(assetBalance);
} else {
assetBalance = IERC20(_assetAddress).balanceOf(address(this));
IERC20(_assetAddress).safeTransfer(msg.sender, assetBalance);
}
emit LogWithdraw(msg.sender, _assetAddress, assetBalance);
}
function setter(address _routerA, address _routerB, address _token) external onlyOwner returns(bool){
routerA = _routerA;
routerB = _routerB;
token = _token;
return true;
}
function returnOwner() external view returns(address){
return owner;
}
function returnToken() external view returns(address){
return token;
}
function returnWETH() external view returns(address){
return WETH;
}
fallback() external payable {}
}
I'm receiving SafeERC20: low-level call failed when calling flashloan(). There's something wrong with my approving logic or something else?
The contract is funded with enough to pay the fees. Could someone give a hint? Thank you!

Related

How to create a withdraw function

I created a basic contract for crowdfunding. But cannot figure out how to create a withdraw function.
Withdraw function will transfer a campaign's collected funds to the campaign's owner. This is my full crowdfunding contract:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.9;
contract CrowdFunding {
event testConditon(
uint256 deadline,
uint256 currentTimeInMs,
uint256 diff,
bool condition
);
struct Campaign {
address payable owner;
string title;
string description;
uint256 target;
uint256 deadline;
uint256 amountCollected;
string image;
address[] donators;
uint256[] donations;
}
mapping(uint256 => Campaign) public campaigns;
uint256 public numberOfCampaigns = 0;
function createCampaign(
address _owner,
string memory _title,
string memory _description,
uint256 _target,
uint256 _deadline,
string memory _image
) public returns (uint256) {
Campaign storage campaign = campaigns[numberOfCampaigns];
uint256 currentTimeInMs = block.timestamp * 1000;
emit testConditon(
_deadline,
currentTimeInMs,
_deadline - currentTimeInMs,
_deadline > currentTimeInMs
);
require(
_deadline > currentTimeInMs,
"The deadline must be in the future"
);
campaign.owner = payable(_owner);
campaign.title = _title;
campaign.description = _description;
campaign.target = _target;
campaign.deadline = _deadline;
campaign.image = _image;
campaign.amountCollected = 0;
numberOfCampaigns++;
return numberOfCampaigns - 1;
}
function donateToCampaign(uint256 _id) public payable {
uint256 amount = msg.value;
Campaign storage campaign = campaigns[_id];
campaign.donators.push(msg.sender);
campaign.donations.push(amount);
(bool sent, ) = payable(campaign.owner).call{value: amount}("");
if (sent) {
campaign.amountCollected += amount;
}
}
function getDonators(uint256 _id)
public
view
returns (address[] memory, uint256[] memory)
{
return (campaigns[_id].donators, campaigns[_id].donations);
}
function getCampaigns() public view returns (Campaign[] memory) {
Campaign[] memory allCampaigns = new Campaign[](numberOfCampaigns);
for (uint256 i = 0; i < numberOfCampaigns; i++) {
Campaign storage item = campaigns[i];
allCampaigns[i] = item;
}
return allCampaigns;
}
function withdraw(uint256 _id) public {
Campaign storage campaign = campaigns[_id];
require(
campaign.amountCollected >= campaign.target,
"The campaign has not reached its target"
);
//deadline has passed
// require(
// campaign.deadline < block.timestamp * 1000,
// "The deadline has not passed yet"
// );
require(
campaign.owner == msg.sender,
"Only the owner of the campaign can withdraw the funds"
);
campaign.owner.transfer(campaign.amountCollected);
campaign.amountCollected = 0;
}
}
I have no idea how to solve this issue.
Looks like you are missing the payable keyword to trigger the token transfer. Try this:
function withdraw(uint256 _id) public {
Campaign storage campaign = campaigns[_id];
(bool success, ) = payable(campaign.owner).call{value: campaign.amountCollected}("");
require(success, "Withdrawal failure");
campaign.amountCollected = 0;
}
Warning: this function and your function are having a reentrency vulnerability.

AAVE flash loan borrow more tokens always error at hardhat

I'm using hardhat to doing AAVE flash loan, when I borrow one token, is successful, but borrow > 1 tokens, always error.
* deploy on eth mainnet-fork
flash-loans-test.js
const { ethers } = require("hardhat");
const hre = require("hardhat");
describe("AaveFlashLoans", function () {
it("Excute flashloans", async function () {
const contractAddress = "0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5";
const flashLoansFactory = await ethers.getContractFactory("AaveFlashLoans");
const flashLoans = await flashLoansFactory.deploy(contractAddress);
await flashLoans.deployed();
const token = await ethers.getContractAt("IERC20", "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"); //weth
const BALANCE_AMOUNT = ethers.utils.parseEther("2");
await hre.network.provider.request({
method: "hardhat_impersonateAccount",
params: ["0xf07704777d6bc182bf2c67fbda48913169b84983"],
});
const signer = await ethers.getSigner("0xf07704777d6bc182bf2c67fbda48913169b84983");
await token
.connect(signer)
.transfer(flashLoans.address, BALANCE_AMOUNT);
await flashLoans.myFlashLoanCall();
const balance = await token.balanceOf(flashLoans.address)
console.log(balance);
});
});
AaveFlashLoans.sol
pragma solidity ^0.8.0;
import {SafeMath} from "#openzeppelin/contracts/utils/math/SafeMath.sol";
import {IERC20} from "#openzeppelin/contracts/token/ERC20/IERC20.sol";
import {FlashLoanReceiverBase} from "./FlashLoanReceiverBase.sol";
import {ILendingPoolAddressesProvider} from "./ILendingPoolAddressesProvider.sol";
import "hardhat/console.sol";
contract AaveFlashLoans is FlashLoanReceiverBase {
using SafeMath for uint256;
constructor(ILendingPoolAddressesProvider _addressProvider)
FlashLoanReceiverBase(_addressProvider)
{}
function executeOperation(
address[] calldata assets,
uint256[] calldata amounts,
uint256[] calldata premiums,
address,
bytes calldata
) external override returns (bool) {
for (uint256 i = 0; i < assets.length; i++) {
uint256 amountOwing = amounts[i].add(premiums[i]);
IERC20(assets[i]).approve(address(LENDING_POOL), amountOwing);
console.log(amounts[i]);
console.log(premiums[i]);
}
return true;
}
function myFlashLoanCall() public {
address receiverAddress = address(this);
address[] memory assets = new address[](1);
assets[0] = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); // weth
uint256[] memory amounts = new uint256[](1);
amounts[0] = 1 ether;
uint256[] memory modes = new uint256[](1);
modes[0] = 0;
address onBehalfOf = address(this);
bytes memory params = "";
uint16 referralCode = 0;
LENDING_POOL.flashLoan(
receiverAddress,
assets,
amounts,
modes,
onBehalfOf,
params,
referralCode
);
}
}
above code is successful
successful image1
successful image2
but when I add 2 tokens, it will error
AaveFlashLoans.sol
pragma solidity ^0.8.0;
import {SafeMath} from "#openzeppelin/contracts/utils/math/SafeMath.sol";
import {IERC20} from "#openzeppelin/contracts/token/ERC20/IERC20.sol";
import {FlashLoanReceiverBase} from "./FlashLoanReceiverBase.sol";
import {ILendingPoolAddressesProvider} from "./ILendingPoolAddressesProvider.sol";
import "hardhat/console.sol";
contract AaveFlashLoans is FlashLoanReceiverBase {
using SafeMath for uint256;
constructor(ILendingPoolAddressesProvider _addressProvider)
FlashLoanReceiverBase(_addressProvider)
{}
function executeOperation(
address[] calldata assets,
uint256[] calldata amounts,
uint256[] calldata premiums,
address,
bytes calldata
) external override returns (bool) {
for (uint256 i = 0; i < assets.length; i++) {
uint256 amountOwing = amounts[i].add(premiums[i]);
IERC20(assets[i]).approve(address(LENDING_POOL), amountOwing);
console.log(amounts[i]);
console.log(premiums[i]);
}
return true;
}
function myFlashLoanCall() public {
address receiverAddress = address(this);
address[] memory assets = new address[](2);
assets[0] = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); // weth
assets[1] = address(0xdAC17F958D2ee523a2206206994597C13D831ec7); // usdt
uint256[] memory amounts = new uint256[](2);
amounts[0] = 1 ether;
amounts[1] = 1 ether;
uint256[] memory modes = new uint256[](2);
modes[0] = 0;
modes[1] = 0;
address onBehalfOf = address(this);
bytes memory params = "";
uint16 referralCode = 0;
LENDING_POOL.flashLoan(
receiverAddress,
assets,
amounts,
modes,
onBehalfOf,
params,
referralCode
);
}
}
above code is error
error image
Please help, thanks a lot lot lot lot...
Since the USDT token has 6 decimals, by inputting the amount as 1 ether, this corresponds to the amount: 1000000000000 USDT. There is not enough availableLiquidity in the pool to borrow this amount, so this is why the tx is reverting. If you reduce the amount, then it should succeed.

'Gas Estimation Failed' Error when trying to deploy a contract on remix to the Ganache test net

This is what the error reads:
Gas estimation errored with the following message (see below). The
transaction execution will likely fail. Do you want to force sending?
Internal JSON-RPC error. { "message": "Returned error: project ID does
not have access to archive state", "code": -32000, "data": { "stack":
"Error: Returned error: project ID does not have access to archive
state\n at Object.ErrorResponse
(/usr/local/lib/node_modules/ganache-cli/build/ganache-core.node.cli.js:55:2110625)\n
at a
(/usr/local/lib/node_modules/ganache-cli/build/ganache-core.node.cli.js:55:2108932)\n
at
/usr/local/lib/node_modules/ganache-cli/build/ganache-core.node.cli.js:55:2093154\n
at runMicrotasks ()\n at processTicksAndRejections
(internal/process/task_queues.js:95:5)", "name": "Error" } }
I have made the settings for deployment on remix:
Injected Web 3
pragma solidity ^0.5.0;
pragma experimental ABIEncoderV2;
interface Structs {
struct Val {
uint256 value;
}
enum ActionType {
Deposit, // supply tokens
Withdraw, // borrow tokens
Transfer, // transfer balance between accounts
Buy, // buy an amount of some token (externally)
Sell, // sell an amount of some token (externally)
Trade, // trade tokens against another account
Liquidate, // liquidate an undercollateralized or expiring account
Vaporize, // use excess tokens to zero-out a completely negative account
Call // send arbitrary data to an address
}
enum AssetDenomination {
Wei // the amount is denominated in wei
}
enum AssetReference {
Delta // the amount is given as a delta from the current value
}
struct AssetAmount {
bool sign; // true if positive
AssetDenomination denomination;
AssetReference ref;
uint256 value;
}
struct ActionArgs {
ActionType actionType;
uint256 accountId;
AssetAmount amount;
uint256 primaryMarketId;
uint256 secondaryMarketId;
address otherAddress;
uint256 otherAccountId;
bytes data;
}
struct Info {
address owner; // The address that owns the account
uint256 number; // A nonce that allows a single address to control many accounts
}
struct Wei {
bool sign; // true if positive
uint256 value;
}
}
contract DyDxPool is Structs {
function getAccountWei(Info memory account, uint256 marketId) public view returns (Wei memory);
function operate(Info[] memory, ActionArgs[] memory) public;
}
/**
* #dev Interface of the ERC20 standard as defined in the EIP. Does not include
* the optional functions; to access them see `ERC20Detailed`.
*/
interface IERC20 {
function balanceOf(address account) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
}
contract DyDxFlashLoan is Structs {
DyDxPool pool = DyDxPool(0x1E0447b19BB6EcFdAe1e4AE1694b0C3659614e4e);
address public WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
mapping(address => uint256) public currencies;
constructor() public {
currencies[WETH] = 1;
}
modifier onlyPool() {
require(
msg.sender == address(pool),
"FlashLoan: could be called by DyDx pool only"
);
_;
}
function tokenToMarketId(address token) public view returns (uint256) {
uint256 marketId = currencies[token];
require(marketId != 0, "FlashLoan: Unsupported token");
return marketId - 1;
}
// the DyDx will call `callFunction(address sender, Info memory accountInfo, bytes memory data) public` after during `operate` call
function flashloan(address token, uint256 amount, bytes memory data)
internal
{
IERC20(token).approve(address(pool), amount + 1);
Info[] memory infos = new Info[](1);
ActionArgs[] memory args = new ActionArgs[](3);
infos[0] = Info(address(this), 0);
AssetAmount memory wamt = AssetAmount(
false,
AssetDenomination.Wei,
AssetReference.Delta,
amount
);
ActionArgs memory withdraw;
withdraw.actionType = ActionType.Withdraw;
withdraw.accountId = 0;
withdraw.amount = wamt;
withdraw.primaryMarketId = tokenToMarketId(token);
withdraw.otherAddress = address(this);
args[0] = withdraw;
ActionArgs memory call;
call.actionType = ActionType.Call;
call.accountId = 0;
call.otherAddress = address(this);
call.data = data;
args[1] = call;
ActionArgs memory deposit;
AssetAmount memory damt = AssetAmount(
true,
AssetDenomination.Wei,
AssetReference.Delta,
amount + 1
);
deposit.actionType = ActionType.Deposit;
deposit.accountId = 0;
deposit.amount = damt;
deposit.primaryMarketId = tokenToMarketId(token);
deposit.otherAddress = address(this);
args[2] = deposit;
pool.operate(infos, args);
}
}
contract Flashloan is DyDxFlashLoan {
uint256 public loan;
constructor() public payable {
(bool success, ) = WETH.call.value(msg.value)("");
require(success, "fail to get weth");
}
function getFlashloan(address flashToken, uint256 flashAmount) external {
uint256 balanceBefore = IERC20(flashToken).balanceOf(address(this));
bytes memory data = abi.encode(flashToken, flashAmount, balanceBefore);
flashloan(flashToken, flashAmount, data); // execution goes to `callFunction`
}
function callFunction(
address, /* sender */
Info calldata, /* accountInfo */
bytes calldata data
) external onlyPool {
(address flashToken, uint256 flashAmount, uint256 balanceBefore) = abi
.decode(data, (address, uint256, uint256));
uint256 balanceAfter = IERC20(flashToken).balanceOf(address(this));
require(
balanceAfter - balanceBefore == flashAmount,
"contract did not get the loan"
);
loan = balanceAfter;
// Use the money here!
}
}

uint256 input in RemixIDE

I'm trying to learn developing my first smart contract in order to use flash loans on aave. I have a problem regarding the amount input for the transaction.
I've a function asking for the amount of token I need and the type is uint256. When I type 10 I only receive 0.0000000000000001 by the flash loan. Why? Maybe it's a stupid question but I'm not understanding what's wrong.
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.6.12;
import { FlashLoanReceiverBase } from "./FlashLoanReceiverBase.sol";
import { ILendingPool, ILendingPoolAddressesProvider, IERC20 } from "./Interfaces.sol";
import { SafeMath } from "./Libraries.sol";
import "./Ownable.sol";
/*
* A contract that executes the following logic in a single atomic transaction:
*
*
*/
contract BatchFlashDemo is FlashLoanReceiverBase, Ownable {
ILendingPoolAddressesProvider provider;
using SafeMath for uint256;
uint256 flashDaiAmt0;
address lendingPoolAddr;
// mumbai reserve asset addresses
address mumbaiDai = 0x001B3B4d0F3714Ca98ba10F6042DaEbF0B1B7b6F;
// intantiate lending pool addresses provider and get lending pool address
constructor(ILendingPoolAddressesProvider _addressProvider) FlashLoanReceiverBase(_addressProvider) public {
provider = _addressProvider;
lendingPoolAddr = provider.getLendingPool();
}
/**
This function is called after your contract has received the flash loaned amount
*/
function executeOperation(
address[] calldata assets,
uint256[] calldata amounts,
uint256[] calldata premiums,
address initiator,
bytes calldata params
)
external
override
returns (bool)
{
/*
*
*
*/
// Approve the LendingPool contract allowance to *pull* the owed amount
// i.e. AAVE V2's way of repaying the flash loan
for (uint i = 0; i < assets.length; i++) {
uint amountOwing = amounts[i].add(premiums[i]);
IERC20(assets[i]).approve(address(_lendingPool), amountOwing);
}
return true;
}
/*
* This function is manually called to commence the flash loans sequence
*/
function executeFlashLoans(uint256 _flashDaiAmt0) public onlyOwner {
address receiverAddress = address(this);
// the various assets to be flashed
address[] memory assets = new address[](1);
assets[0] = mumbaiDai;
// the amount to be flashed for each asset
uint256[] memory amounts = new uint256[](1);
amounts[0] = _flashDaiAmt0;
flashDaiAmt0 = _flashDaiAmt0;
// 0 = no debt, 1 = stable, 2 = variable
uint256[] memory modes = new uint256[](1);
modes[0] = 0;
address onBehalfOf = address(this);
bytes memory params = "";
uint16 referralCode = 0;
_lendingPool.flashLoan(
receiverAddress,
assets,
amounts,
modes,
onBehalfOf,
params,
referralCode
);
}
/*
* Rugpull all ERC20 tokens from the contract
*/
function rugPull() public payable onlyOwner {
// withdraw all ETH
msg.sender.call{ value: address(this).balance }("");
// withdraw all x ERC20 tokens
IERC20(mumbaiDai).transfer(msg.sender, IERC20(mumbaiDai).balanceOf(address(this)));
}
}
I'm deploying passing this address as parameter:
0x178113104fEcbcD7fF8669a0150721e231F0FD4B
it is the Lending Pool Addresses Provider contract taken from here: https://docs.aave.com/developers/v/2.0/deployed-contracts/matic-polygon-market.
When I try asking for more than 1000tokens I get the error. Hope this helps to reproduce.
EVM doesn't support decimal numbers. So a common approach is to declare a number of decimals in the contract, and then include them with the stored values.
Example:
When a contract uses 2 decimals, value of 1 is stored as 100.
Aave protocol works with ERC20 tokens, where the standard defines a function named decimals() to return a number of decimal places.

When deploying a smart contract, isn't the "owner" of the contract (by default) the one who deploys it?

I am trying to use a function in a flashloan contract that uses the onlyOwner modifier. When I send the flashloan transaction, I get returned the error: "caller is not the owner".
I am using the same account that I used to create the contract when running this flash loan, so I'm not sure why it's not recognizing me as the owner.
You can see here that it is the same address to create the contract and to send the failed transaction.
https://kovan.etherscan.io/address/0x91109bde85e0ab631d1983ce07b910ce4e99078a
Click on the first transaction there ^^ to see the error message.
code of onlyOwner modifier: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol
Contract file
pragma solidity ^0.6.6;
import "./aave/FlashLoanReceiverBaseV2.sol";
import "../../interfaces/v2/ILendingPoolAddressesProviderV2.sol";
import "../../interfaces/v2/ILendingPoolV2.sol";
contract FlashloanV2 is FlashLoanReceiverBaseV2, Withdrawable {
constructor(address _addressProvider) FlashLoanReceiverBaseV2(_addressProvider) public {}
/**
* #dev This function must be called only be the LENDING_POOL and takes care of repaying
* active debt positions, migrating collateral and incurring new V2 debt token debt.
*
* #param assets The array of flash loaned assets used to repay debts.
* #param amounts The array of flash loaned asset amounts used to repay debts.
* #param premiums The array of premiums incurred as additional debts.
* #param initiator The address that initiated the flash loan, unused.
* #param params The byte array containing, in this case, the arrays of aTokens and aTokenAmounts.
*/
function executeOperation(
address[] calldata assets,
uint256[] calldata amounts,
uint256[] calldata premiums,
address initiator,
bytes calldata params
)
external
override
returns (bool)
{
//
// This contract now has the funds requested.
// Your logic goes here.
//
// At the end of your logic above, this contract owes
// the flashloaned amounts + premiums.
// Therefore ensure your contract has enough to repay
// these amounts.
// Approve the LendingPool contract allowance to *pull* the owed amount
for (uint i = 0; i < assets.length; i++) {
uint amountOwing = amounts[i].add(premiums[i]);
IERC20(assets[i]).approve(address(LENDING_POOL), amountOwing);
}
for (uint i = 0; i < assets.length; i++) {
withdraw(assets[i]);
}
return true;
}
function _flashloan(address[] memory assets, uint256[] memory amounts) internal {
address receiverAddress = address(this);
address onBehalfOf = address(this);
bytes memory params = "";
uint16 referralCode = 0;
uint256[] memory modes = new uint256[](assets.length);
// 0 = no debt (flash), 1 = stable, 2 = variable
for (uint256 i = 0; i < assets.length; i++) {
modes[i] = 0;
}
LENDING_POOL.flashLoan(
receiverAddress,
assets,
amounts,
modes,
onBehalfOf,
params,
referralCode
);
}
/*
* Flash multiple assets
*/
function flashloan(address[] memory assets, uint256[] memory amounts) public onlyOwner {
_flashloan(assets, amounts);
}
/*
* Flash loan 1000000000000000000 wei (1 ether) worth of `_asset`
*/
function flashloan(address _asset) public onlyOwner {
bytes memory data = "";
uint amount = 1 ether;
address[] memory assets = new address[](1);
assets[0] = _asset;
uint256[] memory amounts = new uint256[](1);
amounts[0] = amount;
_flashloan(assets, amounts);
}
}
Withdrawable.sol (where withdraw() function comes from)
pragma solidity ^0.6.6;
import "#openzeppelin/contracts/token/ERC20/ERC20.sol";
import "#openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "#openzeppelin/contracts/access/Ownable.sol";
/**
Ensures that any contract that inherits from this contract is able to
withdraw funds that are accidentally received or stuck.
*/
contract Withdrawable is Ownable {
using SafeERC20 for ERC20;
address constant ETHER = address(0);
event LogWithdraw(
address indexed _from,
address indexed _assetAddress,
uint amount
);
/**
* #dev Withdraw asset.
* #param _assetAddress Asset to be withdrawn.
*/
function withdraw(address _assetAddress) public onlyOwner {
uint assetBalance;
if (_assetAddress == ETHER) {
address self = address(this); // workaround for a possible solidity bug
assetBalance = self.balance;
msg.sender.transfer(assetBalance);
} else {
assetBalance = ERC20(_assetAddress).balanceOf(address(this));
ERC20(_assetAddress).safeTransfer(msg.sender, assetBalance);
}
emit LogWithdraw(msg.sender, _assetAddress, assetBalance);
}
}