错误:处理事务时 VM 异常:使用原因字符串“BAL#400”恢复:REENTRANCY

问题描述 投票:0回答:1

我编写了以下合约,以便在平衡器和 uniswapv3 之间执行交叉闪贷。合约尚未完成,但是我遇到了 REENTRANCY 错误,这可能是由我在 receiveFlashLoan 钩子上覆盖套利逻辑的部分引起的。我放置合约,测试文件和测试结果如下:

闪贷.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@balancer-labs/v2-interfaces/contracts/vault/IVault.sol";
import "@balancer-labs/v2-interfaces/contracts/vault/IFlashLoanRecipient.sol";
import "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";

contract CrossFlashLoan is IFlashLoanRecipient {
    /*///////////////////////////////////////////////////////////////
                        FLASHLOAN CONSTANTS 
    //////////////////////////////////////////////////////////////*/
    IVault private constant vault = IVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8);//Balancer Vault address on Ethereum Mainnet
    /*///////////////////////////////////////////////////////////////
                        STATE VARIABLES
    //////////////////////////////////////////////////////////////*/
    /**
     * @dev flashloanTokens will be always borrowed tokens and only one token can be borrowed each time
     * Only 0th elementh of amount array will be used to determine the amount of loan that will be received
     * flashloanTokens array consists of only wrapped ether by default 
     * while strategyTokens consist of only bal by default 
     */
    ISwapRouter private  UNISWAP_ROUTER = ISwapRouter(0xE592427A0AEce92De3Edee1F18E0157C05861564); // Uniswap Router address on Ethereum Mainnet
    address  BALANCER_POOL_ADDRESS =  0x9dDE0b1d39d0d2C6589Cde1BFeD3542d2a3C5b11; // Balancer Pool Address 
    IERC20[] flashLoanTokens=[IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2)];
    IERC20[] strategyTokens=[IERC20(IERC20(0xba100000625a3754423978a60c9317c58a424e3D))]; 
    uint256[]  amount;
    /*///////////////////////////////////////////////////////////////
                        CUSTOM ERRORS 
    //////////////////////////////////////////////////////////////*/
    error zeroAddressDetected();
    error onlyOneBorrowingTokenSupported();
    error callerIsNotContract();
    
    /*///////////////////////////////////////////////////////////////
                        EXTERNAL FUNCTIONS  
    //////////////////////////////////////////////////////////////*/
    /**
     * 
     * @param _amount The amount which will be borrowed by caller 
     */   
    function executeFlashLoan(
        uint256 _amount
    ) external{
      //check if executor is 0 address 
      if(msg.sender==address(0)){
        revert zeroAddressDetected();
      }
      uint256[] memory amounts = new uint256[](1);
      amounts[0] = _amount ;
      vault.flashLoan(this,flashLoanTokens,amounts,abi.encode(msg.sender));
    }
    // Function to be called by Balancer flash loan
    function receiveFlashLoan(
        IERC20[] memory tokens,
        uint256[] memory amounts,
        uint256[] memory feeAmounts,
        bytes memory userData
    ) external override{
        // Checks
        if(tokens.length!=1){
            revert onlyOneBorrowingTokenSupported();
        }
        if(address(tokens[0])==address(0)){
            revert zeroAddressDetected();
        }    
        // Arbitrage Logic
        // Ensure proper ERC-20 token handling
        address receipient;
        (receipient) = abi.decode(userData, (address));
        if(receipient == address(0)){
            revert zeroAddressDetected();
        }
    
        // Effects
        // 1. Swap borrowed WETH to BAL on Uniswap V3
        tokens[0].approve(address(UNISWAP_ROUTER), amounts[0]);
        ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
            tokenIn: address(tokens[0]),
            tokenOut: address(strategyTokens[0]),
            fee: 3000, // 0.3% fee
            recipient: address(this),
            deadline: block.timestamp,
            amountIn: amounts[0],
            amountOutMinimum: 0,
            sqrtPriceLimitX96: 0
        });
        UNISWAP_ROUTER.exactInputSingle(params);
    
        // Update state after external call
        amounts[0] = strategyTokens[0].balanceOf(address(this));
    
        // 2. Send BAL to Balancer Pool
        strategyTokens[0].approve(address(vault), amounts[0]);
    
        // 3. Swap BAL to WETH back on BalancerV2 then Repay the Loan
        vault.swap(
            IVault.SingleSwap(
                bytes32(abi.encodePacked(BALANCER_POOL_ADDRESS)),
                IVault.SwapKind.GIVEN_OUT,
                IAsset(address(strategyTokens[0])),
                IAsset(address(tokens[0])),
                amounts[0],
                ""
            ),
            IVault.FundManagement(
                address(this),
                true,
                payable(BALANCER_POOL_ADDRESS),
                true
            ),
            amounts[0] + feeAmounts[0],
            15 minutes
        );    
        // Interactions
        // Send funds to EOA (externally owned account)
        amounts[0] = flashLoanTokens[0].balanceOf(address(this));
        flashLoanTokens[0].approve(receipient,amounts[0]);
        tokens[0].transferFrom(address(this),receipient, amounts[0]);
    }
    
 
    

    /*///////////////////////////////////////////////////////////////
                        VIEW FUNCTIONS  
    //////////////////////////////////////////////////////////////*/

    function FlashloanToken() external view returns(address){
        return address(flashLoanTokens[0]);
    }
    function StrategyToken() external view returns(address){
        return address(strategyTokens[0]);
    }
}

闪贷_test.ts

import { expect } from "chai";
import { ethers } from "hardhat"
describe("FlashLoan Test",()=>{
  let FlashLoan:any,flashloan:any;
  before("Deploy Contract",async()=>{
    const [deployer] = await ethers.getSigners();
    FlashLoan = await ethers.getContractFactory("CrossFlashLoan",deployer);
    flashloan = await FlashLoan.deploy();
  })
  it("Default flashloan token is wrapped ether",async()=>{
    let [owner]= await ethers.getSigners()
    expect(await flashloan.connect(owner).FlashloanToken()).is.eq('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2');
  })
  it("Default strategy Token is balancer",async()=>{
    let [owner]= await ethers.getSigners()
    expect(await flashloan.connect(owner).StrategyToken()).is.eq('0xba100000625a3754423978a60c9317c58a424e3D');
  })
  it("Execute flashloan",async()=>{
    let [owner]= await ethers.getSigners()
    expect(await flashloan.connect(owner).executeFlashLoan(ethers.parseEther('1')));
  })
})

测试结果闪贷测试

   ` ✔ Default flashloan token is wrapped ether
    ✔ Default strategy Token is balancer
    1) Execute flashloan


  2 passing (5s)
  1 failing

  1) FlashLoan Test
       Execute flashloan:
     Error: VM Exception while processing transaction: reverted with reason string 'BAL#400'
    at <UnrecognizedContract>.<unknown> (0xba12222222228d8ba445958a75a0704d566bf2c8)
    at CrossFlashLoan.receiveFlashLoan (contracts/Flashloan.sol:99)
    at <UnrecognizedContract>.<unknown> (0xba12222222228d8ba445958a75a0704d566bf2c8)
    at CrossFlashLoan.executeFlashLoan (contracts/Flashloan.sol:53)
    at async HardhatNode._mineBlockWithPendingTxs (node_modules\hardhat\src\internal\hardhat-network\provider\node.ts:1866:23)
    at async HardhatNode.mineBlock (node_modules\hardhat\src\internal\hardhat-network\provider\node.ts:524:16)
    at async EthModule._sendTransactionAndReturnHash (node_modules\hardhat\src\internal\hardhat-network\provider\modules\eth.ts:1482:18)   
    at async HardhatNetworkProvider.request (node_modules\hardhat\src\internal\hardhat-network\provider\provider.ts:124:18)
    at async HardhatEthersSigner.sendTransaction (node_modules\@nomicfoundation\hardhat-ethers\src\signers.ts:125:18)
    at async send (node_modules\ethers\src.ts\contract\contract.ts:313:20)
    at async Proxy.executeFlashLoan (node_modules\ethers\src.ts\contract\contract.ts:352:16)

我检查了 receiveFlashLoan 和 Execute flashloan 函数中的所有检查。并多次阅读平衡器文档。另外,我检查了以下具有 flashLoan 函数定义的网址:https://github.com/balancer/balancer-v2-monorepo/blob/master/pkg/vault/contracts/FlashLoans.sol但是我无法修复该错误在我的代码中。 Mycode多次调用flashloan函数,但我不知道是什么原因造成的。我想确定这个错误的原因并找到解决方法。

blockchain ethers.js hardhat reentrancy
1个回答
0
投票

我尝试使用uniswap和pancakeswap,将approve更改为safeApprove等。在非shell中,我尝试了所有相关和不相关的方法来改进我的代码并解决错误,但我保存的是,该错误与缺乏任何这些改进无关。当我发现以下github问题https://github.com/balancer/balancer-v2-monorepo/issues/2346时我了解到它并回答了我的问题。由于调用vault.swap函数而发生重入错误。但是我不能简单地使用uniswap,pancakeswap等来代替它,或者也不能使用平衡器的另一个交换功能。因此有两种方法可以解决问题。第一种方法是从平衡器v2获取闪贷并使用平衡器v1执行交换,第二种方法是正在使用另一种协议进行闪贷。在我的研究结束时,我了解到最好的选择是使用另一种协议来获取闪贷并执行掉期。

© www.soinside.com 2019 - 2024. All rights reserved.