I have this flashloan contract here:

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;

        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);  

            block.timestamp + 1200

        uint tokenBalance = IERC20(token).balanceOf(address(this));
        IERC20(token).approve(routerB, tokenBalance);

            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)
        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;


     *  Flash multiple assets
    function flashloan(address[] memory assets, uint256[] memory amounts)
        _flashloan(assets, amounts);

     *  Flash loan 100000000000000000 wei (0.1 ether) worth of `_asset`
    function flashloan(address _asset) public onlyOwner {
        bytes memory data = "";
        uint256 amount = 10000 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;
        } 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 {}

Aparently everything is ok excepts that flashloan()doesn't appear on the truffle build. Yet i still receiving this error:

Error: execution reverted: SafeERC20: low-level call failed

Can someone give me a hint? I've already funded the contract.

1 Answer 1


For anyone interested on how it ends:


The problem was working with WMATIC as if it was WETH itself. Insteed of using such things as address(this).balance i used IERC20(token).balanceOf(cttAddress)

  • But WETH is an ERC20, you can't get a contract balance with address(this).balance
    – Madbreaks
    Nov 30, 2022 at 22:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.