APE Price: $0.64 (-9.63%)

Contract Diff Checker

Contract Name:
WrappedNative

Contract Source Code:

// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import "./interfaces/IWrappedNative.sol";
import "./interfaces/IWrappedNativeExtended.sol";

string constant VERSION = "1";
string constant NAME = "Wrapped Native";
string constant SYMBOL = "WNATIVE";
uint8 constant DECIMALS = 18;

bytes32 constant UPPER_BIT_MASK = 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
uint256 constant ZERO = 0;
uint256 constant ONE = 1;
uint256 constant INFRASTRUCTURE_TAX_THRESHOLD = 9; // When convenienceFeeBps is greater than 9, we apply infrastructure tax to convenience fee.
uint256 constant INFRASTRUCTURE_TAX_BPS = 10_00;
uint256 constant FEE_DENOMINATOR = 100_00;

uint256 constant WITHDRAWAL_EVENT_TOPIC_0 = 0x7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65;
uint256 constant DEPOSIT_EVENT_TOPIC_0 = 0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c;
uint256 constant APPROVAL_EVENT_TOPIC_0 = 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925;
uint256 constant TRANSFER_EVENT_TOPIC_0 = 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;
uint256 constant PERMIT_NONCE_INVALIDATED_EVENT_TOPIC_0 = 0x8dc5a0b2e80f26187d38744e9559150e3bd6e06fccefbe737fd33411cfb15151;
uint256 constant MASTER_NONCE_INVALIDATED_EVENT_TOPIC_0 = 0x9614574d6542397172c19ba2bf4588434feeb977576e92b7b59b38242ab59609;

bytes32 constant PERMIT_TRANSFER_TYPEHASH =
    keccak256("PermitTransfer(address operator,uint256 amount,uint256 nonce,uint256 expiration,uint256 masterNonce)");

bytes32 constant PERMIT_WITHDRAWAL_TYPEHASH =
    keccak256("PermitWithdrawal(address operator,uint256 amount,uint256 nonce,uint256 expiration,uint256 masterNonce,address to,address convenienceFeeReceiver,uint256 convenienceFeeBps)");

bytes4 constant SELECTOR_IS_NONCE_USED = IWrappedNativeExtended.isNonceUsed.selector;
bytes4 constant SELECTOR_MASTER_NONCES = IWrappedNativeExtended.masterNonces.selector;
bytes4 constant SELECTOR_TOTAL_SUPPLY = IWrappedNative.totalSupply.selector;
bytes4 constant SELECTOR_DOMAIN_SEPARATOR_V4 = IWrappedNativeExtended.domainSeparatorV4.selector;
bytes4 constant SELECTOR_NAME = IWrappedNative.name.selector;
bytes4 constant SELECTOR_SYMBOL = IWrappedNative.symbol.selector;
bytes4 constant SELECTOR_DECIMALS = IWrappedNative.decimals.selector;

// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import "./Constants.sol";
import "./utils/EIP712.sol";
import "./utils/Math.sol";

/**
 * @title  WrappedNative
 * @author Limit Break, Inc.
 * @notice A contract that wraps native tokens (e.g. Ether) into an ERC-20 token.  Designed as a
 *         canonical replacement for WETH9 that can be deployed to a consistent, deterministic address on all chains.
 *
 * @notice WrappedNative features the following improvements over WETH9:
 *
 * @notice - **Deterministically Deployable By Anyone To A Consistent Address On Any Chain!**
 * @notice - **More Gas Efficient Operations Than WETH9!**
 * @notice - **`approve` and `transfer` functions are payable** - will auto-deposit when `msg.value > 0`.  This feature
 *           will allow a user to wrap and approve a protocol in a single action instead of two, improving UX and saving gas.
 * @notice - **`depositTo`** - allows a depositor to specify the address to give WNATIVE to.  
 *           Much more gas efficient for operations such as native refunds from protocols compared to `deposit + transfer`.
 * @notice - **`withdrawToAccount`** - allows a withdrawer to withdraw to a different address.
 * @notice - **`withdrawSplit`** - allows a withdrawer to withdraw and send native tokens to several addresses at once.
 * @notice - **Permit Functions** - allows for transfers and withdrawals to be approved to spenders/operators gaslessly using EIP-712 signatures.
 *           Permitted withdrawals allow gas sponsorship to unwrap wrapped native tokens on the user's behalf, for a small convenience fee specified by the app.
 *           This is useful when user has no native tokens on a new chain but they have received wrapped native tokens.
 */

contract WrappedNative is EIP712 {

    /// @dev Storage of user master nonces for permit processing.
    mapping (address => uint256) private _masterNonces;

    /// @dev Storage of permit nonces for permit processing.  Uses bitmaps for gas-efficient storage.
    mapping (address => mapping (uint256 => uint256)) private _permitNonces;

    /// @notice Stores the wrapped native token balance of each user.
    mapping (address => uint256) public balanceOf;

    /// @notice Stores the wrapped native token allowance for each user/spender pair.
    mapping (address => mapping (address => uint)) public allowance;

    /// @notice Address that will receive infrastructure fee taxes on permit transfers.
    address immutable ADDRESS_INFRASTRUCTURE_TAX;

    constructor(address infrastructureTaxRecipient) EIP712(NAME, VERSION) {
        ADDRESS_INFRASTRUCTURE_TAX = infrastructureTaxRecipient;
    }

    //=================================================
    //== Deposit / Fallback Function Implementations ==
    //=================================================

    /**
     * @notice Fallback function to deposit funds into the contract, or to call various view functions.
     *         If the `msg.value` is greater than zero, the function will deposit the funds into the
     *         `msg.sender` account. If the `msg.value` is zero, the function will check the `msg.sig`
     *         to determine which view function is being called.  If a matching function selector is found
     *         the function will execute and return the appropriate value. If no matching function selector is found,
     *         the function will revert.
     *         
     * @notice The reason seldom-used view functions have been implemented via fallback is to save gas costs
     *         elsewhere in the contract in common operations that have a runtime gas cost.
     *
     * @notice The following function selectors are implemented via fallback:
     * 
     * @notice - **function isNonceUsed(address account, uint256 nonce) external view returns (bool)**
     * @notice - **function masterNonces(address account) external view returns (uint256)**
     * @notice - **function totalSupply() external view returns (uint256)**
     * @notice - **function domainSeparatorV4() external view returns (bytes32)**
     * @notice - **function name() external view returns (string)**
     * @notice - **function symbol() external view returns (string)**
     * @notice - **function decimals() external view returns (uint8)**
     *
     * @dev     Throws when `msg.value` == 0 and the `msg.sig` does not match any of the implemented view functions.
     */
    fallback() external payable {
        if (msg.value > 0) {
            deposit();
        } else {
            if (msg.sig == SELECTOR_IS_NONCE_USED) { // isNonceUsed(address account, uint256 nonce)
                (address account, uint256 nonce) = abi.decode(msg.data[4:], (address,uint256));
                bool isUsed = ((_permitNonces[account][uint248(nonce >> 8)] >> uint8(nonce)) & ONE) == ONE;
                assembly {
                    mstore(0x00, isUsed)
                    return(0x00, 0x20)
                }
            } else if (msg.sig == SELECTOR_MASTER_NONCES) { // masterNonces(address account)
                assembly {
                    if lt(calldatasize(), 0x24) {
                        revert(0,0)
                    }
                    mstore(0x00, shr(0x60, shl(0x60, calldataload(0x04))))
                    mstore(0x20, _masterNonces.slot)
                    mstore(0x00, sload(keccak256(0x00, 0x40)))
                    return(0x00, 0x20)
                }
            } else if (msg.sig == SELECTOR_TOTAL_SUPPLY) { // totalSupply()
                assembly {
                    mstore(0x00, selfbalance())
                    return(0x00, 0x20)
                }
            } else if (msg.sig == SELECTOR_DOMAIN_SEPARATOR_V4) { // domainSeparatorV4()
                bytes32 domainSeparator = _domainSeparatorV4();
                assembly {
                    mstore(0x00, domainSeparator)
                    return(0x00, 0x20)
                }
            } else if (msg.sig == SELECTOR_NAME) { // name()
                bytes memory nameReturnValue = abi.encode(NAME);
                assembly {
                    return(add(nameReturnValue, 0x20), mload(nameReturnValue))
                }
            } else if (msg.sig == SELECTOR_SYMBOL) { // symbol()
                bytes memory symbolReturnValue = abi.encode(SYMBOL);
                assembly {
                    return(add(symbolReturnValue, 0x20), mload(symbolReturnValue))
                }
            } else if (msg.sig == SELECTOR_DECIMALS) { // decimals()
                assembly {
                    mstore(0x00, 0x12) // 18
                    return(0x00, 0x20)
                }
            } else {
                revert();
            }
        }
    }

    /**
     * @notice Deposits `msg.value` funds into the `msg.sender` account, increasing their wrapped native token balance.
     * @notice This function is triggered when native funds are sent to this contract with no calldata.
     */
    receive() external payable {
        deposit();
    }

    //=================================================
    //========== Basic Deposits / Withdrawals =========
    //=================================================

    /**
     * @notice Deposits `msg.value` funds into the `msg.sender` account, increasing their wrapped native token balance.
     *
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. This contract's native token balance has increased by `msg.value`.
     * @dev    2. The `msg.sender`'s native token balance has decreased by `msg.value`.
     * @dev    3. The `msg.sender`'s wrapped native token balance has increased by `msg.value`.
     * @dev    4. A `Deposit` event has been emitted.  The `msg.sender` address is logged in the event.
     */
    function deposit() public payable {
        depositTo(msg.sender);
    }

    /**
     * @notice Deposits `msg.value` funds into specified user's account, increasing their wrapped native token balance.
     *
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. This contract's native token balance has increased by `msg.value`.
     * @dev    2. The `msg.sender`'s native token balance has decreased by `msg.value`.
     * @dev    3. The `to` account's wrapped native token balance has increased by `msg.value`.
     * @dev    4. A `Deposit` event has been emitted.  Caveat: The `to` address is logged in the event, not `msg.sender`.
     *
     * @param to  The address that receives wrapped native tokens.
     */
    function depositTo(address to) public payable {
        assembly {
            mstore(0x00, to)
            mstore(0x20, balanceOf.slot)
            let balanceSlot := keccak256(0x00, 0x40)

            sstore(balanceSlot, add(sload(balanceSlot), callvalue()))

            mstore(0x00, callvalue())
            log2(0x00, 0x20, DEPOSIT_EVENT_TOPIC_0, to)
        }
    }

    /**
     * @notice Withdraws `amount` funds from the `msg.sender` account, decreasing their wrapped native token balance.
     *
     * @dev    Throws when the `msg.sender`'s wrapped native token balance is less than `amount` to withdraw.
     * @dev    Throws when the unwrapped native funds cannot be transferred to the `msg.sender` account.
     *
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. This contract's native token balance has decreased by `amount`.
     * @dev    2. The `msg.sender`'s wrapped native token balance has decreased by `amount`.
     * @dev    3. The `msg.sender`'s native token balance has increased by `amount`.
     * @dev    4. A `Withdrawal` event has been emitted.  The `msg.sender` address is logged in the event.
     *
     * @param amount  The amount of wrapped native tokens to withdraw.
     */
    function withdraw(uint256 amount) public {
        withdrawToAccount(msg.sender, amount);
    }

    /**
     * @notice Withdraws `amount` funds from the `msg.sender` account, decreasing their wrapped native token balance.
     *
     * @dev    Throws when the `msg.sender`'s wrapped native token balance is less than `amount` to withdraw.
     * @dev    Throws when the unwrapped native funds cannot be transferred to the `to` account.
     *
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. This contract's native token balance has decreased by `amount`.
     * @dev    2. The `msg.sender`'s wrapped native token balance has decreased by `amount`.
     * @dev    3. The `to` account's native token balance has increased by `amount`.
     * @dev    4. A `Withdrawal` event has been emitted.  Caveat: The `msg.sender` address is logged in the event, not `to`.
     *
     * @param to  The address that receives the unwrapped native tokens.
     * @param amount  The amount of wrapped native tokens to withdraw.
     */
    function withdrawToAccount(address to, uint256 amount) public {
        _withdrawFromAccount(msg.sender, to, amount);
    }

    /**
     * @notice Withdraws funds from the `msg.sender` and splits the funds between multiple receiver addresses.
     *
     * @dev    Throws when the `msg.sender`'s wrapped native token balance is less than the sum of `amounts` to withdraw.
     * @dev    Throws when the unwrapped native funds cannot be transferred to one or more of the receiver addresses.
     * @dev    Throws when the `toAddresses` and `amounts` arrays are not the same length.
     *
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. This contract's native token balance has decreased by the sum of `amounts`.
     * @dev    2. The `msg.sender`'s wrapped native token balance has decreased by the sum of `amounts`.
     * @dev    3. The receiver addresses' native token balances have increased by the corresponding amounts in `amounts`.
     * @dev    4. A `Withdrawal` event has been emitted for each receiver address.  Caveat: The `msg.sender` address is 
     *            logged in the events, not the receiver address.
     *
     * @param toAddresses  The addresses that receive the unwrapped native tokens.
     * @param amounts  The amounts of wrapped native tokens to withdraw for each receiver address.
     */
    function withdrawSplit(address[] calldata toAddresses, uint256[] calldata amounts) external {
        if (toAddresses.length != amounts.length) {
            revert();
        }

        for (uint256 i = 0; i < toAddresses.length;) {
            withdrawToAccount(toAddresses[i], amounts[i]);
            unchecked {
                ++i;
            }
        }
    }

    //=================================================
    //========== ERC-20 Approvals & Transfers =========
    //=================================================

    /**
     * @notice Approves `spender` to spend/transfer `amount` of the `msg.sender`'s wrapped native tokens.
     *         When `amount` is set to `type(uint256).max`, the approval is unlimited.
     *
     * @notice Unlike a typical ERC-20 token, this function is payable, allowing for a `deposit` and approval to be
     *         executed simultaneously.  If `msg.value` is greater than zero, the function will deposit the funds
     *         into the `msg.sender` account before approving the `spender` to spend/transfer the funds.
     *         If `msg.value` is zero, the function will only approve the `spender` to spend/transfer the funds.
     *         This feature is intended to improve the UX of users using wrapped native tokens so that users don't have
     *         to perform two transactions to first deposit, then approve the spending of their tokens, saving gas in
     *         the process.
     *
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. The `spender` is approved to spend/transfer `amount` of the `msg.sender`'s wrapped native tokens.
     * @dev    2. A `Approval` event has been emitted.  The `msg.sender` address, `spender` address, and `amount` of the 
     *            updated approval are logged in the event.
     * @dev    3. If `msg.value` is greater than zero, the `msg.sender`'s wrapped native token balance has increased by 
     *            `msg.value`.
     * @dev    4. If `msg.value` is greater than zero, a `Deposit` event has been emitted.  The `msg.sender` address is 
     *            logged in the event.
     *
     * @param spender  The address that is approved to spend/transfer the `msg.sender`'s wrapped native tokens.
     * @param amount   The amount of wrapped native tokens that the `spender` is approved to spend/transfer. Approved
     *                 spending is unlimited when this values is set to `type(uint256).max`.
     *
     * @return Always returns `true`.
     */
    function approve(address spender, uint256 amount) public payable returns (bool) {
        if (msg.value > 0) {
            deposit();
        }

        assembly {
            mstore(0x00, caller())
            mstore(0x20, allowance.slot)
            mstore(0x20, keccak256(0x00, 0x40))
            mstore(0x00, spender)
            let allowanceSlot := keccak256(0x00, 0x40)

            sstore(allowanceSlot, amount)

            mstore(0x00, amount)
            log3(0x00, 0x20, APPROVAL_EVENT_TOPIC_0, caller(), spender)

            mstore(0x00, 0x01)
            return(0x00, 0x20)
        }
    }

    /**
     * @notice Transfers an `amount` of wrapped native tokens from the `msg.sender` to the `to` address.
     *
     * @notice If the `msg.value` is greater than zero, the function will deposit the funds into the `msg.sender` account
     *         before transferring the wrapped funds.  Otherwise, the function will only transfer the funds.
     *
     * @dev    Throws when the `msg.sender` has an insufficient balance to transfer `amount` of wrapped native tokens.
     *
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. When `msg.value` is greater than zero, this contract's native token balance has increased by `msg.value`.
     * @dev    2. When `msg.value` is greater than zero, the `msg.sender`'s native token balance has decreased by `msg.value`.
     * @dev    3. When `msg.value` is greater than zero, the `msg.sender`'s wrapped native token balance has increased by `msg.value`.
     * @dev    4. When `msg.value` is greater than zero, a `Deposit` event has been emitted.  The `msg.sender` address is logged in the event.
     * @dev    5. The `amount` of wrapped native tokens has been transferred from the `msg.sender` account to the `to` account.
     * @dev    6. A `Transfer` event has been emitted.  The `msg.sender` address, `to` address, and `amount` are logged in the event.
     *
     * @param to  The address that receives the wrapped native tokens.
     * @param amount  The amount of wrapped native tokens to transfer.
     *
     * @return Always returns `true`.
     */
    function transfer(address to, uint256 amount) public payable returns (bool) {
        return transferFrom(msg.sender, to, amount);
    }

    /**
     * @notice Transfers an `amount` of wrapped native tokens from the `from` to the `to` address.
     *
     * @notice If the `msg.value` is greater than zero, the function will deposit the funds into the `from` account
     *         before transferring the wrapped funds.  Otherwise, the function will only transfer the funds.
     * @notice **As a reminder, the `msg.sender`'s native tokens will be deposited and the `from` (not the `msg.sender`) 
     *         address will be credited before the transfer.  Integrating spender/operator protocols MUST be aware that 
     *         deposits made during transfers will not credit their own account.**
     *
     * @dev    Throws when the `from` account has an insufficient balance to transfer `amount` of wrapped native tokens.
     * @dev    Throws when the `msg.sender` is not the `from` address, and the `msg.sender` has not been approved
     *         by `from` for an allowance greater than or equal to `amount`.
     *
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. When `msg.value` is greater than zero, this contract's native token balance has increased by `msg.value`.
     * @dev    2. When `msg.value` is greater than zero, the `msg.sender`'s native token balance has decreased by `msg.value`.
     * @dev    3. When `msg.value` is greater than zero, the `from` account's wrapped native token balance has increased by `msg.value`.
     * @dev    4. When `msg.value` is greater than zero, a `Deposit` event has been emitted.  The `from` address is logged in the event.
     * @dev    5. The `amount` of wrapped native tokens has been transferred from the `from` account to the `to` account.
     * @dev    6. A `Transfer` event has been emitted.  The `from` address, `to` address, and `amount` are logged in the event.
     *
     * @param from  The address that transfers the wrapped native tokens.
     * @param to    The address that receives the wrapped native tokens.
     * @param amount  The amount of wrapped native tokens to transfer.
     *
     * @return Always returns `true`.
     */
    function transferFrom(address from, address to, uint256 amount) public payable returns (bool) {
        if (msg.value > 0) {
            depositTo(from);
        }

        assembly {
            mstore(0x00, from)
            mstore(0x20, balanceOf.slot)
            let balanceSlotFrom := keccak256(0x00, 0x40)
            let balanceValFrom := sload(balanceSlotFrom)
            if lt(balanceValFrom, amount) {
                revert(0,0)
            }
            sstore(balanceSlotFrom, sub(balanceValFrom, amount))

            mstore(0x00, to)
            let balanceSlotTo := keccak256(0x00, 0x40)
            sstore(balanceSlotTo, add(sload(balanceSlotTo), amount))

            if iszero(eq(from, caller())) {
                mstore(0x00, from)
                mstore(0x20, allowance.slot)
                mstore(0x20, keccak256(0x00, 0x40))
                mstore(0x00, caller())
                let allowanceSlot := keccak256(0x00, 0x40)
                let allowanceVal := sload(allowanceSlot)

                if iszero(eq(allowanceVal, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)) {
                    if lt(allowanceVal, amount) {
                        revert(0,0)
                    }
                    sstore(allowanceSlot, sub(allowanceVal, amount))
                }
            }

            mstore(0x00, amount)
            log3(0x00, 0x20, TRANSFER_EVENT_TOPIC_0, from, to)

            mstore(0x00, 0x01)
            return(0x00, 0x20)
        }
    }

    //=================================================
    //======= Permitted Transfers / Withdrawals =======
    //=================================================

    /**
     * @notice Allows the `msg.sender` to revoke/cancel all prior permitted transfer and withdrawal signatures.
     *
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. The `msg.sender`'s master nonce has been incremented by `1` in contract storage, rendering all signed
     *            permits using the prior master nonce unusable.
     * @dev    2. A `MasterNonceInvalidated` event has been emitted.
     */
    function revokeMyOutstandingPermits() external {
        assembly {
            mstore(0x00, caller())
            mstore(0x20, _masterNonces.slot)
            let masterNonceSlot := keccak256(0x00, 0x40)
            let invalidatedNonce := sload(masterNonceSlot)
            sstore(masterNonceSlot, add(0x01, invalidatedNonce))
            log3(0x00, 0x00, MASTER_NONCE_INVALIDATED_EVENT_TOPIC_0, caller(), invalidatedNonce)
        }
    }

    /**
     * @notice Allows the `msg.sender` to revoke/cancel a single, previously signed permitted transfer or withdrawal 
     *         signature by specifying the nonce of the individual permit.
     *
     * @dev    Throws when the `msg.sender` has already revoked the permit nonce.
     * @dev    Throws when the permit nonce was already used successfully.
     *
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. The specified `nonce` for the `msg.sender` has been revoked and can
     *            no longer be used to execute a permitted transfer or withdrawal.
     * @dev    2. A `PermitNonceInvalidated` event has been emitted.
     *
     * @param  nonce The nonce that was signed in the permitted transfer or withdrawal.
     */
    function revokeMyNonce(uint256 nonce) external {
        _checkAndInvalidateNonce(msg.sender, nonce);
    }

    /**
     * @notice Allows a spender/operator to transfer wrapped native tokens from the `from` account to the `to` account
     *         using a gasless signature from the `from` account so that the `from` account does not need to pay gas
     *         to set an on-chain allowance.
     *
     * @notice If the `msg.value` is greater than zero, the function will deposit the funds into the `from` account
     *         before transferring the wrapped funds.  Otherwise, the function will only transfer the funds.
     * @notice **As a reminder, the `msg.sender`'s native tokens will be deposited and the `from` (not the `msg.sender`) 
     *         address will be credited before the transfer.  Integrating spender/operator protocols MUST be aware that 
     *         deposits made during transfers will not credit their own account.**
     *
     * @dev    Throws when the `from` account is the zero address.
     * @dev    Throws when the `msg.sender` does not match the operator/spender from the signed transfer permit.
     * @dev    Throws when the permitAmount does not match the signed transfer permit. 
     * @dev    Throws when the nonce does not match the signed transfer permit.
     * @dev    Throws when the expiration does not match the signed transfer permit.
     * @dev    Throws when the permit has expired.
     * @dev    Throws when the requested transfer amount exceeds the maximum permitted transfer amount. 
     * @dev    Throws when the permit nonce has already been used or revoked/cancelled.
     * @dev    Throws when the master nonce has been revoked/cancelled since the permit was signed.
     * @dev    Throws when the permit signature is invalid, or was not signed by the `from` account.
     * @dev    Throws when the `from` account has an insufficient balance to transfer `transferAmount` of wrapped native tokens.
     * 
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. When `msg.value` is greater than zero, this contract's native token balance has increased by `msg.value`.
     * @dev    2. When `msg.value` is greater than zero, the `msg.sender`'s native token balance has decreased by `msg.value`.
     * @dev    3. When `msg.value` is greater than zero, the `from` account's wrapped native token balance has increased by `msg.value`.
     * @dev    4. When `msg.value` is greater than zero, a `Deposit` event has been emitted.  The `from` address is logged in the event.
     * @dev    5. `nonce` for `from` account is invalidated.
     * @dev    6. A `PermitNonceInvalidated` event has been emitted.
     * @dev    7. The `transferAmount` of wrapped native tokens has been transferred from the `from` account to the `to` account.
     * @dev    8. A `Transfer` event has been emitted.  The `from` address, `to` address, and `transferAmount` are logged in the event.
     *
     * @param from  The address that transfers the wrapped native tokens.
     * @param to    The address that receives the wrapped native tokens.
     * @param transferAmount  The amount of wrapped native tokens to transfer.
     * @param permitAmount  The maximum amount of wrapped native tokens that can be transferred, signed in permit.
     * @param nonce  The nonce, signed in permit.
     * @param expiration  The expiration timestamp, signed in permit.
     * @param signedPermit  The signature of the permit.
     */
    function permitTransfer(
        address from,
        address to,
        uint256 transferAmount,
        uint256 permitAmount,
        uint256 nonce,
        uint256 expiration,
        bytes calldata signedPermit
    ) external payable {
        if (msg.value > 0) {
            depositTo(from);
        }

        if (block.timestamp > expiration ||
            transferAmount > permitAmount ||
            from == address(0)) {
            revert();
        }

        _checkAndInvalidateNonce(from, nonce);
        
        _verifyPermitSignature(
            from,
            _hashTypedDataV4(
                keccak256(
                    abi.encode(
                        PERMIT_TRANSFER_TYPEHASH,
                        msg.sender,
                        permitAmount,
                        nonce,
                        expiration,
                        _masterNonces[from]
                    )
                )
            ), 
            signedPermit
        );

        _balanceTransfer(from, to, transferAmount);
    }

    /**
     * @notice Allows a spender/operator to withdraw wrapped native tokens from the `from` account to the `to` account
     *         using a gasless signature signed by the `from` account to prove authorization of the withdrawal.
     *
     * @dev    Throws when the `from` account is the zero address.
     * @dev    Throws when the `msg.sender` does not match the operator/spender from the signed withdrawal permit.
     * @dev    Throws when the permit has expired.
     * @dev    Throws when the amount does not match the signed withdrawal permit. 
     * @dev    Throws when the nonce does not match the signed withdrawal permit.
     * @dev    Throws when the expiration does not match the signed withdrawal permit.
     * @dev    Throws when the convenience fee reciever and fee does not match the signed withdrawal permit.
     * @dev    Throws when the `to` address does not match the signed withdrawal permit.
     * @dev    Throws when the permit nonce has already been used or revoked/cancelled.
     * @dev    Throws when the master nonce has been revoked/cancelled since the permit was signed.
     * @dev    Throws when the permit signature is invalid, or was not signed by the `from` account.
     * @dev    Throws when the `from` account has an insufficient balance to transfer `transferAmount` of wrapped native tokens.
     *
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. This contract's native token balance has decreased by `amount`, less convenience and infrastructure
     *            fees that remain wrapped.
     * @dev    2. The `from` account's wrapped native token balance has decreased by `amount`.
     * @dev    3. The `to` account's native token balance has increased by `amount`, less convenience and/or infrastructure fees.
     * @dev    4. The `convenienceFeeReceiver` account's wrapped native token balance has increased by the convenience fee.
     * @dev    5. The infrastructure tax account's wrapped native token balance has increased by the infrastructure fee.
     * @dev    6. `nonce` for `from` account is invalidated.
     * @dev    7. A `PermitNonceInvalidated` event has been emitted.
     * @dev    8. A `Withdrawal` event has been emitted.  Caveat: The `from` address is logged in the event, not `to` or `msg.sender`.
     *
     * @param from  The address that from which funds are withdrawn.
     * @param to  The address that receives the withdrawn funds.
     * @param amount  The amount of wrapped native tokens to withdraw.
     * @param nonce  The nonce, signed in permit.
     * @param expiration  The expiration timestamp, signed in permit.
     * @param convenienceFeeReceiver  The address that receives the convenience fee.
     * @param convenienceFeeBps  The basis points of the convenience fee.
     * @param signedPermit  The signature of the permit.
     */
    function doPermittedWithdraw(
        address from,
        address to,
        uint256 amount,
        uint256 nonce,
        uint256 expiration,
        address convenienceFeeReceiver,
        uint256 convenienceFeeBps,
        bytes calldata signedPermit
    ) external {
        if (block.timestamp > expiration ||
            from == address(0)) {
            revert();
        }

        _checkAndInvalidateNonce(from, nonce);

        _verifyPermitSignature(
            from,
            _hashTypedDataV4(
                keccak256(
                    abi.encode(
                        PERMIT_WITHDRAWAL_TYPEHASH,
                        msg.sender,
                        amount,
                        nonce,
                        expiration,
                        _masterNonces[from],
                        to,
                        convenienceFeeReceiver,
                        convenienceFeeBps
                    )
                )
            ), 
            signedPermit
        );

        (
            uint256 userAmount, 
            uint256 convenienceFee, 
            uint256 infrastructureFee
        ) = _computeWithdrawalSplits(amount, convenienceFeeReceiver, convenienceFeeBps);

        if (convenienceFee > 0) {
            _balanceTransfer(from, convenienceFeeReceiver, convenienceFee);
        }

        if (infrastructureFee > 0) {
            _balanceTransfer(from, ADDRESS_INFRASTRUCTURE_TAX, infrastructureFee);
        }

        _withdrawFromAccount(from, to, userAmount);
    }

    //=================================================
    //========= Miscellaneous Helper Functions ========
    //=================================================

    /**
     * @dev Helper function that transfers wrapped native token balance between accounts.
     *
     * @dev Throws when the `from` account has an insufficient balance to transfer `amount` of wrapped native tokens.
     *
     * @param from  The address from which the wrapped native tokens is transferred.
     * @param to  The address to which the wrapped native tokens are transferred.
     * @param amount  The amount of wrapped native tokens to transfer.
     */
    function _balanceTransfer(address from, address to, uint256 amount) private {
        assembly {
            mstore(0x00, from)
            mstore(0x20, balanceOf.slot)
            let balanceSlotFrom := keccak256(0x00, 0x40)
            let balanceValFrom := sload(balanceSlotFrom)
            if lt(balanceValFrom, amount) {
                revert(0,0)
            }
            sstore(balanceSlotFrom, sub(balanceValFrom, amount))

            mstore(0x00, to)
            let balanceSlotTo := keccak256(0x00, 0x40)
            sstore(balanceSlotTo, add(sload(balanceSlotTo), amount))

            mstore(0x00, amount)
            log3(0x00, 0x20, TRANSFER_EVENT_TOPIC_0, from, to)
        }
    }

    /**
     * @dev Helper function that withdraws wrapped native tokens from an account to another account.
     *
     * @dev Throws when the `from` account has an insufficient balance to transfer `amount` of wrapped native tokens.
     * @dev Throws when the unwrapped native funds cannot be transferred to the `to` account.
     *
     * @param from  The address from which the wrapped native tokens are withdrawn.
     * @param to  The address to which the native tokens are transferred.
     * @param amount  The amount of wrapped native tokens to withdraw.
     */
    function _withdrawFromAccount(address from, address to, uint256 amount) private {
        assembly {
            mstore(0x00, from)
            mstore(0x20, balanceOf.slot)
            let balanceSlot := keccak256(0x00, 0x40)

            let balanceVal := sload(balanceSlot)
            let updatedBalance := sub(balanceVal, amount)
            sstore(balanceSlot, updatedBalance)

            mstore(0x00, amount)
            log2(0x00, 0x20, WITHDRAWAL_EVENT_TOPIC_0, from)

            if or(gt(updatedBalance, balanceVal), iszero(call(gas(), to, amount, 0, 0, 0, 0))) {
                revert(0,0)
            }
        }
    }

    /**
     * @dev Helper function that checks and invalidates a permit nonce.
     * 
     * @dev Throws when the permit nonce has already been used or revoked/cancelled.
     * 
     * @param account  The account that signed the permit.
     * @param nonce  The nonce that was signed in the permit.
     */
    function _checkAndInvalidateNonce(address account, uint256 nonce) private {
        unchecked {
            if (uint256(_permitNonces[account][uint248(nonce >> 8)] ^= (ONE << uint8(nonce))) & 
                (ONE << uint8(nonce)) == ZERO) {
                revert();
            }
        }

        assembly {
            log3(0x00, 0x00, PERMIT_NONCE_INVALIDATED_EVENT_TOPIC_0, account, nonce)
        }
    }

    //=================================================
    //============= Fee Split Calculations ============
    //=================================================

    /**
     * @dev Helper function that computes the withdrawal fee split amounts.
     *
     * @param amount  The amount of wrapped native tokens to split.
     * @param convenienceFeeReceiver  The address that receives the convenience fee.
     * @param convenienceFeeBps  The basis points of the convenience fee.
     */
    function _computeWithdrawalSplits(
        uint256 amount,
        address convenienceFeeReceiver,
        uint256 convenienceFeeBps
    ) private pure returns (uint256 userAmount, uint256 convenienceFee, uint256 convenienceFeeInfrastructure) {
        if (convenienceFeeBps > FEE_DENOMINATOR) {
            revert();
        }

        if (amount > type(uint240).max) {
            revert();
        }

        if (convenienceFeeReceiver == address(0)) {
            convenienceFeeBps = 0;
        }

        unchecked {
            if (convenienceFeeBps > INFRASTRUCTURE_TAX_THRESHOLD) {
                convenienceFee = amount * convenienceFeeBps / FEE_DENOMINATOR;
                convenienceFeeInfrastructure = convenienceFee * INFRASTRUCTURE_TAX_BPS / FEE_DENOMINATOR;
                convenienceFee -= convenienceFeeInfrastructure;
                userAmount = amount - convenienceFee - convenienceFeeInfrastructure;
            } else if (convenienceFeeBps > 0) {
                convenienceFeeInfrastructure = amount / FEE_DENOMINATOR;
                convenienceFee = amount * (convenienceFeeBps - ONE) / FEE_DENOMINATOR;
                userAmount = amount - convenienceFee - convenienceFeeInfrastructure;
            } else {
                convenienceFeeInfrastructure = amount / FEE_DENOMINATOR;
                userAmount = amount - convenienceFeeInfrastructure;
            }
        }
    }

    //=================================================
    //============ Signature Verification =============
    //=================================================

    /**
     * @notice  Verifies a permit signature based on the bytes length of the signature provided.
     * 
     * @dev     Throws when -
     * @dev         The bytes signature length is 64 or 65 bytes AND
     * @dev         The ECDSA recovered signer is not the expectedSigner AND
     * @dev         The expectedSigner's code length is zero OR the expectedSigner does not return a valid EIP-1271 response
     * @dev 
     * @dev         OR
     * @dev
     * @dev         The bytes signature length is not 64 or 65 bytes AND
     * @dev         The expectedSigner's code length is zero OR the expectedSigner does not return a valid EIP-1271 response
     */
    function _verifyPermitSignature(
        address expectedSigner, 
        bytes32 digest, 
        bytes calldata signature
    ) private view {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // Divide the signature in r, s and v variables
            /// @solidity memory-safe-assembly
            assembly {
                r := calldataload(signature.offset)
                s := calldataload(add(signature.offset, 32))
                v := byte(0, calldataload(add(signature.offset, 64)))
            }
            (bool isError, address signer) = _ecdsaRecover(digest, v, r, s);
            if (expectedSigner != signer || isError) {
                _verifyEIP1271Signature(expectedSigner, digest, signature);
            }
        } else if (signature.length == 64) {
            bytes32 r;
            bytes32 vs;
            // Divide the signature in r and vs variables
            /// @solidity memory-safe-assembly
            assembly {
                r := calldataload(signature.offset)
                vs := calldataload(add(signature.offset, 32))
            }
            (bool isError, address signer) = _ecdsaRecover(digest, r, vs);
            if (expectedSigner != signer || isError) {
                _verifyEIP1271Signature(expectedSigner, digest, signature);
            }
        } else {
            _verifyEIP1271Signature(expectedSigner, digest, signature);
        }
    }

    /**
     * @notice Verifies an EIP-1271 signature.
     * 
     * @dev    Throws when `signer` code length is zero OR the EIP-1271 call does not
     * @dev    return the correct magic value.
     * 
     * @param signer     The signer address to verify a signature with
     * @param hash       The hash digest to verify with the signer
     * @param signature  The signature to verify
     */
    function _verifyEIP1271Signature(address signer, bytes32 hash, bytes calldata signature) private view {
        if(signer.code.length == 0) {
            revert();
        }

        if (!_safeIsValidSignature(signer, hash, signature)) {
            revert();
        }
    }

    /**
     * @notice  Overload of the `_ecdsaRecover` function to unpack the `v` and `s` values
     * 
     * @param digest    The hash digest that was signed
     * @param r         The `r` value of the signature
     * @param vs        The packed `v` and `s` values of the signature
     * 
     * @return isError  True if the ECDSA function is provided invalid inputs
     * @return signer   The recovered address from ECDSA
     */
    function _ecdsaRecover(bytes32 digest, bytes32 r, bytes32 vs) private pure returns (bool isError, address signer) {
        unchecked {
            bytes32 s = vs & UPPER_BIT_MASK;
            uint8 v = uint8(uint256(vs >> 255)) + 27;

            (isError, signer) = _ecdsaRecover(digest, v, r, s);
        }
    }

    /**
     * @notice  Recovers the signer address using ECDSA
     * 
     * @dev     Does **NOT** revert if invalid input values are provided or `signer` is recovered as address(0)
     * @dev     Returns an `isError` value in those conditions that is handled upstream
     * 
     * @param digest    The hash digest that was signed
     * @param v         The `v` value of the signature
     * @param r         The `r` value of the signature
     * @param s         The `s` value of the signature
     * 
     * @return isError  True if the ECDSA function is provided invalid inputs
     * @return signer   The recovered address from ECDSA
     */
    function _ecdsaRecover(bytes32 digest, uint8 v, bytes32 r, bytes32 s) private pure returns (bool isError, address signer) {
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            // Invalid signature `s` value - return isError = true and signer = address(0) to check EIP-1271
            return (true, address(0));
        }

        signer = ecrecover(digest, v, r, s);
        isError = (signer == address(0));
    }

    /**
     * @notice A gas efficient, and fallback-safe way to call the isValidSignature function for EIP-1271.
     *
     * @param signer     The EIP-1271 signer to call to check for a valid signature.
     * @param hash       The hash digest to verify with the EIP-1271 signer.
     * @param signature  The supplied signature to verify.
     * 
     * @return isValid   True if the EIP-1271 signer returns the EIP-1271 magic value.
     */
    function _safeIsValidSignature(
        address signer,
        bytes32 hash,
        bytes calldata signature
    ) private view returns(bool isValid) {
        assembly {
            function _callIsValidSignature(_signer, _hash, _signatureOffset, _signatureLength) -> _isValid {
                let ptr := mload(0x40)
                // store isValidSignature(bytes32,bytes) selector
                mstore(ptr, hex"1626ba7e")
                // store bytes32 hash value in abi encoded location
                mstore(add(ptr, 0x04), _hash)
                // store abi encoded location of the bytes signature data
                mstore(add(ptr, 0x24), 0x40)
                // store bytes signature length
                mstore(add(ptr, 0x44), _signatureLength)
                // copy calldata bytes signature to memory
                calldatacopy(add(ptr, 0x64), _signatureOffset, _signatureLength)
                // calculate data length based on abi encoded data with rounded up signature length
                let dataLength := add(0x64, and(add(_signatureLength, 0x1F), not(0x1F)))
                // update free memory pointer
                mstore(0x40, add(ptr, dataLength))

                // static call _signer with abi encoded data
                // skip return data check if call failed or return data size is not at least 32 bytes
                if and(iszero(lt(returndatasize(), 0x20)), staticcall(gas(), _signer, ptr, dataLength, 0x00, 0x20)) {
                    // check if return data is equal to isValidSignature magic value
                    _isValid := eq(mload(0x00), hex"1626ba7e")
                    leave
                }
            }
            isValid := _callIsValidSignature(signer, hash, signature.offset, signature.length)
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

interface IWrappedNative {
    // ERC20 Specific
    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);

    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address to, uint256 value) external payable returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 value) external payable returns (bool);
    function transferFrom(address from, address to, uint256 value) external payable returns (bool);

    // ERC20 Metadata Specific
    function name() external view returns (string memory);
    function symbol() external view returns (string memory);
    function decimals() external view returns (uint8);

    // Wrapped Native Specific
    event Deposit(address indexed to, uint256 amount);
    event Withdrawal(address indexed from, uint256 amount);

    function deposit() external payable;
    function withdraw(uint256 amount) external;
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import "./IWrappedNative.sol";

interface IWrappedNativeExtended is IWrappedNative {
    // Wrapped Native Permit Specific
    event PermitNonceInvalidated(address indexed account,uint256 indexed nonce);
    event MasterNonceInvalidated(address indexed account, uint256 indexed nonce);

    // Enhancements for Deposits and Withdrawals
    function depositTo(address to) external payable;
    function withdrawToAccount(address to, uint256 amount) external;
    function withdrawSplit(address[] calldata toAddresses, uint256[] calldata amounts) external;

    // Permit Processing
    function domainSeparatorV4() external view returns (bytes32);

    function isNonceUsed(address account, uint256 nonce) external view returns (bool);
    function masterNonces(address account) external view returns (uint256);

    function revokeMyOutstandingPermits() external;
    function revokeMyNonce(uint256 nonce) external;

    function permitTransfer(
        address from,
        address to,
        uint256 transferAmount,
        uint256 permitAmount,
        uint256 nonce,
        uint256 expiration,
        bytes calldata signedPermit
    ) external payable;

    function doPermittedWithdraw(
        address from,
        address to,
        uint256 amount,
        uint256 nonce,
        uint256 expiration,
        address convenienceFeeReceiver,
        uint256 convenienceFeeBps,
        bytes calldata signedPermit
    ) external;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/EIP712.sol)

pragma solidity ^0.8.20;

import "./MessageHashUtils.sol";

/**
 * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
 *
 * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
 * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
 * they need in their contracts using a combination of `abi.encode` and `keccak256`.
 *
 * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
 * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
 * ({_hashTypedDataV4}).
 *
 * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
 * the chain id to protect against replay attacks on an eventual fork of the chain.
 *
 * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
 * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
 *
 * NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
 * separator of the implementation contract. This will cause the `_domainSeparatorV4` function to always rebuild the
 * separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
 *
 * _Available since v3.4._
 *
 * @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
 */
abstract contract EIP712 {
    bytes32 private constant _TYPE_HASH =
        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");

    // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
    // invalidate the cached domain separator if the chain id changes.
    bytes32 private immutable _cachedDomainSeparator;
    uint256 private immutable _cachedChainId;

    bytes32 private immutable _hashedName;
    bytes32 private immutable _hashedVersion;

    /**
     * @dev Initializes the domain separator and parameter caches.
     *
     * The meaning of `name` and `version` is specified in
     * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
     *
     * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
     * - `version`: the current major version of the signing domain.
     *
     * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
     * contract upgrade].
     */
    constructor(string memory name, string memory version) {
        _hashedName = keccak256(bytes(name));
        _hashedVersion = keccak256(bytes(version));

        _cachedChainId = block.chainid;
        _cachedDomainSeparator = _buildDomainSeparator();
    }

    /**
     * @dev Returns the domain separator for the current chain.
     */
    function _domainSeparatorV4() internal view returns (bytes32) {
        if (block.chainid == _cachedChainId) {
            return _cachedDomainSeparator;
        } else {
            return _buildDomainSeparator();
        }
    }

    function _buildDomainSeparator() private view returns (bytes32) {
        return keccak256(abi.encode(_TYPE_HASH, _hashedName, _hashedVersion, block.chainid, address(this)));
    }

    /**
     * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
     * function returns the hash of the fully encoded EIP712 message for this domain.
     *
     * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
     *
     * ```solidity
     * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
     *     keccak256("Mail(address to,string contents)"),
     *     mailTo,
     *     keccak256(bytes(mailContents))
     * )));
     * address signer = ECDSA.recover(digest, signature);
     * ```
     */
    function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
        return MessageHashUtils.toTypedDataHash(_domainSeparatorV4(), structHash);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Muldiv operation overflow.
     */
    error MathOverflowedMulDiv();

    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds towards infinity instead
     * of rounding towards zero.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            // Guarantee the same behavior as in a regular Solidity division.
            return a / b;
        }

        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
     * denominator == 0.
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
     * Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0 = x * y; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            if (denominator <= prod1) {
                revert MathOverflowedMulDiv();
            }

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator.
            // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.

            uint256 twos = denominator & (0 - denominator);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
            // works in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
     * towards zero.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
        }
    }

    /**
     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
        return uint8(rounding) % 2 == 1;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol)

pragma solidity ^0.8.20;

import {Strings} from "./Strings.sol";

/**
 * @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
 *
 * The library provides methods for generating a hash of a message that conforms to the
 * https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
 * specifications.
 */
library MessageHashUtils {
    /**
     * @dev Returns the keccak256 digest of an EIP-191 signed data with version
     * `0x45` (`personal_sign` messages).
     *
     * The digest is calculated by prefixing a bytes32 `messageHash` with
     * `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the
     * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
     *
     * NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
     * keccak256, although any bytes32 value can be safely used because the final digest will
     * be re-hashed.
     *
     * See {ECDSA-recover}.
     */
    function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash
            mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
            digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
        }
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-191 signed data with version
     * `0x45` (`personal_sign` messages).
     *
     * The digest is calculated by prefixing an arbitrary `message` with
     * `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the
     * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
     *
     * See {ECDSA-recover}.
     */
    function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
        return
            keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message));
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-191 signed data with version
     * `0x00` (data with intended validator).
     *
     * The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended
     * `validator` address. Then hashing the result.
     *
     * See {ECDSA-recover}.
     */
    function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked(hex"19_00", validator, data));
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`).
     *
     * The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
     * `\x19\x01` and hashing the result. It corresponds to the hash signed by the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
     *
     * See {ECDSA-recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, hex"19_01")
            mstore(add(ptr, 0x02), domainSeparator)
            mstore(add(ptr, 0x22), structHash)
            digest := keccak256(ptr, 0x42)
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two signed numbers.
     */
    function min(int256 a, int256 b) internal pure returns (int256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)

pragma solidity ^0.8.20;

import {Math} from "./Math.sol";
import {SignedMath} from "./SignedMath.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant HEX_DIGITS = "0123456789abcdef";
    uint8 private constant ADDRESS_LENGTH = 20;

    /**
     * @dev The `value` string doesn't fit in the specified `length`.
     */
    error StringsInsufficientHexLength(uint256 value, uint256 length);

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toStringSigned(int256 value) internal pure returns (string memory) {
        return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        uint256 localValue = value;
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = HEX_DIGITS[localValue & 0xf];
            localValue >>= 4;
        }
        if (localValue != 0) {
            revert StringsInsufficientHexLength(value, length);
        }
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
     * representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

Please enter a contract address above to load the contract details and source code.

Context size (optional):