Skip to main content

Overview

WethGate is a non-upgradeable helper contract that receives native ETH and immediately forwards it to upgradeable contracts. This is necessary due to increased SLOAD gas costs after the Istanbul hard fork. Contract Location: contracts/transfers/WethGate.sol

Why WethGate?

Upgradeable contracts using OpenZeppelin’s proxy pattern cannot receive ETH via transfer() after EIP-1884 (Istanbul hard fork) increased SLOAD gas costs from 200 to 800.
WethGate acts as a relay: it receives ETH (which has a 2300 gas stipend) and forwards it using .call{value}() which has no gas limit.

Main Function

withdrawWeth

function withdrawWeth(
    uint256 _amount,
    address _receiver
) external
Unwraps WETH and sends ETH to the receiver.
_amount
uint256
required
Amount of WETH to unwrap
_receiver
address
required
Address to receive the unwrapped ETH

Integration Pattern

Using WethGate
// In DeBridgeGate contract

// Approve WethGate to spend WETH
weth.approve(address(wethGate), amount);

// WethGate unwraps and forwards to receiver
wethGate.withdrawWeth(amount, receiver);

Technical Background

The Istanbul hard fork (EIP-1884) increased the gas cost of the SLOAD opcode from 200 to 800 gas. This caused issues for proxy patterns that store implementation addresses in storage.When .transfer() sends ETH (with only 2300 gas), the receiving proxy contract doesn’t have enough gas to:
  1. Load implementation address (800 gas)
  2. Delegate call to implementation
  3. Execute receive logic
Solution: Use a non-upgradeable relay contract (WethGate) that doesn’t use SLOAD.

Security

  • Simple, non-upgradeable design
  • No storage variables
  • Only callable by authorized contracts
  • Immediate forwarding of funds

Further Reading

OpenZeppelin Forum Discussion

Detailed explanation of the Istanbul hardfork issue

Build docs developers (and LLMs) love