dinngo / protocolink-js-sdk

TypeScript SDK for Protocolink's API, lending, common libraries.
https://protocolink.com
MIT License
14 stars 10 forks source link

Cannot estimate gas on flash loan #51

Open lucarducci opened 5 months ago

lucarducci commented 5 months ago

I have the following script to request a USD flash loan on Polygon blockchain:

import * as api from "@protocolink/api";
import * as common from "@protocolink/common";
import ethers from "ethers";
import dotenv from "dotenv";

// Config
dotenv.config();
let chainId = common.ChainId.polygon;
let loanToken = "USDC";
let amount = 1;
let network = "matic";
let account = process.env.ACCOUNT;
let mnemonic = process.env.MNEMONIC;

try {
  // Setup wallet

  let wallet = ethers.Wallet.fromMnemonic(mnemonic);
  let provider = new ethers.providers.InfuraProvider(network, process.env.INFURA_KEY);
  let signer = wallet.connect(provider);

  console.log("Start bot");

  // Get token list for loan
  const tokenList = await api.protocols.aavev3.getFlashLoanTokenList(chainId);
  let underlyingToken;
  for (let token of tokenList) {
    if (token.symbol == loanToken) {
      underlyingToken = token;
    }
  }

  const loans = [
    {
      token: underlyingToken,
      amount: amount.toString(),
    },
  ];

  // Create loan
  const flashLoanQuotation = await api.protocols.aavev3.getFlashLoanQuotation(chainId, {
    loans,
  });

  let i = underlyingToken.address;
  console.log(
    "Amount to repay: ",
    flashLoanQuotation.repays.tokenAmountMap[i].amount + " " + flashLoanQuotation.repays.tokenAmountMap[i].token.symbol
  );

  const [flashLoanLoanLogic, flashLoanRepayLogic] = api.protocols.aavev3.newFlashLoanLogicPair(loans);
  const logics = [flashLoanLoanLogic, flashLoanRepayLogic];

  // Build router data
  const routerData: api.RouterData = {
    chainId,
    account: account,
    logics: logics,
  };
  let estimateResult = await api.estimateRouterData(routerData, { permit2Type: "permit" });

  console.log(
    "FUNDS: ",
    estimateResult.funds.tokenAmountMap[i].amount + " " + estimateResult.funds.tokenAmountMap[i].token.symbol
  );
  console.log("FEE 1: ", estimateResult.fees[0].feeAmount.amount + " " + estimateResult.fees[0].feeAmount.token.symbol);
  console.log("FEE 2: ", estimateResult.fees[1].feeAmount.amount + " " + estimateResult.fees[0].feeAmount.token.symbol);

  // Increase fee
  estimateResult.fees[0].feeAmount.amount = (Number(estimateResult.fees[0].feeAmount.amount) * 20).toString();
  estimateResult.fees[1].feeAmount.amount = (Number(estimateResult.fees[1].feeAmount.amount) * 20).toString();

  // Approve
  for (const approval of estimateResult.approvals) {
    console.log("Approve ...");
    const txApprove = await signer.sendTransaction(approval);
    txApprove.wait();
    console.log("Gas limit: ", Number(txApprove.gasLimit));
  }

  // Permit
  if (estimateResult.permitData) {
    console.log("Sign ...");
    const permitSig = await signer._signTypedData(
      estimateResult.permitData.domain,
      estimateResult.permitData.types,
      estimateResult.permitData.values
    );
    // console.log("PermitSig", permitSig);
    routerData.permitData = estimateResult.permitData;
    routerData.permitSig = permitSig;
  } else {
    console.log("NOTHING TO SIGN");
  }

  // Execute
  console.log("Execute ...");
  const transactionRequest = await api.buildRouterTransactionRequest(routerData);

  console.log("Send ...");
  const tx = await signer.sendTransaction(transactionRequest);
  console.log(tx);
} catch (e) {
  console.error("ERROR: ", e.reason || e);
}

This is the output log:

Start bot
Amount to repay:  1.0005 USDC
FUNDS:  0.0005 USDC
FEE 1:  0.000667898340426478 MATIC
FEE 2:  0.000001335796680852 MATIC
Approve ...
Gas limit:  56107
Sign ...
Set fees
Execute ...
Send ...
ERROR:  cannot estimate gas; transaction may fail or may require manual gas limit

Why is it trowing this error on gas fees?

zodahu commented 5 months ago

Hey @lucarducci, thank you for your question.

lucarducci commented 5 months ago

Also with the balancer flash loan I get the 2 objects in approval and the permitData in the estimateResult. Is it normal? I keep getting the same error.

How can I set a manual gas limit in protocolink?

zodahu commented 5 months ago
  1. This is unusual. Did you implement newFlashLoanLogicPair similar to the example? If so, your routerData should look like this:"
{
  "chainId": 137,
  "account": "0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa",
  "logics": [
    {
      "rid": "balancer-v2:flash-loan",
      "fields": {
        "id": "6f6cc4d7-001f-4be8-a444-3f98abb9ba24",
        "loans": [
          {
            "token": {
              "chainId": 137,
              "address": "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270",
              "decimals": 18,
              "symbol": "WMATIC",
              "name": "Wrapped Matic"
            },
            "amount": "10000"
          }
        ],
        "isLoan": true
      }
    },
    {
      "rid": "balancer-v2:flash-loan",
      "fields": {
        "id": "6f6cc4d7-001f-4be8-a444-3f98abb9ba24",
        "loans": [
          {
            "token": {
              "chainId": 137,
              "address": "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270",
              "decimals": 18,
              "symbol": "WMATIC",
              "name": "Wrapped Matic"
            },
            "amount": "10000"
          }
        ],
        "isLoan": false
      }
    }
  ]
}
  1. Ethers.js’s signer.sendTransaction() allows to set both gasLimit and gasPrice.
lucarducci commented 5 months ago

It worked adding transactionRequest.gasLimit = txApprove.gasLimit

Now I get the transaction completed with no errors but I can't see the tx in the blockchain explorer, it should be here: https://polygonscan.com/tx/0xbb4fed62d09fa508b757a67ee1786f7be86214d233653996a98ba7f2800f338c

And also I don't see the few USDC that I should have paid from my wallet. Am I missing something?

Below the full log of the result of the sendTransaction function:

{
  type: 2,
  chainId: 137,
  nonce: 100,
  maxPriorityFeePerGas: BigNumber { _hex: '0x59682f00', _isBigNumber: true },
  maxFeePerGas: BigNumber { _hex: '0x59682f3e', _isBigNumber: true },
  gasPrice: null,
  gasLimit: BigNumber { _hex: '0x01b656', _isBigNumber: true },
  to: '0xDec80E988F4baF43be69c13711453013c212feA8',
  value: BigNumber { _hex: '0x18cf45cf0201d3', _isBigNumber: true },
  data: '0xa1de453700000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000380000000000000000000000000ffff5a88840ff1f168e163acd771dfb292164cfa0000000000000000000000000000000000000000000000000000000000000fe0000000000000000000000000000000000000000000000000000000000000106000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001842b67b57000000000000000000000000053e09dd70e20a0b5ceaf2447484063dccbe3f2190000000000000000000000003c499c542cef5e3811e1192ce70d8cc03d5c3359000000000000000000000000ffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000006660a8ff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000006e544a364fb3412e25809aa882efd5b5d5ea36d100000000000000000000000000000000000000000000000000000000663a6d7f00000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000041f9ce17b1431022eb169654c935a99bcdb94a5ce4a7f63de3422a315071da5d880e656f4b966ebdcb360513bf872233a0cb1d892f8fafa3310c72f6456a90a1711c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008436c7851600000000000000000000000053e09dd70e20a0b5ceaf2447484063dccbe3f2190000000000000000000000006e544a364fb3412e25809aa882efd5b5d5ea36d10000000000000000000000000000000000000000000000000000000000001ad90000000000000000000000003c499c542cef5e3811e1192ce70d8cc03d5c33590000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000b400000000000000000000000000000000000000000000000000000000000000c2000000000000000000000000000000000000000000000000000000000663a6d8400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000794a61358d6845594f94dc1db02a252b5b4814ad00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000a60000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006f81cf774052d03873b32944a036bf0647bfb5bf0000000000000000000000000000000000000000000000000000000000000964ab9c4b5d0000000000000000000000006f81cf774052d03873b32944a036bf0647bfb5bf00000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000010000000000000000000000003c499c542cef5e3811e1192ce70d8cc03d5c3359000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000009896800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000005a0000000000000000000000000e592427a0aece92de3edee1f18e0157c0586156400000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000104414bf3890000000000000000000000003c499c542cef5e3811e1192ce70d8cc03d5c3359000000000000000000000000c2132d05d31c914a87c6611c10748aeb04b58e8f00000000000000000000000000000000000000000000000000000000000000640000000000000000000000006e544a364fb3412e25809aa882efd5b5d5ea36d10000000000000000000000000000000000000000000000000000000066391d2f00000000000000000000000000000000000000000000000000000000009896800000000000000000000000000000000000000000000000000000000000970a1a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000003c499c542cef5e3811e1192ce70d8cc03d5c335900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000989680000000000000000000000000e592427a0aece92de3edee1f18e0157c0586156400000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000144c04b8d59000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000006e544a364fb3412e25809aa882efd5b5d5ea36d10000000000000000000000000000000000000000000000000000000066391d2f00000000000000000000000000000000000000000000000000000000009890ac00000000000000000000000000000000000000000000000000000000009708a10000000000000000000000000000000000000000000000000000000000000042c2132d05d31c914a87c6611c10748aeb04b58e8f0000648f3cf7ad23cd3cadbd9735aff958023239c6a0630000643c499c542cef5e3811e1192ce70d8cc03d5c3359000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000c2132d05d31c914a87c6611c10748aeb04b58e8f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009890ac0000000000000000000000003c499c542cef5e3811e1192ce70d8cc03d5c335900000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044a9059cbb0000000000000000000000006f81cf774052d03873b32944a036bf0647bfb5bf000000000000000000000000000000000000000000000000000000000098aa080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000003c499c542cef5e3811e1192ce70d8cc03d5c33590000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000098aa080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000018becd5d414633616176652d76333a666c6173682d6c6f616e0000000000000000000000000000000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000000107871c0bba07065726d6974323a70756c6c2d746f6b656e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001fb20753f85f89be6f42d228667d70e62d1ba5f75000000000000000000002710000000000000000000000000000000000000000000000000000000000000004151991e886b4a7b7353154311fc5c451f6a91f78ad40333907e32b74fb1e2206340c289c0fcafa37430fb1e4644753f300446f22a22f44a14510d7c373cd47f4e1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000003c499c542cef5e3811e1192ce70d8cc03d5c3359000000000000000000000000c2132d05d31c914a87c6611c10748aeb04b58e8f',
  accessList: [],
  hash: '0xbb4fed62d09fa508b757a67ee1786f7be86214d233653996a98ba7f2800f338c',
  v: 0,
  r: '0xbe9c5136bedc0f66a8263c22cb383d452368466a2398517013922722e3352484',
  s: '0x2be3ec6375fe2fc420da53c7ee97de4cf93fe60bb1143bef030c93c58cba66ed',
  from: '0x53e09Dd70E20a0b5CeaF2447484063dcCbE3f219',
  confirmations: 0,
  wait: [Function (anonymous)]
}
zodahu commented 5 months ago
lucarducci commented 5 months ago

I see I can get it with provider.getGasPrice() or provider.getFeeData() but both don't works. Then I tried with the contract.estimateGas but I miss some info, here what I tried:

  // Is this correct? I took it from the transactionRequest
  let protolinkContractAddress="0xDec80E988F4baF43be69c13711453013c212feA8"; 
  let contract = new ethers.Contract(protolinkContractAddress, ABIprotolink, provider);
  // here I miss the parameters to execute this function
  let estimategas = await contract.estimateGas.executeWithSignerFee();

If this is a standard procedure to get the gas limit it would be helpful to add this to the docs.

zodahu commented 5 months ago

This Tenderly simulation indicates that 0x53e09dd70e20a0b5ceaf2447484063dccbe3f219 has not yet approved USDC for its agent 0x6E544A364fb3412E25809aA882EfD5b5d5ea36D1 (calcAgent).

If you are unfamiliar with how the Uniswap off-chain permit2 sign works, I recommend using permit2Type: approve initially (see documentation). First, execute the two approvals obtained from await api.estimateRouterData(routerData, { permit2Type: 'approve'}) on-chain. Afterwards, use Ethers.js’s estimateGas to estimate the gas limit and verify whether the transaction reverts.

zodahu commented 4 months ago

@lucarducci Is there anything else we can help you with? And feel free to join our Discord we're here and happy to help!

Juniorduc44 commented 2 months ago

So are flash loans working? Using furucombo I noticed it just fails the transactions.

Anyone know of alternative that works or have code that can be forked?