首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >硬帽子错误:调用还原异常[参见: https://links.ethers.org/v5-errors-CALL_EXCEPTION ](method=“balanceOf(地址)”)

硬帽子错误:调用还原异常[参见: https://links.ethers.org/v5-errors-CALL_EXCEPTION ](method=“balanceOf(地址)”)
EN

Ethereum用户
提问于 2023-01-17 10:37:18
回答 1查看 79关注 0票数 0

对于稳健而言,这是非常新鲜的,并试图测试一项允许储户存款人存取款的非银行智能合同。

我有4个测试,当我运行测试时,3/4将返回这个错误,而我根本找不出它。任何建议都将不胜感激!

Smart合同:

代码语言:javascript
复制
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0;

import "./IERC20.sol"; import "./ILendingPool.sol";


contract DeFiBank {
    address[] public depositors;
    uint public immutable amount;
    uint public immutable fee;
    bool public isWithdrawing;
    mapping(address => uint) public bankBalance;
    mapping(address => bool) public hasDeposited;

    // AAVE v2 lending pool
    ILendingPool pool = ILendingPool(0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9);
    // the USDC stablecoin
    IERC20 usdc = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
    // aave interest bearing USDC
    IERC20 aUsdc = IERC20(0x9bA00D6856a4eDF4665BcA2C2309936572473B7E);

    constructor(uint _amount) {
        amount = _amount;
        // the bank will take a 0.5% fee on all amounts deposits 
        fee = amount * 5 / 1000;
    }

    function deposit() public payable {
        // transfer usdc from the depositor to the bank smartcontract to be deposited
        bool success = usdc.transferFrom(msg.sender, address(this), amount);

        // ensure the return value of the 'transferfrom' function is successful
        require(success, "transfer of funds failed");

        // bank will subtract their fee so ony the difference will be deposited in the pool
        // update the amount that will be deposited into the lending pool
        uint newAmount = (amount - fee);

        // approve the bank smartcontract transferring amount to aave lending pool
        // require statement used to check if the approval was successful or not 
        // and will stop the function from executing if so
        require(usdc.approve(address(pool), newAmount), 'approval failed');

        // deposit usdc into aave lending pool
        pool.deposit(address(usdc), newAmount, address(this), 0);

        // update depositors bank balance in the mapping
        bankBalance[msg.sender] = bankBalance[msg.sender] + newAmount;

        // add user to the list of depositors if they haven't already deposited
        if(!hasDeposited[msg.sender]) {
            depositors.push(msg.sender);
        }

        //update deposited status to keep track
        hasDeposited[msg.sender] = true;
    }
    // function to enable depositors to withdraw only their interest earned
    function withdrawlInterest() public {
        uint interest = (aUsdc.balanceOf(address(this)) - (amount));

        // approve the bank smartcontract spending the interest bearing aUsdc token
        // require statement used to check if the approval was successful or not 
        require(aUsdc.approve(address(pool), interest), "approval failed");

        // prevent reentrancy
        require(!isWithdrawing, "reentrancy detected");
        isWithdrawing = true;

        // iterate through all the depositors and allow any individual depositor to withdrawl any interest earned
        // but still keep the initial amount deposited within the bank
        // this will be an external call to withdrawl the interest on each depositor once and once only
        // this will prevent the external call from being made multiple times over each iteration potentially causing an unintended behavior or an infinite loop
        // the returned value will be saved in the array 'withdrawn'to check the amount withdrawn is the right amount
        // before using that amount to update the depsitors bank balance
        uint[] memory withdrawn = new uint[](depositors.length);
        for(uint i = 0; i < depositors.length; i++) {
            withdrawn[i] = pool.withdraw(address(usdc), interest, depositors[i]);

            // check that the withdrawal was successful and the correct amount was withdrawn i.e. only the interest
            // <= instead of the strict equality operator ==
            // This is so if the variable withdrawn[i] is less than interest for any reason, the function will still execute and the user's can still withdrawl.
            require(withdrawn[i] <= interest, "Incorrect amount withdrawn");
        }
        isWithdrawing = false;

        for(uint i = 0; i < depositors.length; i++) {
            // only withdrawl interest if interest has accrued
            if(withdrawn[i] > 0) {
                // update users bank balance
                bankBalance[depositors[i]] = bankBalance[depositors[i]] - withdrawn[i]; 
            }
        }
    }

    // function to allow depositors to withdraw their total balance(initial deposit + interest)
    function withdrawBalance() public {
        uint balance = aUsdc.balanceOf(address(this));

        // require statement used to check if the approval was successful or not 
        require(aUsdc.approve(address(pool), balance));

        // prevent reentrancy
        require(!isWithdrawing, "reentrancy detected");
        isWithdrawing = true;

        uint[] memory withdrawn2 = new uint[](depositors.length);
        for(uint i = 0; i < depositors.length; i++) {
            withdrawn2[i] = pool.withdraw(address(usdc), balance, depositors[i]);

            // check that the withdrawal was successful and the correct amount was withdrawn i.e. the entire balance
            require(withdrawn2[i] <= balance, "Incorrect amount withdrawn");
        }
        isWithdrawing = false;

        for(uint i = 0; i < depositors.length; i++) {
            // update their balance to 0
            bankBalance[depositors[i]] = 0;

            // delete depositor if they've withdrawn their entire balance
            delete depositors[i];
        }  
    } } 

JS测试:

代码语言:javascript
复制
const { expect, assert } = require("chai");
const { ethers, faucet } = require("hardhat");

describe('DeFiBank', function () {
  let deFiBank;
  let usdc;
  let aUsdc;
  let depositors;
  const amount = ethers.utils.parseEther('100');
  
  this.beforeEach(async () => {
    usdc = await ethers.getContractAt("IERC20", "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48");
    aUsdc = await ethers.getContractAt("IERC20", "0x9bA00D6856a4eDF4665BcA2C2309936572473B7E");
    pool = await ethers.getContractAt("ILendingPool", "0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9");

    depositors = (await ethers.provider.listAccounts()).slice(1, 4);

    const DeFiBank = await ethers.getContractFactory("DeFiBank");
    deFiBank = await DeFiBank.deploy(amount);
    await deFiBank.deployed();    

  });

  it('should set the banks take fee', async () => {
    const fee = await deFiBank.fee();
    assert(fee);
  });

  it('should deposit usdc into bank smart contract and update depositors balance', async () => {
    for (let i = 0; i < depositors.length; i++) {
      const signer = await ethers.provider.getSigner(depositors[i]);
      // ensure each depositor has approved the contract to transfer amount to smart contract
      await usdc.connect(signer).approve(deFiBank.address, amount);
      // depositors intial usdc balance
      const initialUsdcBalance = await usdc.balanceOf(depositors[i]);
      // call deposit function
      await deFiBank.connect(signer).deposit({value: amount});
      // depositors new usdc balance
      const finalUsdcBalance = await usdc.balanceOf(depositors[i]);
      // Check that the depositor's USDC balance decreases by the deposit amount
      expect.equal(finalUsdcBalance.toString(), (initialUsdcBalance.sub(amount)).toString());
      // check the transfer was successful
      const contractUsdcBalance = await usdc.balanceOf(deFiBank.address);
      expect.equal(contractUsdcBalance.toString(), (contractUsdcBalance.add(amount).toString()));
      // revert if transfer was unsuccessful
      await assert.reverts(deFiBank.connect(signer).deposit({value: amount}), "transfer of funds failed");
      // approve and deposit the usdc into the leding pool
      const fee = await deFiBank.fee();
      assert(fee);
      // bank only deposits amount - fees
      const newAmount = amount - fee;
      await usdc.connect(signer).approve(pool.address, newAmount);
      // revert if approval fails
      await assert.reverts(usdc.connect(signer).approve(pool.address, newAmount), "approval failed");
      // check that the DeFiBank contract now has an aUsdc balance
      const contractAUsdcBalance = await aUsdc.balanceOf(deFiBank.address);
      expect((contractAUsdcBalance.gte(newAmount.mul(depositors.length))).toString());
      // Check that the depositor's bank balance(mapping) increases by 
      // (amount - fee) after deposit() is called
      const initalBankBalance = await deFiBank.bankBalance(depositors[i]);
      const finalBankBalance = await deFiBank.bankBalance(depositors[i]);
      expect.equal(finalBankBalance.toString(), (initalBankBalance.add(newAmount)).toString());
      // check the depositor has been added to the list of depositors if they have not already deposited
      const depositorAdded = await deFiBank.hasDeposited(depositors[i]);
      assert.isTrue(depositorAdded);
    }
  });
  
  it('should withdrawal Interest and update deopsitors balance', async () => {
    for(let i = 0; i < depositors.length; i++) {
      const signer = await ethers.provider.getSigner(depositors[i]);
      const initBankAusdcBalance = await aUsdc.balanceOf(deFiBank.address);
      const interest = await (aUsdc.balanceOf(deFiBank.address)).sub(initBankAusdcBalance);
      const finBankAusdcBalance = await aUsdc.balanceOf(deFiBank.address);

      // ensure depositors have approved the pool to spend their aUsdc to transfer the interest earned
      await aUsdc.connect(signer).approve(pool.address, interest);
      // revert transaction if the approval failed
      await assert.reverts(aUsdc.connect(signer).approve(pool.address, interest), "approval failed");

      // withdrawl interest
      const withdrawn = await pool.connect(signer).withdrawlInterest({value: interest});
      // check the withdrawn amount is equal to expected amount
      assert.isTrue(withdrawn == interest);
      // revert if amount withdrawn is not equal to interest rate
      await assert.reverts(pool.connect(signer).withdrawlInterest({value: interest}), "Incorrect amount withdrawn");

      // check the bank contract's intial aUsdc balance decreases by the interest amount
      expect.equal(finBankAusdcBalance, initBankAusdcBalance.sub(interest));
      
      // check that the depositers usdc balance increases by the interest amount
      const initDepositorUsdcBalance = await usdc.balanceOf(depositors[i]);
      const finDepositorUsdcBalance = await usdc.balanceOf(depositors[i]);
      expect.equal(finDepositorUsdcBalance, initDepositorUsdcBalance.add(interest));

      // Check that the depositor's bank balance(mapping) decreases by 
      // the interest amount after withdrawlInterest() has been called only if interest > 0
      if(interest > 0) {
        const initlBankBalance = await deFiBank.bankBalance(depositors[i]);
        const finBankBalance = await deFiBank.bankBalance(depositors[i]);
        expect.equal(finBankBalance, initlBankBalance.sub(interest));
      }
      // check to make sure depositor can't withdraw interest more than once by calling the function recursively
      await pool.connect(signer).withdrawlInterest({value: interest});
      await assert.reverts(pool.connect(signer).withdrawlInterest({value: interest}), "reentrancy detected");
    }
  });

  it('should withdrawl total usdc balance and update depositors bank balance', async () => {
    for(let i = 0; i < depositors.length; i++) {
      const signer = await ethers.provider.getSigner(depositors[i]);
      const initialBankAusdcBalance = await aUsdc.balanceOf(deFiBank.address);
      const interest = await (aUsdc.balanceOf(deFiBank.address)).sub(initialBankAusdcBalance);

      // ensure depositors have approved the pool to spend their aUsdc to transfer their total balance
      await aUsdc.connect(signer).approve(pool.address, (initialBankAusdcBalance + interest));
      // revert transaction if the approval failed
      await assert.reverts(aUsdc.connect(signer).approve(pool.address, (initialBankAusdcBalance + interest), "approval failed"));

      // withdrawl total balance
      const withdrawl = await pool.connect(signer).withdrawlBalance({value: amount + interest});
      // check the withdrawn amount is equal to expected amount
      assert.isTrue(withdrawl == (initialBankAusdcBalance + interest));
      // revert if amount withdrawn is not equal to expected amount
      await assert.reverts(pool.connect(signer).withdrawlBalance({value: initialBankAusdcBalance}), "Incorrect amount withdrawn");

      // check the bank contract's intial aUsdc balance decreases by the interest amount
      const finalBankAusdcBalance = await aUsdc.balanceOf(deFiBank.address);
      expect.equal(finalBankAusdcBalance, initialBankAusdcBalance.sub(amount + interest));
      
      // check that the depositers usdc balance increases by the interest amount
      const initialDepositorUsdcBalance = await usdc.balanceOf(depositors[i]);
      const finalDepositorUsdcBalance = await usdc.balanceOf(depositors[i]);
      expect.equal(finalDepositorUsdcBalance, initialDepositorUsdcBalance.add(amount + interest));

      // Check that the depositor's bank balance(mapping) should be 0 
      const BankBalance = await deFiBank.bankBalance(depositors[i]);
      expect.equal(bankBalance, 0);
      
      // check to make sure depositor can't withdraw balance more than once 
      // by calling the function recursively e.g. reentrancy attack
      await pool.connect(signer).withdrawlBalance({value: amount + interest});
      await assert.reverts(pool.connect(signer).withdrawlBalance({value: amount + interest}), "reentrancy detected");
    }
  });
});

错误出现在最后的3个测试中: withdrawlInterest函数和withdrawlBalance函数。

EN

回答 1

Ethereum用户

发布于 2023-01-17 12:35:34

从您的代码中,我假设您需要分叉ethereum,而您可能不是。

你的帖子也缺乏信息,我不知道你在用什么工具。

如果您使用“硬帽子”,这里是您的方便链接:https://hardhat.org/hardhat-network/docs/guides/forking-other-networks

您将需要具有ethereum存档节点的节点提供程序。我可以推荐的一个好的节点提供程序是:https://www.alchemy.com

票数 0
EN
页面原文内容由Ethereum提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://ethereum.stackexchange.com/questions/143130

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档