Summary
When the withdraw function is called we do not sent the ether balance to the host as it is stored in a seperate mapping.
Vulnerability Details
When the withdraw function is called we do not send the ether balance to the host, it is left in the contract and we have no way to withdraw it. We can only get the values of the WETH, WBTC and USDC tokens.
function withdraw() external onlyHost {
address _host = getHost();
i_WETH.safeTransfer(_host, i_WETH.balanceOf(address(this)));
i_WBTC.safeTransfer(_host, i_WBTC.balanceOf(address(this)));
i_USDC.safeTransfer(_host, i_USDC.balanceOf(address(this)));
}
Impact
We have funds left in the contract that won't be withdrawable and will be stuck inside of it, making funding with etherum directly useless.
Tools Used
Manual review
Foundry testing
POC
function test_withdrawAsHostDoesNotWithdrawEtherDeposited() public {
uint256 wethAmount;
uint256 wbtcAmount;
uint256 usdcAmount;
uint256 ethAmount;
bool sent;
address payable _cd = payable(address(cd));
vm.deal(user1, 10 ether);
vm.deal(user2, 10 ether);
vm.startPrank(user1);
cd.deposit(address(wbtc), 0.5e18);
wbtcAmount += 0.5e18;
cd.deposit(address(weth), 2e18);
wethAmount += 2e18;
(sent, ) = _cd.call{value: 5e18}("");
ethAmount += 5e18;
require(sent, "transfer failed");
assertEq(user1.balance, 5e18);
assertEq(address(cd).balance, 5e18);
vm.stopPrank();
vm.startPrank(user2);
cd.deposit(address(usdc), 2e18);
usdcAmount += 2e18;
cd.deposit(address(wbtc), 1e18);
wbtcAmount += 1e18;
(sent, ) = _cd.call{value: 5e18}("");
ethAmount += 5e18;
require(sent, "transfer failed");
assertEq(user1.balance, 5e18);
assertEq(address(cd).balance, 10e18);
vm.stopPrank();
vm.startPrank(deployer);
cd.withdraw();
vm.stopPrank();
assertEq(wbtc.balanceOf(deployer), wbtcAmount);
assertEq(weth.balanceOf(deployer), wethAmount);
assertEq(usdc.balanceOf(deployer), usdcAmount);
assertEq(deployer.balance, 0);
}
Recommendations
We should add logic to the withdraw function to transfer ethers that were deposited to the contract to the host address.
function withdraw() external onlyHost {
address _host = getHost();
i_WETH.safeTransfer(_host, i_WETH.balanceOf(address(this)));
i_WBTC.safeTransfer(_host, i_WBTC.balanceOf(address(this)));
i_USDC.safeTransfer(_host, i_USDC.balanceOf(address(this)));
(bool success, ) = _host.call{value: address(this).balance}("");
require(success, "Transfer failed.");
}