2

I have successfully deployed and executed a flashloan on the Polygon network in testnet and have since adapted it for mainnet.

On Mainnet it compiles and gets deployed with no problem, but when I deploy in Remix and execute the requestFlashLoan function with any of the following addresses, followed by 1 USDC (1000000) I get these error messages:

Aave: aUSDC Token V3 - requestFlashLoan(0x625e7708f30ca75bfd92586e17077590c60eb4cd,1000000) -- Fail with error '27'

Token USD Coin (PoS) - requestFlashLoan(0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174,1000000) -- Fail with error 'ERC20: transfer amount exceeds balance

Token Aave Matic Market USDC - requestFlashLoan(0x1a13F4Ca1d028320A707D99520AbFefca3998b7F,1000000) -- Fail with error '27'

I am not sure if I simply haven't found the right token address or if there's something else I have overlooked.

I used the addresses pool provider as recommended by Aave: 0xa97684ead0e402dc232d5a977953df7ecbab3cdb

I have my .env, hardhat.config.js and package.js files all in working order, as I said compiling and deployment is a breeze, no errors or warnings.

So here is my Solidiy script:

// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;

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


contract FlashLoan is FlashLoanSimpleReceiverBase {
    address payable owner;

    constructor(address _addressProvider) FlashLoanSimpleReceiverBase(IPoolAddressesProvider(_addressProvider))
    {
        owner = payable(msg.sender);
    }

    function executeOperation(
    address asset,
    uint256 amount,
    uint256 premium,
    address initiator,
    bytes calldata params
  ) external override returns (bool) {
    // We have the funds at this point
    // custom logic
    uint256 amountOwed = amount + premium;
    IERC20(asset).approve(address(POOL), amountOwed);

    return true;
  }

  function requestFlashLoan(address _token, uint256 _amount) public{
    address receiverAddress = address(this);
    address asset = _token;
    uint256 amount = _amount; 
    bytes memory params = "";
    uint16 referralCode = 0;

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

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

 function withdraw(address _tokenAddress) external onlyOwner{
    IERC20 token = IERC20(_tokenAddress);
    token.transfer(msg.sender, token.balanceOf(address(this)));
 }

    modifier onlyOwner(){

        require(msg.sender == owner, "Only the contract owner can call this function" );
        _;
    }

    receive() external payable {}

}

And here is my deployment script:

    const hre = require("hardhat");
    
    async function main() {
     const FlashLoan = await hre.ethers.getContractFactory("FlashLoan");
     const flashLoan = await FlashLoan.deploy(
      "0xa97684ead0e402dC232d5A977953DF7ECBaB3CDb"
      );
    
     await flashLoan.deployed();
     console.log("Flash loan contract deployed: ", flashLoan.address)
    }
    
    main().catch((error) => {
      console.error(error);
      process.exitCode = 1;
    });

With the contract deployed I then go to remix, compile the Solidity script then interact with my contract address to pull up the functions for the flashloan in remix and add the address and the amount and the amount and click on getFlashLoan.

enter image description here

But it never succeeds and I always get a warning pop-up regarding gas fees saying it will probably fail as soon as I click on the getFlashLoan function, so I max out the gas fees.

enter image description here

It always gets mined and appears as a failed transaction on Polygonscan with the one of the two error messages mentioned above.

enter image description here

enter image description here

Does anyone have any idea what it might be?

This is the original code for the flashloan on testnet (Mumbai):

// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;

import {FlashLoanSimpleReceiverBase} from "@aave/core-v3/contracts/flashloan/base/FlashLoanSimpleReceiverBase.sol";
import {IPoolAddressesProvider} from "@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol";
import {IERC20} from "@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20.sol";

contract FlashLoan is FlashLoanSimpleReceiverBase {
    address payable owner;

    constructor(address _addressProvider)
        FlashLoanSimpleReceiverBase(IPoolAddressesProvider(_addressProvider))
    {
        owner = payable(msg.sender);
    }

    /**
        This function is called after your contract has received the flash loaned amount
     */
    function executeOperation(
        address asset,
        uint256 amount,
        uint256 premium,
        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 amount + premiums.
        // Therefore ensure your contract has enough to repay
        // these amounts.

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

        return true;
    }

    function requestFlashLoan(address _token, uint256 _amount) public {
        address receiverAddress = address(this);
        address asset = _token;
        uint256 amount = _amount;
        bytes memory params = "";
        uint16 referralCode = 0;

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

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

    function withdraw(address _tokenAddress) external onlyOwner {
        IERC20 token = IERC20(_tokenAddress);
        token.transfer(msg.sender, token.balanceOf(address(this)));
    }

    modifier onlyOwner() {
        require(
            msg.sender == owner,
            "Only the contract owner can call this function"
        );
        _;
    }

    receive() external payable {}
}

And this is the deployment script:

const hre = require("hardhat");

async function main() {
  console.log("deploying...");
  const FlashLoan = await hre.ethers.getContractFactory("FlashLoan");
  const flashLoan = await FlashLoan.deploy(
    "0xc4dCB5126a3AfEd129BC3668Ea19285A9f56D15D"
  );

  await flashLoan.deployed();

  console.log("Flash loan contract deployed: ", flashLoan.address);
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

Thanks in Advance.

9
  • 1
    Sure, this is the contract address for testnet, it's a bit of a mess: 0x47B578b88bc728F87A220dbF18E8560B8ab11dA9. The successful transaction was this one: mumbai.polygonscan.com/tx/…
    – Peter
    Commented Aug 30, 2023 at 17:17
  • 1
    Just realised it's in bytecode, I added the testnet contract and deployment script to my question. Thanks @RohanNero
    – Peter
    Commented Aug 30, 2023 at 17:27
  • 1
    Excellent, let me give that a try.
    – Peter
    Commented Aug 30, 2023 at 17:29
  • 1
    Thanks @RohanNero will take a look. So the flashloan was successful! polygonscan.com/tx/… Thanks so much for your help! Next step: I already have a successful swap script just need to link the two together to get a functioning flashloan that interacts with a swap, here goes nothing :)
    – Peter
    Commented Aug 30, 2023 at 17:45
  • 1
    Thanks @RohanNero, have zero expectations in this regard. But would be unbelievable if that actually happend.
    – Peter
    Commented Aug 30, 2023 at 18:01

1 Answer 1

0

The ERC20: transfer amount exceeds balance error means that you are trying to transfer or transferFrom more tokens than you currently hold.

The USDC token address you provided in your answer seems to be the correct one, so I don't think that is the issue.

I think the issue is being caused by the flashloan needing to be repaid with a premium. For example, you are borrowing $1 USDC, but when Aave tries to repay the loan on your behalf, they want to transferFrom the $1 USDC plus a little extra. So when the transferFrom call is made with $1.05~ USDC, it reverts because your flashloan receiver contract only has the initial $1 USDC.

You could add a conditional that views your USDC balanceOf before you attempt to repay the flashloan:

error InsufficientBalance(uint bal, uint amount);



function executeOperation(
        address asset,
        uint256 amount,
        uint256 premium,
        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 amount + premiums.
        // Therefore ensure your contract has enough to repay
        // these amounts.

        // Check balance and revert if flashloan can't be repaid
        uint256 bal = getBalance(asset);
        uint256 amountOwed = amount + premium;
        if(bal < amountOwed) { 
        revert InsufficientBalance(bal, amountOwed);
        }

       // Approve the Pool contract allowance to *pull* the owed amount
        
        IERC20(asset).approve(address(POOL), amountOwed);
        return true;
    }

This way if your contract doesn't have enough USDC, the function call will revert with message that displays your balance compared to the amount you are trying to transfer.

I see you have a withdraw function already written, but for future reference to anyone reading this, if you do plan on sending real funds you a contract you've deployed, make sure you fully understand the code you've written and ensure that there is a way for you to withdraw your tokens, otherwise they will be lost forever.

4
  • Thanks so much for your reply. I will try this, but what I don't understand is why do I need to hold a balance of USDC to borrow USDC? I thought I just need to hold enough Matic to cover the gas fees and the loan fee. Or is the loan fee charged in USDC? I am only testing with very small amounts at the moment. I would never bet with large volumes until I have a solid grasp.
    – Peter
    Commented Aug 30, 2023 at 16:49
  • Sorry @Peter, I might have misunderstood the issue originally, could you provide Polygonscan links to the contracts you are calling?
    – Rohan Nero
    Commented Aug 30, 2023 at 16:52
  • It's just one contract which I have interacted with many times and failed: polygonscan.com/address/…
    – Peter
    Commented Aug 30, 2023 at 16:54
  • Ok thank you, I'll look through it now
    – Rohan Nero
    Commented Aug 30, 2023 at 17:01

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.