对于稳健而言,这是非常新鲜的,并试图测试一项允许储户存款人存取款的非银行智能合同。
我有4个测试,当我运行测试时,3/4将返回这个错误,而我根本找不出它。任何建议都将不胜感激!
Smart合同:
// 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测试:
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函数。
发布于 2023-01-17 12:35:34
从您的代码中,我假设您需要分叉ethereum,而您可能不是。
你的帖子也缺乏信息,我不知道你在用什么工具。
如果您使用“硬帽子”,这里是您的方便链接:https://hardhat.org/hardhat-network/docs/guides/forking-other-networks
您将需要具有ethereum存档节点的节点提供程序。我可以推荐的一个好的节点提供程序是:https://www.alchemy.com
https://ethereum.stackexchange.com/questions/143130
复制相似问题