Tired of WETH9, we created an overall better version of the commonly used contract, providing a trustless, immutable, and standardized way for smart contracts to abstract away the difference between the native ETH asset and fungible ERC-20 tokens. We call it WETH10, the Messi Wrapped Ether.
How to Submit a Solution:
Use the below submission form to submit the solution: https://quillaudits.typeform.com/QuillCTF
Objective of CTF
The contract currently has 10 ethers. (Check the Foundry configuration.) You are Bob (the White Hat). Your job is to rescue all the funds from the contract, starting with 1 ether, in only one transaction.
Note: You can create POCs using Foundry/Hardhat. Without proper POC, your submissions will not be accepted.
Contract Code:
pragma solidity ^0.8.0;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
// The Messi Wrapped Ether
contract WETH10 is ERC20("Messi Wrapped Ether", "WETH10"), ReentrancyGuard {
receive() external payable {
deposit();
}
function _burnAll() internal {
_burn(msg.sender, balanceOf(msg.sender));
}
function deposit() public payable nonReentrant {
_mint(msg.sender, msg.value);
}
function withdraw(uint256 wad) external nonReentrant {
Address.sendValue(payable(msg.sender), wad);
_burn(msg.sender, wad);
}
function withdrawAll() external nonReentrant {
Address.sendValue(payable(msg.sender), balanceOf(msg.sender));
_burnAll();
}
/// @notice Request a flash loan in ETH
function execute(address receiver, uint256 amount, bytes calldata data) external nonReentrant {
uint256 prevBalance = address(this).balance;
Address.functionCallWithValue(receiver, data, amount);
require(address(this).balance >= prevBalance, "flash loan not returned");
}
}
Foundry setUp: (You can also use other frameworks like Hardhat or Brownie)
contract Weth10Test is Test {
WETH10 public weth;
address owner;
address bob;
function setUp() public {
weth = new WETH10();
bob = makeAddr("bob");
vm.deal(address(weth), 10 ether);
vm.deal(address(bob), 1 ether);
}
function testHack() public {
assertEq(address(weth).balance, 10 ether, "weth contract should have 10 ether");
vm.startPrank(bob);
// hack time!
vm.stopPrank();
assertEq(address(weth).balance, 0, "empty weth contract");
assertEq(bob.balance, 11 ether, "player should end with 11 ether");
}
}