ETH Price: $1,893.50 (+1.79%)

Transaction Decoder

Block:
21753250 at Feb-01-2025 05:45:59 PM +UTC
Transaction Fee:
0.000196143166170336 ETH $0.37
Gas Used:
74,976 Gas / 2.616079361 Gwei

Emitted Events:

283 SimpleStaking.Staked( user=[Sender] 0xf3676dc97400b23f8b4486d1e360afcca749fc60, amount=57396188486712301963056 )
284 MocaToken.Transfer( from=[Sender] 0xf3676dc97400b23f8b4486d1e360afcca749fc60, to=[Receiver] SimpleStaking, value=57396188486712301963056 )

Account State Difference:

  Address   Before After State Difference Code
(beaverbuild)
19.012990363149517981 Eth19.012993362189517981 Eth0.00000299904
0x9a98E6B6...F1885AeD2
(Moca: Staking v1)
0xF3676Dc9...ca749FC60
0.010991838002171014 Eth
Nonce: 838
0.010795694836000678 Eth
Nonce: 839
0.000196143166170336
0xF944e35f...7d311e8c5

Execution Trace

SimpleStaking.stake( amount=57396188486712301963056 )
  • MocaToken.transferFrom( from=0xF3676Dc97400b23F8b4486D1E360AfCca749FC60, to=0x9a98E6B60784634AE273F2FB84519C7F1885AeD2, value=57396188486712301963056 ) => ( True )
    File 1 of 2: SimpleStaking
    // SPDX-License-Identifier: UNLICENSED
    pragma solidity 0.8.24;
    import {SafeERC20, IERC20} from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
    import {Ownable2Step, Ownable} from "./../lib/openzeppelin-contracts/contracts/access/Ownable2Step.sol";
    import {Pausable} from "./../lib/openzeppelin-contracts/contracts/utils/Pausable.sol";
    contract SimpleStaking is Ownable2Step, Pausable {
        using SafeERC20 for IERC20;
        // interfaces 
        IERC20 internal immutable MOCA_TOKEN;
        // startTime
        uint256 internal immutable _startTime;
        // pool data 
        uint256 internal _totalStaked;
        uint256 internal _totalCumulativeWeight;
        uint256 internal _poolLastUpdateTimestamp; 
        struct Data {
            uint256 amount;
            uint256 cumulativeWeight;
            uint256 lastUpdateTimestamp;
        }
        mapping(address user => Data userData) internal _users;
        address internal _updater;
        // events 
        event Staked(address indexed user, uint256 amount);
        event Unstaked(address indexed user, uint256 amount);
        event StakedBehalf(address[] indexed users, uint256[] indexed amounts);
        constructor(address mocaToken, uint256 startTime_, address owner, address updater) Ownable(owner){
            // ensure startTime is not far-dated
            require(startTime_ <= block.timestamp + 30 days, "Far-dated start"); 
            require(startTime_ >= block.timestamp, "StartTime in past");
            MOCA_TOKEN = IERC20(mocaToken);
            
            _startTime = startTime_;
            _updater = updater;
        }
        /*//////////////////////////////////////////////////////////////
                                    EXTERNAL
        //////////////////////////////////////////////////////////////*/
        /**
         * @notice User to stake MocaTokens
         * @dev User can stake for another address of choice
         * @param amount Tokens to stake, 1e8 precision
         */
        function stake(uint256 amount) external whenNotPaused {
            require(amount > 0, "Zero amount");
            // cache
            Data memory userData_ = _users[msg.sender];
       
            // book pool's previous
            _updatePool();
            // book user's previous
            Data memory userData = _updateUserCumulativeWeight(userData_);
        
            // book inflow
            userData.amount += amount;
            _totalStaked += amount;
            // user: update storage
            _users[msg.sender] = userData;
            emit Staked(msg.sender, amount);
     
            // grab MOCA
            MOCA_TOKEN.safeTransferFrom(msg.sender, address(this), amount);
        }
        /**
         * @notice User to unstake MocaTokens
         * @param amount Tokens to unstake, 1e8 precision
         */
        function unstake(uint256 amount) external {
            require(block.timestamp >= _startTime, "Not started");
            require(amount > 0, "Zero amount");
            // cache
            Data memory userData_ = _users[msg.sender];
            // sanity checks
            require(userData_.amount >= amount, "Insufficient balance");
            // book pool's previous
            _updatePool();
            // book user's previous
            Data memory userData = _updateUserCumulativeWeight(userData_);
            // book outflow
            userData.amount -= amount;
            _totalStaked -= amount;      // sstore 
            // user: update state 
            _users[msg.sender] = userData;
            emit Unstaked(msg.sender, amount);
            // transfer moca
            MOCA_TOKEN.safeTransfer(msg.sender, amount);
        }
        /**
         * @notice Owner to stake on behalf of users for distribution
         * @dev Gas used: 84,805 for length =1, incrementing by 2600 for every additional loop
         * @param users Array of address 
         * @param amounts Array of stake amounts, 1e18 precision
         */
        function stakeBehalf(address[] calldata users, uint256[] calldata amounts) external whenNotPaused {
            require(msg.sender == _updater, "Incorrect caller");
            uint256 usersLength = users.length;
            uint256 amountLength = amounts.length;
            require(usersLength == amountLength, "Incorrect lengths");
            require(usersLength > 0, "Empty array");
            // book pool's previous
            _updatePool();
            uint256 totalAmount;
            for (uint256 i; i < usersLength; ++i){
                address onBehalfOf = users[i];
                uint256 amount = amounts[i];
                // cache
                Data memory userData_ = _users[onBehalfOf];
            
                // book user's previous
                Data memory userData = _updateUserCumulativeWeight(userData_);
                // book inflow
                userData.amount += amount;
                // user: update storage
                _users[onBehalfOf] = userData;
                // increment totalAmount
                totalAmount += amount;
            }
            
            emit StakedBehalf(users, amounts);
            _totalStaked += totalAmount;         //sstore
            // grab MOCA
            MOCA_TOKEN.safeTransferFrom(msg.sender, address(this), totalAmount);
        }
        /**
         * @notice Owner to pause contract
         */
        function pause() external onlyOwner {
            _pause();
        }
        /**
         * @notice Owner to unpause contract
         */
        function unpause() external onlyOwner {
            _unpause();
        }
        
        /**
         * @notice Owner to change updater address
         * @param newUpdater new updater address
         */
        function changeUpdater(address newUpdater) external onlyOwner {
            _updater = newUpdater;
        }
        /*//////////////////////////////////////////////////////////////
                                    INTERNAL
        //////////////////////////////////////////////////////////////*/
        function _updatePool() internal {
            
            // no update of the _poolLastUpdateTimestamp otherwise it can be set to before
            // _poolLastUpdateTimestamp =0, when t = startTime
            if (block.timestamp <= _startTime) {
                return; 
            }
            if(_totalStaked > 0){
                if(block.timestamp > _poolLastUpdateTimestamp){
                    uint256 timeDelta = _getTimeDelta(block.timestamp, _poolLastUpdateTimestamp);
                    uint256 unbookedWeight = timeDelta * _totalStaked;
                    
                    // sstore
                    _totalCumulativeWeight += unbookedWeight;
                }
            }
            
            // sstore
            _poolLastUpdateTimestamp = block.timestamp;
        }
        function _updateUserCumulativeWeight(Data memory userData) internal returns(Data memory) {
            
            // staking not started: return early
            uint256 startTime = _startTime;
            if (block.timestamp <= startTime) {
                return userData;
            }
            // staking has begun
            if(userData.amount > 0){
                if(block.timestamp > userData.lastUpdateTimestamp){
                    
                    // timeDelta: 0 if staking has not begun 
                    uint256 timeDelta = _getTimeDelta(block.timestamp, userData.lastUpdateTimestamp);
                    uint256 unbookedWeight = timeDelta * userData.amount;
                    // update user
                    userData.cumulativeWeight += unbookedWeight;
                }
            }
            
            userData.lastUpdateTimestamp = block.timestamp;
            return userData;
        }
        function _getTimeDelta(uint256 to, uint256 from) internal view returns (uint256) {
            // cache
            uint256 startTime = _startTime;
            
            if(from < startTime){
                from = startTime;
            }
            return (to - from);
        }
        /*//////////////////////////////////////////////////////////////
                                    GETTERS
        //////////////////////////////////////////////////////////////*/
        ///@notice returns moca token address    
        function getMocaToken() external view returns(address) {
            return address(MOCA_TOKEN);
        }
        ///@notice returns _startTime
        function getStartTime() external view returns(uint256) {
            return _startTime;
        }
        ///@notice returns _totalStaked
        function getTotalStaked() external view returns(uint256) {
            return _totalStaked;
        }
        ///@notice returns _totalCumulativeWeight
        function getTotalCumulativeWeight() external view returns(uint256) {
            return _totalCumulativeWeight;
        }
        ///@notice returns _poolLastUpdateTimestamp
        function getPoolLastUpdateTimestamp() external view returns(uint256) {
            return _poolLastUpdateTimestamp;
        }
        ///@notice returns user data struct
        function getUser(address user) external view returns(Data memory) {
            return _users[user];
        } 
        ///@notice returns user's cumulative weight
        ///@dev returns 0 if staking has not begun
        function getUserCumulativeWeight(address user) external view returns(uint256) {
            // cache
            Data memory userData = _users[user];
            // staking not started: return early
            if (block.timestamp <= _startTime) {
                return 0;
            }
            // calc. unbooked
            if(userData.amount > 0) {
                if(block.timestamp > userData.lastUpdateTimestamp){
                    uint256 timeDelta = _getTimeDelta(block.timestamp, userData.lastUpdateTimestamp);
                    uint256 unbookedWeight = userData.amount * timeDelta;
                    return (userData.cumulativeWeight + unbookedWeight);
                }
            }
            // updated to latest, nothing unbooked 
            return userData.cumulativeWeight;
        }
        ///@notice returns pool's total cumulative weight (incl. pending)
        ///@dev returns 0 if staking has not begun
        function getPoolCumulativeWeight() external view returns(uint256) {
            // staking not started
            if (block.timestamp <= _startTime) {
                return 0; 
            }
            // calc. unbooked
            if(block.timestamp > _poolLastUpdateTimestamp){
                uint256 timeDelta = _getTimeDelta(block.timestamp, _poolLastUpdateTimestamp);
                uint256 unbookedWeight = _totalStaked * timeDelta;
                return (_totalCumulativeWeight + unbookedWeight);
            }
            // updated to latest, nothing unbooked 
            return _totalCumulativeWeight;
        }
        ///@notice returns _updater
        function getUpdater() external view returns(address){
            return _updater;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
    pragma solidity ^0.8.20;
    import {IERC20} from "../IERC20.sol";
    import {IERC20Permit} from "../extensions/IERC20Permit.sol";
    import {Address} from "../../../utils/Address.sol";
    /**
     * @title SafeERC20
     * @dev Wrappers around ERC20 operations that throw on failure (when the token
     * contract returns false). Tokens that return no value (and instead revert or
     * throw on failure) are also supported, non-reverting calls are assumed to be
     * successful.
     * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
     * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
     */
    library SafeERC20 {
        using Address for address;
        /**
         * @dev An operation with an ERC20 token failed.
         */
        error SafeERC20FailedOperation(address token);
        /**
         * @dev Indicates a failed `decreaseAllowance` request.
         */
        error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
        /**
         * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
         * non-reverting calls are assumed to be successful.
         */
        function safeTransfer(IERC20 token, address to, uint256 value) internal {
            _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
        }
        /**
         * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
         * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
         */
        function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
            _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
        }
        /**
         * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
         * non-reverting calls are assumed to be successful.
         */
        function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
            uint256 oldAllowance = token.allowance(address(this), spender);
            forceApprove(token, spender, oldAllowance + value);
        }
        /**
         * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
         * value, non-reverting calls are assumed to be successful.
         */
        function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
            unchecked {
                uint256 currentAllowance = token.allowance(address(this), spender);
                if (currentAllowance < requestedDecrease) {
                    revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
                }
                forceApprove(token, spender, currentAllowance - requestedDecrease);
            }
        }
        /**
         * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
         * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
         * to be set to zero before setting it to a non-zero value, such as USDT.
         */
        function forceApprove(IERC20 token, address spender, uint256 value) internal {
            bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
            if (!_callOptionalReturnBool(token, approvalCall)) {
                _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
                _callOptionalReturn(token, approvalCall);
            }
        }
        /**
         * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
         * on the return value: the return value is optional (but if data is returned, it must not be false).
         * @param token The token targeted by the call.
         * @param data The call data (encoded using abi.encode or one of its variants).
         */
        function _callOptionalReturn(IERC20 token, bytes memory data) private {
            // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
            // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
            // the target address contains contract code and also asserts for success in the low-level call.
            bytes memory returndata = address(token).functionCall(data);
            if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
                revert SafeERC20FailedOperation(address(token));
            }
        }
        /**
         * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
         * on the return value: the return value is optional (but if data is returned, it must not be false).
         * @param token The token targeted by the call.
         * @param data The call data (encoded using abi.encode or one of its variants).
         *
         * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
         */
        function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
            // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
            // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
            // and not revert is the subcall reverts.
            (bool success, bytes memory returndata) = address(token).call(data);
            return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable2Step.sol)
    pragma solidity ^0.8.20;
    import {Ownable} from "./Ownable.sol";
    /**
     * @dev Contract module which provides access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * The initial owner is specified at deployment time in the constructor for `Ownable`. This
     * can later be changed with {transferOwnership} and {acceptOwnership}.
     *
     * This module is used through inheritance. It will make available all functions
     * from parent (Ownable).
     */
    abstract contract Ownable2Step is Ownable {
        address private _pendingOwner;
        event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
        /**
         * @dev Returns the address of the pending owner.
         */
        function pendingOwner() public view virtual returns (address) {
            return _pendingOwner;
        }
        /**
         * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public virtual override onlyOwner {
            _pendingOwner = newOwner;
            emit OwnershipTransferStarted(owner(), newOwner);
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
         * Internal function without access restriction.
         */
        function _transferOwnership(address newOwner) internal virtual override {
            delete _pendingOwner;
            super._transferOwnership(newOwner);
        }
        /**
         * @dev The new owner accepts the ownership transfer.
         */
        function acceptOwnership() public virtual {
            address sender = _msgSender();
            if (pendingOwner() != sender) {
                revert OwnableUnauthorizedAccount(sender);
            }
            _transferOwnership(sender);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)
    pragma solidity ^0.8.20;
    import {Context} from "../utils/Context.sol";
    /**
     * @dev Contract module which allows children to implement an emergency stop
     * mechanism that can be triggered by an authorized account.
     *
     * This module is used through inheritance. It will make available the
     * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
     * the functions of your contract. Note that they will not be pausable by
     * simply including this module, only once the modifiers are put in place.
     */
    abstract contract Pausable is Context {
        bool private _paused;
        /**
         * @dev Emitted when the pause is triggered by `account`.
         */
        event Paused(address account);
        /**
         * @dev Emitted when the pause is lifted by `account`.
         */
        event Unpaused(address account);
        /**
         * @dev The operation failed because the contract is paused.
         */
        error EnforcedPause();
        /**
         * @dev The operation failed because the contract is not paused.
         */
        error ExpectedPause();
        /**
         * @dev Initializes the contract in unpaused state.
         */
        constructor() {
            _paused = false;
        }
        /**
         * @dev Modifier to make a function callable only when the contract is not paused.
         *
         * Requirements:
         *
         * - The contract must not be paused.
         */
        modifier whenNotPaused() {
            _requireNotPaused();
            _;
        }
        /**
         * @dev Modifier to make a function callable only when the contract is paused.
         *
         * Requirements:
         *
         * - The contract must be paused.
         */
        modifier whenPaused() {
            _requirePaused();
            _;
        }
        /**
         * @dev Returns true if the contract is paused, and false otherwise.
         */
        function paused() public view virtual returns (bool) {
            return _paused;
        }
        /**
         * @dev Throws if the contract is paused.
         */
        function _requireNotPaused() internal view virtual {
            if (paused()) {
                revert EnforcedPause();
            }
        }
        /**
         * @dev Throws if the contract is not paused.
         */
        function _requirePaused() internal view virtual {
            if (!paused()) {
                revert ExpectedPause();
            }
        }
        /**
         * @dev Triggers stopped state.
         *
         * Requirements:
         *
         * - The contract must not be paused.
         */
        function _pause() internal virtual whenNotPaused {
            _paused = true;
            emit Paused(_msgSender());
        }
        /**
         * @dev Returns to normal state.
         *
         * Requirements:
         *
         * - The contract must be paused.
         */
        function _unpause() internal virtual whenPaused {
            _paused = false;
            emit Unpaused(_msgSender());
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Interface of the ERC20 standard as defined in the EIP.
     */
    interface IERC20 {
        /**
         * @dev Emitted when `value` tokens are moved from one account (`from`) to
         * another (`to`).
         *
         * Note that `value` may be zero.
         */
        event Transfer(address indexed from, address indexed to, uint256 value);
        /**
         * @dev Emitted when the allowance of a `spender` for an `owner` is set by
         * a call to {approve}. `value` is the new allowance.
         */
        event Approval(address indexed owner, address indexed spender, uint256 value);
        /**
         * @dev Returns the value of tokens in existence.
         */
        function totalSupply() external view returns (uint256);
        /**
         * @dev Returns the value of tokens owned by `account`.
         */
        function balanceOf(address account) external view returns (uint256);
        /**
         * @dev Moves a `value` amount of tokens from the caller's account to `to`.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transfer(address to, uint256 value) external returns (bool);
        /**
         * @dev Returns the remaining number of tokens that `spender` will be
         * allowed to spend on behalf of `owner` through {transferFrom}. This is
         * zero by default.
         *
         * This value changes when {approve} or {transferFrom} are called.
         */
        function allowance(address owner, address spender) external view returns (uint256);
        /**
         * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
         * caller's tokens.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * IMPORTANT: Beware that changing an allowance with this method brings the risk
         * that someone may use both the old and the new allowance by unfortunate
         * transaction ordering. One possible solution to mitigate this race
         * condition is to first reduce the spender's allowance to 0 and set the
         * desired value afterwards:
         * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
         *
         * Emits an {Approval} event.
         */
        function approve(address spender, uint256 value) external returns (bool);
        /**
         * @dev Moves a `value` amount of tokens from `from` to `to` using the
         * allowance mechanism. `value` is then deducted from the caller's
         * allowance.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(address from, address to, uint256 value) external returns (bool);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
     * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
     *
     * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
     * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
     * need to send a transaction, and thus is not required to hold Ether at all.
     *
     * ==== Security Considerations
     *
     * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
     * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
     * considered as an intention to spend the allowance in any specific way. The second is that because permits have
     * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
     * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
     * generally recommended is:
     *
     * ```solidity
     * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
     *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
     *     doThing(..., value);
     * }
     *
     * function doThing(..., uint256 value) public {
     *     token.safeTransferFrom(msg.sender, address(this), value);
     *     ...
     * }
     * ```
     *
     * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
     * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
     * {SafeERC20-safeTransferFrom}).
     *
     * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
     * contracts should have entry points that don't rely on permit.
     */
    interface IERC20Permit {
        /**
         * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
         * given ``owner``'s signed approval.
         *
         * IMPORTANT: The same issues {IERC20-approve} has related to transaction
         * ordering also apply here.
         *
         * Emits an {Approval} event.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         * - `deadline` must be a timestamp in the future.
         * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
         * over the EIP712-formatted function arguments.
         * - the signature must use ``owner``'s current nonce (see {nonces}).
         *
         * For more information on the signature format, see the
         * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
         * section].
         *
         * CAUTION: See Security Considerations above.
         */
        function permit(
            address owner,
            address spender,
            uint256 value,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) external;
        /**
         * @dev Returns the current nonce for `owner`. This value must be
         * included whenever a signature is generated for {permit}.
         *
         * Every successful call to {permit} increases ``owner``'s nonce by one. This
         * prevents a signature from being used multiple times.
         */
        function nonces(address owner) external view returns (uint256);
        /**
         * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
         */
        // solhint-disable-next-line func-name-mixedcase
        function DOMAIN_SEPARATOR() external view returns (bytes32);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Collection of functions related to the address type
     */
    library Address {
        /**
         * @dev The ETH balance of the account is not enough to perform the operation.
         */
        error AddressInsufficientBalance(address account);
        /**
         * @dev There's no code at `target` (it is not a contract).
         */
        error AddressEmptyCode(address target);
        /**
         * @dev A call to an address target failed. The target may have reverted.
         */
        error FailedInnerCall();
        /**
         * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
         * `recipient`, forwarding all available gas and reverting on errors.
         *
         * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
         * of certain opcodes, possibly making contracts go over the 2300 gas limit
         * imposed by `transfer`, making them unable to receive funds via
         * `transfer`. {sendValue} removes this limitation.
         *
         * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
         *
         * IMPORTANT: because control is transferred to `recipient`, care must be
         * taken to not create reentrancy vulnerabilities. Consider using
         * {ReentrancyGuard} or the
         * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
         */
        function sendValue(address payable recipient, uint256 amount) internal {
            if (address(this).balance < amount) {
                revert AddressInsufficientBalance(address(this));
            }
            (bool success, ) = recipient.call{value: amount}("");
            if (!success) {
                revert FailedInnerCall();
            }
        }
        /**
         * @dev Performs a Solidity function call using a low level `call`. A
         * plain `call` is an unsafe replacement for a function call: use this
         * function instead.
         *
         * If `target` reverts with a revert reason or custom error, it is bubbled
         * up by this function (like regular Solidity function calls). However, if
         * the call reverted with no returned reason, this function reverts with a
         * {FailedInnerCall} error.
         *
         * Returns the raw returned data. To convert to the expected return value,
         * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
         *
         * Requirements:
         *
         * - `target` must be a contract.
         * - calling `target` with `data` must not revert.
         */
        function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCallWithValue(target, data, 0);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but also transferring `value` wei to `target`.
         *
         * Requirements:
         *
         * - the calling contract must have an ETH balance of at least `value`.
         * - the called Solidity function must be `payable`.
         */
        function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
            if (address(this).balance < value) {
                revert AddressInsufficientBalance(address(this));
            }
            (bool success, bytes memory returndata) = target.call{value: value}(data);
            return verifyCallResultFromTarget(target, success, returndata);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a static call.
         */
        function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
            (bool success, bytes memory returndata) = target.staticcall(data);
            return verifyCallResultFromTarget(target, success, returndata);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a delegate call.
         */
        function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
            (bool success, bytes memory returndata) = target.delegatecall(data);
            return verifyCallResultFromTarget(target, success, returndata);
        }
        /**
         * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
         * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
         * unsuccessful call.
         */
        function verifyCallResultFromTarget(
            address target,
            bool success,
            bytes memory returndata
        ) internal view returns (bytes memory) {
            if (!success) {
                _revert(returndata);
            } else {
                // only check if target is a contract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                if (returndata.length == 0 && target.code.length == 0) {
                    revert AddressEmptyCode(target);
                }
                return returndata;
            }
        }
        /**
         * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
         * revert reason or with a default {FailedInnerCall} error.
         */
        function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
            if (!success) {
                _revert(returndata);
            } else {
                return returndata;
            }
        }
        /**
         * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
         */
        function _revert(bytes memory returndata) private pure {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly
                /// @solidity memory-safe-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert FailedInnerCall();
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
    pragma solidity ^0.8.20;
    import {Context} from "../utils/Context.sol";
    /**
     * @dev Contract module which provides a basic access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * The initial owner is set to the address provided by the deployer. This can
     * later be changed with {transferOwnership}.
     *
     * This module is used through inheritance. It will make available the modifier
     * `onlyOwner`, which can be applied to your functions to restrict their use to
     * the owner.
     */
    abstract contract Ownable is Context {
        address private _owner;
        /**
         * @dev The caller account is not authorized to perform an operation.
         */
        error OwnableUnauthorizedAccount(address account);
        /**
         * @dev The owner is not a valid owner account. (eg. `address(0)`)
         */
        error OwnableInvalidOwner(address owner);
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        /**
         * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
         */
        constructor(address initialOwner) {
            if (initialOwner == address(0)) {
                revert OwnableInvalidOwner(address(0));
            }
            _transferOwnership(initialOwner);
        }
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            _checkOwner();
            _;
        }
        /**
         * @dev Returns the address of the current owner.
         */
        function owner() public view virtual returns (address) {
            return _owner;
        }
        /**
         * @dev Throws if the sender is not the owner.
         */
        function _checkOwner() internal view virtual {
            if (owner() != _msgSender()) {
                revert OwnableUnauthorizedAccount(_msgSender());
            }
        }
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions. Can only be called by the current owner.
         *
         * NOTE: Renouncing ownership will leave the contract without an owner,
         * thereby disabling any functionality that is only available to the owner.
         */
        function renounceOwnership() public virtual onlyOwner {
            _transferOwnership(address(0));
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public virtual onlyOwner {
            if (newOwner == address(0)) {
                revert OwnableInvalidOwner(address(0));
            }
            _transferOwnership(newOwner);
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Internal function without access restriction.
         */
        function _transferOwnership(address newOwner) internal virtual {
            address oldOwner = _owner;
            _owner = newOwner;
            emit OwnershipTransferred(oldOwner, newOwner);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Provides information about the current execution context, including the
     * sender of the transaction and its data. While these are generally available
     * via msg.sender and msg.data, they should not be accessed in such a direct
     * manner, since when dealing with meta-transactions the account sending and
     * paying for execution may not be the actual sender (as far as an application
     * is concerned).
     *
     * This contract is only required for intermediate, library-like contracts.
     */
    abstract contract Context {
        function _msgSender() internal view virtual returns (address) {
            return msg.sender;
        }
        function _msgData() internal view virtual returns (bytes calldata) {
            return msg.data;
        }
        function _contextSuffixLength() internal view virtual returns (uint256) {
            return 0;
        }
    }
    

    File 2 of 2: MocaToken
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Standard ERC20 Errors
     * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
     */
    interface IERC20Errors {
        /**
         * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
         * @param sender Address whose tokens are being transferred.
         * @param balance Current balance for the interacting account.
         * @param needed Minimum amount required to perform a transfer.
         */
        error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
        /**
         * @dev Indicates a failure with the token `sender`. Used in transfers.
         * @param sender Address whose tokens are being transferred.
         */
        error ERC20InvalidSender(address sender);
        /**
         * @dev Indicates a failure with the token `receiver`. Used in transfers.
         * @param receiver Address to which tokens are being transferred.
         */
        error ERC20InvalidReceiver(address receiver);
        /**
         * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
         * @param spender Address that may be allowed to operate on tokens without being their owner.
         * @param allowance Amount of tokens a `spender` is allowed to operate with.
         * @param needed Minimum amount required to perform a transfer.
         */
        error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
        /**
         * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
         * @param approver Address initiating an approval operation.
         */
        error ERC20InvalidApprover(address approver);
        /**
         * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
         * @param spender Address that may be allowed to operate on tokens without being their owner.
         */
        error ERC20InvalidSpender(address spender);
    }
    /**
     * @dev Standard ERC721 Errors
     * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
     */
    interface IERC721Errors {
        /**
         * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
         * Used in balance queries.
         * @param owner Address of the current owner of a token.
         */
        error ERC721InvalidOwner(address owner);
        /**
         * @dev Indicates a `tokenId` whose `owner` is the zero address.
         * @param tokenId Identifier number of a token.
         */
        error ERC721NonexistentToken(uint256 tokenId);
        /**
         * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
         * @param sender Address whose tokens are being transferred.
         * @param tokenId Identifier number of a token.
         * @param owner Address of the current owner of a token.
         */
        error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
        /**
         * @dev Indicates a failure with the token `sender`. Used in transfers.
         * @param sender Address whose tokens are being transferred.
         */
        error ERC721InvalidSender(address sender);
        /**
         * @dev Indicates a failure with the token `receiver`. Used in transfers.
         * @param receiver Address to which tokens are being transferred.
         */
        error ERC721InvalidReceiver(address receiver);
        /**
         * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
         * @param operator Address that may be allowed to operate on tokens without being their owner.
         * @param tokenId Identifier number of a token.
         */
        error ERC721InsufficientApproval(address operator, uint256 tokenId);
        /**
         * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
         * @param approver Address initiating an approval operation.
         */
        error ERC721InvalidApprover(address approver);
        /**
         * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
         * @param operator Address that may be allowed to operate on tokens without being their owner.
         */
        error ERC721InvalidOperator(address operator);
    }
    /**
     * @dev Standard ERC1155 Errors
     * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
     */
    interface IERC1155Errors {
        /**
         * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
         * @param sender Address whose tokens are being transferred.
         * @param balance Current balance for the interacting account.
         * @param needed Minimum amount required to perform a transfer.
         * @param tokenId Identifier number of a token.
         */
        error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
        /**
         * @dev Indicates a failure with the token `sender`. Used in transfers.
         * @param sender Address whose tokens are being transferred.
         */
        error ERC1155InvalidSender(address sender);
        /**
         * @dev Indicates a failure with the token `receiver`. Used in transfers.
         * @param receiver Address to which tokens are being transferred.
         */
        error ERC1155InvalidReceiver(address receiver);
        /**
         * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
         * @param operator Address that may be allowed to operate on tokens without being their owner.
         * @param owner Address of the current owner of a token.
         */
        error ERC1155MissingApprovalForAll(address operator, address owner);
        /**
         * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
         * @param approver Address initiating an approval operation.
         */
        error ERC1155InvalidApprover(address approver);
        /**
         * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
         * @param operator Address that may be allowed to operate on tokens without being their owner.
         */
        error ERC1155InvalidOperator(address operator);
        /**
         * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
         * Used in batch transfers.
         * @param idsLength Length of the array of token identifiers
         * @param valuesLength Length of the array of token amounts
         */
        error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)
    pragma solidity ^0.8.20;
    import {IERC20} from "./IERC20.sol";
    import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
    import {Context} from "../../utils/Context.sol";
    import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";
    /**
     * @dev Implementation of the {IERC20} interface.
     *
     * This implementation is agnostic to the way tokens are created. This means
     * that a supply mechanism has to be added in a derived contract using {_mint}.
     *
     * TIP: For a detailed writeup see our guide
     * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
     * to implement supply mechanisms].
     *
     * The default value of {decimals} is 18. To change this, you should override
     * this function so it returns a different value.
     *
     * We have followed general OpenZeppelin Contracts guidelines: functions revert
     * instead returning `false` on failure. This behavior is nonetheless
     * conventional and does not conflict with the expectations of ERC20
     * applications.
     *
     * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
     * This allows applications to reconstruct the allowance for all accounts just
     * by listening to said events. Other implementations of the EIP may not emit
     * these events, as it isn't required by the specification.
     */
    abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
        mapping(address account => uint256) private _balances;
        mapping(address account => mapping(address spender => uint256)) private _allowances;
        uint256 private _totalSupply;
        string private _name;
        string private _symbol;
        /**
         * @dev Sets the values for {name} and {symbol}.
         *
         * All two of these values are immutable: they can only be set once during
         * construction.
         */
        constructor(string memory name_, string memory symbol_) {
            _name = name_;
            _symbol = symbol_;
        }
        /**
         * @dev Returns the name of the token.
         */
        function name() public view virtual returns (string memory) {
            return _name;
        }
        /**
         * @dev Returns the symbol of the token, usually a shorter version of the
         * name.
         */
        function symbol() public view virtual returns (string memory) {
            return _symbol;
        }
        /**
         * @dev Returns the number of decimals used to get its user representation.
         * For example, if `decimals` equals `2`, a balance of `505` tokens should
         * be displayed to a user as `5.05` (`505 / 10 ** 2`).
         *
         * Tokens usually opt for a value of 18, imitating the relationship between
         * Ether and Wei. This is the default value returned by this function, unless
         * it's overridden.
         *
         * NOTE: This information is only used for _display_ purposes: it in
         * no way affects any of the arithmetic of the contract, including
         * {IERC20-balanceOf} and {IERC20-transfer}.
         */
        function decimals() public view virtual returns (uint8) {
            return 18;
        }
        /**
         * @dev See {IERC20-totalSupply}.
         */
        function totalSupply() public view virtual returns (uint256) {
            return _totalSupply;
        }
        /**
         * @dev See {IERC20-balanceOf}.
         */
        function balanceOf(address account) public view virtual returns (uint256) {
            return _balances[account];
        }
        /**
         * @dev See {IERC20-transfer}.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - the caller must have a balance of at least `value`.
         */
        function transfer(address to, uint256 value) public virtual returns (bool) {
            address owner = _msgSender();
            _transfer(owner, to, value);
            return true;
        }
        /**
         * @dev See {IERC20-allowance}.
         */
        function allowance(address owner, address spender) public view virtual returns (uint256) {
            return _allowances[owner][spender];
        }
        /**
         * @dev See {IERC20-approve}.
         *
         * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
         * `transferFrom`. This is semantically equivalent to an infinite approval.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         */
        function approve(address spender, uint256 value) public virtual returns (bool) {
            address owner = _msgSender();
            _approve(owner, spender, value);
            return true;
        }
        /**
         * @dev See {IERC20-transferFrom}.
         *
         * Emits an {Approval} event indicating the updated allowance. This is not
         * required by the EIP. See the note at the beginning of {ERC20}.
         *
         * NOTE: Does not update the allowance if the current allowance
         * is the maximum `uint256`.
         *
         * Requirements:
         *
         * - `from` and `to` cannot be the zero address.
         * - `from` must have a balance of at least `value`.
         * - the caller must have allowance for ``from``'s tokens of at least
         * `value`.
         */
        function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
            address spender = _msgSender();
            _spendAllowance(from, spender, value);
            _transfer(from, to, value);
            return true;
        }
        /**
         * @dev Moves a `value` amount of tokens from `from` to `to`.
         *
         * This internal function is equivalent to {transfer}, and can be used to
         * e.g. implement automatic token fees, slashing mechanisms, etc.
         *
         * Emits a {Transfer} event.
         *
         * NOTE: This function is not virtual, {_update} should be overridden instead.
         */
        function _transfer(address from, address to, uint256 value) internal {
            if (from == address(0)) {
                revert ERC20InvalidSender(address(0));
            }
            if (to == address(0)) {
                revert ERC20InvalidReceiver(address(0));
            }
            _update(from, to, value);
        }
        /**
         * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
         * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
         * this function.
         *
         * Emits a {Transfer} event.
         */
        function _update(address from, address to, uint256 value) internal virtual {
            if (from == address(0)) {
                // Overflow check required: The rest of the code assumes that totalSupply never overflows
                _totalSupply += value;
            } else {
                uint256 fromBalance = _balances[from];
                if (fromBalance < value) {
                    revert ERC20InsufficientBalance(from, fromBalance, value);
                }
                unchecked {
                    // Overflow not possible: value <= fromBalance <= totalSupply.
                    _balances[from] = fromBalance - value;
                }
            }
            if (to == address(0)) {
                unchecked {
                    // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
                    _totalSupply -= value;
                }
            } else {
                unchecked {
                    // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
                    _balances[to] += value;
                }
            }
            emit Transfer(from, to, value);
        }
        /**
         * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
         * Relies on the `_update` mechanism
         *
         * Emits a {Transfer} event with `from` set to the zero address.
         *
         * NOTE: This function is not virtual, {_update} should be overridden instead.
         */
        function _mint(address account, uint256 value) internal {
            if (account == address(0)) {
                revert ERC20InvalidReceiver(address(0));
            }
            _update(address(0), account, value);
        }
        /**
         * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
         * Relies on the `_update` mechanism.
         *
         * Emits a {Transfer} event with `to` set to the zero address.
         *
         * NOTE: This function is not virtual, {_update} should be overridden instead
         */
        function _burn(address account, uint256 value) internal {
            if (account == address(0)) {
                revert ERC20InvalidSender(address(0));
            }
            _update(account, address(0), value);
        }
        /**
         * @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.
         *
         * This internal function is equivalent to `approve`, and can be used to
         * e.g. set automatic allowances for certain subsystems, etc.
         *
         * Emits an {Approval} event.
         *
         * Requirements:
         *
         * - `owner` cannot be the zero address.
         * - `spender` cannot be the zero address.
         *
         * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
         */
        function _approve(address owner, address spender, uint256 value) internal {
            _approve(owner, spender, value, true);
        }
        /**
         * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
         *
         * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
         * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
         * `Approval` event during `transferFrom` operations.
         *
         * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
         * true using the following override:
         * ```
         * function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
         *     super._approve(owner, spender, value, true);
         * }
         * ```
         *
         * Requirements are the same as {_approve}.
         */
        function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
            if (owner == address(0)) {
                revert ERC20InvalidApprover(address(0));
            }
            if (spender == address(0)) {
                revert ERC20InvalidSpender(address(0));
            }
            _allowances[owner][spender] = value;
            if (emitEvent) {
                emit Approval(owner, spender, value);
            }
        }
        /**
         * @dev Updates `owner` s allowance for `spender` based on spent `value`.
         *
         * Does not update the allowance value in case of infinite allowance.
         * Revert if not enough allowance is available.
         *
         * Does not emit an {Approval} event.
         */
        function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
            uint256 currentAllowance = allowance(owner, spender);
            if (currentAllowance != type(uint256).max) {
                if (currentAllowance < value) {
                    revert ERC20InsufficientAllowance(spender, currentAllowance, value);
                }
                unchecked {
                    _approve(owner, spender, currentAllowance - value, false);
                }
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Interface of the ERC20 standard as defined in the EIP.
     */
    interface IERC20 {
        /**
         * @dev Emitted when `value` tokens are moved from one account (`from`) to
         * another (`to`).
         *
         * Note that `value` may be zero.
         */
        event Transfer(address indexed from, address indexed to, uint256 value);
        /**
         * @dev Emitted when the allowance of a `spender` for an `owner` is set by
         * a call to {approve}. `value` is the new allowance.
         */
        event Approval(address indexed owner, address indexed spender, uint256 value);
        /**
         * @dev Returns the value of tokens in existence.
         */
        function totalSupply() external view returns (uint256);
        /**
         * @dev Returns the value of tokens owned by `account`.
         */
        function balanceOf(address account) external view returns (uint256);
        /**
         * @dev Moves a `value` amount of tokens from the caller's account to `to`.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transfer(address to, uint256 value) external returns (bool);
        /**
         * @dev Returns the remaining number of tokens that `spender` will be
         * allowed to spend on behalf of `owner` through {transferFrom}. This is
         * zero by default.
         *
         * This value changes when {approve} or {transferFrom} are called.
         */
        function allowance(address owner, address spender) external view returns (uint256);
        /**
         * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
         * caller's tokens.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * IMPORTANT: Beware that changing an allowance with this method brings the risk
         * that someone may use both the old and the new allowance by unfortunate
         * transaction ordering. One possible solution to mitigate this race
         * condition is to first reduce the spender's allowance to 0 and set the
         * desired value afterwards:
         * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
         *
         * Emits an {Approval} event.
         */
        function approve(address spender, uint256 value) external returns (bool);
        /**
         * @dev Moves a `value` amount of tokens from `from` to `to` using the
         * allowance mechanism. `value` is then deducted from the caller's
         * allowance.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(address from, address to, uint256 value) external returns (bool);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)
    pragma solidity ^0.8.20;
    import {IERC20} from "../IERC20.sol";
    /**
     * @dev Interface for the optional metadata functions from the ERC20 standard.
     */
    interface IERC20Metadata is IERC20 {
        /**
         * @dev Returns the name of the token.
         */
        function name() external view returns (string memory);
        /**
         * @dev Returns the symbol of the token.
         */
        function symbol() external view returns (string memory);
        /**
         * @dev Returns the decimals places of the token.
         */
        function decimals() external view returns (uint8);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Provides information about the current execution context, including the
     * sender of the transaction and its data. While these are generally available
     * via msg.sender and msg.data, they should not be accessed in such a direct
     * manner, since when dealing with meta-transactions the account sending and
     * paying for execution may not be the actual sender (as far as an application
     * is concerned).
     *
     * This contract is only required for intermediate, library-like contracts.
     */
    abstract contract Context {
        function _msgSender() internal view virtual returns (address) {
            return msg.sender;
        }
        function _msgData() internal view virtual returns (bytes calldata) {
            return msg.data;
        }
        function _contextSuffixLength() internal view virtual returns (uint256) {
            return 0;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.20;
    import { EIP712Domain } from "./EIP712Domain.sol";
    import { SignatureChecker } from "./utils/SignatureChecker.sol";
    import { MessageHashUtils } from "./utils/MessageHashUtils.sol";
    import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
    /**
     * @title EIP-3009
     * @notice Provide internal implementation for gas-abstracted transfers
     * @dev Contracts that inherit from this must wrap these with publicly
     * accessible functions, optionally adding modifiers where necessary
     */
    abstract contract EIP3009 is ERC20, EIP712Domain {
        // keccak256("TransferWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)")
        bytes32 
            public constant TRANSFER_WITH_AUTHORIZATION_TYPEHASH = 0x7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267;
        // keccak256("ReceiveWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)")
        bytes32
            public constant RECEIVE_WITH_AUTHORIZATION_TYPEHASH = 0xd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de8;
        // keccak256("CancelAuthorization(address authorizer,bytes32 nonce)")
        bytes32
            public constant CANCEL_AUTHORIZATION_TYPEHASH = 0x158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a1597429;
        /**
         * @dev authorizer address => nonce => bool (true if nonce is used)
         */
        mapping(address => mapping(bytes32 => bool)) private _authorizationStates;
        event AuthorizationUsed(address indexed authorizer, bytes32 indexed nonce);
        event AuthorizationCanceled(address indexed authorizer, bytes32 indexed nonce);
        /**
         * @notice Returns the state of an authorization
         * @dev Nonces are randomly generated 32-byte data unique to the authorizer's address
         * @param authorizer    Authorizer's address
         * @param nonce         Nonce of the authorization
         * @return True if the nonce is used
         */
        function authorizationState(address authorizer, bytes32 nonce) external view returns (bool) {
            return _authorizationStates[authorizer][nonce];
        }
        /**
         * @notice Execute a transfer with a signed authorization
         * @param from          Payer's address (Authorizer)
         * @param to            Payee's address
         * @param value         Amount to be transferred
         * @param validAfter    The time after which this is valid (unix time)
         * @param validBefore   The time before which this is valid (unix time)
         * @param nonce         Unique nonce
         * @param v             v of the signature
         * @param r             r of the signature
         * @param s             s of the signature
         */
        function _transferWithAuthorization(
            address from,
            address to,
            uint256 value,
            uint256 validAfter,
            uint256 validBefore,
            bytes32 nonce,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) internal {
            _transferWithAuthorization(
                from,
                to,
                value,
                validAfter,
                validBefore,
                nonce,
                abi.encodePacked(r, s, v)
            );
        }
        /**
         * @notice Execute a transfer with a signed authorization
         * @dev EOA wallet signatures should be packed in the order of r, s, v.
         * @param from          Payer's address (Authorizer)
         * @param to            Payee's address
         * @param value         Amount to be transferred
         * @param validAfter    The time after which this is valid (unix time)
         * @param validBefore   The time before which this is valid (unix time)
         * @param nonce         Unique nonce
         * @param signature     Signature byte array produced by an EOA wallet or a contract wallet
         */
        function _transferWithAuthorization(
            address from,
            address to,
            uint256 value,
            uint256 validAfter,
            uint256 validBefore,
            bytes32 nonce,
            bytes memory signature
        ) internal {
            _requireValidAuthorization(from, nonce, validAfter, validBefore);
            _requireValidSignature(
                from,
                keccak256(
                    abi.encode(
                        TRANSFER_WITH_AUTHORIZATION_TYPEHASH,
                        from,
                        to,
                        value,
                        validAfter,
                        validBefore,
                        nonce
                    )
                ),
                signature
            );
            _markAuthorizationAsUsed(from, nonce);
            _transfer(from, to, value);
        }
        /**
         * @notice Receive a transfer with a signed authorization from the payer
         * @dev This has an additional check to ensure that the payee's address
         * matches the caller of this function to prevent front-running attacks.
         * @param from          Payer's address (Authorizer)
         * @param to            Payee's address
         * @param value         Amount to be transferred
         * @param validAfter    The time after which this is valid (unix time)
         * @param validBefore   The time before which this is valid (unix time)
         * @param nonce         Unique nonce
         * @param v             v of the signature
         * @param r             r of the signature
         * @param s             s of the signature
         */
        function _receiveWithAuthorization(
            address from,
            address to,
            uint256 value,
            uint256 validAfter,
            uint256 validBefore,
            bytes32 nonce,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) internal {
            _receiveWithAuthorization(
                from,
                to,
                value,
                validAfter,
                validBefore,
                nonce,
                abi.encodePacked(r, s, v)
            );
        }
        /**
         * @notice Receive a transfer with a signed authorization from the payer
         * @dev This has an additional check to ensure that the payee's address
         * matches the caller of this function to prevent front-running attacks.
         * EOA wallet signatures should be packed in the order of r, s, v.
         * @param from          Payer's address (Authorizer)
         * @param to            Payee's address
         * @param value         Amount to be transferred
         * @param validAfter    The time after which this is valid (unix time)
         * @param validBefore   The time before which this is valid (unix time)
         * @param nonce         Unique nonce
         * @param signature     Signature byte array produced by an EOA wallet or a contract wallet
         */
        function _receiveWithAuthorization(
            address from,
            address to,
            uint256 value,
            uint256 validAfter,
            uint256 validBefore,
            bytes32 nonce,
            bytes memory signature
        ) internal {
            require(to == msg.sender, "Caller must be the payee");
            _requireValidAuthorization(from, nonce, validAfter, validBefore);
            _requireValidSignature(
                from,
                keccak256(
                    abi.encode(
                        RECEIVE_WITH_AUTHORIZATION_TYPEHASH,
                        from,
                        to,
                        value,
                        validAfter,
                        validBefore,
                        nonce
                    )
                ),
                signature
            );
            _markAuthorizationAsUsed(from, nonce);
            _transfer(from, to, value);
        }
        /**
         * @notice Attempt to cancel an authorization
         * @param authorizer    Authorizer's address
         * @param nonce         Nonce of the authorization
         * @param v             v of the signature
         * @param r             r of the signature
         * @param s             s of the signature
         */
        function _cancelAuthorization(
            address authorizer,
            bytes32 nonce,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) internal {
            
            _cancelAuthorization(authorizer, nonce, abi.encodePacked(r, s, v));
        }
        /**
         * @notice Attempt to cancel an authorization
         * @dev EOA wallet signatures should be packed in the order of r, s, v.
         * @param authorizer    Authorizer's address
         * @param nonce         Nonce of the authorization
         * @param signature     Signature byte array produced by an EOA wallet or a contract wallet
         */
        function _cancelAuthorization(address authorizer, bytes32 nonce, bytes memory signature) internal {
            _requireUnusedAuthorization(authorizer, nonce);
            _requireValidSignature(
                authorizer,
                keccak256(
                    abi.encode(CANCEL_AUTHORIZATION_TYPEHASH, authorizer, nonce)
                ),
                signature
            );
            _authorizationStates[authorizer][nonce] = true;
            emit AuthorizationCanceled(authorizer, nonce);
        }
        /**
         * @notice Validates that signature against input data struct
         * @param signer        Signer's address
         * @param dataHash      Hash of encoded data struct
         * @param signature     Signature byte array produced by an EOA wallet or a contract wallet
         */
        function _requireValidSignature(address signer, bytes32 dataHash, bytes memory signature) private view {
            require(
                SignatureChecker.isValidSignatureNow(signer, MessageHashUtils.toTypedDataHash(_domainSeparator(), dataHash), signature), 
                "Invalid signature");
        }
        /**
         * @notice Check that an authorization is unused
         * @param authorizer    Authorizer's address
         * @param nonce         Nonce of the authorization
         */
        function _requireUnusedAuthorization(address authorizer, bytes32 nonce) private view {
            require(!_authorizationStates[authorizer][nonce], "Authorization is used or canceled");
        }
        /**
         * @notice Check that authorization is valid
         * @param authorizer    Authorizer's address
         * @param nonce         Nonce of the authorization
         * @param validAfter    The time after which this is valid (unix time)
         * @param validBefore   The time before which this is valid (unix time)
         */
        function _requireValidAuthorization(
            address authorizer,
            bytes32 nonce,
            uint256 validAfter,
            uint256 validBefore
        ) private view {
            require(block.timestamp > validAfter, "Authorization is not yet valid");
            require(block.timestamp < validBefore, "Authorization is expired");
            
            _requireUnusedAuthorization(authorizer, nonce);
        }
        /**
         * @notice Mark an authorization as used
         * @param authorizer    Authorizer's address
         * @param nonce         Nonce of the authorization
         */
        function _markAuthorizationAsUsed(address authorizer, bytes32 nonce) private {
            _authorizationStates[authorizer][nonce] = true;
            emit AuthorizationUsed(authorizer, nonce);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.20;
    // solhint-disable func-name-mixedcase
    /**
     * @title EIP712 Domain
     */
    contract EIP712Domain {
        
        bytes32 internal _DOMAIN_SEPARATOR;
        // to prevent signature replay attacks in the event the chain forks
        // fork chain would have different chain id to original chain
        uint256 internal immutable _DEPLOYMENT_CHAINID;
        /**
         * @notice Get the EIP712 Domain Separator.
         * @return The bytes32 EIP712 domain separator.
         */
        function DOMAIN_SEPARATOR() external view returns (bytes32) {
            return _domainSeparator();
        }
        /**
         * @dev Internal method to get the EIP712 Domain Separator.
         * @return The bytes32 EIP712 domain separator.
         */
        function _domainSeparator() internal virtual view returns (bytes32) {
            return _DOMAIN_SEPARATOR;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.20;
    /**
     * @dev Interface of the ERC1271 standard signature validation method for
     * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
     */
    interface IERC1271 {
        /**
         * @dev Should return whether the signature provided is valid for the provided data
         * @param hash          Hash of the data to be signed
         * @param signature     Signature byte array associated with the provided data hash
         * @return magicValue   bytes4 magic value 0x1626ba7e when function passes
         */
        function isValidSignature(bytes32 hash, bytes memory signature)
            external
            view
            returns (bytes4 magicValue);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.22;
    import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
    import { EIP3009 } from "./EIP3009.sol";
    import { EIP712 } from "./utils/EIP712.sol";
    contract MocaToken is EIP3009 {
        string internal constant _version = "v1";
        constructor(string memory name, string memory symbol, address treasury) ERC20(name, symbol) {
            
            _DEPLOYMENT_CHAINID = block.chainid; 
            _DOMAIN_SEPARATOR = EIP712.makeDomainSeparator(name, _version);
            _mint(treasury, (8_888_888_888 * 1e18));
        }
        
        /*//////////////////////////////////////////////////////////////
                                     EIP721
        //////////////////////////////////////////////////////////////*/
        function _domainSeparator() internal override view returns (bytes32) {
            return block.chainid == _DEPLOYMENT_CHAINID ? _DOMAIN_SEPARATOR : EIP712.makeDomainSeparator(name(), _version);
        }
        /**
         * @notice Fn caller can only burn their own tokens
         * @param amount          Amount of tokens to burn 
         */
        function burn(uint256 amount) external {
            _burn(msg.sender, amount);
        }
        /**
         * @notice Execute a transfer with a signed authorization
         * @param from          Payer's address (Authorizer)
         * @param to            Payee's address
         * @param value         Amount to be transferred
         * @param validAfter    The time after which this is valid (unix time)
         * @param validBefore   The time before which this is valid (unix time)
         * @param nonce         Unique nonce
         * @param v             v of the signature
         * @param r             r of the signature
         * @param s             s of the signature
         */
        function transferWithAuthorization(address from, address to, uint256 value, uint256 validAfter, uint256 validBefore, bytes32 nonce, uint8 v, bytes32 r, bytes32 s) external {
            _transferWithAuthorization(
                from,
                to,
                value,
                validAfter,
                validBefore,
                nonce,
                v,
                r,
                s
            );
        }
        /**
         * @notice Attempt to cancel an authorization
         * @dev Works only if the authorization is not yet used.
         * EOA wallet signatures should be packed in the order of r, s, v.
         * @param authorizer    Authorizer's address
         * @param nonce         Nonce of the authorization
         * @param v             v of the signature
         * @param r             r of the signature
         * @param s             s of the signature
         */
        function cancelAuthorization(address authorizer, bytes32 nonce, uint8 v, bytes32 r, bytes32 s) external {
            _cancelAuthorization(authorizer, nonce, v, r, s);
        }
        /**
         * @notice Receive a transfer with a signed authorization from the payer
         * @dev This has an additional check to ensure that the payee's address
         * matches the caller of this function to prevent front-running attacks.
         * EOA wallet signatures should be packed in the order of r, s, v.
         * @param from          Payer's address (Authorizer)
         * @param to            Payee's address
         * @param value         Amount to be transferred
         * @param validAfter    The time after which this is valid (unix time)
         * @param validBefore   The time before which this is valid (unix time)
         * @param nonce         Unique nonce
         * @param v             v of the signature
         * @param r             r of the signature
         * @param s             s of the signature
         */
        function receiveWithAuthorization(
            address from,
            address to,
            uint256 value,
            uint256 validAfter,
            uint256 validBefore,
            bytes32 nonce,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) external {
            _receiveWithAuthorization(
                from,
                to,
                value,
                validAfter,
                validBefore,
                nonce,
                v,
                r,
                s
            );
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.20;
    /**
     * @title ECRecover
     * @notice A library that provides a safe ECDSA recovery function
     */
    library ECRecover {
        /**
         * @notice Recover signer's address from a signed message
         * @dev Adapted from: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/65e4ffde586ec89af3b7e9140bdc9235d1254853/contracts/cryptography/ECDSA.sol
         * Modifications: Accept v, r, and s as separate arguments
         * @param digest    Keccak-256 hash digest of the signed message
         * @param v         v of the signature
         * @param r         r of the signature
         * @param s         s of the signature
         * @return Signer address
         */
        function recover(
            bytes32 digest,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) internal pure returns (address) {
            // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
            // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
            // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
            // signatures from current libraries generate a unique signature with an s-value in the lower half order.
            //
            // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
            // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
            // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
            // these malleable signatures as well.
            if (
                uint256(s) >
                0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0
            ) {
                revert("ECRecover: invalid signature 's' value");
            }
            if (v != 27 && v != 28) {
                revert("ECRecover: invalid signature 'v' value");
            }
            // If the signature is valid (and not malleable), return the signer address
            address signer = ecrecover(digest, v, r, s);
            require(signer != address(0), "ECRecover: invalid signature");
            return signer;
        }
        /**
         * @notice Recover signer's address from a signed message
         * @dev Adapted from: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/0053ee040a7ff1dbc39691c9e67a69f564930a88/contracts/utils/cryptography/ECDSA.sol
         * @param digest    Keccak-256 hash digest of the signed message
         * @param signature Signature byte array associated with hash
         * @return Signer address
         */
        function recover(bytes32 digest, bytes memory signature)
            internal
            pure
            returns (address)
        {
            require(signature.length == 65, "ECRecover: invalid signature length");
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return recover(digest, v, r, s);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.20;
    /**
     * @title EIP712
     * @notice A library that provides EIP712 helper functions
     */
    library EIP712 {
        /**
         * @notice Make EIP712 domain separator
         * @param name      Contract name
         * @param version   Contract version
         * @param chainId   Blockchain ID
         * @return Domain separator
         */
        function makeDomainSeparator(
            string memory name,
            string memory version,
            uint256 chainId
        ) internal view returns (bytes32) {
            return
                keccak256(
                    abi.encode(
                        // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
                        0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f,
                        keccak256(bytes(name)),
                        keccak256(bytes(version)),
                        chainId,
                        address(this)
                    )
                );
        }
        /**
         * @notice Make EIP712 domain separator
         * @param name      Contract name
         * @param version   Contract version
         * @return Domain separator
         */
        function makeDomainSeparator(string memory name, string memory version)
            internal
            view
            returns (bytes32)
        {
            uint256 chainId;
            assembly {
                chainId := chainid()
            }
            return makeDomainSeparator(name, version, chainId);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.20;
    /**
     * @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-712 typed data (EIP-191 version `0x01`).
         * Adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/21bb89ef5bfc789b9333eb05e3ba2b7b284ac77c/contracts/utils/cryptography/MessageHashUtils.sol
         *
         * 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.
         *
         * @param domainSeparator    Domain separator
         * @param structHash         Hashed EIP-712 data struct
         * @return digest            The keccak256 digest of an EIP-712 typed data
         */
        function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash)
            internal
            pure
            returns (bytes32 digest)
        {
            assembly {
                let ptr := mload(0x40)
                mstore(ptr, "\\x19\\x01")
                mstore(add(ptr, 0x02), domainSeparator)
                mstore(add(ptr, 0x22), structHash)
                digest := keccak256(ptr, 0x42)
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.20;
    import { ECRecover } from "./ECRecover.sol";
    import { IERC1271 } from "./../IERC1271.sol";
    /**
     * @dev Signature verification helper that can be used instead of `ECRecover.recover` to seamlessly support both ECDSA
     * signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets.
     *
     * Adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/21bb89ef5bfc789b9333eb05e3ba2b7b284ac77c/contracts/utils/cryptography/SignatureChecker.sol
     */
    library SignatureChecker {
        /**
         * @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the
         * signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECRecover.recover`.
         * @param signer        Address of the claimed signer
         * @param digest        Keccak-256 hash digest of the signed message
         * @param signature     Signature byte array associated with hash
         */
        function isValidSignatureNow(address signer, bytes32 digest, bytes memory signature) internal view returns (bool) {
            if (!isContract(signer)) {
                return ECRecover.recover(digest, signature) == signer;
            }
            return isValidERC1271SignatureNow(signer, digest, signature);
        }
        /**
         * @dev Checks if a signature is valid for a given signer and data hash. The signature is validated
         * against the signer smart contract using ERC1271.
         * @param signer        Address of the claimed signer
         * @param digest        Keccak-256 hash digest of the signed message
         * @param signature     Signature byte array associated with hash
         *
         * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
         * change through time. It could return true at block N and false at block N+1 (or the opposite).
         */
        function isValidERC1271SignatureNow(address signer, bytes32 digest, bytes memory signature) internal view returns (bool) {
            (bool success, bytes memory result) = signer.staticcall(
                abi.encodeWithSelector(
                    IERC1271.isValidSignature.selector,
                    digest,
                    signature
                )
            );
            return (success && result.length >= 32 && abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.selector));
        }
        /**
         * @dev Checks if the input address is a smart contract.
         */
        function isContract(address addr) internal view returns (bool) {
            uint256 size;
            assembly {
                size := extcodesize(addr)
            }
            return size > 0;
        }
    }