I use hardhat to test this function:
// Pool.sol
function withdraw(uint256 _withdrawAmount) public payable returns (bool) {
require(_withdrawAmount <= getBalance(), "overdrawn");
balances[msg.sender] -= _withdrawAmount;
totalContractBalance -= _withdrawAmount;
(bool sent, ) = msg.sender.call{value: _withdrawAmount}("");
require(sent, "Failed to withdraw");
return true;
}
by using:
// test/Pool.js
it('Should withdrawSomeBalance', async function () {
await pool.connect(addr1).addBalance(5)
await pool.connect(addr1).withdraw(2)
expect(await pool.connect(addr1).getBalance()).to.equal(3)
})
I got:
Error: VM Exception while processing transaction: reverted with reason string 'Failed to withdraw'
But when I comment out require(sent, "Failed to withdraw")
in Pool.sol
it pass the test.
Both addBalance()
and getBalance()
pass test.
Is there anything I made incorrectly for the contract or the test?
How can I include the require(sent, "Failed to withdraw")
and make the whole test more sense? Thanks.
===
Pool.sol
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.4;
import "hardhat/console.sol";
contract Pool {
uint256 totalContractBalance = 0;
mapping(address => uint256) balances; // user ETH in wei
function getContractBalance() public view returns (uint256) {
return totalContractBalance;
}
function addBalance(uint256 _amount) public payable returns (bool) {
balances[msg.sender] = _amount;
totalContractBalance += _amount;
return true;
}
function getBalance() public view returns (uint256) {
return balances[msg.sender];
}
function withdraw(uint256 _withdrawAmount) public payable returns (bool) {
require(_withdrawAmount <= getBalance(), "overdrawn");
balances[msg.sender] -= _withdrawAmount;
totalContractBalance -= _withdrawAmount;
(bool sent, ) = msg.sender.call{value: _withdrawAmount}("");
require(sent, "Failed to withdraw"); // comment out this will pass the test!
return true;
}
receive() external payable {}
}
/test/Pool.js
const { expect } = require('chai')
const { ethers } = require('hardhat')
describe('Pool', function () {
let Pool, pool, owner, addr1, addr2
beforeEach(async () => {
Pool = await ethers.getContractFactory('Pool')
pool = await Pool.deploy()
;[owner, addr1, addr2, _] = await ethers.getSigners()
//await pool.deployed();
})
describe('Deployment', function () {
it('Should set the right inital balance', async function () {
expect(await pool.getContractBalance()).to.equal(0)
})
})
describe('Transactions', function () {
it('Should return contract balance from addBalance', async function () {
await pool.addBalance(1)
//await addSomeBalance.wait()
expect(await pool.getContractBalance()).to.equal(1)
})
it('Should return user balance from addBalance', async function () {
await pool.addBalance(11)
//await addSomeBalance.wait()
expect(await pool.getBalance()).to.equal(11)
})
it('Should withdrawSomeBalance', async function () {
await pool.connect(addr1).addBalance(5)
//await addSomeBalance.wait()
await pool.connect(addr1).withdraw(2)
//await withdrawSomeBalance.wait()
expect(await pool.connect(addr1).getBalance()).to.equal(3)
})
})
})
testing output:
Pool
Deployment
✓ Should set the right inital balance
Transactions
✓ Should return contract balance from addBalance
✓ Should return user balance from addBalance
1) Should withdrawSomeBalance
3 passing (1s)
1 failing
1) Pool
Transactions
Should withdrawSomeBalance:
Error: VM Exception while processing transaction: reverted with reason string 'Failed to withdraw'
Best Answer
The function
withdraw
fails because this call returns false indicating the transfer failedIt fails because the contract doesn't have enough ether.
The script isn't sending ether with
addBalance(5)
it just increases the internal balances mapping.To send ether with the call have to write something like this: