Solidity – Withdraw Fails in Foundry Test: Common Issues and Fixes

forgefoundrysolidity

I want to test a function that I have written for my smart contract with Forge from Foundry.

This is the function that allows to withdraw the Ether in this contract.

function withdraw() public onlyOwner {
        address _owner = owner();
        uint256 amount = address(this).balance;
        (bool sent, ) = _owner.call{value: amount}(
            "Revenue from NFT Sale"
        );
        require(sent, "Failed to send Ether");
    }

When I write a test case it fails for the withdrawal part, but I cannot see why

function testWithdrawExample() public {
        // owner of the contract is just the default account
        // which is the deployer of the test contract
        // => deployer = address(this);
        // ... prepare state

        // execute function
        cryptoDevs.withdraw();
 
        // ... assert state
    }

All what I see when I use high verbosity (traces visible)

├─ [7845] CryptoDevs::withdraw() 
    │   ├─ [190] CryptoDevsTest::52657665{value: 200000000000000000}(6e75652066726f6d204e46542043727970746f20446576732053616c65) 
    │   │   └─ ← ()
    │   └─ ← "Failed to send Ether"
    └─ ← "Failed to send Ether"

Best Answer

When you want to withdraw and the signing account is the deployer and test contract itself (it's the same), like you have, then you need to give the contract TestContract is Test the ability to receive ether, like you would do for any other contract. Add the following to the contracts body.

// Function to receive Ether. msg.data must be empty
receive() external payable {}

// Fallback function is called when msg.data is not empty
fallback() external payable {}

Other than that you could use vm.prank and specify an EOA which is capable of receiving ether anyway. Remember you would need to use vm.prank for contract deployment (owner is not test contract but EOA and before withdraw function execution)

Related Topic