Motivation
There are several issues and inefficiencies that face smart contract developers when using the widely used WETH9 implementation. This post aims to discuss tradeoffs of different design details of a new and improved WETH contract. I’ve already begun working on it under the name “YAM-WETH”, standing for “Yet Another Maximized Wrapped Ether implementation”. You can view the repo here.
Core WETH9 Issues
Silent Fallback Method
WETH9 has a notorious silent fallback method that will silently accept any call even if the selector does not match any implemented method. This is a common foot gun for smart contract developers who expect most token contracts to revert if they’re called with a method they do not implement. This was also the cause of Multicoin’s $ 1M bridge hack.
Inefficient Common Patterns
There are some very common patterns that smart contracts go through when contract’s such as DEX routers interact with WETH, however due to WETH9 only implementing a basic deposit
and withdraw
method these patterns usually require multiple calls and otherwise unused receive()
methods.
- “Deposit & Transfer”
WETH9.deposit{ value: amount}(); WETH9.transfer(recipient, amount);
- “Withdraw & Transfer”
receive() external payable { require(msg.sender == address(WETH9)); } // ... WETH9.withdraw(amount); SafeTransferLib.safeTransferETH(recipient, amount);
- “Withdraw From”
receive() external payable { require(msg.sender == address(WETH)); } // ... WETH9.transferFrom(account, address(this), amount); WETH9.withdraw(amount);
- “Withdraw From & Transfer”
receive() external payable { require(msg.sender == address(WETH)); } // ... WETH9.transferFrom(from, address(this), amount); WETH9.withdraw(amount); SafeTransferLib.safeTransferETH(to, amount);
Outside of simplicity there’s no reason why patterns couldn’t be made accessible via individual, direct calls. Making such patterns accessible within a single call allows for direct crediting of balances without the unnecessary costs of accessing added intermediary storage slots.
Design Tradeoffs
General Efficiency
While my current implementation is technically in solidity most of the actual business logic is written in inline-assembly. The tradeoff here is implementation simplicity & ease of auditability vs. efficiency. However I believe for a contract as widely used as WETH the implementation should aim to be as efficent as possible because of how commonly WETH is used in the wider ecosystem.
Leveraging Leftover Space In The Balance Slot
Since WETH’s total supply is fundamentally limited by the total amount of circulating ETH it’s total supply, at least on mainnet is likely to always fit within 96-bits giving it a maximum of 79.2B ETH. This leaves 160-bits leftover in the storage slot that stores user balances.
Storing a “Primary Operator” in the leftover space
In the current YAM-WETH implementation, this is used to store the address of a special “primary operator” of the individual account that can spend WETH on behalf of an account as if they had an infinite allowance. Since it’s stored in the balance slot this allows for more efficient setting and transfer froms for that operator because the allowance slot does not have to be touched.
The question also arises whether the allowance
method should also return type(uint).max
for the primary operator to make it play better with frontends and other contracts. One could also imagine emitting Approval
events when the primary operator is set although this would complicate the approve
method a little.
ERC-2612 Permits
Since the ERC-2612 standard fundamentally relies on EC-Recover there’s a question as to how long-lasting an implementation that implements the standard will be due to quantum resistance. The ecosystem will surely have to find a solution since other tokens also depend on EC-Recover but it’s not entirely known how Ethereum will implement quantum resistance. If it’s mainly done via account abstraction an ERC-2612 compliant implementation may become vulnerable in the future.
Flash Minting
The main tradeoff of allowing “flash minting” would be that WETH may become partially unbacked for the duration of a transaction. This may prevent certain contracts, in certain contexts from always redeeming WETH for ETH 1:1. This downside can be limited but not eliminated by putting a cap on the percentage of the supply that can be flash minted. This will ensure that unless the borrower is able to trigger a large redemption outside of their loan contracts are unlikely to ever be in a situation where they cannot redeem their WETH for ETH 1:1 unless ownership is extremely concentrated.
Closing Words
WETH is an incredibly important piece of the smart contract ecosystem therefore I think it’s extremely important I consider and take feedback if I aim to create a newer version that’s not only improved but also widely used.