// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; /** * @title WETH10 * @notice Enhanced WETH implementation with ERC-3156 flash loans and withdraw-on-transfer-to-zero * @dev Based on WETH10 specification with additional quality-of-life features */ interface IERC3156FlashBorrower { function onFlashLoan( address initiator, address token, uint256 amount, uint256 fee, bytes calldata data ) external returns (bytes32); } interface IERC3156FlashLender { function maxFlashLoan(address token) external view returns (uint256); function flashFee(address token, uint256 amount) external view returns (uint256); function flashLoan( IERC3156FlashBorrower receiver, address token, uint256 amount, bytes calldata data ) external returns (bool); } contract WETH10 { string public constant name = "Wrapped Ether"; string public constant symbol = "WETH"; uint8 public constant decimals = 18; bytes32 public constant FLASH_LOAN_CALLBACK_SUCCESS = keccak256("ERC3156FlashBorrower.onFlashLoan"); uint256 public constant FLASH_FEE = 0; // No fee for flash loans event Approval(address indexed src, address indexed guy, uint256 wad); event Transfer(address indexed src, address indexed dst, uint256 wad); event Deposit(address indexed dst, uint256 wad); event Withdrawal(address indexed src, uint256 wad); mapping(address => uint256) public balanceOf; mapping(address => mapping(address => uint256)) public allowance; receive() external payable { deposit(); } function deposit() public payable { balanceOf[msg.sender] += msg.value; emit Deposit(msg.sender, msg.value); } function withdraw(uint256 wad) public { require(balanceOf[msg.sender] >= wad, "WETH10: insufficient balance"); balanceOf[msg.sender] -= wad; payable(msg.sender).transfer(wad); emit Withdrawal(msg.sender, wad); } function totalSupply() public view returns (uint256) { return address(this).balance; } function approve(address guy, uint256 wad) public returns (bool) { allowance[msg.sender][guy] = wad; emit Approval(msg.sender, guy, wad); return true; } function transfer(address dst, uint256 wad) public returns (bool) { return transferFrom(msg.sender, dst, wad); } function transferFrom( address src, address dst, uint256 wad ) public returns (bool) { require(balanceOf[src] >= wad, "WETH10: insufficient balance"); if (src != msg.sender && allowance[src][msg.sender] != type(uint256).max) { require(allowance[src][msg.sender] >= wad, "WETH10: insufficient allowance"); allowance[src][msg.sender] -= wad; } balanceOf[src] -= wad; balanceOf[dst] += wad; emit Transfer(src, dst, wad); return true; } // ERC-3156 Flash Loan Implementation function maxFlashLoan(address token) external view returns (uint256) { return token == address(this) ? address(this).balance : 0; } function flashFee(address token, uint256 amount) external view returns (uint256) { require(token == address(this), "WETH10: unsupported token"); return FLASH_FEE; } function flashLoan( IERC3156FlashBorrower receiver, address token, uint256 amount, bytes calldata data ) external returns (bool) { require(token == address(this), "WETH10: unsupported token"); require(amount > 0, "WETH10: invalid amount"); require(address(this).balance >= amount, "WETH10: insufficient liquidity"); // Calculate fee (FLASH_FEE is 0) uint256 fee = FLASH_FEE; uint256 repayAmount = amount + fee; // Transfer WETH to receiver (mint) balanceOf[address(receiver)] += amount; emit Transfer(address(0), address(receiver), amount); // Callback receiver require( receiver.onFlashLoan(msg.sender, token, amount, fee, data) == FLASH_LOAN_CALLBACK_SUCCESS, "WETH10: flash loan callback failed" ); // Verify repayment: receiver must have enough balance to repay (amount + fee) require( balanceOf[address(receiver)] >= repayAmount, "WETH10: insufficient repayment" ); // Repay flash loan (burn) balanceOf[address(receiver)] -= repayAmount; emit Transfer(address(receiver), address(0), repayAmount); return true; } }