# WETH + EIP-3074 (Also posted this on [Ethereum Magicians](https://ethereum-magicians.org/t/getting-rid-of-weth-using-eip-3074/8041).) While randomly looking at [WETH](https://etherscan.io/address/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2#code) I was wondering, > Hmm, this was written such a long time ago, what would it look like with modern Solidity? WETH9 was written for 0.4.18 (2017!) and we are at 0.8.10 today. (Martin, what does 9 stands for?) So I started iteratively changing it, adding small improvements one-by-one: 1. Turning those constants into actual constants 2. Upgrading the syntax of the fallback function 3. Adding the `emit` keyword to events 4. Fine-tuning `public` into `external` where it makes sense 5. Using custom errors (`revert InsufficientBalance()`) 6. Adding even more type casting, especially for that comparison with `-1` 7. (And maybe renaming `wad` :grimacing:) Cool. This took a few minutes. The code was pretty nice to begin with as it didn't had any unneeded complexity. Now what? I remembered those old attempts of trying to get rid of WETH as it is a nuisance. There were some proposals to enshrine it as a "precompile". There was a proposal, [WETH10](https://github.com/WETH10/WETH10), to introduce [EIP-2612](https://eips.ethereum.org/EIPS/eip-2612) permits and flash loans into it. And a rewrite into Yul+, [WETH11](https://github.com/dmihal/WETH11), mostly motivated by saving gas ([some explainer thread here](https://twitter.com/dmihal/status/1475949119483088898)). Getting rid of WETH for good always intrigued me as a goal, therefore spent some time in the past thinking about those enshrining proposals. It just occurred to me that perhaps with [EIP-3074](https://eips.ethereum.org/EIPS/eip-3074) we can make this happen! One would need to call `authorize` on this new contract, and from that point it gains access to the authorizer's Ether. Transfers are still subject to the well known allowance system. (I assume by the time EIP-3074 goes live there will be nice ways to revoke authorizations.) Here's the rough code I put together in a few minutes: **Disclaimer: do not use this for anything.** ```solidity= pragma solidity ^0.8.0; library EIP3074 { function transferEther(bytes32 commit, uint8 yParity, uint r, uint s, address sender, address recipient, uint amount) public { assembly { // NOTE: Verbatim actually isn't enabled in inline assembly yet function auth(a, b, c, d) -> e { e := verbatim_4i_1o(hex"f6", a, b, c, d) } function authcall(a, b, c, d, e, f, g, h) -> i { i := verbatim_8i_1o(hex"f7", a, b, c, d, e, f, g, h) } let authorized := auth(commit, yParity, r, s) if iszero(eq(authorized, sender)) { revert(0, 0) } let success := authcall(gas(), recipient, 0, amount, 0, 0, 0, 0) if iszero(success) { revert(0, 0) } } } } contract WETH3074 { string public constant name = "Wrapped Ether"; string public constant symbol = "WETH"; uint8 public constant decimals = 18; event Approval(address indexed src, address indexed guy, uint wad); event Transfer(address indexed src, address indexed dst, uint wad); mapping (address => mapping (address => uint)) public allowance; struct AuthParams { bytes32 commit; uint8 yParity; uint r; uint s; } mapping (address => AuthParams) private authParams; function totalSupply() external pure returns (uint) { // TODO: what to do with this? return uint(int(-1)); } function balanceOf(address account) public view returns (uint) { return account.balance; } function approve(address guy, uint wad) external returns (bool) { allowance[msg.sender][guy] = wad; emit Approval(msg.sender, guy, wad); return true; } function transfer(address dst, uint wad) external returns (bool) { return transferFrom(msg.sender, dst, wad); } function transferFrom(address src, address dst, uint wad) public returns (bool) { require(balanceOf(src) >= wad); // TODO use custom error if (src != msg.sender && allowance[src][msg.sender] != uint(int(-1))) { require(allowance[src][msg.sender] >= wad); // TODO use custom error allowance[src][msg.sender] -= wad; } AuthParams memory params = authParams[src]; EIP3074.transferEther(params.commit, params.yParity, params.r, params.s, src, dst, wad); emit Transfer(src, dst, wad); return true; } function authorize(bytes32 commit, uint8 yParity, uint r, uint s) external { authParams[msg.sender] = AuthParams(commit, yParity, r, s); } } ``` It has at least the following problems: 1. The `totalSupply` is not set properly -- it is not possible to extract this information from within the chain, yet 2. The `Transfer` event is only emitted if transfer is done via the token, but accounts can still natively do transfers 3. EIP-3074 actually doesn't yet allow `valueExt`, i.e. transfers of Ether of the authorizing account 4. EIP-3074 is nowhere near to adoption yet 5. The authorisation parameters could be stored more efficiently Coincidentally this also solves the [complaint](https://twitter.com/nicksdjohnson/status/1392590935498715137) of WETH9 not emitting a transfer on deposit, since there's no need for depositing. Thanks @hrkrshnn and @matt for the brief review. P.S. There are security considerations about EIP-3074, which are not specific to this use case. It is probably better discussing them in the [existing issues](https://ethereum-magicians.org/t/eip-3074-auth-and-authcall-opcodes/4880).