Christmas Dinner

First Flight #31
Beginner FriendlyFoundrySolidity
100 EXP
View results
Submission Details
Severity: high
Valid

`ChristmasDinner.withdraw` function can be called by the Host any time making it impossible for participants to get a refund

Description: The ChristmasDinner::withdraw function transfers all the ERC20 tokens to the Host without changing the ChristmasDinner::balances array meaning that users will still have their ERC20 token balances written in the array but when they'll try to get a refund using ChristmasDinner::refund the function will revert and they won't be able to get their funds back.

This can have dire consequences where participants can't get their funds back may they change their mind about attending the event(dinner). This vulnerability can be maliciously used by the Host and seems like he has the ability to scam the participants making the contract centralized.

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: Participants won't be able to get a refund anymore

Proof of Concept:

  1. Users deposit ERC20 tokens to the contract

  2. Host calls the ChristmasDinner::withdraw function taking all the ERC20 tokens

  3. If a participants tries to get a refund by calling ChristmasDinner::refund function, the transaction will revert because of insufficient balance

PoC Code

function test_withdrawAsHostBeforeDeadline() public {
uint256 wethAmount;
uint256 wbtcAmount;
uint256 usdcAmount;
assertEq(wbtc.balanceOf(user1), 2e18);
vm.startPrank(user1);
cd.deposit(address(wbtc), 0.5e18);
wbtcAmount += 0.5e18;
cd.deposit(address(weth), 2e18);
wethAmount += 2e18;
vm.stopPrank();
vm.startPrank(user2);
cd.deposit(address(usdc), 2e18);
usdcAmount += 2e18;
cd.deposit(address(wbtc), 1e18);
wbtcAmount += 1e18;
vm.stopPrank();
vm.startPrank(deployer);
vm.warp(1 + 3 days);
cd.withdraw();
vm.stopPrank();
assertEq(wbtc.balanceOf(deployer), wbtcAmount);
assertEq(weth.balanceOf(deployer), wethAmount);
assertEq(usdc.balanceOf(deployer), usdcAmount);
assertEq(wbtc.balanceOf(user1), 1.5e18);
vm.startPrank(user1);
vm.expectRevert();
cd.refund();
}

Recommendation: To prevent this, we should only allow ChristmasDinner::withdraw function to be called by the host ONLY after the deadline.

////////////////////////////////////////////////////////////////
////////////////// Custom Errors /////////////////
////////////////////////////////////////////////////////////////
error NotHost();
error BeyondDeadline();
+ error BeforeDeadline();
error DeadlineAlreadySet();
error OnlyParticipantsCanBeHost();
error NotSupportedToken();
function withdraw() external onlyHost {
+ if(block.timestamp < deadline) {
+ error BeforeDeadline();
+ }
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)));
}
Updates

Lead Judging Commences

0xtimefliez Lead Judge 10 months ago
Submission Judgement Published
Validated
Assigned finding tags:

withdraw is callable before deadline ends

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.