// 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;
    }
}

