WETH9.sol

Model: claude-sonnet-4-6 | 6/8/2026

Summary

high1
medium2
low5
info2
Risk:high
10 issues total

Priority fixes:

  1. Upgrade to Solidity 0.8.x to gain built-in overflow/underflow protection and resolve all deprecated syntax issues
  2. Replace msg.sender.transfer() with a low-level call{value:}() pattern and emit events before external calls to follow checks-effects-interactions
  3. Update deprecated syntax: add emit keyword for events, replace this.balance with address(this).balance, and replace unnamed fallback with receive()

high (1)

high#1 Outdated Solidity version with known vulnerabilities

pragma solidity ^0.4.18

The contract uses Solidity 0.4.18, which lacks many security improvements introduced in later versions. In particular, there is no built-in overflow/underflow protection (SafeMath was not standard), and there are numerous compiler bugs fixed in later versions. The contract relies on implicit arithmetic safety assumptions.

Fix: Upgrade to Solidity 0.8.x which includes built-in overflow/underflow checks, or at minimum use 0.6.x with SafeMath.

Show suggested fix
pragma solidity ^0.8.0;

medium (2)

medium#2 Reentrancy risk in withdraw() — ETH sent before event emission (minor)

withdraw

While the balance is decremented before the external call (msg.sender.transfer(wad)), which follows the checks-effects-interactions pattern, the Withdrawal event is emitted after the external call. In Solidity 0.4.x, transfer() only forwards 2300 gas so reentrancy is practically mitigated, but the event should be emitted before the external call for correctness and to follow best practices. More importantly, in a modernized version using call{value:}(), this ordering would become a real reentrancy risk.

Fix: Emit the Withdrawal event before the external ETH transfer.

Show suggested fix
function withdraw(uint wad) public {
    require(balanceOf[msg.sender] >= wad);
    balanceOf[msg.sender] -= wad;
    emit Withdrawal(msg.sender, wad);
    (bool success, ) = msg.sender.call{value: wad}("");
    require(success, "ETH transfer failed");
}
medium#3 Integer overflow/underflow possible in deposit() and transferFrom()

deposit, transferFrom

In Solidity ^0.4.18, arithmetic operations do not revert on overflow/underflow. In deposit(), balanceOf[msg.sender] += msg.value could overflow for a sufficiently large msg.value. In transferFrom(), balanceOf[dst] += wad could overflow. Although practically unlikely, it is a real vulnerability class.

Fix: Use SafeMath library or upgrade to Solidity 0.8.x for automatic overflow checks.

Show suggested fix
// Using Solidity 0.8.x — overflow/underflow revert automatically
function deposit() public payable {
    balanceOf[msg.sender] += msg.value;
    emit Deposit(msg.sender, msg.value);
}

function transferFrom(address src, address dst, uint wad) public returns (bool) {
    require(balanceOf[src] >= wad, "Insufficient balance");
    if (src != msg.sender && allowance[src][msg.sender] != type(uint256).max) {
        require(allowance[src][msg.sender] >= wad, "Insufficient allowance");
        allowance[src][msg.sender] -= wad;
    }
    balanceOf[src] -= wad;
    balanceOf[dst] += wad;
    emit Transfer(src, dst, wad);
    return true;
}

low (5)

low#4 Deprecated event emission syntax (missing emit keyword)

deposit, withdraw, approve, transferFrom

In Solidity 0.4.21+, the emit keyword is required for emitting events. The code calls events like function calls (e.g., Deposit(msg.sender, msg.value)) which is deprecated and will not compile in modern Solidity versions.

Fix: Add the emit keyword before all event emissions.

Show suggested fix
emit Deposit(msg.sender, msg.value);
emit Withdrawal(msg.sender, wad);
emit Approval(msg.sender, guy, wad);
emit Transfer(src, dst, wad);
low#5 Use of this.balance is deprecated

totalSupply

this.balance is deprecated in modern Solidity. address(this).balance is the correct syntax. This will cause compilation errors in Solidity >=0.5.0.

Fix: Replace this.balance with address(this).balance.

Show suggested fix
function totalSupply() public view returns (uint) {
    return address(this).balance;
}
low#6 Unnamed fallback function (deprecated syntax)

fallback function (unnamed)

The unnamed payable fallback function syntax function() public payable is deprecated since Solidity 0.6.0. Modern Solidity requires explicit receive() external payable or fallback() external payable declarations.

Fix: Use receive() external payable for plain ETH transfers and fallback() external payable for unknown calldata.

Show suggested fix
receive() external payable {
    deposit();
}
low#7 Missing zero-address check in transferFrom

transferFrom

The transferFrom function does not validate that dst is not the zero address. Transferring tokens to address(0) would burn them without any corresponding accounting or event clarity.

Fix: Add a require(dst != address(0)) check at the beginning of transferFrom.

Show suggested fix
function transferFrom(address src, address dst, uint wad) public returns (bool) {
    require(dst != address(0), "Transfer to zero address");
    require(balanceOf[src] >= wad, "Insufficient balance");
    if (src != msg.sender && allowance[src][msg.sender] != type(uint256).max) {
        require(allowance[src][msg.sender] >= wad, "Insufficient allowance");
        allowance[src][msg.sender] -= wad;
    }
    balanceOf[src] -= wad;
    balanceOf[dst] += wad;
    emit Transfer(src, dst, wad);
    return true;
}
low#8 use of transfer() for ETH sending — may break with future gas cost changes

withdraw

msg.sender.transfer(wad) forwards only 2300 gas, which may fail if the recipient is a smart contract with a more complex fallback function. This was considered safe historically but is now discouraged following EIP-1884 and related gas cost changes.

Fix: Use a low-level call with reentrancy protection instead: (bool success, ) = msg.sender.call{value: wad}(""); require(success);

Show suggested fix
function withdraw(uint wad) public {
    require(balanceOf[msg.sender] >= wad, "Insufficient balance");
    balanceOf[msg.sender] -= wad;
    emit Withdrawal(msg.sender, wad);
    (bool success, ) = msg.sender.call{value: wad}("");
    require(success, "ETH transfer failed");
}

info (2)

info#9 State variables can be declared as immutable or constant

name, symbol, decimals

The name, symbol, and decimals state variables are set at declaration and never modified. Declaring them as constant saves gas by avoiding storage reads.

Fix: Declare them as constant.

Show suggested fix
string public constant name = "Wrapped Ether";
string public constant symbol = "WETH";
uint8 public constant decimals = 18;
info#10 No ERC-20 compliant return value check on transfer/approve

approve, transfer, transferFrom

While the functions do return bool, there is no standardized error messaging and the contract does not follow ERC-20 NatSpec documentation. This is a best practice issue for interoperability.

Fix: Add NatSpec comments and ensure full ERC-20 interface compliance including revert messages.

Analyze another contract|Tokens: 8962 in / 2326 out