ETH Price: $2,430.05 (-0.09%)
Gas: 1.77 Gwei

Transaction Decoder

Block:
22788453 at Jun-26-2025 11:55:23 AM +UTC
Transaction Fee:
0.000178512915904542 ETH $0.43
Gas Used:
134,143 Gas / 1.330765794 Gwei

Emitted Events:

186 EntryPoint.Deposited( account=0x4037ebad5b945b3dc018e52f94de24626b6aab7d, totalDeposit=251139208981452 )
187 EntryPoint.BeforeExecution( )
188 EntryPoint.UserOperationEvent( userOpHash=5B7580589D0605F62843ACD1F972B438CF469A3AFCCA882F01B6CB357D86710F, sender=0x4037ebad5b945b3dc018e52f94de24626b6aab7d, paymaster=0x00000000...000000000, nonce=11353020751406791111577014220896730740983999252662705193528039059414428155904, success=False, actualGasCost=182951021238200, actualGasUsed=152443 )

Account State Difference:

  Address   Before After State Difference Code
0x00000000...6f37da032
(Entry Point 0.7.0)
111.139833851007966624 Eth111.139839659664879839 Eth0.000005808656913215
0x4037EBAD...26b6AAB7D
0.002934898051986957 Eth
Nonce: 3
0.002746138373835542 Eth
Nonce: 4
0.000188759678151415
0x4337009B...Aa065187E
(Pimlico: ERC-4337 Bundler 9)
0.279542040581488714 Eth
Nonce: 1181
0.279546478686822372 Eth
Nonce: 1182
0.000004438105333658
(BuilderNet)
81.680163736401608309 Eth81.680188303135194651 Eth0.000024566733586342

Execution Trace

EntryPoint.handleOps( ops=, beneficiary=0x4337009Be43c7ECc9bfbDe0F1780553Aa065187E )
  • 0x4037ebad5b945b3dc018e52f94de24626b6aab7d.19822f7c( )
    • K1MeeValidator.validateUserOp( userOp=[{name:sender, type:address, order:1, indexed:false, value:0x4037EBAD5b945b3dC018E52f94DE24626b6AAB7D, valueString:0x4037EBAD5b945b3dC018E52f94DE24626b6AAB7D}, {name:nonce, type:uint256, order:2, indexed:false, value:11353020751406791111577014220896730740983999252662705193528039059414428155904, valueString:11353020751406791111577014220896730740983999252662705193528039059414428155904}, {name:initCode, type:bytes, order:3, indexed:false, value:0x, valueString:0x}, {name:callData, type:bytes, order:4, indexed:false, value:0xE9AE5C5300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000003414AB39C8D18C50566D2AE50DF781BEFB14604003000000000000000000000000000000000000000000000000000A534AAF400400000000000000000000000000, valueString:0xE9AE5C5300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000003414AB39C8D18C50566D2AE50DF781BEFB14604003000000000000000000000000000000000000000000000000000A534AAF400400000000000000000000000000}, {name:accountGasLimits, type:bytes32, order:5, indexed:false, value:0000000000000000000000000001320B00000000000000000000000000004623, valueString:0000000000000000000000000001320B00000000000000000000000000004623}, {name:preVerificationGas, type:uint256, order:6, indexed:false, value:75694, valueString:75694}, {name:gasFees, type:bytes32, order:7, indexed:false, value:0000000000000000000000000321162000000000000000000000000057080DE5, valueString:0000000000000000000000000321162000000000000000000000000057080DE5}, {name:paymasterAndData, type:bytes, order:8, indexed:false, value:0x, valueString:0x}, {name:signature, type:bytes, order:9, indexed:false, value:0x10E6ACF9657BE7A90144778251FA3EFA217706D1BD48765553255DABE3F4F8D3037DC1656B71E815BE85008BAE44AF7B2D10D2FDEFE28099BC1F56389866BB5F1C, valueString:0x10E6ACF9657BE7A90144778251FA3EFA217706D1BD48765553255DABE3F4F8D3037DC1656B71E815BE85008BAE44AF7B2D10D2FDEFE28099BC1F56389866BB5F1C}], userOpHash=5B7580589D0605F62843ACD1F972B438CF469A3AFCCA882F01B6CB357D86710F ) => ( 0 )
      • Null: 0x000...001.5b758058( )
      • Null: 0x000...001.c9c71ed4( )
      • ETH 0.000188759678151415 EntryPoint.CALL( )
      • EntryPoint.innerHandleOp( callData=0xE9AE5C5300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000003414AB39C8D18C50566D2AE50DF781BEFB14604003000000000000000000000000000000000000000000000000000A534AAF400400000000000000000000000000, opInfo=[{name:mUserOp, type:tuple, order:1, indexed:false, value:[{name:sender, type:address, order:1, indexed:false, value:0x4037EBAD5b945b3dC018E52f94DE24626b6AAB7D, valueString:0x4037EBAD5b945b3dC018E52f94DE24626b6AAB7D}, {name:nonce, type:uint256, order:2, indexed:false, value:11353020751406791111577014220896730740983999252662705193528039059414428155904, valueString:11353020751406791111577014220896730740983999252662705193528039059414428155904}, {name:verificationGasLimit, type:uint256, order:3, indexed:false, value:78347, valueString:78347}, {name:callGasLimit, type:uint256, order:4, indexed:false, value:17955, valueString:17955}, {name:paymasterVerificationGasLimit, type:uint256, order:5, indexed:false, value:0, valueString:0}, {name:paymasterPostOpGasLimit, type:uint256, order:6, indexed:false, value:0, valueString:0}, {name:preVerificationGas, type:uint256, order:7, indexed:false, value:75694, valueString:75694}, {name:paymaster, type:address, order:8, indexed:false, value:0x0000000000000000000000000000000000000000, valueString:0x0000000000000000000000000000000000000000}, {name:maxFeePerGas, type:uint256, order:9, indexed:false, value:1460145637, valueString:1460145637}, {name:maxPriorityFeePerGas, type:uint256, order:10, indexed:false, value:52500000, valueString:52500000}], valueString:[{name:sender, type:address, order:1, indexed:false, value:0x4037EBAD5b945b3dC018E52f94DE24626b6AAB7D, valueString:0x4037EBAD5b945b3dC018E52f94DE24626b6AAB7D}, {name:nonce, type:uint256, order:2, indexed:false, value:11353020751406791111577014220896730740983999252662705193528039059414428155904, valueString:11353020751406791111577014220896730740983999252662705193528039059414428155904}, {name:verificationGasLimit, type:uint256, order:3, indexed:false, value:78347, valueString:78347}, {name:callGasLimit, type:uint256, order:4, indexed:false, value:17955, valueString:17955}, {name:paymasterVerificationGasLimit, type:uint256, order:5, indexed:false, value:0, valueString:0}, {name:paymasterPostOpGasLimit, type:uint256, order:6, indexed:false, value:0, valueString:0}, {name:preVerificationGas, type:uint256, order:7, indexed:false, value:75694, valueString:75694}, {name:paymaster, type:address, order:8, indexed:false, value:0x0000000000000000000000000000000000000000, valueString:0x0000000000000000000000000000000000000000}, {name:maxFeePerGas, type:uint256, order:9, indexed:false, value:1460145637, valueString:1460145637}, {name:maxPriorityFeePerGas, type:uint256, order:10, indexed:false, value:52500000, valueString:52500000}]}, {name:userOpHash, type:bytes32, order:2, indexed:false, value:5B7580589D0605F62843ACD1F972B438CF469A3AFCCA882F01B6CB357D86710F, valueString:5B7580589D0605F62843ACD1F972B438CF469A3AFCCA882F01B6CB357D86710F}, {name:prefund, type:uint256, order:3, indexed:false, value:251139208981452, valueString:251139208981452}, {name:contextOffset, type:uint256, order:4, indexed:false, value:96, valueString:96}, {name:preOpGas, type:uint256, order:5, indexed:false, value:138376, valueString:138376}], context=0x ) => ( actualGasCost=182951021238200 )
        • 0x4037ebad5b945b3dc018e52f94de24626b6aab7d.e9ae5c53( )
          • ETH 0.00290633 0x14ab39c8d18c50566d2ae50df781befb14604003.CALL( )
          • ETH 0.0001829510212382 Pimlico: ERC-4337 Bundler 9.CALL( )
            File 1 of 2: EntryPoint
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)
            pragma solidity ^0.8.20;
            import {IERC165} from "./IERC165.sol";
            /**
             * @dev Implementation of the {IERC165} interface.
             *
             * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
             * for the additional interface id that will be supported. For example:
             *
             * ```solidity
             * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
             *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
             * }
             * ```
             */
            abstract contract ERC165 is IERC165 {
                /**
                 * @dev See {IERC165-supportsInterface}.
                 */
                function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
                    return interfaceId == type(IERC165).interfaceId;
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
            pragma solidity ^0.8.20;
            /**
             * @dev Interface of the ERC165 standard, as defined in the
             * https://eips.ethereum.org/EIPS/eip-165[EIP].
             *
             * Implementers can declare support of contract interfaces, which can then be
             * queried by others ({ERC165Checker}).
             *
             * For an implementation, see {ERC165}.
             */
            interface IERC165 {
                /**
                 * @dev Returns true if this contract implements the interface defined by
                 * `interfaceId`. See the corresponding
                 * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
                 * to learn more about how these ids are created.
                 *
                 * This function call must use less than 30 000 gas.
                 */
                function supportsInterface(bytes4 interfaceId) external view returns (bool);
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)
            pragma solidity ^0.8.20;
            /**
             * @dev Contract module that helps prevent reentrant calls to a function.
             *
             * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
             * available, which can be applied to functions to make sure there are no nested
             * (reentrant) calls to them.
             *
             * Note that because there is a single `nonReentrant` guard, functions marked as
             * `nonReentrant` may not call one another. This can be worked around by making
             * those functions `private`, and then adding `external` `nonReentrant` entry
             * points to them.
             *
             * TIP: If you would like to learn more about reentrancy and alternative ways
             * to protect against it, check out our blog post
             * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
             */
            abstract contract ReentrancyGuard {
                // Booleans are more expensive than uint256 or any type that takes up a full
                // word because each write operation emits an extra SLOAD to first read the
                // slot's contents, replace the bits taken up by the boolean, and then write
                // back. This is the compiler's defense against contract upgrades and
                // pointer aliasing, and it cannot be disabled.
                // The values being non-zero value makes deployment a bit more expensive,
                // but in exchange the refund on every call to nonReentrant will be lower in
                // amount. Since refunds are capped to a percentage of the total
                // transaction's gas, it is best to keep them low in cases like this one, to
                // increase the likelihood of the full refund coming into effect.
                uint256 private constant NOT_ENTERED = 1;
                uint256 private constant ENTERED = 2;
                uint256 private _status;
                /**
                 * @dev Unauthorized reentrant call.
                 */
                error ReentrancyGuardReentrantCall();
                constructor() {
                    _status = NOT_ENTERED;
                }
                /**
                 * @dev Prevents a contract from calling itself, directly or indirectly.
                 * Calling a `nonReentrant` function from another `nonReentrant`
                 * function is not supported. It is possible to prevent this from happening
                 * by making the `nonReentrant` function external, and making it call a
                 * `private` function that does the actual work.
                 */
                modifier nonReentrant() {
                    _nonReentrantBefore();
                    _;
                    _nonReentrantAfter();
                }
                function _nonReentrantBefore() private {
                    // On the first call to nonReentrant, _status will be NOT_ENTERED
                    if (_status == ENTERED) {
                        revert ReentrancyGuardReentrantCall();
                    }
                    // Any calls to nonReentrant after this point will fail
                    _status = ENTERED;
                }
                function _nonReentrantAfter() private {
                    // By storing the original value once again, a refund is triggered (see
                    // https://eips.ethereum.org/EIPS/eip-2200)
                    _status = NOT_ENTERED;
                }
                /**
                 * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
                 * `nonReentrant` function in the call stack.
                 */
                function _reentrancyGuardEntered() internal view returns (bool) {
                    return _status == ENTERED;
                }
            }
            // SPDX-License-Identifier: GPL-3.0
            pragma solidity ^0.8.23;
            /* solhint-disable avoid-low-level-calls */
            /* solhint-disable no-inline-assembly */
            import "../interfaces/IAccount.sol";
            import "../interfaces/IAccountExecute.sol";
            import "../interfaces/IPaymaster.sol";
            import "../interfaces/IEntryPoint.sol";
            import "../utils/Exec.sol";
            import "./StakeManager.sol";
            import "./SenderCreator.sol";
            import "./Helpers.sol";
            import "./NonceManager.sol";
            import "./UserOperationLib.sol";
            import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
            import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
            /*
             * Account-Abstraction (EIP-4337) singleton EntryPoint implementation.
             * Only one instance required on each chain.
             */
            /// @custom:security-contact https://bounty.ethereum.org
            contract EntryPoint is IEntryPoint, StakeManager, NonceManager, ReentrancyGuard, ERC165 {
                using UserOperationLib for PackedUserOperation;
                SenderCreator private immutable _senderCreator = new SenderCreator();
                function senderCreator() internal view virtual returns (SenderCreator) {
                    return _senderCreator;
                }
                //compensate for innerHandleOps' emit message and deposit refund.
                // allow some slack for future gas price changes.
                uint256 private constant INNER_GAS_OVERHEAD = 10000;
                // Marker for inner call revert on out of gas
                bytes32 private constant INNER_OUT_OF_GAS = hex"deaddead";
                bytes32 private constant INNER_REVERT_LOW_PREFUND = hex"deadaa51";
                uint256 private constant REVERT_REASON_MAX_LEN = 2048;
                uint256 private constant PENALTY_PERCENT = 10;
                /// @inheritdoc IERC165
                function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                    // note: solidity "type(IEntryPoint).interfaceId" is without inherited methods but we want to check everything
                    return interfaceId == (type(IEntryPoint).interfaceId ^ type(IStakeManager).interfaceId ^ type(INonceManager).interfaceId) ||
                        interfaceId == type(IEntryPoint).interfaceId ||
                        interfaceId == type(IStakeManager).interfaceId ||
                        interfaceId == type(INonceManager).interfaceId ||
                        super.supportsInterface(interfaceId);
                }
                /**
                 * Compensate the caller's beneficiary address with the collected fees of all UserOperations.
                 * @param beneficiary - The address to receive the fees.
                 * @param amount      - Amount to transfer.
                 */
                function _compensate(address payable beneficiary, uint256 amount) internal {
                    require(beneficiary != address(0), "AA90 invalid beneficiary");
                    (bool success, ) = beneficiary.call{value: amount}("");
                    require(success, "AA91 failed send to beneficiary");
                }
                /**
                 * Execute a user operation.
                 * @param opIndex    - Index into the opInfo array.
                 * @param userOp     - The userOp to execute.
                 * @param opInfo     - The opInfo filled by validatePrepayment for this userOp.
                 * @return collected - The total amount this userOp paid.
                 */
                function _executeUserOp(
                    uint256 opIndex,
                    PackedUserOperation calldata userOp,
                    UserOpInfo memory opInfo
                )
                internal
                returns
                (uint256 collected) {
                    uint256 preGas = gasleft();
                    bytes memory context = getMemoryBytesFromOffset(opInfo.contextOffset);
                    bool success;
                    {
                        uint256 saveFreePtr;
                        assembly ("memory-safe") {
                            saveFreePtr := mload(0x40)
                        }
                        bytes calldata callData = userOp.callData;
                        bytes memory innerCall;
                        bytes4 methodSig;
                        assembly {
                            let len := callData.length
                            if gt(len, 3) {
                                methodSig := calldataload(callData.offset)
                            }
                        }
                        if (methodSig == IAccountExecute.executeUserOp.selector) {
                            bytes memory executeUserOp = abi.encodeCall(IAccountExecute.executeUserOp, (userOp, opInfo.userOpHash));
                            innerCall = abi.encodeCall(this.innerHandleOp, (executeUserOp, opInfo, context));
                        } else
                        {
                            innerCall = abi.encodeCall(this.innerHandleOp, (callData, opInfo, context));
                        }
                        assembly ("memory-safe") {
                            success := call(gas(), address(), 0, add(innerCall, 0x20), mload(innerCall), 0, 32)
                            collected := mload(0)
                            mstore(0x40, saveFreePtr)
                        }
                    }
                    if (!success) {
                        bytes32 innerRevertCode;
                        assembly ("memory-safe") {
                            let len := returndatasize()
                            if eq(32,len) {
                                returndatacopy(0, 0, 32)
                                innerRevertCode := mload(0)
                            }
                        }
                        if (innerRevertCode == INNER_OUT_OF_GAS) {
                            // handleOps was called with gas limit too low. abort entire bundle.
                            //can only be caused by bundler (leaving not enough gas for inner call)
                            revert FailedOp(opIndex, "AA95 out of gas");
                        } else if (innerRevertCode == INNER_REVERT_LOW_PREFUND) {
                            // innerCall reverted on prefund too low. treat entire prefund as "gas cost"
                            uint256 actualGas = preGas - gasleft() + opInfo.preOpGas;
                            uint256 actualGasCost = opInfo.prefund;
                            emitPrefundTooLow(opInfo);
                            emitUserOperationEvent(opInfo, false, actualGasCost, actualGas);
                            collected = actualGasCost;
                        } else {
                            emit PostOpRevertReason(
                                opInfo.userOpHash,
                                opInfo.mUserOp.sender,
                                opInfo.mUserOp.nonce,
                                Exec.getReturnData(REVERT_REASON_MAX_LEN)
                            );
                            uint256 actualGas = preGas - gasleft() + opInfo.preOpGas;
                            collected = _postExecution(
                                IPaymaster.PostOpMode.postOpReverted,
                                opInfo,
                                context,
                                actualGas
                            );
                        }
                    }
                }
                function emitUserOperationEvent(UserOpInfo memory opInfo, bool success, uint256 actualGasCost, uint256 actualGas) internal virtual {
                    emit UserOperationEvent(
                        opInfo.userOpHash,
                        opInfo.mUserOp.sender,
                        opInfo.mUserOp.paymaster,
                        opInfo.mUserOp.nonce,
                        success,
                        actualGasCost,
                        actualGas
                    );
                }
                function emitPrefundTooLow(UserOpInfo memory opInfo) internal virtual {
                    emit UserOperationPrefundTooLow(
                        opInfo.userOpHash,
                        opInfo.mUserOp.sender,
                        opInfo.mUserOp.nonce
                    );
                }
                /// @inheritdoc IEntryPoint
                function handleOps(
                    PackedUserOperation[] calldata ops,
                    address payable beneficiary
                ) public nonReentrant {
                    uint256 opslen = ops.length;
                    UserOpInfo[] memory opInfos = new UserOpInfo[](opslen);
                    unchecked {
                        for (uint256 i = 0; i < opslen; i++) {
                            UserOpInfo memory opInfo = opInfos[i];
                            (
                                uint256 validationData,
                                uint256 pmValidationData
                            ) = _validatePrepayment(i, ops[i], opInfo);
                            _validateAccountAndPaymasterValidationData(
                                i,
                                validationData,
                                pmValidationData,
                                address(0)
                            );
                        }
                        uint256 collected = 0;
                        emit BeforeExecution();
                        for (uint256 i = 0; i < opslen; i++) {
                            collected += _executeUserOp(i, ops[i], opInfos[i]);
                        }
                        _compensate(beneficiary, collected);
                    }
                }
                /// @inheritdoc IEntryPoint
                function handleAggregatedOps(
                    UserOpsPerAggregator[] calldata opsPerAggregator,
                    address payable beneficiary
                ) public nonReentrant {
                    uint256 opasLen = opsPerAggregator.length;
                    uint256 totalOps = 0;
                    for (uint256 i = 0; i < opasLen; i++) {
                        UserOpsPerAggregator calldata opa = opsPerAggregator[i];
                        PackedUserOperation[] calldata ops = opa.userOps;
                        IAggregator aggregator = opa.aggregator;
                        //address(1) is special marker of "signature error"
                        require(
                            address(aggregator) != address(1),
                            "AA96 invalid aggregator"
                        );
                        if (address(aggregator) != address(0)) {
                            // solhint-disable-next-line no-empty-blocks
                            try aggregator.validateSignatures(ops, opa.signature) {} catch {
                                revert SignatureValidationFailed(address(aggregator));
                            }
                        }
                        totalOps += ops.length;
                    }
                    UserOpInfo[] memory opInfos = new UserOpInfo[](totalOps);
                    uint256 opIndex = 0;
                    for (uint256 a = 0; a < opasLen; a++) {
                        UserOpsPerAggregator calldata opa = opsPerAggregator[a];
                        PackedUserOperation[] calldata ops = opa.userOps;
                        IAggregator aggregator = opa.aggregator;
                        uint256 opslen = ops.length;
                        for (uint256 i = 0; i < opslen; i++) {
                            UserOpInfo memory opInfo = opInfos[opIndex];
                            (
                                uint256 validationData,
                                uint256 paymasterValidationData
                            ) = _validatePrepayment(opIndex, ops[i], opInfo);
                            _validateAccountAndPaymasterValidationData(
                                i,
                                validationData,
                                paymasterValidationData,
                                address(aggregator)
                            );
                            opIndex++;
                        }
                    }
                    emit BeforeExecution();
                    uint256 collected = 0;
                    opIndex = 0;
                    for (uint256 a = 0; a < opasLen; a++) {
                        UserOpsPerAggregator calldata opa = opsPerAggregator[a];
                        emit SignatureAggregatorChanged(address(opa.aggregator));
                        PackedUserOperation[] calldata ops = opa.userOps;
                        uint256 opslen = ops.length;
                        for (uint256 i = 0; i < opslen; i++) {
                            collected += _executeUserOp(opIndex, ops[i], opInfos[opIndex]);
                            opIndex++;
                        }
                    }
                    emit SignatureAggregatorChanged(address(0));
                    _compensate(beneficiary, collected);
                }
                /**
                 * A memory copy of UserOp static fields only.
                 * Excluding: callData, initCode and signature. Replacing paymasterAndData with paymaster.
                 */
                struct MemoryUserOp {
                    address sender;
                    uint256 nonce;
                    uint256 verificationGasLimit;
                    uint256 callGasLimit;
                    uint256 paymasterVerificationGasLimit;
                    uint256 paymasterPostOpGasLimit;
                    uint256 preVerificationGas;
                    address paymaster;
                    uint256 maxFeePerGas;
                    uint256 maxPriorityFeePerGas;
                }
                struct UserOpInfo {
                    MemoryUserOp mUserOp;
                    bytes32 userOpHash;
                    uint256 prefund;
                    uint256 contextOffset;
                    uint256 preOpGas;
                }
                /**
                 * Inner function to handle a UserOperation.
                 * Must be declared "external" to open a call context, but it can only be called by handleOps.
                 * @param callData - The callData to execute.
                 * @param opInfo   - The UserOpInfo struct.
                 * @param context  - The context bytes.
                 * @return actualGasCost - the actual cost in eth this UserOperation paid for gas
                 */
                function innerHandleOp(
                    bytes memory callData,
                    UserOpInfo memory opInfo,
                    bytes calldata context
                ) external returns (uint256 actualGasCost) {
                    uint256 preGas = gasleft();
                    require(msg.sender == address(this), "AA92 internal call only");
                    MemoryUserOp memory mUserOp = opInfo.mUserOp;
                    uint256 callGasLimit = mUserOp.callGasLimit;
                    unchecked {
                        // handleOps was called with gas limit too low. abort entire bundle.
                        if (
                            gasleft() * 63 / 64 <
                            callGasLimit +
                            mUserOp.paymasterPostOpGasLimit +
                            INNER_GAS_OVERHEAD
                        ) {
                            assembly ("memory-safe") {
                                mstore(0, INNER_OUT_OF_GAS)
                                revert(0, 32)
                            }
                        }
                    }
                    IPaymaster.PostOpMode mode = IPaymaster.PostOpMode.opSucceeded;
                    if (callData.length > 0) {
                        bool success = Exec.call(mUserOp.sender, 0, callData, callGasLimit);
                        if (!success) {
                            bytes memory result = Exec.getReturnData(REVERT_REASON_MAX_LEN);
                            if (result.length > 0) {
                                emit UserOperationRevertReason(
                                    opInfo.userOpHash,
                                    mUserOp.sender,
                                    mUserOp.nonce,
                                    result
                                );
                            }
                            mode = IPaymaster.PostOpMode.opReverted;
                        }
                    }
                    unchecked {
                        uint256 actualGas = preGas - gasleft() + opInfo.preOpGas;
                        return _postExecution(mode, opInfo, context, actualGas);
                    }
                }
                /// @inheritdoc IEntryPoint
                function getUserOpHash(
                    PackedUserOperation calldata userOp
                ) public view returns (bytes32) {
                    return
                        keccak256(abi.encode(userOp.hash(), address(this), block.chainid));
                }
                /**
                 * Copy general fields from userOp into the memory opInfo structure.
                 * @param userOp  - The user operation.
                 * @param mUserOp - The memory user operation.
                 */
                function _copyUserOpToMemory(
                    PackedUserOperation calldata userOp,
                    MemoryUserOp memory mUserOp
                ) internal pure {
                    mUserOp.sender = userOp.sender;
                    mUserOp.nonce = userOp.nonce;
                    (mUserOp.verificationGasLimit, mUserOp.callGasLimit) = UserOperationLib.unpackUints(userOp.accountGasLimits);
                    mUserOp.preVerificationGas = userOp.preVerificationGas;
                    (mUserOp.maxPriorityFeePerGas, mUserOp.maxFeePerGas) = UserOperationLib.unpackUints(userOp.gasFees);
                    bytes calldata paymasterAndData = userOp.paymasterAndData;
                    if (paymasterAndData.length > 0) {
                        require(
                            paymasterAndData.length >= UserOperationLib.PAYMASTER_DATA_OFFSET,
                            "AA93 invalid paymasterAndData"
                        );
                        (mUserOp.paymaster, mUserOp.paymasterVerificationGasLimit, mUserOp.paymasterPostOpGasLimit) = UserOperationLib.unpackPaymasterStaticFields(paymasterAndData);
                    } else {
                        mUserOp.paymaster = address(0);
                        mUserOp.paymasterVerificationGasLimit = 0;
                        mUserOp.paymasterPostOpGasLimit = 0;
                    }
                }
                /**
                 * Get the required prefunded gas fee amount for an operation.
                 * @param mUserOp - The user operation in memory.
                 */
                function _getRequiredPrefund(
                    MemoryUserOp memory mUserOp
                ) internal pure returns (uint256 requiredPrefund) {
                    unchecked {
                        uint256 requiredGas = mUserOp.verificationGasLimit +
                            mUserOp.callGasLimit +
                            mUserOp.paymasterVerificationGasLimit +
                            mUserOp.paymasterPostOpGasLimit +
                            mUserOp.preVerificationGas;
                        requiredPrefund = requiredGas * mUserOp.maxFeePerGas;
                    }
                }
                /**
                 * Create sender smart contract account if init code is provided.
                 * @param opIndex  - The operation index.
                 * @param opInfo   - The operation info.
                 * @param initCode - The init code for the smart contract account.
                 */
                function _createSenderIfNeeded(
                    uint256 opIndex,
                    UserOpInfo memory opInfo,
                    bytes calldata initCode
                ) internal {
                    if (initCode.length != 0) {
                        address sender = opInfo.mUserOp.sender;
                        if (sender.code.length != 0)
                            revert FailedOp(opIndex, "AA10 sender already constructed");
                        address sender1 = senderCreator().createSender{
                            gas: opInfo.mUserOp.verificationGasLimit
                        }(initCode);
                        if (sender1 == address(0))
                            revert FailedOp(opIndex, "AA13 initCode failed or OOG");
                        if (sender1 != sender)
                            revert FailedOp(opIndex, "AA14 initCode must return sender");
                        if (sender1.code.length == 0)
                            revert FailedOp(opIndex, "AA15 initCode must create sender");
                        address factory = address(bytes20(initCode[0:20]));
                        emit AccountDeployed(
                            opInfo.userOpHash,
                            sender,
                            factory,
                            opInfo.mUserOp.paymaster
                        );
                    }
                }
                /// @inheritdoc IEntryPoint
                function getSenderAddress(bytes calldata initCode) public {
                    address sender = senderCreator().createSender(initCode);
                    revert SenderAddressResult(sender);
                }
                /**
                 * Call account.validateUserOp.
                 * Revert (with FailedOp) in case validateUserOp reverts, or account didn't send required prefund.
                 * Decrement account's deposit if needed.
                 * @param opIndex         - The operation index.
                 * @param op              - The user operation.
                 * @param opInfo          - The operation info.
                 * @param requiredPrefund - The required prefund amount.
                 */
                function _validateAccountPrepayment(
                    uint256 opIndex,
                    PackedUserOperation calldata op,
                    UserOpInfo memory opInfo,
                    uint256 requiredPrefund,
                    uint256 verificationGasLimit
                )
                    internal
                    returns (
                        uint256 validationData
                    )
                {
                    unchecked {
                        MemoryUserOp memory mUserOp = opInfo.mUserOp;
                        address sender = mUserOp.sender;
                        _createSenderIfNeeded(opIndex, opInfo, op.initCode);
                        address paymaster = mUserOp.paymaster;
                        uint256 missingAccountFunds = 0;
                        if (paymaster == address(0)) {
                            uint256 bal = balanceOf(sender);
                            missingAccountFunds = bal > requiredPrefund
                                ? 0
                                : requiredPrefund - bal;
                        }
                        try
                            IAccount(sender).validateUserOp{
                                gas: verificationGasLimit
                            }(op, opInfo.userOpHash, missingAccountFunds)
                        returns (uint256 _validationData) {
                            validationData = _validationData;
                        } catch {
                            revert FailedOpWithRevert(opIndex, "AA23 reverted", Exec.getReturnData(REVERT_REASON_MAX_LEN));
                        }
                        if (paymaster == address(0)) {
                            DepositInfo storage senderInfo = deposits[sender];
                            uint256 deposit = senderInfo.deposit;
                            if (requiredPrefund > deposit) {
                                revert FailedOp(opIndex, "AA21 didn't pay prefund");
                            }
                            senderInfo.deposit = deposit - requiredPrefund;
                        }
                    }
                }
                /**
                 * In case the request has a paymaster:
                 *  - Validate paymaster has enough deposit.
                 *  - Call paymaster.validatePaymasterUserOp.
                 *  - Revert with proper FailedOp in case paymaster reverts.
                 *  - Decrement paymaster's deposit.
                 * @param opIndex                            - The operation index.
                 * @param op                                 - The user operation.
                 * @param opInfo                             - The operation info.
                 * @param requiredPreFund                    - The required prefund amount.
                 */
                function _validatePaymasterPrepayment(
                    uint256 opIndex,
                    PackedUserOperation calldata op,
                    UserOpInfo memory opInfo,
                    uint256 requiredPreFund
                ) internal returns (bytes memory context, uint256 validationData) {
                    unchecked {
                        uint256 preGas = gasleft();
                        MemoryUserOp memory mUserOp = opInfo.mUserOp;
                        address paymaster = mUserOp.paymaster;
                        DepositInfo storage paymasterInfo = deposits[paymaster];
                        uint256 deposit = paymasterInfo.deposit;
                        if (deposit < requiredPreFund) {
                            revert FailedOp(opIndex, "AA31 paymaster deposit too low");
                        }
                        paymasterInfo.deposit = deposit - requiredPreFund;
                        uint256 pmVerificationGasLimit = mUserOp.paymasterVerificationGasLimit;
                        try
                            IPaymaster(paymaster).validatePaymasterUserOp{gas: pmVerificationGasLimit}(
                                op,
                                opInfo.userOpHash,
                                requiredPreFund
                            )
                        returns (bytes memory _context, uint256 _validationData) {
                            context = _context;
                            validationData = _validationData;
                        } catch {
                            revert FailedOpWithRevert(opIndex, "AA33 reverted", Exec.getReturnData(REVERT_REASON_MAX_LEN));
                        }
                        if (preGas - gasleft() > pmVerificationGasLimit) {
                            revert FailedOp(opIndex, "AA36 over paymasterVerificationGasLimit");
                        }
                    }
                }
                /**
                 * Revert if either account validationData or paymaster validationData is expired.
                 * @param opIndex                 - The operation index.
                 * @param validationData          - The account validationData.
                 * @param paymasterValidationData - The paymaster validationData.
                 * @param expectedAggregator      - The expected aggregator.
                 */
                function _validateAccountAndPaymasterValidationData(
                    uint256 opIndex,
                    uint256 validationData,
                    uint256 paymasterValidationData,
                    address expectedAggregator
                ) internal view {
                    (address aggregator, bool outOfTimeRange) = _getValidationData(
                        validationData
                    );
                    if (expectedAggregator != aggregator) {
                        revert FailedOp(opIndex, "AA24 signature error");
                    }
                    if (outOfTimeRange) {
                        revert FailedOp(opIndex, "AA22 expired or not due");
                    }
                    // pmAggregator is not a real signature aggregator: we don't have logic to handle it as address.
                    // Non-zero address means that the paymaster fails due to some signature check (which is ok only during estimation).
                    address pmAggregator;
                    (pmAggregator, outOfTimeRange) = _getValidationData(
                        paymasterValidationData
                    );
                    if (pmAggregator != address(0)) {
                        revert FailedOp(opIndex, "AA34 signature error");
                    }
                    if (outOfTimeRange) {
                        revert FailedOp(opIndex, "AA32 paymaster expired or not due");
                    }
                }
                /**
                 * Parse validationData into its components.
                 * @param validationData - The packed validation data (sigFailed, validAfter, validUntil).
                 * @return aggregator the aggregator of the validationData
                 * @return outOfTimeRange true if current time is outside the time range of this validationData.
                 */
                function _getValidationData(
                    uint256 validationData
                ) internal view returns (address aggregator, bool outOfTimeRange) {
                    if (validationData == 0) {
                        return (address(0), false);
                    }
                    ValidationData memory data = _parseValidationData(validationData);
                    // solhint-disable-next-line not-rely-on-time
                    outOfTimeRange = block.timestamp > data.validUntil || block.timestamp < data.validAfter;
                    aggregator = data.aggregator;
                }
                /**
                 * Validate account and paymaster (if defined) and
                 * also make sure total validation doesn't exceed verificationGasLimit.
                 * This method is called off-chain (simulateValidation()) and on-chain (from handleOps)
                 * @param opIndex - The index of this userOp into the "opInfos" array.
                 * @param userOp  - The userOp to validate.
                 */
                function _validatePrepayment(
                    uint256 opIndex,
                    PackedUserOperation calldata userOp,
                    UserOpInfo memory outOpInfo
                )
                    internal
                    returns (uint256 validationData, uint256 paymasterValidationData)
                {
                    uint256 preGas = gasleft();
                    MemoryUserOp memory mUserOp = outOpInfo.mUserOp;
                    _copyUserOpToMemory(userOp, mUserOp);
                    outOpInfo.userOpHash = getUserOpHash(userOp);
                    // Validate all numeric values in userOp are well below 128 bit, so they can safely be added
                    // and multiplied without causing overflow.
                    uint256 verificationGasLimit = mUserOp.verificationGasLimit;
                    uint256 maxGasValues = mUserOp.preVerificationGas |
                        verificationGasLimit |
                        mUserOp.callGasLimit |
                        mUserOp.paymasterVerificationGasLimit |
                        mUserOp.paymasterPostOpGasLimit |
                        mUserOp.maxFeePerGas |
                        mUserOp.maxPriorityFeePerGas;
                    require(maxGasValues <= type(uint120).max, "AA94 gas values overflow");
                    uint256 requiredPreFund = _getRequiredPrefund(mUserOp);
                    validationData = _validateAccountPrepayment(
                        opIndex,
                        userOp,
                        outOpInfo,
                        requiredPreFund,
                        verificationGasLimit
                    );
                    if (!_validateAndUpdateNonce(mUserOp.sender, mUserOp.nonce)) {
                        revert FailedOp(opIndex, "AA25 invalid account nonce");
                    }
                    unchecked {
                        if (preGas - gasleft() > verificationGasLimit) {
                            revert FailedOp(opIndex, "AA26 over verificationGasLimit");
                        }
                    }
                    bytes memory context;
                    if (mUserOp.paymaster != address(0)) {
                        (context, paymasterValidationData) = _validatePaymasterPrepayment(
                            opIndex,
                            userOp,
                            outOpInfo,
                            requiredPreFund
                        );
                    }
                    unchecked {
                        outOpInfo.prefund = requiredPreFund;
                        outOpInfo.contextOffset = getOffsetOfMemoryBytes(context);
                        outOpInfo.preOpGas = preGas - gasleft() + userOp.preVerificationGas;
                    }
                }
                /**
                 * Process post-operation, called just after the callData is executed.
                 * If a paymaster is defined and its validation returned a non-empty context, its postOp is called.
                 * The excess amount is refunded to the account (or paymaster - if it was used in the request).
                 * @param mode      - Whether is called from innerHandleOp, or outside (postOpReverted).
                 * @param opInfo    - UserOp fields and info collected during validation.
                 * @param context   - The context returned in validatePaymasterUserOp.
                 * @param actualGas - The gas used so far by this user operation.
                 */
                function _postExecution(
                    IPaymaster.PostOpMode mode,
                    UserOpInfo memory opInfo,
                    bytes memory context,
                    uint256 actualGas
                ) private returns (uint256 actualGasCost) {
                    uint256 preGas = gasleft();
                    unchecked {
                        address refundAddress;
                        MemoryUserOp memory mUserOp = opInfo.mUserOp;
                        uint256 gasPrice = getUserOpGasPrice(mUserOp);
                        address paymaster = mUserOp.paymaster;
                        if (paymaster == address(0)) {
                            refundAddress = mUserOp.sender;
                        } else {
                            refundAddress = paymaster;
                            if (context.length > 0) {
                                actualGasCost = actualGas * gasPrice;
                                if (mode != IPaymaster.PostOpMode.postOpReverted) {
                                    try IPaymaster(paymaster).postOp{
                                        gas: mUserOp.paymasterPostOpGasLimit
                                    }(mode, context, actualGasCost, gasPrice)
                                    // solhint-disable-next-line no-empty-blocks
                                    {} catch {
                                        bytes memory reason = Exec.getReturnData(REVERT_REASON_MAX_LEN);
                                        revert PostOpReverted(reason);
                                    }
                                }
                            }
                        }
                        actualGas += preGas - gasleft();
                        // Calculating a penalty for unused execution gas
                        {
                            uint256 executionGasLimit = mUserOp.callGasLimit + mUserOp.paymasterPostOpGasLimit;
                            uint256 executionGasUsed = actualGas - opInfo.preOpGas;
                            // this check is required for the gas used within EntryPoint and not covered by explicit gas limits
                            if (executionGasLimit > executionGasUsed) {
                                uint256 unusedGas = executionGasLimit - executionGasUsed;
                                uint256 unusedGasPenalty = (unusedGas * PENALTY_PERCENT) / 100;
                                actualGas += unusedGasPenalty;
                            }
                        }
                        actualGasCost = actualGas * gasPrice;
                        uint256 prefund = opInfo.prefund;
                        if (prefund < actualGasCost) {
                            if (mode == IPaymaster.PostOpMode.postOpReverted) {
                                actualGasCost = prefund;
                                emitPrefundTooLow(opInfo);
                                emitUserOperationEvent(opInfo, false, actualGasCost, actualGas);
                            } else {
                                assembly ("memory-safe") {
                                    mstore(0, INNER_REVERT_LOW_PREFUND)
                                    revert(0, 32)
                                }
                            }
                        } else {
                            uint256 refund = prefund - actualGasCost;
                            _incrementDeposit(refundAddress, refund);
                            bool success = mode == IPaymaster.PostOpMode.opSucceeded;
                            emitUserOperationEvent(opInfo, success, actualGasCost, actualGas);
                        }
                    } // unchecked
                }
                /**
                 * The gas price this UserOp agrees to pay.
                 * Relayer/block builder might submit the TX with higher priorityFee, but the user should not.
                 * @param mUserOp - The userOp to get the gas price from.
                 */
                function getUserOpGasPrice(
                    MemoryUserOp memory mUserOp
                ) internal view returns (uint256) {
                    unchecked {
                        uint256 maxFeePerGas = mUserOp.maxFeePerGas;
                        uint256 maxPriorityFeePerGas = mUserOp.maxPriorityFeePerGas;
                        if (maxFeePerGas == maxPriorityFeePerGas) {
                            //legacy mode (for networks that don't support basefee opcode)
                            return maxFeePerGas;
                        }
                        return min(maxFeePerGas, maxPriorityFeePerGas + block.basefee);
                    }
                }
                /**
                 * The offset of the given bytes in memory.
                 * @param data - The bytes to get the offset of.
                 */
                function getOffsetOfMemoryBytes(
                    bytes memory data
                ) internal pure returns (uint256 offset) {
                    assembly {
                        offset := data
                    }
                }
                /**
                 * The bytes in memory at the given offset.
                 * @param offset - The offset to get the bytes from.
                 */
                function getMemoryBytesFromOffset(
                    uint256 offset
                ) internal pure returns (bytes memory data) {
                    assembly ("memory-safe") {
                        data := offset
                    }
                }
                /// @inheritdoc IEntryPoint
                function delegateAndRevert(address target, bytes calldata data) external {
                    (bool success, bytes memory ret) = target.delegatecall(data);
                    revert DelegateAndRevert(success, ret);
                }
            }
            // SPDX-License-Identifier: GPL-3.0
            pragma solidity ^0.8.23;
            /* solhint-disable no-inline-assembly */
             /*
              * For simulation purposes, validateUserOp (and validatePaymasterUserOp)
              * must return this value in case of signature failure, instead of revert.
              */
            uint256 constant SIG_VALIDATION_FAILED = 1;
            /*
             * For simulation purposes, validateUserOp (and validatePaymasterUserOp)
             * return this value on success.
             */
            uint256 constant SIG_VALIDATION_SUCCESS = 0;
            /**
             * Returned data from validateUserOp.
             * validateUserOp returns a uint256, which is created by `_packedValidationData` and
             * parsed by `_parseValidationData`.
             * @param aggregator  - address(0) - The account validated the signature by itself.
             *                      address(1) - The account failed to validate the signature.
             *                      otherwise - This is an address of a signature aggregator that must
             *                                  be used to validate the signature.
             * @param validAfter  - This UserOp is valid only after this timestamp.
             * @param validaUntil - This UserOp is valid only up to this timestamp.
             */
            struct ValidationData {
                address aggregator;
                uint48 validAfter;
                uint48 validUntil;
            }
            /**
             * Extract sigFailed, validAfter, validUntil.
             * Also convert zero validUntil to type(uint48).max.
             * @param validationData - The packed validation data.
             */
            function _parseValidationData(
                uint256 validationData
            ) pure returns (ValidationData memory data) {
                address aggregator = address(uint160(validationData));
                uint48 validUntil = uint48(validationData >> 160);
                if (validUntil == 0) {
                    validUntil = type(uint48).max;
                }
                uint48 validAfter = uint48(validationData >> (48 + 160));
                return ValidationData(aggregator, validAfter, validUntil);
            }
            /**
             * Helper to pack the return value for validateUserOp.
             * @param data - The ValidationData to pack.
             */
            function _packValidationData(
                ValidationData memory data
            ) pure returns (uint256) {
                return
                    uint160(data.aggregator) |
                    (uint256(data.validUntil) << 160) |
                    (uint256(data.validAfter) << (160 + 48));
            }
            /**
             * Helper to pack the return value for validateUserOp, when not using an aggregator.
             * @param sigFailed  - True for signature failure, false for success.
             * @param validUntil - Last timestamp this UserOperation is valid (or zero for infinite).
             * @param validAfter - First timestamp this UserOperation is valid.
             */
            function _packValidationData(
                bool sigFailed,
                uint48 validUntil,
                uint48 validAfter
            ) pure returns (uint256) {
                return
                    (sigFailed ? 1 : 0) |
                    (uint256(validUntil) << 160) |
                    (uint256(validAfter) << (160 + 48));
            }
            /**
             * keccak function over calldata.
             * @dev copy calldata into memory, do keccak and drop allocated memory. Strangely, this is more efficient than letting solidity do it.
             */
                function calldataKeccak(bytes calldata data) pure returns (bytes32 ret) {
                    assembly ("memory-safe") {
                        let mem := mload(0x40)
                        let len := data.length
                        calldatacopy(mem, data.offset, len)
                        ret := keccak256(mem, len)
                    }
                }
            /**
             * The minimum of two numbers.
             * @param a - First number.
             * @param b - Second number.
             */
                function min(uint256 a, uint256 b) pure returns (uint256) {
                    return a < b ? a : b;
                }
            // SPDX-License-Identifier: GPL-3.0
            pragma solidity ^0.8.23;
            import "../interfaces/INonceManager.sol";
            /**
             * nonce management functionality
             */
            abstract contract NonceManager is INonceManager {
                /**
                 * The next valid sequence number for a given nonce key.
                 */
                mapping(address => mapping(uint192 => uint256)) public nonceSequenceNumber;
                /// @inheritdoc INonceManager
                function getNonce(address sender, uint192 key)
                public view override returns (uint256 nonce) {
                    return nonceSequenceNumber[sender][key] | (uint256(key) << 64);
                }
                // allow an account to manually increment its own nonce.
                // (mainly so that during construction nonce can be made non-zero,
                // to "absorb" the gas cost of first nonce increment to 1st transaction (construction),
                // not to 2nd transaction)
                function incrementNonce(uint192 key) public override {
                    nonceSequenceNumber[msg.sender][key]++;
                }
                /**
                 * validate nonce uniqueness for this account.
                 * called just after validateUserOp()
                 * @return true if the nonce was incremented successfully.
                 *         false if the current nonce doesn't match the given one.
                 */
                function _validateAndUpdateNonce(address sender, uint256 nonce) internal returns (bool) {
                    uint192 key = uint192(nonce >> 64);
                    uint64 seq = uint64(nonce);
                    return nonceSequenceNumber[sender][key]++ == seq;
                }
            }
            // SPDX-License-Identifier: GPL-3.0
            pragma solidity ^0.8.23;
            /**
             * Helper contract for EntryPoint, to call userOp.initCode from a "neutral" address,
             * which is explicitly not the entryPoint itself.
             */
            contract SenderCreator {
                /**
                 * Call the "initCode" factory to create and return the sender account address.
                 * @param initCode - The initCode value from a UserOp. contains 20 bytes of factory address,
                 *                   followed by calldata.
                 * @return sender  - The returned address of the created account, or zero address on failure.
                 */
                function createSender(
                    bytes calldata initCode
                ) external returns (address sender) {
                    address factory = address(bytes20(initCode[0:20]));
                    bytes memory initCallData = initCode[20:];
                    bool success;
                    /* solhint-disable no-inline-assembly */
                    assembly ("memory-safe") {
                        success := call(
                            gas(),
                            factory,
                            0,
                            add(initCallData, 0x20),
                            mload(initCallData),
                            0,
                            32
                        )
                        sender := mload(0)
                    }
                    if (!success) {
                        sender = address(0);
                    }
                }
            }
            // SPDX-License-Identifier: GPL-3.0-only
            pragma solidity ^0.8.23;
            import "../interfaces/IStakeManager.sol";
            /* solhint-disable avoid-low-level-calls */
            /* solhint-disable not-rely-on-time */
            /**
             * Manage deposits and stakes.
             * Deposit is just a balance used to pay for UserOperations (either by a paymaster or an account).
             * Stake is value locked for at least "unstakeDelay" by a paymaster.
             */
            abstract contract StakeManager is IStakeManager {
                /// maps paymaster to their deposits and stakes
                mapping(address => DepositInfo) public deposits;
                /// @inheritdoc IStakeManager
                function getDepositInfo(
                    address account
                ) public view returns (DepositInfo memory info) {
                    return deposits[account];
                }
                /**
                 * Internal method to return just the stake info.
                 * @param addr - The account to query.
                 */
                function _getStakeInfo(
                    address addr
                ) internal view returns (StakeInfo memory info) {
                    DepositInfo storage depositInfo = deposits[addr];
                    info.stake = depositInfo.stake;
                    info.unstakeDelaySec = depositInfo.unstakeDelaySec;
                }
                /// @inheritdoc IStakeManager
                function balanceOf(address account) public view returns (uint256) {
                    return deposits[account].deposit;
                }
                receive() external payable {
                    depositTo(msg.sender);
                }
                /**
                 * Increments an account's deposit.
                 * @param account - The account to increment.
                 * @param amount  - The amount to increment by.
                 * @return the updated deposit of this account
                 */
                function _incrementDeposit(address account, uint256 amount) internal returns (uint256) {
                    DepositInfo storage info = deposits[account];
                    uint256 newAmount = info.deposit + amount;
                    info.deposit = newAmount;
                    return newAmount;
                }
                /**
                 * Add to the deposit of the given account.
                 * @param account - The account to add to.
                 */
                function depositTo(address account) public virtual payable {
                    uint256 newDeposit = _incrementDeposit(account, msg.value);
                    emit Deposited(account, newDeposit);
                }
                /**
                 * Add to the account's stake - amount and delay
                 * any pending unstake is first cancelled.
                 * @param unstakeDelaySec The new lock duration before the deposit can be withdrawn.
                 */
                function addStake(uint32 unstakeDelaySec) public payable {
                    DepositInfo storage info = deposits[msg.sender];
                    require(unstakeDelaySec > 0, "must specify unstake delay");
                    require(
                        unstakeDelaySec >= info.unstakeDelaySec,
                        "cannot decrease unstake time"
                    );
                    uint256 stake = info.stake + msg.value;
                    require(stake > 0, "no stake specified");
                    require(stake <= type(uint112).max, "stake overflow");
                    deposits[msg.sender] = DepositInfo(
                        info.deposit,
                        true,
                        uint112(stake),
                        unstakeDelaySec,
                        0
                    );
                    emit StakeLocked(msg.sender, stake, unstakeDelaySec);
                }
                /**
                 * Attempt to unlock the stake.
                 * The value can be withdrawn (using withdrawStake) after the unstake delay.
                 */
                function unlockStake() external {
                    DepositInfo storage info = deposits[msg.sender];
                    require(info.unstakeDelaySec != 0, "not staked");
                    require(info.staked, "already unstaking");
                    uint48 withdrawTime = uint48(block.timestamp) + info.unstakeDelaySec;
                    info.withdrawTime = withdrawTime;
                    info.staked = false;
                    emit StakeUnlocked(msg.sender, withdrawTime);
                }
                /**
                 * Withdraw from the (unlocked) stake.
                 * Must first call unlockStake and wait for the unstakeDelay to pass.
                 * @param withdrawAddress - The address to send withdrawn value.
                 */
                function withdrawStake(address payable withdrawAddress) external {
                    DepositInfo storage info = deposits[msg.sender];
                    uint256 stake = info.stake;
                    require(stake > 0, "No stake to withdraw");
                    require(info.withdrawTime > 0, "must call unlockStake() first");
                    require(
                        info.withdrawTime <= block.timestamp,
                        "Stake withdrawal is not due"
                    );
                    info.unstakeDelaySec = 0;
                    info.withdrawTime = 0;
                    info.stake = 0;
                    emit StakeWithdrawn(msg.sender, withdrawAddress, stake);
                    (bool success,) = withdrawAddress.call{value: stake}("");
                    require(success, "failed to withdraw stake");
                }
                /**
                 * Withdraw from the deposit.
                 * @param withdrawAddress - The address to send withdrawn value.
                 * @param withdrawAmount  - The amount to withdraw.
                 */
                function withdrawTo(
                    address payable withdrawAddress,
                    uint256 withdrawAmount
                ) external {
                    DepositInfo storage info = deposits[msg.sender];
                    require(withdrawAmount <= info.deposit, "Withdraw amount too large");
                    info.deposit = info.deposit - withdrawAmount;
                    emit Withdrawn(msg.sender, withdrawAddress, withdrawAmount);
                    (bool success,) = withdrawAddress.call{value: withdrawAmount}("");
                    require(success, "failed to withdraw");
                }
            }
            // SPDX-License-Identifier: GPL-3.0
            pragma solidity ^0.8.23;
            /* solhint-disable no-inline-assembly */
            import "../interfaces/PackedUserOperation.sol";
            import {calldataKeccak, min} from "./Helpers.sol";
            /**
             * Utility functions helpful when working with UserOperation structs.
             */
            library UserOperationLib {
                uint256 public constant PAYMASTER_VALIDATION_GAS_OFFSET = 20;
                uint256 public constant PAYMASTER_POSTOP_GAS_OFFSET = 36;
                uint256 public constant PAYMASTER_DATA_OFFSET = 52;
                /**
                 * Get sender from user operation data.
                 * @param userOp - The user operation data.
                 */
                function getSender(
                    PackedUserOperation calldata userOp
                ) internal pure returns (address) {
                    address data;
                    //read sender from userOp, which is first userOp member (saves 800 gas...)
                    assembly {
                        data := calldataload(userOp)
                    }
                    return address(uint160(data));
                }
                /**
                 * Relayer/block builder might submit the TX with higher priorityFee,
                 * but the user should not pay above what he signed for.
                 * @param userOp - The user operation data.
                 */
                function gasPrice(
                    PackedUserOperation calldata userOp
                ) internal view returns (uint256) {
                    unchecked {
                        (uint256 maxPriorityFeePerGas, uint256 maxFeePerGas) = unpackUints(userOp.gasFees);
                        if (maxFeePerGas == maxPriorityFeePerGas) {
                            //legacy mode (for networks that don't support basefee opcode)
                            return maxFeePerGas;
                        }
                        return min(maxFeePerGas, maxPriorityFeePerGas + block.basefee);
                    }
                }
                /**
                 * Pack the user operation data into bytes for hashing.
                 * @param userOp - The user operation data.
                 */
                function encode(
                    PackedUserOperation calldata userOp
                ) internal pure returns (bytes memory ret) {
                    address sender = getSender(userOp);
                    uint256 nonce = userOp.nonce;
                    bytes32 hashInitCode = calldataKeccak(userOp.initCode);
                    bytes32 hashCallData = calldataKeccak(userOp.callData);
                    bytes32 accountGasLimits = userOp.accountGasLimits;
                    uint256 preVerificationGas = userOp.preVerificationGas;
                    bytes32 gasFees = userOp.gasFees;
                    bytes32 hashPaymasterAndData = calldataKeccak(userOp.paymasterAndData);
                    return abi.encode(
                        sender, nonce,
                        hashInitCode, hashCallData,
                        accountGasLimits, preVerificationGas, gasFees,
                        hashPaymasterAndData
                    );
                }
                function unpackUints(
                    bytes32 packed
                ) internal pure returns (uint256 high128, uint256 low128) {
                    return (uint128(bytes16(packed)), uint128(uint256(packed)));
                }
                //unpack just the high 128-bits from a packed value
                function unpackHigh128(bytes32 packed) internal pure returns (uint256) {
                    return uint256(packed) >> 128;
                }
                // unpack just the low 128-bits from a packed value
                function unpackLow128(bytes32 packed) internal pure returns (uint256) {
                    return uint128(uint256(packed));
                }
                function unpackMaxPriorityFeePerGas(PackedUserOperation calldata userOp)
                internal pure returns (uint256) {
                    return unpackHigh128(userOp.gasFees);
                }
                function unpackMaxFeePerGas(PackedUserOperation calldata userOp)
                internal pure returns (uint256) {
                    return unpackLow128(userOp.gasFees);
                }
                function unpackVerificationGasLimit(PackedUserOperation calldata userOp)
                internal pure returns (uint256) {
                    return unpackHigh128(userOp.accountGasLimits);
                }
                function unpackCallGasLimit(PackedUserOperation calldata userOp)
                internal pure returns (uint256) {
                    return unpackLow128(userOp.accountGasLimits);
                }
                function unpackPaymasterVerificationGasLimit(PackedUserOperation calldata userOp)
                internal pure returns (uint256) {
                    return uint128(bytes16(userOp.paymasterAndData[PAYMASTER_VALIDATION_GAS_OFFSET : PAYMASTER_POSTOP_GAS_OFFSET]));
                }
                function unpackPostOpGasLimit(PackedUserOperation calldata userOp)
                internal pure returns (uint256) {
                    return uint128(bytes16(userOp.paymasterAndData[PAYMASTER_POSTOP_GAS_OFFSET : PAYMASTER_DATA_OFFSET]));
                }
                function unpackPaymasterStaticFields(
                    bytes calldata paymasterAndData
                ) internal pure returns (address paymaster, uint256 validationGasLimit, uint256 postOpGasLimit) {
                    return (
                        address(bytes20(paymasterAndData[: PAYMASTER_VALIDATION_GAS_OFFSET])),
                        uint128(bytes16(paymasterAndData[PAYMASTER_VALIDATION_GAS_OFFSET : PAYMASTER_POSTOP_GAS_OFFSET])),
                        uint128(bytes16(paymasterAndData[PAYMASTER_POSTOP_GAS_OFFSET : PAYMASTER_DATA_OFFSET]))
                    );
                }
                /**
                 * Hash the user operation data.
                 * @param userOp - The user operation data.
                 */
                function hash(
                    PackedUserOperation calldata userOp
                ) internal pure returns (bytes32) {
                    return keccak256(encode(userOp));
                }
            }
            // SPDX-License-Identifier: GPL-3.0
            pragma solidity >=0.7.5;
            import "./PackedUserOperation.sol";
            interface IAccount {
                /**
                 * Validate user's signature and nonce
                 * the entryPoint will make the call to the recipient only if this validation call returns successfully.
                 * signature failure should be reported by returning SIG_VALIDATION_FAILED (1).
                 * This allows making a "simulation call" without a valid signature
                 * Other failures (e.g. nonce mismatch, or invalid signature format) should still revert to signal failure.
                 *
                 * @dev Must validate caller is the entryPoint.
                 *      Must validate the signature and nonce
                 * @param userOp              - The operation that is about to be executed.
                 * @param userOpHash          - Hash of the user's request data. can be used as the basis for signature.
                 * @param missingAccountFunds - Missing funds on the account's deposit in the entrypoint.
                 *                              This is the minimum amount to transfer to the sender(entryPoint) to be
                 *                              able to make the call. The excess is left as a deposit in the entrypoint
                 *                              for future calls. Can be withdrawn anytime using "entryPoint.withdrawTo()".
                 *                              In case there is a paymaster in the request (or the current deposit is high
                 *                              enough), this value will be zero.
                 * @return validationData       - Packaged ValidationData structure. use `_packValidationData` and
                 *                              `_unpackValidationData` to encode and decode.
                 *                              <20-byte> sigAuthorizer - 0 for valid signature, 1 to mark signature failure,
                 *                                 otherwise, an address of an "authorizer" contract.
                 *                              <6-byte> validUntil - Last timestamp this operation is valid. 0 for "indefinite"
                 *                              <6-byte> validAfter - First timestamp this operation is valid
                 *                                                    If an account doesn't use time-range, it is enough to
                 *                                                    return SIG_VALIDATION_FAILED value (1) for signature failure.
                 *                              Note that the validation code cannot use block.timestamp (or block.number) directly.
                 */
                function validateUserOp(
                    PackedUserOperation calldata userOp,
                    bytes32 userOpHash,
                    uint256 missingAccountFunds
                ) external returns (uint256 validationData);
            }
            // SPDX-License-Identifier: GPL-3.0
            pragma solidity >=0.7.5;
            import "./PackedUserOperation.sol";
            interface IAccountExecute {
                /**
                 * Account may implement this execute method.
                 * passing this methodSig at the beginning of callData will cause the entryPoint to pass the full UserOp (and hash)
                 * to the account.
                 * The account should skip the methodSig, and use the callData (and optionally, other UserOp fields)
                 *
                 * @param userOp              - The operation that was just validated.
                 * @param userOpHash          - Hash of the user's request data.
                 */
                function executeUserOp(
                    PackedUserOperation calldata userOp,
                    bytes32 userOpHash
                ) external;
            }
            // SPDX-License-Identifier: GPL-3.0
            pragma solidity >=0.7.5;
            import "./PackedUserOperation.sol";
            /**
             * Aggregated Signatures validator.
             */
            interface IAggregator {
                /**
                 * Validate aggregated signature.
                 * Revert if the aggregated signature does not match the given list of operations.
                 * @param userOps   - Array of UserOperations to validate the signature for.
                 * @param signature - The aggregated signature.
                 */
                function validateSignatures(
                    PackedUserOperation[] calldata userOps,
                    bytes calldata signature
                ) external view;
                /**
                 * Validate signature of a single userOp.
                 * This method should be called by bundler after EntryPointSimulation.simulateValidation() returns
                 * the aggregator this account uses.
                 * First it validates the signature over the userOp. Then it returns data to be used when creating the handleOps.
                 * @param userOp        - The userOperation received from the user.
                 * @return sigForUserOp - The value to put into the signature field of the userOp when calling handleOps.
                 *                        (usually empty, unless account and aggregator support some kind of "multisig".
                 */
                function validateUserOpSignature(
                    PackedUserOperation calldata userOp
                ) external view returns (bytes memory sigForUserOp);
                /**
                 * Aggregate multiple signatures into a single value.
                 * This method is called off-chain to calculate the signature to pass with handleOps()
                 * bundler MAY use optimized custom code perform this aggregation.
                 * @param userOps              - Array of UserOperations to collect the signatures from.
                 * @return aggregatedSignature - The aggregated signature.
                 */
                function aggregateSignatures(
                    PackedUserOperation[] calldata userOps
                ) external view returns (bytes memory aggregatedSignature);
            }
            /**
             ** Account-Abstraction (EIP-4337) singleton EntryPoint implementation.
             ** Only one instance required on each chain.
             **/
            // SPDX-License-Identifier: GPL-3.0
            pragma solidity >=0.7.5;
            /* solhint-disable avoid-low-level-calls */
            /* solhint-disable no-inline-assembly */
            /* solhint-disable reason-string */
            import "./PackedUserOperation.sol";
            import "./IStakeManager.sol";
            import "./IAggregator.sol";
            import "./INonceManager.sol";
            interface IEntryPoint is IStakeManager, INonceManager {
                /***
                 * An event emitted after each successful request.
                 * @param userOpHash    - Unique identifier for the request (hash its entire content, except signature).
                 * @param sender        - The account that generates this request.
                 * @param paymaster     - If non-null, the paymaster that pays for this request.
                 * @param nonce         - The nonce value from the request.
                 * @param success       - True if the sender transaction succeeded, false if reverted.
                 * @param actualGasCost - Actual amount paid (by account or paymaster) for this UserOperation.
                 * @param actualGasUsed - Total gas used by this UserOperation (including preVerification, creation,
                 *                        validation and execution).
                 */
                event UserOperationEvent(
                    bytes32 indexed userOpHash,
                    address indexed sender,
                    address indexed paymaster,
                    uint256 nonce,
                    bool success,
                    uint256 actualGasCost,
                    uint256 actualGasUsed
                );
                /**
                 * Account "sender" was deployed.
                 * @param userOpHash - The userOp that deployed this account. UserOperationEvent will follow.
                 * @param sender     - The account that is deployed
                 * @param factory    - The factory used to deploy this account (in the initCode)
                 * @param paymaster  - The paymaster used by this UserOp
                 */
                event AccountDeployed(
                    bytes32 indexed userOpHash,
                    address indexed sender,
                    address factory,
                    address paymaster
                );
                /**
                 * An event emitted if the UserOperation "callData" reverted with non-zero length.
                 * @param userOpHash   - The request unique identifier.
                 * @param sender       - The sender of this request.
                 * @param nonce        - The nonce used in the request.
                 * @param revertReason - The return bytes from the (reverted) call to "callData".
                 */
                event UserOperationRevertReason(
                    bytes32 indexed userOpHash,
                    address indexed sender,
                    uint256 nonce,
                    bytes revertReason
                );
                /**
                 * An event emitted if the UserOperation Paymaster's "postOp" call reverted with non-zero length.
                 * @param userOpHash   - The request unique identifier.
                 * @param sender       - The sender of this request.
                 * @param nonce        - The nonce used in the request.
                 * @param revertReason - The return bytes from the (reverted) call to "callData".
                 */
                event PostOpRevertReason(
                    bytes32 indexed userOpHash,
                    address indexed sender,
                    uint256 nonce,
                    bytes revertReason
                );
                /**
                 * UserOp consumed more than prefund. The UserOperation is reverted, and no refund is made.
                 * @param userOpHash   - The request unique identifier.
                 * @param sender       - The sender of this request.
                 * @param nonce        - The nonce used in the request.
                 */
                event UserOperationPrefundTooLow(
                    bytes32 indexed userOpHash,
                    address indexed sender,
                    uint256 nonce
                );
                /**
                 * An event emitted by handleOps(), before starting the execution loop.
                 * Any event emitted before this event, is part of the validation.
                 */
                event BeforeExecution();
                /**
                 * Signature aggregator used by the following UserOperationEvents within this bundle.
                 * @param aggregator - The aggregator used for the following UserOperationEvents.
                 */
                event SignatureAggregatorChanged(address indexed aggregator);
                /**
                 * A custom revert error of handleOps, to identify the offending op.
                 * Should be caught in off-chain handleOps simulation and not happen on-chain.
                 * Useful for mitigating DoS attempts against batchers or for troubleshooting of factory/account/paymaster reverts.
                 * NOTE: If simulateValidation passes successfully, there should be no reason for handleOps to fail on it.
                 * @param opIndex - Index into the array of ops to the failed one (in simulateValidation, this is always zero).
                 * @param reason  - Revert reason. The string starts with a unique code "AAmn",
                 *                  where "m" is "1" for factory, "2" for account and "3" for paymaster issues,
                 *                  so a failure can be attributed to the correct entity.
                 */
                error FailedOp(uint256 opIndex, string reason);
                /**
                 * A custom revert error of handleOps, to report a revert by account or paymaster.
                 * @param opIndex - Index into the array of ops to the failed one (in simulateValidation, this is always zero).
                 * @param reason  - Revert reason. see FailedOp(uint256,string), above
                 * @param inner   - data from inner cought revert reason
                 * @dev note that inner is truncated to 2048 bytes
                 */
                error FailedOpWithRevert(uint256 opIndex, string reason, bytes inner);
                error PostOpReverted(bytes returnData);
                /**
                 * Error case when a signature aggregator fails to verify the aggregated signature it had created.
                 * @param aggregator The aggregator that failed to verify the signature
                 */
                error SignatureValidationFailed(address aggregator);
                // Return value of getSenderAddress.
                error SenderAddressResult(address sender);
                // UserOps handled, per aggregator.
                struct UserOpsPerAggregator {
                    PackedUserOperation[] userOps;
                    // Aggregator address
                    IAggregator aggregator;
                    // Aggregated signature
                    bytes signature;
                }
                /**
                 * Execute a batch of UserOperations.
                 * No signature aggregator is used.
                 * If any account requires an aggregator (that is, it returned an aggregator when
                 * performing simulateValidation), then handleAggregatedOps() must be used instead.
                 * @param ops         - The operations to execute.
                 * @param beneficiary - The address to receive the fees.
                 */
                function handleOps(
                    PackedUserOperation[] calldata ops,
                    address payable beneficiary
                ) external;
                /**
                 * Execute a batch of UserOperation with Aggregators
                 * @param opsPerAggregator - The operations to execute, grouped by aggregator (or address(0) for no-aggregator accounts).
                 * @param beneficiary      - The address to receive the fees.
                 */
                function handleAggregatedOps(
                    UserOpsPerAggregator[] calldata opsPerAggregator,
                    address payable beneficiary
                ) external;
                /**
                 * Generate a request Id - unique identifier for this request.
                 * The request ID is a hash over the content of the userOp (except the signature), the entrypoint and the chainid.
                 * @param userOp - The user operation to generate the request ID for.
                 * @return hash the hash of this UserOperation
                 */
                function getUserOpHash(
                    PackedUserOperation calldata userOp
                ) external view returns (bytes32);
                /**
                 * Gas and return values during simulation.
                 * @param preOpGas         - The gas used for validation (including preValidationGas)
                 * @param prefund          - The required prefund for this operation
                 * @param accountValidationData   - returned validationData from account.
                 * @param paymasterValidationData - return validationData from paymaster.
                 * @param paymasterContext - Returned by validatePaymasterUserOp (to be passed into postOp)
                 */
                struct ReturnInfo {
                    uint256 preOpGas;
                    uint256 prefund;
                    uint256 accountValidationData;
                    uint256 paymasterValidationData;
                    bytes paymasterContext;
                }
                /**
                 * Returned aggregated signature info:
                 * The aggregator returned by the account, and its current stake.
                 */
                struct AggregatorStakeInfo {
                    address aggregator;
                    StakeInfo stakeInfo;
                }
                /**
                 * Get counterfactual sender address.
                 * Calculate the sender contract address that will be generated by the initCode and salt in the UserOperation.
                 * This method always revert, and returns the address in SenderAddressResult error
                 * @param initCode - The constructor code to be passed into the UserOperation.
                 */
                function getSenderAddress(bytes memory initCode) external;
                error DelegateAndRevert(bool success, bytes ret);
                /**
                 * Helper method for dry-run testing.
                 * @dev calling this method, the EntryPoint will make a delegatecall to the given data, and report (via revert) the result.
                 *  The method always revert, so is only useful off-chain for dry run calls, in cases where state-override to replace
                 *  actual EntryPoint code is less convenient.
                 * @param target a target contract to make a delegatecall from entrypoint
                 * @param data data to pass to target in a delegatecall
                 */
                function delegateAndRevert(address target, bytes calldata data) external;
            }
            // SPDX-License-Identifier: GPL-3.0
            pragma solidity >=0.7.5;
            interface INonceManager {
                /**
                 * Return the next nonce for this sender.
                 * Within a given key, the nonce values are sequenced (starting with zero, and incremented by one on each userop)
                 * But UserOp with different keys can come with arbitrary order.
                 *
                 * @param sender the account address
                 * @param key the high 192 bit of the nonce
                 * @return nonce a full nonce to pass for next UserOp with this sender.
                 */
                function getNonce(address sender, uint192 key)
                external view returns (uint256 nonce);
                /**
                 * Manually increment the nonce of the sender.
                 * This method is exposed just for completeness..
                 * Account does NOT need to call it, neither during validation, nor elsewhere,
                 * as the EntryPoint will update the nonce regardless.
                 * Possible use-case is call it with various keys to "initialize" their nonces to one, so that future
                 * UserOperations will not pay extra for the first transaction with a given key.
                 */
                function incrementNonce(uint192 key) external;
            }
            // SPDX-License-Identifier: GPL-3.0
            pragma solidity >=0.7.5;
            import "./PackedUserOperation.sol";
            /**
             * The interface exposed by a paymaster contract, who agrees to pay the gas for user's operations.
             * A paymaster must hold a stake to cover the required entrypoint stake and also the gas for the transaction.
             */
            interface IPaymaster {
                enum PostOpMode {
                    // User op succeeded.
                    opSucceeded,
                    // User op reverted. Still has to pay for gas.
                    opReverted,
                    // Only used internally in the EntryPoint (cleanup after postOp reverts). Never calling paymaster with this value
                    postOpReverted
                }
                /**
                 * Payment validation: check if paymaster agrees to pay.
                 * Must verify sender is the entryPoint.
                 * Revert to reject this request.
                 * Note that bundlers will reject this method if it changes the state, unless the paymaster is trusted (whitelisted).
                 * The paymaster pre-pays using its deposit, and receive back a refund after the postOp method returns.
                 * @param userOp          - The user operation.
                 * @param userOpHash      - Hash of the user's request data.
                 * @param maxCost         - The maximum cost of this transaction (based on maximum gas and gas price from userOp).
                 * @return context        - Value to send to a postOp. Zero length to signify postOp is not required.
                 * @return validationData - Signature and time-range of this operation, encoded the same as the return
                 *                          value of validateUserOperation.
                 *                          <20-byte> sigAuthorizer - 0 for valid signature, 1 to mark signature failure,
                 *                                                    other values are invalid for paymaster.
                 *                          <6-byte> validUntil - last timestamp this operation is valid. 0 for "indefinite"
                 *                          <6-byte> validAfter - first timestamp this operation is valid
                 *                          Note that the validation code cannot use block.timestamp (or block.number) directly.
                 */
                function validatePaymasterUserOp(
                    PackedUserOperation calldata userOp,
                    bytes32 userOpHash,
                    uint256 maxCost
                ) external returns (bytes memory context, uint256 validationData);
                /**
                 * Post-operation handler.
                 * Must verify sender is the entryPoint.
                 * @param mode          - Enum with the following options:
                 *                        opSucceeded - User operation succeeded.
                 *                        opReverted  - User op reverted. The paymaster still has to pay for gas.
                 *                        postOpReverted - never passed in a call to postOp().
                 * @param context       - The context value returned by validatePaymasterUserOp
                 * @param actualGasCost - Actual gas used so far (without this postOp call).
                 * @param actualUserOpFeePerGas - the gas price this UserOp pays. This value is based on the UserOp's maxFeePerGas
                 *                        and maxPriorityFee (and basefee)
                 *                        It is not the same as tx.gasprice, which is what the bundler pays.
                 */
                function postOp(
                    PostOpMode mode,
                    bytes calldata context,
                    uint256 actualGasCost,
                    uint256 actualUserOpFeePerGas
                ) external;
            }
            // SPDX-License-Identifier: GPL-3.0-only
            pragma solidity >=0.7.5;
            /**
             * Manage deposits and stakes.
             * Deposit is just a balance used to pay for UserOperations (either by a paymaster or an account).
             * Stake is value locked for at least "unstakeDelay" by the staked entity.
             */
            interface IStakeManager {
                event Deposited(address indexed account, uint256 totalDeposit);
                event Withdrawn(
                    address indexed account,
                    address withdrawAddress,
                    uint256 amount
                );
                // Emitted when stake or unstake delay are modified.
                event StakeLocked(
                    address indexed account,
                    uint256 totalStaked,
                    uint256 unstakeDelaySec
                );
                // Emitted once a stake is scheduled for withdrawal.
                event StakeUnlocked(address indexed account, uint256 withdrawTime);
                event StakeWithdrawn(
                    address indexed account,
                    address withdrawAddress,
                    uint256 amount
                );
                /**
                 * @param deposit         - The entity's deposit.
                 * @param staked          - True if this entity is staked.
                 * @param stake           - Actual amount of ether staked for this entity.
                 * @param unstakeDelaySec - Minimum delay to withdraw the stake.
                 * @param withdrawTime    - First block timestamp where 'withdrawStake' will be callable, or zero if already locked.
                 * @dev Sizes were chosen so that deposit fits into one cell (used during handleOp)
                 *      and the rest fit into a 2nd cell (used during stake/unstake)
                 *      - 112 bit allows for 10^15 eth
                 *      - 48 bit for full timestamp
                 *      - 32 bit allows 150 years for unstake delay
                 */
                struct DepositInfo {
                    uint256 deposit;
                    bool staked;
                    uint112 stake;
                    uint32 unstakeDelaySec;
                    uint48 withdrawTime;
                }
                // API struct used by getStakeInfo and simulateValidation.
                struct StakeInfo {
                    uint256 stake;
                    uint256 unstakeDelaySec;
                }
                /**
                 * Get deposit info.
                 * @param account - The account to query.
                 * @return info   - Full deposit information of given account.
                 */
                function getDepositInfo(
                    address account
                ) external view returns (DepositInfo memory info);
                /**
                 * Get account balance.
                 * @param account - The account to query.
                 * @return        - The deposit (for gas payment) of the account.
                 */
                function balanceOf(address account) external view returns (uint256);
                /**
                 * Add to the deposit of the given account.
                 * @param account - The account to add to.
                 */
                function depositTo(address account) external payable;
                /**
                 * Add to the account's stake - amount and delay
                 * any pending unstake is first cancelled.
                 * @param _unstakeDelaySec - The new lock duration before the deposit can be withdrawn.
                 */
                function addStake(uint32 _unstakeDelaySec) external payable;
                /**
                 * Attempt to unlock the stake.
                 * The value can be withdrawn (using withdrawStake) after the unstake delay.
                 */
                function unlockStake() external;
                /**
                 * Withdraw from the (unlocked) stake.
                 * Must first call unlockStake and wait for the unstakeDelay to pass.
                 * @param withdrawAddress - The address to send withdrawn value.
                 */
                function withdrawStake(address payable withdrawAddress) external;
                /**
                 * Withdraw from the deposit.
                 * @param withdrawAddress - The address to send withdrawn value.
                 * @param withdrawAmount  - The amount to withdraw.
                 */
                function withdrawTo(
                    address payable withdrawAddress,
                    uint256 withdrawAmount
                ) external;
            }
            // SPDX-License-Identifier: GPL-3.0
            pragma solidity >=0.7.5;
            /**
             * User Operation struct
             * @param sender                - The sender account of this request.
             * @param nonce                 - Unique value the sender uses to verify it is not a replay.
             * @param initCode              - If set, the account contract will be created by this constructor/
             * @param callData              - The method call to execute on this account.
             * @param accountGasLimits      - Packed gas limits for validateUserOp and gas limit passed to the callData method call.
             * @param preVerificationGas    - Gas not calculated by the handleOps method, but added to the gas paid.
             *                                Covers batch overhead.
             * @param gasFees               - packed gas fields maxPriorityFeePerGas and maxFeePerGas - Same as EIP-1559 gas parameters.
             * @param paymasterAndData      - If set, this field holds the paymaster address, verification gas limit, postOp gas limit and paymaster-specific extra data
             *                                The paymaster will pay for the transaction instead of the sender.
             * @param signature             - Sender-verified signature over the entire request, the EntryPoint address and the chain ID.
             */
            struct PackedUserOperation {
                address sender;
                uint256 nonce;
                bytes initCode;
                bytes callData;
                bytes32 accountGasLimits;
                uint256 preVerificationGas;
                bytes32 gasFees;
                bytes paymasterAndData;
                bytes signature;
            }
            // SPDX-License-Identifier: LGPL-3.0-only
            pragma solidity ^0.8.23;
            // solhint-disable no-inline-assembly
            /**
             * Utility functions helpful when making different kinds of contract calls in Solidity.
             */
            library Exec {
                function call(
                    address to,
                    uint256 value,
                    bytes memory data,
                    uint256 txGas
                ) internal returns (bool success) {
                    assembly ("memory-safe") {
                        success := call(txGas, to, value, add(data, 0x20), mload(data), 0, 0)
                    }
                }
                function staticcall(
                    address to,
                    bytes memory data,
                    uint256 txGas
                ) internal view returns (bool success) {
                    assembly ("memory-safe") {
                        success := staticcall(txGas, to, add(data, 0x20), mload(data), 0, 0)
                    }
                }
                function delegateCall(
                    address to,
                    bytes memory data,
                    uint256 txGas
                ) internal returns (bool success) {
                    assembly ("memory-safe") {
                        success := delegatecall(txGas, to, add(data, 0x20), mload(data), 0, 0)
                    }
                }
                // get returned data from last call or calldelegate
                function getReturnData(uint256 maxLen) internal pure returns (bytes memory returnData) {
                    assembly ("memory-safe") {
                        let len := returndatasize()
                        if gt(len, maxLen) {
                            len := maxLen
                        }
                        let ptr := mload(0x40)
                        mstore(0x40, add(ptr, add(len, 0x20)))
                        mstore(ptr, len)
                        returndatacopy(add(ptr, 0x20), 0, len)
                        returnData := ptr
                    }
                }
                // revert with explicit byte array (probably reverted info from call)
                function revertWithData(bytes memory returnData) internal pure {
                    assembly ("memory-safe") {
                        revert(add(returnData, 32), mload(returnData))
                    }
                }
                function callAndRevert(address to, bytes memory data, uint256 maxLen) internal {
                    bool success = call(to,0,data,gasleft());
                    if (!success) {
                        revertWithData(getReturnData(maxLen));
                    }
                }
            }
            

            File 2 of 2: K1MeeValidator
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.27;
            import {IValidator, MODULE_TYPE_VALIDATOR} from "erc7579/interfaces/IERC7579Module.sol";
            import {ISessionValidator} from "contracts/interfaces/ISessionValidator.sol";
            import {EnumerableSet} from "EnumerableSet4337/EnumerableSet4337.sol";
            import {PackedUserOperation} from "account-abstraction/interfaces/PackedUserOperation.sol";
            import {ERC7739Validator} from "erc7739Validator/ERC7739Validator.sol";
            import {
                SIG_TYPE_SIMPLE,
                SIG_TYPE_ON_CHAIN,
                SIG_TYPE_ERC20_PERMIT,
                EIP1271_SUCCESS,
                EIP1271_FAILED,
                MODULE_TYPE_STATELESS_VALIDATOR,
                SIG_TYPE_MEE_FLOW
            } from "contracts/types/Constants.sol";
            // Fusion libraries - validate userOp using on-chain tx or off-chain permit
            import {PermitValidatorLib} from "contracts/lib/fusion/PermitValidatorLib.sol";
            import {TxValidatorLib} from "contracts/lib/fusion/TxValidatorLib.sol";
            import {SimpleValidatorLib} from "contracts/lib/fusion/SimpleValidatorLib.sol";
            import {NoMeeFlowLib} from "contracts/lib/fusion/NoMeeFlowLib.sol";
            import {EcdsaLib} from "contracts/lib/util/EcdsaLib.sol";
            /**
             * @title K1MeeValidator
             * @dev   An ERC-7579 validator (module type 1) and stateless validator (module type 7) for the MEE stack.
             *        Supports 3 MEE modes:
             *        - Simple (Super Tx hash is signed)
             *        - On-chain Tx (Super Tx hash is appended to a regular txn and signed)
             *        - ERC-2612 Permit (Super Tx hash is pasted into deadline field of the ERC-2612 Permit and signed)
             *
             *        Further improvements:
             *        - Further gas optimizations
             *        - Use EIP-712 to make superTx hash not blind => use 7739 for the MEE 1271 flows
             *
             *        Using erc7739 for MEE flows makes no sense currently because user signs blind hashes anyways
             *        (except permit mode, but the superTx hash is still blind in it).
             *        So we just hash smart account address into the og hash for 1271 MEE flow currently.
             *        In future full scale 7739 will replace it when superTx hash is 712 and transparent.
             *
             */
            contract K1MeeValidator is IValidator, ISessionValidator, ERC7739Validator {
                using EnumerableSet for EnumerableSet.AddressSet;
                /*//////////////////////////////////////////////////////////////////////////
                                        CONSTANTS & STORAGE
                //////////////////////////////////////////////////////////////////////////*/
                /// @notice Mapping of smart account addresses to their respective owner addresses
                mapping(address => address) public smartAccountOwners;
                /// @notice Set of safe senders for each smart account
                EnumerableSet.AddressSet private _safeSenders;
                /// @notice Error to indicate that no owner was provided during installation
                error NoOwnerProvided();
                /// @notice Error to indicate that the new owner cannot be the zero address
                error ZeroAddressNotAllowed();
                /// @notice Error to indicate the module is already initialized
                error ModuleAlreadyInitialized();
                /// @notice Error to indicate that the new owner cannot be a contract address
                error NewOwnerIsContract();
                /// @notice Error to indicate that the owner cannot be the zero address
                error OwnerCannotBeZeroAddress();
                /// @notice Error to indicate that the data length is invalid
                error InvalidDataLength();
                /// @notice Error to indicate that the safe senders length is invalid
                error SafeSendersLengthInvalid();
                /*//////////////////////////////////////////////////////////////////////////
                                                 CONFIG
                //////////////////////////////////////////////////////////////////////////*/
                /**
                 * Initialize the module with the given data
                 *
                 * @param data The data to initialize the module with
                 */
                function onInstall(bytes calldata data) external override {
                    require(data.length != 0, NoOwnerProvided());
                    require(!_isInitialized(msg.sender), ModuleAlreadyInitialized());
                    address newOwner = address(bytes20(data[:20]));
                    require(newOwner != address(0), OwnerCannotBeZeroAddress());
                    require(!_isContract(newOwner), NewOwnerIsContract());
                    smartAccountOwners[msg.sender] = newOwner;
                    if (data.length > 20) {
                        _fillSafeSenders(data[20:]);
                    }
                }
                /**
                 * De-initialize the module with the given data
                 */
                function onUninstall(bytes calldata) external override {
                    delete smartAccountOwners[msg.sender];
                    _safeSenders.removeAll(msg.sender);
                }
                /// @notice Transfers ownership of the validator to a new owner
                /// @param newOwner The address of the new owner
                function transferOwnership(address newOwner) external {
                    require(newOwner != address(0), ZeroAddressNotAllowed());
                    require(!_isContract(newOwner), NewOwnerIsContract());
                    smartAccountOwners[msg.sender] = newOwner;
                }
                /**
                 * Check if the module is initialized
                 * @param smartAccount The smart account to check
                 *
                 * @return true if the module is initialized, false otherwise
                 */
                function isInitialized(address smartAccount) external view returns (bool) {
                    return _isInitialized(smartAccount);
                }
                /// @notice Adds a safe sender to the _safeSenders list for the smart account
                function addSafeSender(address sender) external {
                    _safeSenders.add(msg.sender, sender);
                }
                /// @notice Removes a safe sender from the _safeSenders list for the smart account
                function removeSafeSender(address sender) external {
                    _safeSenders.remove(msg.sender, sender);
                }
                /// @notice Checks if a sender is in the _safeSenders list for the smart account
                function isSafeSender(address sender, address smartAccount) external view returns (bool) {
                    return _safeSenders.contains(smartAccount, sender);
                }
                /*//////////////////////////////////////////////////////////////////////////
                                                 MODULE LOGIC
                //////////////////////////////////////////////////////////////////////////*/
                /**
                 * Validates PackedUserOperation
                 *
                 * @param userOp UserOperation to be validated
                 * @param userOpHash Hash of the UserOperation to be validated
                 * @dev fallback flow => non MEE flow => no dedicated prefix introduced for the sake of compatibility.
                 *      It may lead to a case where some signature turns out to have first bytes matching the prefix.
                 *      However, this is very unlikely to happen and even if it does, the consequences are just
                 *      that the signature is not validated which is easily solved by altering userOp => hash => sig.
                 *
                 * @return uint256 the result of the signature validation, which can be:
                 *  - 0 if the signature is valid
                 *  - 1 if the signature is invalid
                 *  - <20-byte> aggregatorOrSigFail, <6-byte> validUntil and <6-byte> validAfter (see ERC-4337
                 * for more details)
                 */
                function validateUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash)
                    external
                    override
                    returns (uint256)
                {
                    bytes4 sigType = bytes4(userOp.signature[0:4]);
                    address owner = getOwner(userOp.sender);
                    if (sigType == SIG_TYPE_SIMPLE) {
                        return SimpleValidatorLib.validateUserOp(userOpHash, userOp.signature[4:], owner);
                    } else if (sigType == SIG_TYPE_ON_CHAIN) {
                        return TxValidatorLib.validateUserOp(userOpHash, userOp.signature[4:], owner);
                    } else if (sigType == SIG_TYPE_ERC20_PERMIT) {
                        return PermitValidatorLib.validateUserOp(userOpHash, userOp.signature[4:], owner);
                    } else {
                        // fallback flow => non MEE flow => no prefix
                        return NoMeeFlowLib.validateUserOp(userOpHash, userOp.signature, owner);
                    }
                }
                /**
                 * Validates an ERC-1271 signature
                 *
                 * @param sender The sender of the ERC-1271 call to the account
                 * @param hash The hash of the message
                 * @param signature The signature of the message
                 *
                 * @return sigValidationResult the result of the signature validation, which can be:
                 *  - EIP1271_SUCCESS if the signature is valid
                 *  - EIP1271_FAILED if the signature is invalid
                 */
                function isValidSignatureWithSender(address sender, bytes32 hash, bytes calldata signature)
                    external
                    view
                    virtual
                    override
                    returns (bytes4 sigValidationResult)
                {
                    if (bytes3(signature[0:3]) != SIG_TYPE_MEE_FLOW) {
                        // Non MEE 7739 flow
                        // goes to ERC7739Validator to apply 7739 magic and then returns back
                        // to this contract's _erc1271IsValidSignatureNowCalldata() method.
                        return _erc1271IsValidSignatureWithSender(sender, hash, _erc1271UnwrapSignature(signature));
                    } else {
                        // non-7739 flow
                        // hash the SA into the `hash` to protect against two SA's with same owner vector
                        return _validateSignatureForOwner(
                            getOwner(msg.sender), keccak256(abi.encodePacked(hash, msg.sender)), _erc1271UnwrapSignature(signature)
                        ) ? EIP1271_SUCCESS : EIP1271_FAILED;
                    }
                }
                /// @notice ISessionValidator interface for smart session
                /// @param hash The hash of the data to validate
                /// @param sig The signature data
                /// @param data The data to validate against (owner address in this case)
                function validateSignatureWithData(bytes32 hash, bytes calldata sig, bytes calldata data)
                    external
                    view
                    returns (bool validSig)
                {
                    require(data.length >= 20, InvalidDataLength());
                    return _validateSignatureForOwner(address(bytes20(data[:20])), hash, sig);
                }
                /**
                 * Get the owner of the smart account
                 * @param smartAccount The address of the smart account
                 * @return The owner of the smart account
                 */
                function getOwner(address smartAccount) public view returns (address) {
                    address owner = smartAccountOwners[smartAccount];
                    return owner == address(0) ? smartAccount : owner;
                }
                /*//////////////////////////////////////////////////////////////////////////
                                                 METADATA
                //////////////////////////////////////////////////////////////////////////*/
                /// @notice Returns the name of the module
                /// @return The name of the module
                function name() external pure returns (string memory) {
                    return "K1MeeValidator";
                }
                /// @notice Returns the version of the module
                /// @return The version of the module
                function version() external pure returns (string memory) {
                    return "1.0.1";
                }
                /// @notice Checks if the module is of the specified type
                /// @param typeId The type ID to check
                /// @return True if the module is of the specified type, false otherwise
                function isModuleType(uint256 typeId) external pure returns (bool) {
                    return typeId == MODULE_TYPE_VALIDATOR || typeId == MODULE_TYPE_STATELESS_VALIDATOR;
                }
                /*//////////////////////////////////////////////////////////////////////////
                                                 INTERNAL
                //////////////////////////////////////////////////////////////////////////*/
                /// @notice Internal method that does the job of validating the signature via ECDSA (secp256k1)
                /// @param owner The address of the owner
                /// @param hash The hash of the data to validate
                /// @param signature The signature data
                function _validateSignatureForOwner(address owner, bytes32 hash, bytes calldata signature)
                    internal
                    view
                    returns (bool)
                {
                    bytes4 sigType = bytes4(signature[0:4]);
                    if (sigType == SIG_TYPE_SIMPLE) {
                        return SimpleValidatorLib.validateSignatureForOwner(owner, hash, signature[4:]);
                    } else if (sigType == SIG_TYPE_ON_CHAIN) {
                        return TxValidatorLib.validateSignatureForOwner(owner, hash, signature[4:]);
                    } else if (sigType == SIG_TYPE_ERC20_PERMIT) {
                        return PermitValidatorLib.validateSignatureForOwner(owner, hash, signature[4:]);
                    } else {
                        // fallback flow => non MEE flow => no prefix
                        return NoMeeFlowLib.validateSignatureForOwner(owner, hash, signature);
                    }
                }
                /// @notice Checks if the smart account is initialized with an owner
                /// @param smartAccount The address of the smart account
                /// @return True if the smart account has an owner, false otherwise
                function _isInitialized(address smartAccount) private view returns (bool) {
                    return smartAccountOwners[smartAccount] != address(0);
                }
                // @notice Fills the _safeSenders list from the given data
                function _fillSafeSenders(bytes calldata data) private {
                    require(data.length % 20 == 0, SafeSendersLengthInvalid());
                    for (uint256 i; i < data.length / 20; i++) {
                        _safeSenders.add(msg.sender, address(bytes20(data[20 * i:20 * (i + 1)])));
                    }
                }
                /// @notice Checks if the address is a contract
                /// @param account The address to check
                /// @return True if the address is a contract, false otherwise
                function _isContract(address account) private view returns (bool) {
                    uint256 size;
                    assembly {
                        size := extcodesize(account)
                    }
                    return size > 0;
                }
                /// @dev Returns whether the `hash` and `signature` are valid.
                ///      Obtains the authorized signer's credentials and calls some
                ///      module's specific internal function to validate the signature
                ///      against credentials.
                function _erc1271IsValidSignatureNowCalldata(bytes32 hash, bytes calldata signature)
                    internal
                    view
                    override
                    returns (bool)
                {
                    // call custom internal function to validate the signature against credentials
                    return EcdsaLib.isValidSignature(getOwner(msg.sender), hash, signature);
                }
                /// @dev Returns whether the `sender` is considered safe, such
                /// that we don't need to use the nested EIP-712 workflow.
                /// See: https://mirror.xyz/curiousapple.eth/pFqAdW2LiJ-6S4sg_u1z08k4vK6BCJ33LcyXpnNb8yU
                // The canonical `MulticallerWithSigner` at 0x000000000000D9ECebf3C23529de49815Dac1c4c
                // is known to include the account in the hash to be signed.
                // msg.sender = Smart Account
                // sender = 1271 og request sender
                function _erc1271CallerIsSafe(address sender) internal view virtual override returns (bool) {
                    return (
                        sender == 0x000000000000D9ECebf3C23529de49815Dac1c4c // MulticallerWithSigner
                            || sender == msg.sender // Smart Account. Assume smart account never sends non safe eip-712 struct
                            || _safeSenders.contains(msg.sender, sender)
                    ); // check if sender is in _safeSenders for the Smart Account
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.21;
            import { PackedUserOperation } from "account-abstraction/interfaces/PackedUserOperation.sol";
            uint256 constant VALIDATION_SUCCESS = 0;
            uint256 constant VALIDATION_FAILED = 1;
            uint256 constant MODULE_TYPE_VALIDATOR = 1;
            uint256 constant MODULE_TYPE_EXECUTOR = 2;
            uint256 constant MODULE_TYPE_FALLBACK = 3;
            uint256 constant MODULE_TYPE_HOOK = 4;
            uint256 constant MODULE_TYPE_PREVALIDATION_HOOK_ERC1271 = 8;
            uint256 constant MODULE_TYPE_PREVALIDATION_HOOK_ERC4337 = 9;
            interface IModule {
                error AlreadyInitialized(address smartAccount);
                error NotInitialized(address smartAccount);
                /**
                 * @dev This function is called by the smart account during installation of the module
                 * @param data arbitrary data that may be required on the module during `onInstall`
                 * initialization
                 *
                 * MUST revert on error (i.e. if module is already enabled)
                 */
                function onInstall(bytes calldata data) external;
                /**
                 * @dev This function is called by the smart account during uninstallation of the module
                 * @param data arbitrary data that may be required on the module during `onUninstall`
                 * de-initialization
                 *
                 * MUST revert on error
                 */
                function onUninstall(bytes calldata data) external;
                /**
                 * @dev Returns boolean value if module is a certain type
                 * @param moduleTypeId the module type ID according the ERC-7579 spec
                 *
                 * MUST return true if the module is of the given type and false otherwise
                 */
                function isModuleType(uint256 moduleTypeId) external view returns (bool);
                /**
                 * @dev Returns if the module was already initialized for a provided smartaccount
                 */
                function isInitialized(address smartAccount) external view returns (bool);
            }
            interface IValidator is IModule {
                error InvalidTargetAddress(address target);
                /**
                 * @dev Validates a transaction on behalf of the account.
                 *         This function is intended to be called by the MSA during the ERC-4337 validaton phase
                 *         Note: solely relying on bytes32 hash and signature is not suffcient for some
                 * validation implementations (i.e. SessionKeys often need access to userOp.calldata)
                 * @param userOp The user operation to be validated. The userOp MUST NOT contain any metadata.
                 * The MSA MUST clean up the userOp before sending it to the validator.
                 * @param userOpHash The hash of the user operation to be validated
                 * @return return value according to ERC-4337
                 */
                function validateUserOp(
                    PackedUserOperation calldata userOp,
                    bytes32 userOpHash
                )
                    external
                    returns (uint256);
                /**
                 * Validator can be used for ERC-1271 validation
                 */
                function isValidSignatureWithSender(
                    address sender,
                    bytes32 hash,
                    bytes calldata data
                )
                    external
                    view
                    returns (bytes4);
            }
            interface IExecutor is IModule { }
            interface IHook is IModule {
                function preCheck(
                    address msgSender,
                    uint256 msgValue,
                    bytes calldata msgData
                )
                    external
                    returns (bytes memory hookData);
                function postCheck(bytes calldata hookData) external;
            }
            interface IFallback is IModule { }
            interface IPreValidationHookERC1271 is IModule {
                function preValidationHookERC1271(
                    address sender,
                    bytes32 hash,
                    bytes calldata data
                )
                    external
                    view
                    returns (bytes32 hookHash, bytes memory hookSignature);
            }
            interface IPreValidationHookERC4337 is IModule {
                function preValidationHookERC4337(
                    PackedUserOperation calldata userOp,
                    uint256 missingAccountFunds,
                    bytes32 userOpHash
                )
                    external
                    returns (bytes32 hookHash, bytes memory hookSignature);
            }
            // SPDX-License-Identifier: LGPL-3.0-only
            pragma solidity ^0.8.23;
            import {IModule} from "erc7579/interfaces/IERC7579Module.sol";
            uint256 constant ERC7579_MODULE_TYPE_STATELESS_VALIDATOR = 7;
            /**
             * ISessionValidator is a contract that validates signatures for a given session.
             * this interface expects to validate the signature in a stateless way.
             * all parameters required to validate the signature are passed in the function call.
             * Only one ISessionValidator is responsible to validate a userOp.
             * if you want to use multiple validators, you can create a ISessionValidator that aggregates multiple signatures that
             * are packed into userOp.signature
             * It is used to validate the signature of a session.
             *  hash The userOp hash
             *  sig The signature of userOp
             *  data the config data that is used to validate the signature
             */
            interface ISessionValidator is IModule {
                function validateSignatureWithData(bytes32 hash, bytes calldata sig, bytes calldata data)
                    external
                    view
                    returns (bool validSig);
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.20;
            import "./AssociatedArrayLib.sol";
            /**
             * Fork of OZ's EnumerableSet that makes all storage access ERC-4337 compliant via associated storage
             * @author zeroknots.eth (rhinestone)
             */
            library EnumerableSet {
                using AssociatedArrayLib for AssociatedArrayLib.Bytes32Array;
                // To implement this library for multiple types with as little code
                // repetition as possible, we write it in terms of a generic Set type with
                // bytes32 values.
                // The Set implementation uses private functions, and user-facing
                // implementations (such as AddressSet) are just wrappers around the
                // underlying Set.
                // This means that we can only create new EnumerableSets for types that fit
                // in bytes32.
                struct Set {
                    // Storage of set values
                    AssociatedArrayLib.Bytes32Array _values;
                    // Position is the index of the value in the `values` array plus 1.
                    // Position 0 is used to mean a value is not in the set.
                    mapping(bytes32 value => mapping(address account => uint256)) _positions;
                }
                /**
                 * @dev Add a value to a set. O(1).
                 *
                 * Returns true if the value was added to the set, that is if it was not
                 * already present.
                 */
                function _add(Set storage set, address account, bytes32 value) private returns (bool) {
                    if (!_contains(set, account, value)) {
                        set._values.push(account, value);
                        // The value is stored at length-1, but we add 1 to all indexes
                        // and use 0 as a sentinel value
                        set._positions[value][account] = set._values.length(account);
                        return true;
                    } else {
                        return false;
                    }
                }
                /**
                 * @dev Removes a value from a set. O(1).
                 *
                 * Returns true if the value was removed from the set, that is if it was
                 * present.
                 */
                function _remove(Set storage set, address account, bytes32 value) private returns (bool) {
                    // We cache the value's position to prevent multiple reads from the same storage slot
                    uint256 position = set._positions[value][account];
                    if (position != 0) {
                        // Equivalent to contains(set, value)
                        // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
                        // the array, and then remove the last element (sometimes called as 'swap and pop').
                        // This modifies the order of the array, as noted in {at}.
                        uint256 valueIndex = position - 1;
                        uint256 lastIndex = set._values.length(account) - 1;
                        if (valueIndex != lastIndex) {
                            bytes32 lastValue = set._values.get(account, lastIndex);
                            // Move the lastValue to the index where the value to delete is
                            set._values.set(account, valueIndex, lastValue);
                            // Update the tracked position of the lastValue (that was just moved)
                            set._positions[lastValue][account] = position;
                        }
                        // Delete the slot where the moved value was stored
                        set._values.pop(account);
                        // Delete the tracked position for the deleted slot
                        delete set._positions[value][account];
                        return true;
                    } else {
                        return false;
                    }
                }
                function _removeAll(Set storage set, address account) internal {
                    // get length of the array
                    uint256 len = _length(set, account);
                    for (uint256 i = 1; i <= len; i++) {
                        // get last value
                        bytes32 value = _at(set, account, len - i);
                        _remove(set, account, value);
                    }
                }
                /**
                 * @dev Returns true if the value is in the set. O(1).
                 */
                function _contains(Set storage set, address account, bytes32 value) private view returns (bool) {
                    return set._positions[value][account] != 0;
                }
                /**
                 * @dev Returns the number of values on the set. O(1).
                 */
                function _length(Set storage set, address account) private view returns (uint256) {
                    return set._values.length(account);
                }
                /**
                 * @dev Returns the value stored at position `index` in the set. O(1).
                 *
                 * Note that there are no guarantees on the ordering of values inside the
                 * array, and it may change when more values are added or removed.
                 *
                 * Requirements:
                 *
                 * - `index` must be strictly less than {length}.
                 */
                function _at(Set storage set, address account, uint256 index) private view returns (bytes32) {
                    return set._values.get(account, index);
                }
                /**
                 * @dev Return the entire set in an array
                 *
                 * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
                 * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
                 * this function has an unbounded cost, and using it as part of a state-changing function may render the function
                 * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
                 */
                function _values(Set storage set, address account) private view returns (bytes32[] memory) {
                    return set._values.getAll(account);
                }
                // Bytes32Set
                struct Bytes32Set {
                    Set _inner;
                }
                /**
                 * @dev Add a value to a set. O(1).
                 *
                 * Returns true if the value was added to the set, that is if it was not
                 * already present.
                 */
                function add(Bytes32Set storage set, address account, bytes32 value) internal returns (bool) {
                    return _add(set._inner, account, value);
                }
                /**
                 * @dev Removes a value from a set. O(1).
                 *
                 * Returns true if the value was removed from the set, that is if it was
                 * present.
                 */
                function remove(Bytes32Set storage set, address account, bytes32 value) internal returns (bool) {
                    return _remove(set._inner, account, value);
                }
                function removeAll(Bytes32Set storage set, address account) internal {
                    return _removeAll(set._inner, account);
                }
                /**
                 * @dev Returns true if the value is in the set. O(1).
                 */
                function contains(Bytes32Set storage set, address account, bytes32 value) internal view returns (bool) {
                    return _contains(set._inner, account, value);
                }
                /**
                 * @dev Returns the number of values in the set. O(1).
                 */
                function length(Bytes32Set storage set, address account) internal view returns (uint256) {
                    return _length(set._inner, account);
                }
                /**
                 * @dev Returns the value stored at position `index` in the set. O(1).
                 *
                 * Note that there are no guarantees on the ordering of values inside the
                 * array, and it may change when more values are added or removed.
                 *
                 * Requirements:
                 *
                 * - `index` must be strictly less than {length}.
                 */
                function at(Bytes32Set storage set, address account, uint256 index) internal view returns (bytes32) {
                    return _at(set._inner, account, index);
                }
                /**
                 * @dev Return the entire set in an array
                 *
                 * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
                 * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
                 * this function has an unbounded cost, and using it as part of a state-changing function may render the function
                 * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
                 */
                function values(Bytes32Set storage set, address account) internal view returns (bytes32[] memory) {
                    bytes32[] memory store = _values(set._inner, account);
                    bytes32[] memory result;
                    /// @solidity memory-safe-assembly
                    assembly {
                        result := store
                    }
                    return result;
                }
                // AddressSet
                struct AddressSet {
                    Set _inner;
                }
                /**
                 * @dev Add a value to a set. O(1).
                 *
                 * Returns true if the value was added to the set, that is if it was not
                 * already present.
                 */
                function add(AddressSet storage set, address account, address value) internal returns (bool) {
                    return _add(set._inner, account, bytes32(uint256(uint160(value))));
                }
                /**
                 * @dev Removes a value from a set. O(1).
                 *
                 * Returns true if the value was removed from the set, that is if it was
                 * present.
                 */
                function remove(AddressSet storage set, address account, address value) internal returns (bool) {
                    return _remove(set._inner, account, bytes32(uint256(uint160(value))));
                }
                function removeAll(AddressSet storage set, address account) internal {
                    return _removeAll(set._inner, account);
                }
                /**
                 * @dev Returns true if the value is in the set. O(1).
                 */
                function contains(AddressSet storage set, address account, address value) internal view returns (bool) {
                    return _contains(set._inner, account, bytes32(uint256(uint160(value))));
                }
                /**
                 * @dev Returns the number of values in the set. O(1).
                 */
                function length(AddressSet storage set, address account) internal view returns (uint256) {
                    return _length(set._inner, account);
                }
                /**
                 * @dev Returns the value stored at position `index` in the set. O(1).
                 *
                 * Note that there are no guarantees on the ordering of values inside the
                 * array, and it may change when more values are added or removed.
                 *
                 * Requirements:
                 *
                 * - `index` must be strictly less than {length}.
                 */
                function at(AddressSet storage set, address account, uint256 index) internal view returns (address) {
                    return address(uint160(uint256(_at(set._inner, account, index))));
                }
                /**
                 * @dev Return the entire set in an array
                 *
                 * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
                 * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
                 * this function has an unbounded cost, and using it as part of a state-changing function may render the function
                 * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
                 */
                function values(AddressSet storage set, address account) internal view returns (address[] memory) {
                    bytes32[] memory store = _values(set._inner, account);
                    address[] memory result;
                    /// @solidity memory-safe-assembly
                    assembly {
                        result := store
                    }
                    return result;
                }
                // UintSet
                struct UintSet {
                    Set _inner;
                }
                /**
                 * @dev Add a value to a set. O(1).
                 *
                 * Returns true if the value was added to the set, that is if it was not
                 * already present.
                 */
                function add(UintSet storage set, address account, uint256 value) internal returns (bool) {
                    return _add(set._inner, account, bytes32(value));
                }
                /**
                 * @dev Removes a value from a set. O(1).
                 *
                 * Returns true if the value was removed from the set, that is if it was
                 * present.
                 */
                function remove(UintSet storage set, address account, uint256 value) internal returns (bool) {
                    return _remove(set._inner, account, bytes32(value));
                }
                function removeAll(UintSet storage set, address account) internal {
                    return _removeAll(set._inner, account);
                }
                /**
                 * @dev Returns true if the value is in the set. O(1).
                 */
                function contains(UintSet storage set, address account, uint256 value) internal view returns (bool) {
                    return _contains(set._inner, account, bytes32(value));
                }
                /**
                 * @dev Returns the number of values in the set. O(1).
                 */
                function length(UintSet storage set, address account) internal view returns (uint256) {
                    return _length(set._inner, account);
                }
                /**
                 * @dev Returns the value stored at position `index` in the set. O(1).
                 *
                 * Note that there are no guarantees on the ordering of values inside the
                 * array, and it may change when more values are added or removed.
                 *
                 * Requirements:
                 *
                 * - `index` must be strictly less than {length}.
                 */
                function at(UintSet storage set, address account, uint256 index) internal view returns (uint256) {
                    return uint256(_at(set._inner, account, index));
                }
                /**
                 * @dev Return the entire set in an array
                 *
                 * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
                 * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
                 * this function has an unbounded cost, and using it as part of a state-changing function may render the function
                 * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
                 */
                function values(UintSet storage set, address account) internal view returns (uint256[] memory) {
                    bytes32[] memory store = _values(set._inner, account);
                    uint256[] memory result;
                    /// @solidity memory-safe-assembly
                    assembly {
                        result := store
                    }
                    return result;
                }
            }
            // SPDX-License-Identifier: GPL-3.0
            pragma solidity >=0.7.5;
            /**
             * User Operation struct
             * @param sender                - The sender account of this request.
             * @param nonce                 - Unique value the sender uses to verify it is not a replay.
             * @param initCode              - If set, the account contract will be created by this constructor/
             * @param callData              - The method call to execute on this account.
             * @param accountGasLimits      - Packed gas limits for validateUserOp and gas limit passed to the callData method call.
             * @param preVerificationGas    - Gas not calculated by the handleOps method, but added to the gas paid.
             *                                Covers batch overhead.
             * @param gasFees               - packed gas fields maxPriorityFeePerGas and maxFeePerGas - Same as EIP-1559 gas parameters.
             * @param paymasterAndData      - If set, this field holds the paymaster address, verification gas limit, postOp gas limit and paymaster-specific extra data
             *                                The paymaster will pay for the transaction instead of the sender.
             * @param signature             - Sender-verified signature over the entire request, the EntryPoint address and the chain ID.
             */
            struct PackedUserOperation {
                address sender;
                uint256 nonce;
                bytes initCode;
                bytes callData;
                bytes32 accountGasLimits;
                uint256 preVerificationGas;
                bytes32 gasFees;
                bytes paymasterAndData;
                bytes signature;
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.27;
            interface IERC5267 {
                function eip712Domain() external view returns (
                    bytes1 fields,
                    string memory name,
                    string memory version,
                    uint256 chainId,
                    address verifyingContract,
                    bytes32 salt,
                    uint256[] memory extensions
                );
            }
            /// @title ERC-7739: Nested Typed Data Sign Support for ERC-7579 Validators
            abstract contract ERC7739Validator {
                error InvalidSignature();
                
                /// @dev `keccak256("PersonalSign(bytes prefixed)")`.
                bytes32 internal constant _PERSONAL_SIGN_TYPEHASH = 0x983e65e5148e570cd828ead231ee759a8d7958721a768f93bc4483ba005c32de;
                bytes32 internal constant _DOMAIN_TYPEHASH = 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;
                bytes4 internal constant SUPPORTS_ERC7739_V1 = 0x77390001;
                /*//////////////////////////////////////////////////////////////////////////
                                                 INTERNAL
                //////////////////////////////////////////////////////////////////////////*/
                /// @dev Returns whether the `signature` is valid for the `hash.
                /// Use this in your validator's `isValidSignatureWithSender` implementation.
                function _erc1271IsValidSignatureWithSender(address sender, bytes32 hash, bytes calldata signature)
                    internal
                    view
                    virtual
                    returns (bytes4)
                {   
                    // detection request
                    // this check only takes 17 gas units
                    // in theory, it can be moved out of this function so it doesn't apply to every
                    // isValidSignatureWithSender() call, but it would require an additional standard
                    // interface for SA to check if the IValidator supports ERC-7739
                    // while isValidSignatureWithSender() is specified by ERC-7579, so
                    // it makes sense to use it in SA to check if the validator supports ERC-7739
                    unchecked {
                        if (signature.length == uint256(0)) {
                            // Forces the compiler to optimize for smaller bytecode size.
                            if (uint256(hash) == ~signature.length / 0xffff * 0x7739) 
                                return SUPPORTS_ERC7739_V1;
                        }
                    }
                    // sig malleability prevention
                    bytes32 s;
                    assembly {
                        // same as `s := mload(add(signature, 0x40))` but for calldata
                        s := calldataload(add(signature.offset, 0x20))
                    }
                    if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
                        revert InvalidSignature();
                    }
                    bool success = _erc1271IsValidSignatureViaSafeCaller(sender, hash, signature)
                        || _erc1271IsValidSignatureViaNestedEIP712(hash, signature)
                        || _erc1271IsValidSignatureViaRPC(hash, signature);
                    bytes4 sigValidationResult;
                    assembly {
                        // `success ? bytes4(keccak256("isValidSignature(bytes32,bytes)")) : 0xffffffff`.
                        // We use `0xffffffff` for invalid, in convention with the reference implementation.
                        sigValidationResult := shl(224, or(0x1626ba7e, sub(0, iszero(success))))
                    }
                    return sigValidationResult;
                }
                /// @dev Returns whether the `msg.sender` is considered safe, such
                /// that we don't need to use the nested EIP-712 workflow.
                /// Override to return true for more callers.
                /// See: https://mirror.xyz/curiousapple.eth/pFqAdW2LiJ-6S4sg_u1z08k4vK6BCJ33LcyXpnNb8yU
                function _erc1271CallerIsSafe(address sender) internal view virtual returns (bool) {
                    // The canonical `MulticallerWithSigner` at 0x000000000000D9ECebf3C23529de49815Dac1c4c
                    // is known to include the account in the hash to be signed.
                    return sender == 0x000000000000D9ECebf3C23529de49815Dac1c4c;
                }
                /// @dev Returns whether the `hash` and `signature` are valid.
                ///      Obtains the authorized signer's credentials and calls some
                ///      module's specific internal function to validate the signature
                ///      against credentials.
                /// Override for your module's custom logic.
                function _erc1271IsValidSignatureNowCalldata(bytes32 hash, bytes calldata signature)
                    internal
                    view
                    virtual
                    returns (bool);
                /// @dev Unwraps and returns the signature.
                function _erc1271UnwrapSignature(bytes calldata signature)
                    internal
                    view
                    virtual
                    returns (bytes calldata result)
                {
                    result = signature;
                    /// @solidity memory-safe-assembly
                    assembly {
                        // Unwraps the ERC6492 wrapper if it exists.
                        // See: https://eips.ethereum.org/EIPS/eip-6492
                        if eq(
                            calldataload(add(result.offset, sub(result.length, 0x20))),
                            mul(0x6492, div(not(shr(address(), address())), 0xffff)) // `0x6492...6492`.
                        ) {
                            let o := add(result.offset, calldataload(add(result.offset, 0x40)))
                            result.length := calldataload(o)
                            result.offset := add(o, 0x20)
                        }
                    }
                }
                /// @dev Performs the signature validation without nested EIP-712 if the caller is
                /// a safe caller. A safe caller must include the address of this account in the hash.
                function _erc1271IsValidSignatureViaSafeCaller(address sender, bytes32 hash, bytes calldata signature)
                    internal
                    view
                    virtual
                    returns (bool result)
                {
                    if (_erc1271CallerIsSafe(sender)) result = _erc1271IsValidSignatureNowCalldata(hash, signature);
                }
                /// @dev ERC1271 signature validation (Nested EIP-712 workflow).
                ///
                /// This uses ECDSA recovery by default (see: `_erc1271IsValidSignatureNowCalldata`).
                /// It also uses a nested EIP-712 approach to prevent signature replays when a single EOA
                /// owns multiple smart contract accounts,
                /// while still enabling wallet UIs (e.g. Metamask) to show the EIP-712 values.
                ///
                /// Crafted for phishing resistance, efficiency, flexibility.
                /// __________________________________________________________________________________________
                ///
                /// Glossary:
                ///
                /// - `APP_DOMAIN_SEPARATOR`: The domain separator of the `hash` passed in by the application.
                ///   Provided by the front end. Intended to be the domain separator of the contract
                ///   that will call `isValidSignature` on this account.
                ///
                /// - `ACCOUNT_DOMAIN_SEPARATOR`: The domain separator of this account.
                ///   See: `EIP712._domainSeparator()`.
                /// __________________________________________________________________________________________
                ///
                /// For the `TypedDataSign` workflow, the final hash will be:
                /// ```
                ///     keccak256(\\x19\\x01 ‖ APP_DOMAIN_SEPARATOR ‖
                ///         hashStruct(TypedDataSign({
                ///             contents: hashStruct(originalStruct),
                ///             name: keccak256(bytes(eip712Domain().name)),
                ///             version: keccak256(bytes(eip712Domain().version)),
                ///             chainId: eip712Domain().chainId,
                ///             verifyingContract: eip712Domain().verifyingContract,
                ///             salt: eip712Domain().salt
                ///         }))
                ///     )
                /// ```
                /// where `‖` denotes the concatenation operator for bytes.
                /// The order of the fields is important: `contents` comes before `name`.
                ///
                /// The signature will be `r ‖ s ‖ v ‖ APP_DOMAIN_SEPARATOR ‖
                ///     contents ‖ contentsDescription ‖ uint16(contentsDescription.length)`,
                /// where:
                /// - `contents` is the bytes32 struct hash of the original struct.
                /// - `contentsDescription` can be either:
                ///     a) `contentsType` (implicit mode)
                ///         where `contentsType` starts with `contentsName`.
                ///     b) `contentsType ‖ contentsName` (explicit mode)
                ///         where `contentsType` may not necessarily start with `contentsName`.
                ///
                /// The `APP_DOMAIN_SEPARATOR` and `contents` will be used to verify if `hash` is indeed correct.
                /// __________________________________________________________________________________________
                ///
                /// For the `PersonalSign` workflow, the final hash will be:
                /// ```
                ///     keccak256(\\x19\\x01 ‖ ACCOUNT_DOMAIN_SEPARATOR ‖
                ///         hashStruct(PersonalSign({
                ///             prefixed: keccak256(bytes(\\x19Ethereum Signed Message:\
             ‖
                ///                 base10(bytes(someString).length) ‖ someString))
                ///         }))
                ///     )
                /// ```
                /// where `‖` denotes the concatenation operator for bytes.
                ///
                /// The `PersonalSign` type hash will be `keccak256("PersonalSign(bytes prefixed)")`.
                /// The signature will be `r ‖ s ‖ v`.
                /// __________________________________________________________________________________________
                ///
                /// For demo and typescript code, see:
                /// - https://github.com/junomonster/nested-eip-712
                /// - https://github.com/frangio/eip712-wrapper-for-eip1271
                ///
                /// Their nomenclature may differ from ours, although the high-level idea is similar.
                ///
                /// Of course, if you have control over the codebase of the wallet client(s) too,
                /// you can choose a more minimalistic signature scheme like
                /// `keccak256(abi.encode(address(this), hash))` instead of all these acrobatics.
                /// All these are just for widespread out-of-the-box compatibility with other wallet clients.
                /// We want to create bazaars, not walled castles.
                /// And we'll use push the Turing Completeness of the EVM to the limits to do so.
                function _erc1271IsValidSignatureViaNestedEIP712(bytes32 hash, bytes calldata signature)
                    internal
                    view
                    virtual
                    returns (bool result)
                {
                    //bytes32 t = _typedDataSignFieldsForAccount(msg.sender);
                    uint256 t = uint256(uint160(address(this)));
                    // Forces the compiler to pop the variables after the scope, avoiding stack-too-deep.
                    if (t != uint256(0)) {
                        (
                            ,
                            string memory name,
                            string memory version,
                            uint256 chainId,
                            address verifyingContract,
                            bytes32 salt,
                        ) = IERC5267(msg.sender).eip712Domain();
                        /// @solidity memory-safe-assembly
                        assembly {
                            t := mload(0x40) // Grab the free memory pointer.
                            // Skip 2 words for the `typedDataSignTypehash` and `contents` struct hash.
                            mstore(add(t, 0x40), keccak256(add(name, 0x20), mload(name)))
                            mstore(add(t, 0x60), keccak256(add(version, 0x20), mload(version)))
                            mstore(add(t, 0x80), chainId)
                            mstore(add(t, 0xa0), shr(96, shl(96, verifyingContract)))
                            mstore(add(t, 0xc0), salt)
                            mstore(0x40, add(t, 0xe0)) // Allocate the memory.
                        }
                    }
                    /// @solidity memory-safe-assembly
                    assembly {
                        let m := mload(0x40) // Cache the free memory pointer.
                        // `c` is `contentsDescription.length`, which is stored in the last 2 bytes of the signature.
                        let c := shr(240, calldataload(add(signature.offset, sub(signature.length, 2))))
                        for {} 1 {} {
                            let l := add(0x42, c) // Total length of appended data (32 + 32 + c + 2).
                            let o := add(signature.offset, sub(signature.length, l)) // Offset of appended data.
                            mstore(0x00, 0x1901) // Store the "\\x19\\x01" prefix.
                            calldatacopy(0x20, o, 0x40) // Copy the `APP_DOMAIN_SEPARATOR` and `contents` struct hash.
                            // Use the `PersonalSign` workflow if the reconstructed hash doesn't match,
                            // or if the appended data is invalid, i.e.
                            // `appendedData.length > signature.length || contentsDescription.length == 0`.
                            if or(xor(keccak256(0x1e, 0x42), hash), or(lt(signature.length, l), iszero(c))) {
                                t := 0 // Set `t` to 0, denoting that we need to `hash = _hashTypedData(hash)`.
                                mstore(t, _PERSONAL_SIGN_TYPEHASH)
                                mstore(0x20, hash) // Store the `prefixed`.
                                hash := keccak256(t, 0x40) // Compute the `PersonalSign` struct hash.
                                break
                            }
                            // Else, use the `TypedDataSign` workflow.
                            // `TypedDataSign({ContentsName} contents,string name,...){ContentsType}`.
                            mstore(m, "TypedDataSign(") // Store the start of `TypedDataSign`'s type encoding.
                            let p := add(m, 0x0e) // Advance 14 bytes to skip "TypedDataSign(".
                            
                            calldatacopy(p, add(o, 0x40), c) // Copy `contentsName`, optimistically.
                            mstore(add(p, c), 40) // Store a '(' after the end.
                            if iszero(eq(byte(0, mload(sub(add(p, c), 1))), 41)) {
                                let e := 0 // Length of `contentsName` in explicit mode.
                                for { let q := sub(add(p, c), 1) } 1 {} {
                                    e := add(e, 1) // Scan backwards until we encounter a ')'.
                                    if iszero(gt(lt(e, c), eq(byte(0, mload(sub(q, e))), 41))) { break }
                                }
                                c := sub(c, e) // Truncate `contentsDescription` to `contentsType`.
                                calldatacopy(p, add(add(o, 0x40), c), e) // Copy `contentsName`.
                                mstore8(add(p, e), 40) // Store a '(' exactly right after the end.
                            }
                            // `d & 1 == 1` means that `contentsName` is invalid.
                            let d := shr(byte(0, mload(p)), 0x7fffffe000000000000010000000000) // Starts with `[a-z(]`.
                            // Advance `p` until we encounter '('.
                            for {} iszero(eq(byte(0, mload(p)), 40)) { p := add(p, 1) } {
                                d := or(shr(byte(0, mload(p)), 0x120100000001), d) // Has a byte in ", )\\x00".
                            }
                            mstore(p, " contents,string name,string") // Store the rest of the encoding.
                            mstore(add(p, 0x1c), " version,uint256 chainId,address")
                            mstore(add(p, 0x3c), " verifyingContract,bytes32 salt)")
                            p := add(p, 0x5c)
                            calldatacopy(p, add(o, 0x40), c) // Copy `contentsType`.
                            // Fill in the missing fields of the `TypedDataSign`.
                            calldatacopy(t, o, 0x40) // Copy the `contents` struct hash to `add(t, 0x20)`.
                            mstore(t, keccak256(m, sub(add(p, c), m))) // Store `typedDataSignTypehash`.
                            // The "\\x19\\x01" prefix is already at 0x00.
                            // `APP_DOMAIN_SEPARATOR` is already at 0x20.
                            mstore(0x40, keccak256(t, 0xe0)) // `hashStruct(typedDataSign)`.
                            // Compute the final hash, corrupted if `contentsName` is invalid.
                            hash := keccak256(0x1e, add(0x42, and(1, d)))
                            signature.length := sub(signature.length, l) // Truncate the signature.
                            break
                        }
                        mstore(0x40, m) // Restore the free memory pointer.
                    }
                    if (t == uint256(0)) hash = _hashTypedDataForAccount(msg.sender, hash); // `PersonalSign` workflow.
                    result = _erc1271IsValidSignatureNowCalldata(hash, signature);
                }
                /// @dev Performs the signature validation without nested EIP-712 to allow for easy sign ins.
                /// This function must always return false or revert if called on-chain.
                function _erc1271IsValidSignatureViaRPC(bytes32 hash, bytes calldata signature)
                    internal
                    view
                    virtual
                    returns (bool result)
                {
                    // Non-zero gasprice is a heuristic to check if a call is on-chain,
                    // but we can't fully depend on it because it can be manipulated.
                    // See: https://x.com/NoahCitron/status/1580359718341484544
                    if (tx.gasprice == uint256(0)) {
                        /// @solidity memory-safe-assembly
                        assembly {
                            mstore(gasprice(), gasprice())
                            // See: https://gist.github.com/Vectorized/3c9b63524d57492b265454f62d895f71
                            let b := 0x000000000000378eDCD5B5B0A24f5342d8C10485 // Basefee contract,
                            pop(staticcall(0xffff, b, codesize(), gasprice(), gasprice(), 0x20))
                            // If `gasprice < basefee`, the call cannot be on-chain, and we can skip the gas burn.
                            if iszero(mload(gasprice())) {
                                let m := mload(0x40) // Cache the free memory pointer.
                                mstore(gasprice(), 0x1626ba7e) // `isValidSignature(bytes32,bytes)`.
                                mstore(0x20, b) // Recycle `b` to denote if we need to burn gas.
                                mstore(0x40, 0x40)
                                let gasToBurn := or(add(0xffff, gaslimit()), gaslimit())
                                // Burns gas computationally efficiently. Also, requires that `gas > gasToBurn`.
                                if or(eq(hash, b), lt(gas(), gasToBurn)) { invalid() }
                                // Make a call to this with `b`, efficiently burning the gas provided.
                                // No valid transaction can consume more than the gaslimit.
                                // See: https://ethereum.github.io/yellowpaper/paper.pdf
                                // Most RPCs perform calls with a gas budget greater than the gaslimit.
                                pop(staticcall(gasToBurn, address(), 0x1c, 0x64, gasprice(), gasprice()))
                                mstore(0x40, m) // Restore the free memory pointer.
                            }
                        }
                        result = _erc1271IsValidSignatureNowCalldata(hash, signature);
                    }
                }
                /// @notice Hashes typed data according to eip-712
                ///         Uses account's domain separator
                /// @param account the smart account, who's domain separator will be used
                /// @param structHash the typed data struct hash
                function _hashTypedDataForAccount(address account, bytes32 structHash) private view returns (bytes32 digest) {
                    (
                        /*bytes1 fields*/,
                        string memory name,
                        string memory version,
                        uint256 chainId,
                        address verifyingContract,
                        /*bytes32 salt*/,
                        /*uint256[] memory extensions*/
                    ) = IERC5267(account).eip712Domain();
                    /// @solidity memory-safe-assembly
                    assembly {
                        //Rebuild domain separator out of 712 domain
                        let m := mload(0x40) // Load the free memory pointer.
                        mstore(m, _DOMAIN_TYPEHASH)
                        mstore(add(m, 0x20), keccak256(add(name, 0x20), mload(name))) // Name hash.
                        mstore(add(m, 0x40), keccak256(add(version, 0x20), mload(version))) // Version hash.
                        mstore(add(m, 0x60), chainId)
                        mstore(add(m, 0x80), verifyingContract)
                        digest := keccak256(m, 0xa0) //domain separator
                        // Hash typed data
                        mstore(0x00, 0x1901000000000000) // Store "\\x19\\x01".
                        mstore(0x1a, digest) // Store the domain separator.
                        mstore(0x3a, structHash) // Store the struct hash.
                        digest := keccak256(0x18, 0x42)
                        // Restore the part of the free memory slot that was overwritten.
                        mstore(0x3a, 0)
                    }
                }
                /// @dev Backwards compatibility stuff
                /// For automatic detection that the smart account supports the nested EIP-712 workflow.
                /// By default, it returns `bytes32(bytes4(keccak256("supportsNestedTypedDataSign()")))`,
                /// denoting support for the default behavior, as implemented in
                /// `_erc1271IsValidSignatureViaNestedEIP712`, which is called in `isValidSignature`.
                /// Future extensions should return a different non-zero `result` to denote different behavior.
                /// This method intentionally returns bytes32 to allow freedom for future extensions.
                function supportsNestedTypedDataSign() public view virtual returns (bytes32 result) {
                    result = bytes4(0xd620c85a);
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.27;
            bytes3 constant SIG_TYPE_MEE_FLOW = 0x177eee;
            bytes4 constant SIG_TYPE_SIMPLE = 0x177eee00;
            bytes4 constant SIG_TYPE_ON_CHAIN = 0x177eee01;
            bytes4 constant SIG_TYPE_ERC20_PERMIT = 0x177eee02;
            // ...other sig types: ERC-7683, Permit2, etc
            bytes4 constant EIP1271_SUCCESS = 0x1626ba7e;
            bytes4 constant EIP1271_FAILED = 0xffffffff;
            uint256 constant MODULE_TYPE_STATELESS_VALIDATOR = 7;
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.27;
            import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
            import {MerkleProof} from "openzeppelin/utils/cryptography/MerkleProof.sol";
            import {EcdsaLib} from "../util/EcdsaLib.sol";
            import {MEEUserOpHashLib} from "../util/MEEUserOpHashLib.sol";
            import {IERC20Permit} from "openzeppelin/token/ERC20/extensions/IERC20Permit.sol";
            import {IERC20} from "openzeppelin/token/ERC20/IERC20.sol";
            import "account-abstraction/core/Helpers.sol";
            /**
             * @dev Library to validate the signature for MEE ERC-2612 Permit mode
             *      This is the mode where superTx hash is pasted into deadline field of the ERC-2612 Permit
             *      So the whole permit is signed along with the superTx hash
             *      For more details see Fusion docs:
             *      - https://ethresear.ch/t/fusion-module-7702-alternative-with-no-protocol-changes/20949
             *      - https://docs.biconomy.io/explained/eoa#fusion-module
             *
             *      @dev Important: since ERC20 permit token knows nothing about the MEE, it will treat the superTx hash as a deadline:
             *      -  if (very unlikely) the superTx hash being converted to uint256 is a timestamp in the past, the permit will fail
             *      -  the deadline with most superTx hashes will be very far in the future
             *
             *      @dev Since at this point bytes32 superTx hash is a blind hash, users and wallets should pay attention if
             *           the permit2 deadline field does not make sense as the timestamp. In this case, it can be a sign of a
             *           phishing attempt (injecting super txn hash as the deadline) and the user should not sign the permit.
             *           This is going to be mitigated in the future by making superTx hash a EIP-712 hash.
             */
            bytes32 constant PERMIT_TYPEHASH =
                keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
            struct DecodedErc20PermitSig {
                IERC20Permit token;
                address spender;
                bytes32 domainSeparator;
                uint256 amount;
                uint256 nonce;
                bool isPermitTx;
                bytes32 superTxHash;
                uint48 lowerBoundTimestamp;
                uint48 upperBoundTimestamp;
                uint8 v;
                bytes32 r;
                bytes32 s;
                bytes32[] proof;
            }
            struct DecodedErc20PermitSigShort {
                address spender;
                bytes32 domainSeparator;
                uint256 amount;
                uint256 nonce;
                bytes32 superTxHash;
                uint8 v;
                bytes32 r;
                bytes32 s;
                bytes32[] proof;
            }
            library PermitValidatorLib {
                error PermitFailed();
                uint8 constant EIP_155_MIN_V_VALUE = 37;
                using MessageHashUtils for bytes32;
                /**
                 * This function parses the given userOpSignature into a DecodedErc20PermitSig data structure.
                 *
                 * Once parsed, the function will check for two conditions:
                 *      1. is the userOp part of the merkle tree
                 *      2. is the recovered message signer equal to the expected signer?
                 *
                 * NOTES: This function will revert if either of following is met:
                 *    1. the userOpSignature couldn't be abi.decoded into a valid DecodedErc20PermitSig struct as defined in this contract
                 *    2. userOp is not part of the merkle tree
                 *    3. recovered Permit message signer wasn't equal to the expected signer
                 *
                 * The function will also perform the Permit approval on the given token in case the
                 * isPermitTx flag was set to true in the decoded signature struct.
                 *
                 * @param userOpHash UserOp hash being validated.
                 * @param parsedSignature Signature provided as the userOp.signature parameter (minus the prepended tx type byte).
                 * @param expectedSigner Signer expected to be recovered when decoding the ERC20OPermit signature.
                 */
                function validateUserOp(bytes32 userOpHash, bytes calldata parsedSignature, address expectedSigner)
                    internal
                    returns (uint256)
                {
                    DecodedErc20PermitSig memory decodedSig = _decodeFullPermitSig(parsedSignature);
                    bytes32 meeUserOpHash = MEEUserOpHashLib.getMEEUserOpHash(
                        userOpHash, decodedSig.lowerBoundTimestamp, decodedSig.upperBoundTimestamp
                    );
                    if (
                        !EcdsaLib.isValidSignature(
                            expectedSigner,
                            _getSignedDataHash(expectedSigner, decodedSig),
                            abi.encodePacked(decodedSig.r, decodedSig.s, uint8(decodedSig.v))
                        )
                    ) {
                        return SIG_VALIDATION_FAILED;
                    }
                    if (!MerkleProof.verify(decodedSig.proof, decodedSig.superTxHash, meeUserOpHash)) {
                        return SIG_VALIDATION_FAILED;
                    }
                    if (decodedSig.isPermitTx) {
                        try decodedSig.token.permit(
                            expectedSigner,
                            decodedSig.spender,
                            decodedSig.amount,
                            uint256(decodedSig.superTxHash),
                            uint8(decodedSig.v),
                            decodedSig.r,
                            decodedSig.s
                        ) {
                            // all good
                        } catch {
                            // check if by some reason this permit was already successfully used (and not spent yet)
                            if (IERC20(address(decodedSig.token)).allowance(expectedSigner, decodedSig.spender) < decodedSig.amount)
                            {
                                // if the above expectationis not true, revert
                                revert PermitFailed();
                            }
                        }
                    }
                    return _packValidationData(false, decodedSig.upperBoundTimestamp, decodedSig.lowerBoundTimestamp);
                }
                function validateSignatureForOwner(address expectedSigner, bytes32 dataHash, bytes calldata parsedSignature)
                    internal
                    view
                    returns (bool)
                {
                    DecodedErc20PermitSigShort calldata decodedSig = _decodeShortPermitSig(parsedSignature);
                    if (
                        !EcdsaLib.isValidSignature(
                            expectedSigner,
                            _getSignedDataHash(expectedSigner, decodedSig),
                            abi.encodePacked(decodedSig.r, decodedSig.s, uint8(decodedSig.v))
                        )
                    ) {
                        return false;
                    }
                    if (!MerkleProof.verify(decodedSig.proof, decodedSig.superTxHash, dataHash)) {
                        return false;
                    }
                    return true;
                }
                function _decodeFullPermitSig(bytes calldata parsedSignature)
                    private
                    pure
                    returns (DecodedErc20PermitSig calldata decodedSig)
                {
                    assembly {
                        decodedSig := add(parsedSignature.offset, 0x20)
                    }
                }
                function _decodeShortPermitSig(bytes calldata parsedSignature)
                    private
                    pure
                    returns (DecodedErc20PermitSigShort calldata)
                {
                    DecodedErc20PermitSigShort calldata decodedSig;
                    assembly {
                        decodedSig := add(parsedSignature.offset, 0x20)
                    }
                    return decodedSig;
                }
                function _getSignedDataHash(address expectedSigner, DecodedErc20PermitSig memory decodedSig)
                    private
                    pure
                    returns (bytes32)
                {
                    uint256 deadline = uint256(decodedSig.superTxHash);
                    bytes32 structHash = keccak256(
                        abi.encode(
                            PERMIT_TYPEHASH, expectedSigner, decodedSig.spender, decodedSig.amount, decodedSig.nonce, deadline
                        )
                    );
                    return _hashTypedData(structHash, decodedSig.domainSeparator);
                }
                function _getSignedDataHash(address expectedSigner, DecodedErc20PermitSigShort memory decodedSig)
                    private
                    pure
                    returns (bytes32)
                {
                    uint256 deadline = uint256(decodedSig.superTxHash);
                    bytes32 structHash = keccak256(
                        abi.encode(
                            PERMIT_TYPEHASH, expectedSigner, decodedSig.spender, decodedSig.amount, decodedSig.nonce, deadline
                        )
                    );
                    return _hashTypedData(structHash, decodedSig.domainSeparator);
                }
                function _hashTypedData(bytes32 structHash, bytes32 domainSeparator) private pure returns (bytes32) {
                    return MessageHashUtils.toTypedDataHash(domainSeparator, structHash);
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.27;
            import {MerkleProof} from "openzeppelin/utils/cryptography/MerkleProof.sol";
            import {RLPReader as RLPDecoder} from "rlp-reader/RLPReader.sol";
            import {RLPEncoder} from "../rlp/RLPEncoder.sol";
            import {MEEUserOpHashLib} from "../util/MEEUserOpHashLib.sol";
            import {EcdsaLib} from "../util/EcdsaLib.sol";
            import {BytesLib} from "byteslib/BytesLib.sol";
            import "account-abstraction/core/Helpers.sol";
            /**
             * @dev Library to validate the signature for MEE on-chain Txn mode
             *      This is the mode where superTx hash is appended to a regular txn (legacy or 1559) calldata
             *      Type 1 (EIP-2930) transactions are not supported.
             *      The whole txn is signed along with the superTx hash
             *      Txn is executed prior to a superTx, so it can pass some funds from the EOA to the smart account
             *      For more details see Fusion docs:
             *      - https://ethresear.ch/t/fusion-module-7702-alternative-with-no-protocol-changes/20949
             *      - https://docs.biconomy.io/explained/eoa#fusion-module
             *      @dev Some smart contracts may not be able to consume the txn with bytes32 appended to the calldata.
             *           However this is very small subset. One of the cases when it can happen is when the smart contract
             *           is has separate receive() and fallback() functions. Then if a txn is a value transfer, it will
             *           be expected to be consumed by the receive() function. However, if there's bytes32 appended to the calldata,
             *           it will be consumed by the fallback() function which may not be expected. In this case, the provided
             *           contracts/forwarder/Forwarder.sol can be used to 'clear' the bytes32 from the calldata.
             *      @dev In theory, the last 32 bytes of calldata from any transaction by the EOA can be interpreted as
             *           a superTx hash. Even if it was not assumed. This introduces the potential risk of phishing attacks
             *           where the user may unknowingly sign a transaction where the last 32 bytes of the calldata end up
             *           being a superTx hash. However, it is not easy to craft a txn that makes sense for a user and allows
             *           arbitrary bytes32 as last 32 bytes. Thus, wallets and users should be aware of this potential risk
             *           and should not sign txns where the last 32 bytes of the calldata do not belong to the function arguments
             *           and are just appended at the end.
             */
            library TxValidatorLib {
                uint8 constant LEGACY_TX_TYPE = 0x00;
                uint8 constant EIP1559_TX_TYPE = 0x02;
                uint8 constant EIP_155_MIN_V_VALUE = 37;
                uint8 constant HASH_BYTE_SIZE = 32;
                uint8 constant TIMESTAMP_BYTE_SIZE = 6;
                uint8 constant PROOF_ITEM_BYTE_SIZE = 32;
                uint8 constant ITX_HASH_BYTE_SIZE = 32;
                using RLPDecoder for RLPDecoder.RLPItem;
                using RLPDecoder for bytes;
                using RLPEncoder for uint256;
                using BytesLib for bytes;
                struct TxData {
                    uint8 txType;
                    uint8 v;
                    bytes32 r;
                    bytes32 s;
                    bytes32 utxHash;
                    bytes32 superTxHash;
                    bytes32[] proof;
                    uint48 lowerBoundTimestamp;
                    uint48 upperBoundTimestamp;
                }
                // To save a bit of gas, not pass timestamps where not needed
                struct TxDataShort {
                    uint8 txType;
                    uint8 v;
                    bytes32 r;
                    bytes32 s;
                    bytes32 utxHash;
                    bytes32 superTxHash;
                    bytes32[] proof;
                }
                struct TxParams {
                    uint256 v;
                    bytes32 r;
                    bytes32 s;
                    bytes callData;
                }
                /**
                 * This function parses the given userOpSignature into a valid fully signed EVM transaction.
                 * Once parsed, the function will check for three conditions:
                 *      1. is the userOp part of the superTX merkle tree
                 *      2. is the recovered tx signer equal to the expected signer?
                 *      3. is the given UserOp a part of the merkle tree
                 *
                 * If all the conditions are met - outside contract can be sure that the expected signer has indeed
                 * approved the given hash by performing given on-chain transaction.
                 *
                 * NOTES: This function will revert if either of following is met:
                 *    1. the userOpSignature couldn't be parsed to a valid fully signed EVM transaction
                 *    2. hash couldn't be extracted from the tx.data
                 *    3. extracted hash wasn't equal to the provided expected hash
                 *    4. recovered signer wasn't equal to the expected signer
                 *
                 * @param userOpHash UserOp hash being validated.
                 * @param parsedSignature Signature provided as the userOp.signature parameter (minus the prepended tx type byte).
                 *                        Expecting to receive fully signed serialized EVM transaction here of type 0x00 (LEGACY)
                 *                        or 0x02 (EIP1556).
                 *                        For LEGACY tx type the "0x00" prefix has to be added manually while the EIP1559 tx type
                 *                        already contains 0x02 prefix.
                 * @param expectedSigner Expected EOA signer of the given EVM transaction => superTX.
                 */
                function validateUserOp(bytes32 userOpHash, bytes calldata parsedSignature, address expectedSigner)
                    internal
                    view
                    returns (uint256)
                {
                    TxData memory decodedTx = decodeTx(parsedSignature);
                    bytes32 meeUserOpHash =
                        MEEUserOpHashLib.getMEEUserOpHash(userOpHash, decodedTx.lowerBoundTimestamp, decodedTx.upperBoundTimestamp);
                    bytes memory signature = abi.encodePacked(decodedTx.r, decodedTx.s, decodedTx.v);
                    if (!EcdsaLib.isValidSignature(expectedSigner, decodedTx.utxHash, signature)) {
                        return SIG_VALIDATION_FAILED;
                    }
                    if (!MerkleProof.verify(decodedTx.proof, decodedTx.superTxHash, meeUserOpHash)) {
                        return SIG_VALIDATION_FAILED;
                    }
                    return _packValidationData(false, decodedTx.upperBoundTimestamp, decodedTx.lowerBoundTimestamp);
                }
                /**
                 * @dev validate the signature for the owner of the superTx
                 *      used fot the 1271 flow and for the stateless validators (erc7579 module type 7)
                 * @param expectedSigner the expected signer of the superTx
                 * @param dataHash the hash of the data to be signed
                 * @param parsedSignature the signature to be validated
                 * @return true if the signature is valid, false otherwise
                 */
                function validateSignatureForOwner(address expectedSigner, bytes32 dataHash, bytes calldata parsedSignature)
                    internal
                    view
                    returns (bool)
                {
                    TxDataShort memory decodedTx = decodeTxShort(parsedSignature);
                    bytes memory signature = abi.encodePacked(decodedTx.r, decodedTx.s, decodedTx.v);
                    if (!EcdsaLib.isValidSignature(expectedSigner, decodedTx.utxHash, signature)) {
                        return false;
                    }
                    if (!MerkleProof.verify(decodedTx.proof, decodedTx.superTxHash, dataHash)) {
                        return false;
                    }
                    return true;
                }
                function decodeTx(bytes calldata self) internal pure returns (TxData memory) {
                    uint8 txType = uint8(self[0]); //first byte is tx type
                    uint48 lowerBoundTimestamp =
                        uint48(bytes6((self[self.length - 2 * TIMESTAMP_BYTE_SIZE:self.length - TIMESTAMP_BYTE_SIZE])));
                    uint48 upperBoundTimestamp = uint48(bytes6(self[self.length - TIMESTAMP_BYTE_SIZE:]));
                    uint8 proofItemsCount = uint8(self[self.length - 2 * TIMESTAMP_BYTE_SIZE - 1]);
                    uint256 appendedDataLen = (uint256(proofItemsCount) * PROOF_ITEM_BYTE_SIZE + 1) + 2 * TIMESTAMP_BYTE_SIZE;
                    bytes calldata rlpEncodedTx = self[1:self.length - appendedDataLen];
                    RLPDecoder.RLPItem memory parsedRlpEncodedTx = rlpEncodedTx.toRlpItem();
                    RLPDecoder.RLPItem[] memory parsedRlpEncodedTxItems = parsedRlpEncodedTx.toList();
                    TxParams memory params = extractParams(txType, parsedRlpEncodedTxItems);
                    return TxData(
                        txType,
                        _adjustV(params.v),
                        params.r,
                        params.s,
                        calculateUnsignedTxHash(txType, rlpEncodedTx, parsedRlpEncodedTx.payloadLen(), params.v, params.r, params.s),
                        extractAppendedHash(params.callData),
                        extractProof(self, proofItemsCount),
                        lowerBoundTimestamp,
                        upperBoundTimestamp
                    );
                }
                function decodeTxShort(bytes calldata self) internal pure returns (TxDataShort memory) {
                    uint8 txType = uint8(self[0]); //first byte is tx type
                    uint8 proofItemsCount = uint8(self[self.length - 1]);
                    uint256 appendedDataLen = (uint256(proofItemsCount) * PROOF_ITEM_BYTE_SIZE + 1);
                    bytes calldata rlpEncodedTx = self[1:self.length - appendedDataLen];
                    RLPDecoder.RLPItem memory parsedRlpEncodedTx = rlpEncodedTx.toRlpItem();
                    RLPDecoder.RLPItem[] memory parsedRlpEncodedTxItems = parsedRlpEncodedTx.toList();
                    TxParams memory params = extractParams(txType, parsedRlpEncodedTxItems);
                    return TxDataShort(
                        txType,
                        _adjustV(params.v),
                        params.r,
                        params.s,
                        calculateUnsignedTxHash(txType, rlpEncodedTx, parsedRlpEncodedTx.payloadLen(), params.v, params.r, params.s),
                        extractAppendedHash(params.callData),
                        extractProofShort(self, proofItemsCount)
                    );
                }
                function extractParams(uint8 txType, RLPDecoder.RLPItem[] memory items)
                    private
                    pure
                    returns (TxParams memory params)
                {
                    uint8 dataPos;
                    uint8 vPos;
                    uint8 rPos;
                    uint8 sPos;
                    if (txType == LEGACY_TX_TYPE) {
                        dataPos = 5;
                        vPos = 6;
                        rPos = 7;
                        sPos = 8;
                    } else if (txType == EIP1559_TX_TYPE) {
                        dataPos = 7;
                        vPos = 9;
                        rPos = 10;
                        sPos = 11;
                    } else {
                        revert("TxValidatorLib:: unsupported evm tx type");
                    }
                    return TxParams(
                        items[vPos].toUint(), bytes32(items[rPos].toUint()), bytes32(items[sPos].toUint()), items[dataPos].toBytes()
                    );
                }
                function extractAppendedHash(bytes memory callData) private pure returns (bytes32 iTxHash) {
                    if (callData.length < ITX_HASH_BYTE_SIZE) revert("TxDecoder:: callData length too short");
                    iTxHash = bytes32(callData.slice(callData.length - ITX_HASH_BYTE_SIZE, ITX_HASH_BYTE_SIZE));
                }
                function extractProof(bytes calldata signedTx, uint8 proofItemsCount)
                    private
                    pure
                    returns (bytes32[] memory proof)
                {
                    proof = new bytes32[](proofItemsCount);
                    uint256 pos = signedTx.length - 2 * TIMESTAMP_BYTE_SIZE - 1;
                    for (proofItemsCount; proofItemsCount > 0; proofItemsCount--) {
                        proof[proofItemsCount - 1] = bytes32(signedTx[pos - PROOF_ITEM_BYTE_SIZE:pos]);
                        pos = pos - PROOF_ITEM_BYTE_SIZE;
                    }
                }
                function extractProofShort(bytes calldata signedTx, uint8 proofItemsCount)
                    private
                    pure
                    returns (bytes32[] memory proof)
                {
                    proof = new bytes32[](proofItemsCount);
                    uint256 pos = signedTx.length - 1;
                    for (proofItemsCount; proofItemsCount > 0; proofItemsCount--) {
                        proof[proofItemsCount - 1] = bytes32(signedTx[pos - PROOF_ITEM_BYTE_SIZE:pos]);
                        pos = pos - PROOF_ITEM_BYTE_SIZE;
                    }
                }
                function calculateUnsignedTxHash(
                    uint8 txType,
                    bytes memory rlpEncodedTx,
                    uint256 rlpEncodedTxPayloadLen,
                    uint256 v,
                    bytes32 r,
                    bytes32 s
                ) private pure returns (bytes32 hash) {
                    uint256 totalSignatureSize =
                        uint256(r).encodeUint().length + uint256(s).encodeUint().length + v.encodeUint().length;
                    uint256 totalPrefixSize = rlpEncodedTx.length - rlpEncodedTxPayloadLen;
                    bytes memory rlpEncodedTxNoSigAndPrefix =
                        rlpEncodedTx.slice(totalPrefixSize, rlpEncodedTx.length - totalSignatureSize - totalPrefixSize);
                    if (txType == EIP1559_TX_TYPE) {
                        return keccak256(abi.encodePacked(txType, prependRlpContentSize(rlpEncodedTxNoSigAndPrefix, "")));
                    } else if (txType == LEGACY_TX_TYPE) {
                        if (v >= EIP_155_MIN_V_VALUE) {
                            return keccak256(
                                prependRlpContentSize(
                                    rlpEncodedTxNoSigAndPrefix,
                                    abi.encodePacked(
                                        uint256(_extractChainIdFromV(v)).encodeUint(),
                                        uint256(0).encodeUint(),
                                        uint256(0).encodeUint()
                                    )
                                )
                            );
                        } else {
                            return keccak256(prependRlpContentSize(rlpEncodedTxNoSigAndPrefix, ""));
                        }
                    } else {
                        revert("TxValidatorLib:: unsupported tx type");
                    }
                }
                function prependRlpContentSize(bytes memory content, bytes memory extraData) public pure returns (bytes memory) {
                    bytes memory combinedContent = abi.encodePacked(content, extraData);
                    return abi.encodePacked(combinedContent.length.encodeLength(RLPDecoder.LIST_SHORT_START), combinedContent);
                }
                function _adjustV(uint256 v) internal pure returns (uint8) {
                    if (v >= EIP_155_MIN_V_VALUE) {
                        return uint8((v - 2 * _extractChainIdFromV(v) - 35) + 27);
                    } else if (v <= 1) {
                        return uint8(v + 27);
                    } else {
                        return uint8(v);
                    }
                }
                function _extractChainIdFromV(uint256 v) internal pure returns (uint256 chainId) {
                    chainId = (v - 35) / 2;
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.27;
            import {MerkleProof} from "openzeppelin/utils/cryptography/MerkleProof.sol";
            import {EcdsaLib} from "../util/EcdsaLib.sol";
            import {MEEUserOpHashLib} from "../util/MEEUserOpHashLib.sol";
            import "account-abstraction/core/Helpers.sol";
            /**
             * @dev Library to validate the signature for MEE Simple mode
             *      In this mode, Fusion is not involved and just the superTx hash is signed
             */
            library SimpleValidatorLib {
                /**
                 * This function parses the given userOpSignature into a Supertransaction signature
                 *
                 * Once parsed, the function will check for two conditions:
                 *      1. is the root supertransaction hash signed by the account owner's EOA
                 *      2. is the userOp actually a part of the given supertransaction
                 *      by checking the leaf based on this userOpHash is a part of the merkle tree represented by root hash = superTxHash
                 *
                 * If both conditions are met - outside contract can be sure that the expected signer has indeed
                 * approved the given userOp - and the userOp is successfully validate.
                 *
                 * @param userOpHash UserOp hash being validated.
                 * @param signatureData Signature provided as the userOp.signature parameter (minus the prepended tx type byte).
                 * @param expectedSigner Signer expected to be recovered when decoding the ERC20OPermit signature.
                 */
                function validateUserOp(bytes32 userOpHash, bytes calldata signatureData, address expectedSigner)
                    internal
                    view
                    returns (uint256)
                {
                    bytes32 superTxHash;
                    uint48 lowerBoundTimestamp;
                    uint48 upperBoundTimestamp;
                    bytes32[] calldata proof;
                    bytes calldata secp256k1Signature;
                    assembly {
                        superTxHash := calldataload(signatureData.offset)
                        lowerBoundTimestamp := calldataload(add(signatureData.offset, 0x20))
                        upperBoundTimestamp := calldataload(add(signatureData.offset, 0x40))
                        let u := calldataload(add(signatureData.offset, 0x60))
                        let s := add(signatureData.offset, u)
                        proof.offset := add(s, 0x20)
                        proof.length := calldataload(s)
                        u := mul(proof.length, 0x20)
                        s := add(proof.offset, u)
                        secp256k1Signature.offset := add(s, 0x20)
                        secp256k1Signature.length := calldataload(s)
                    }
                    bytes32 leaf = MEEUserOpHashLib.getMEEUserOpHash(userOpHash, lowerBoundTimestamp, upperBoundTimestamp);
                    if (!EcdsaLib.isValidSignature(expectedSigner, superTxHash, secp256k1Signature)) {
                        return SIG_VALIDATION_FAILED;
                    }
                    if (!MerkleProof.verify(proof, superTxHash, leaf)) {
                        return SIG_VALIDATION_FAILED;
                    }
                    return _packValidationData(false, upperBoundTimestamp, lowerBoundTimestamp);
                }
                /**
                 * @notice Validates the signature against the expected signer (owner)
                 * @param owner Signer expected to be recovered
                 * @param dataHash data hash being validated.
                 * @param signatureData Signature
                 */
                function validateSignatureForOwner(address owner, bytes32 dataHash, bytes calldata signatureData)
                    internal
                    view
                    returns (bool)
                {
                    bytes32 superTxHash;
                    bytes32[] calldata proof;
                    bytes calldata secp256k1Signature;
                    assembly {
                        superTxHash := calldataload(signatureData.offset)
                        let u := calldataload(add(signatureData.offset, 0x20))
                        let s := add(signatureData.offset, u)
                        proof.offset := add(s, 0x20)
                        proof.length := calldataload(s)
                        u := mul(proof.length, 0x20)
                        s := add(proof.offset, u)
                        secp256k1Signature.offset := add(s, 0x20)
                        secp256k1Signature.length := calldataload(s)
                    }
                    if (!EcdsaLib.isValidSignature(owner, superTxHash, secp256k1Signature)) {
                        return false;
                    }
                    if (!MerkleProof.verify(proof, superTxHash, dataHash)) {
                        return false;
                    }
                    return true;
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.27;
            import "account-abstraction/interfaces/PackedUserOperation.sol";
            import "account-abstraction/core/Helpers.sol";
            import "../util/EcdsaLib.sol";
            library NoMeeFlowLib {
                /**
                 * Standard userOp validator - validates by simply checking if the userOpHash was signed by the account's EOA owner.
                 *
                 * @param userOpHash userOpHash being validated.
                 * @param parsedSignature Signature
                 * @param expectedSigner Signer expected to be recovered
                 */
                function validateUserOp(bytes32 userOpHash, bytes memory parsedSignature, address expectedSigner)
                    internal
                    view
                    returns (uint256)
                {
                    if (!EcdsaLib.isValidSignature(expectedSigner, userOpHash, parsedSignature)) {
                        return SIG_VALIDATION_FAILED;
                    }
                    return SIG_VALIDATION_SUCCESS;
                }
                /**
                 * @notice Validates the signature against the expected signer (owner)
                 * @param expectedSigner Signer expected to be recovered
                 * @param hash Hash of the userOp
                 * @param parsedSignature Signature
                 */
                function validateSignatureForOwner(address expectedSigner, bytes32 hash, bytes memory parsedSignature)
                    internal
                    view
                    returns (bool)
                {
                    return EcdsaLib.isValidSignature(expectedSigner, hash, parsedSignature);
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.27;
            import {ECDSA} from "solady/utils/ECDSA.sol";
            library EcdsaLib {
                using ECDSA for bytes32;
                /**
                 * @dev Solady ECDSA does not revert on incorrect signatures.
                 *      Instead, it returns address(0) as the recovered address.
                 *      Make sure to never pass address(0) as expectedSigner to this function.
                 */
                function isValidSignature(address expectedSigner, bytes32 hash, bytes memory signature)
                    internal
                    view
                    returns (bool)
                {
                    if (hash.tryRecover(signature) == expectedSigner) return true;
                    if (hash.toEthSignedMessageHash().tryRecover(signature) == expectedSigner) return true;
                    return false;
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.20;
            /**
             * Dynamic arrays associated with an account address as per ERC-7562/ERC-4337
             * @author filio.eth (Biconomy), zeroknots.eth (rhinestone)
             */
            library AssociatedArrayLib {
                using AssociatedArrayLib for *;
                error AssociatedArray_OutOfBounds(uint256 index);
                struct Array {
                    uint256 _spacer;
                }
                function _slot(Array storage s, address account) private pure returns (bytes32 __slot) {
                    assembly {
                        mstore(0x00, account)
                        mstore(0x20, s.slot)
                        __slot := keccak256(0x00, 0x40)
                    }
                }
                function _length(Array storage s, address account) private view returns (uint256 __length) {
                    bytes32 slot = _slot(s, account);
                    assembly {
                        __length := sload(slot)
                    }
                }
                function _get(Array storage s, address account, uint256 index) private view returns (bytes32 value) {
                    return _get(_slot(s, account), index);
                }
                function _get(bytes32 slot, uint256 index) private view returns (bytes32 value) {
                    assembly {
                        //if (index >= _length(s, account)) revert AssociatedArray_OutOfBounds(index);
                        if iszero(lt(index, sload(slot))) {
                            mstore(0, 0x8277484f) // `AssociatedArray_OutOfBounds(uint256)`
                            mstore(0x20, index)
                            revert(0x1c, 0x24)
                        }
                        value := sload(add(slot, mul(0x20, add(index, 1))))
                    }
                }
                function _getAll(Array storage s, address account) private view returns (bytes32[] memory values) {
                    bytes32 slot = _slot(s, account);
                    uint256 __length;
                    assembly {
                        __length := sload(slot)
                    }
                    values = new bytes32[](__length);
                    for (uint256 i; i < __length; i++) {
                        values[i] = _get(slot, i);
                    }
                }
                // inefficient. complexity = O(n)
                // use with caution
                // in case of large arrays, consider using EnumerableSet4337 instead
                function _contains(Array storage s, address account, bytes32 value) private view returns (bool) {
                    bytes32 slot = _slot(s, account);
                    uint256 __length;
                    assembly {
                        __length := sload(slot)
                    }
                    for (uint256 i; i < __length; i++) {
                        if (_get(slot, i) == value) {
                            return true;
                        }
                    }
                    return false;
                }
                function _set(Array storage s, address account, uint256 index, bytes32 value) private {
                    _set(_slot(s, account), index, value);
                }
                function _set(bytes32 slot, uint256 index, bytes32 value) private {
                    assembly {
                        //if (index >= _length(s, account)) revert AssociatedArray_OutOfBounds(index);
                        if iszero(lt(index, sload(slot))) {
                            mstore(0, 0x8277484f) // `AssociatedArray_OutOfBounds(uint256)`
                            mstore(0x20, index)
                            revert(0x1c, 0x24)
                        }
                        sstore(add(slot, mul(0x20, add(index, 1))), value)
                    }
                }
                function _push(Array storage s, address account, bytes32 value) private {
                    bytes32 slot = _slot(s, account);
                    assembly {
                        // load length (stored @ slot), add 1 to it => index.
                        // mul index by 0x20 and add it to orig slot to get the next free slot
                        let index := add(sload(slot), 1)
                        sstore(add(slot, mul(0x20, index)), value)
                        sstore(slot, index) //increment length by 1
                    }
                }
                function _pop(Array storage s, address account) private {
                    bytes32 slot = _slot(s, account);
                    uint256 __length;
                    assembly {
                        __length := sload(slot)
                    }
                    if (__length == 0) return;
                    _set(slot, __length - 1, 0);
                    assembly {
                        sstore(slot, sub(__length, 1))
                    }
                }
                function _remove(Array storage s, address account, uint256 index) private {
                    bytes32 slot = _slot(s, account);
                    uint256 __length;
                    assembly {
                        __length := sload(slot)
                        if iszero(lt(index, __length)) {
                            mstore(0, 0x8277484f) // `AssociatedArray_OutOfBounds(uint256)`
                            mstore(0x20, index)
                            revert(0x1c, 0x24)
                        }
                    }
                    _set(slot, index, _get(s, account, __length - 1));
                    assembly {
                        // clear the last slot
                        // this is the 'unchecked' version of _set(slot, __length - 1, 0)
                        // as we use length-1 as index, so the check is excessive.
                        // also removes extra -1 and +1 operations
                        sstore(add(slot, mul(0x20, __length)), 0)
                        // store new length
                        sstore(slot, sub(__length, 1))
                    }
                }
                struct Bytes32Array {
                    Array _inner;
                }
                function length(Bytes32Array storage s, address account) internal view returns (uint256) {
                    return _length(s._inner, account);
                }
                function get(Bytes32Array storage s, address account, uint256 index) internal view returns (bytes32) {
                    return _get(s._inner, account, index);
                }
                function getAll(Bytes32Array storage s, address account) internal view returns (bytes32[] memory) {
                    return _getAll(s._inner, account);
                }
                function contains(Bytes32Array storage s, address account, bytes32 value) internal view returns (bool) {
                    return _contains(s._inner, account, value);
                }
                function add(Bytes32Array storage s, address account, bytes32 value) internal {
                    if (!_contains(s._inner, account, value)) {
                        _push(s._inner, account, value);
                    }
                }
                function set(Bytes32Array storage s, address account, uint256 index, bytes32 value) internal {
                    _set(s._inner, account, index, value);
                }
                function push(Bytes32Array storage s, address account, bytes32 value) internal {
                    _push(s._inner, account, value);
                }
                function pop(Bytes32Array storage s, address account) internal {
                    _pop(s._inner, account);
                }
                function remove(Bytes32Array storage s, address account, uint256 index) internal {
                    _remove(s._inner, account, index);
                }
                struct AddressArray {
                    Array _inner;
                }
                function length(AddressArray storage s, address account) internal view returns (uint256) {
                    return _length(s._inner, account);
                }
                function get(AddressArray storage s, address account, uint256 index) internal view returns (address) {
                    return address(uint160(uint256(_get(s._inner, account, index))));
                }
                function getAll(AddressArray storage s, address account) internal view returns (address[] memory) {
                    bytes32[] memory bytes32Array = _getAll(s._inner, account);
                    address[] memory addressArray;
                    /// @solidity memory-safe-assembly
                    assembly {
                        addressArray := bytes32Array
                    }
                    return addressArray;
                }
                function contains(AddressArray storage s, address account, address value) internal view returns (bool) {
                    return _contains(s._inner, account, bytes32(uint256(uint160(value))));
                }
                function add(AddressArray storage s, address account, address value) internal {
                    if (!_contains(s._inner, account, bytes32(uint256(uint160(value))))) {
                        _push(s._inner, account, bytes32(uint256(uint160(value))));
                    }
                }
                function set(AddressArray storage s, address account, uint256 index, address value) internal {
                    _set(s._inner, account, index, bytes32(uint256(uint160(value))));
                }
                function push(AddressArray storage s, address account, address value) internal {
                    _push(s._inner, account, bytes32(uint256(uint160(value))));
                }
                function pop(AddressArray storage s, address account) internal {
                    _pop(s._inner, account);
                }
                function remove(AddressArray storage s, address account, uint256 index) internal {
                    _remove(s._inner, account, index);
                }
                struct UintArray {
                    Array _inner;
                }
                function length(UintArray storage s, address account) internal view returns (uint256) {
                    return _length(s._inner, account);
                }
                function get(UintArray storage s, address account, uint256 index) internal view returns (uint256) {
                    return uint256(_get(s._inner, account, index));
                }
                function getAll(UintArray storage s, address account) internal view returns (uint256[] memory) {
                    bytes32[] memory bytes32Array = _getAll(s._inner, account);
                    uint256[] memory uintArray;
                    /// @solidity memory-safe-assembly
                    assembly {
                        uintArray := bytes32Array
                    }
                    return uintArray;
                }
                function contains(UintArray storage s, address account, uint256 value) internal view returns (bool) {
                    return _contains(s._inner, account, bytes32(value));
                }
                function add(UintArray storage s, address account, uint256 value) internal {
                    if (!_contains(s._inner, account, bytes32(value))) {
                        _push(s._inner, account, bytes32(value));
                    }
                }
                function set(UintArray storage s, address account, uint256 index, uint256 value) internal {
                    _set(s._inner, account, index, bytes32(value));
                }
                function push(UintArray storage s, address account, uint256 value) internal {
                    _push(s._inner, account, bytes32(value));
                }
                function pop(UintArray storage s, address account) internal {
                    _pop(s._inner, account);
                }
                function remove(UintArray storage s, address account, uint256 index) internal {
                    _remove(s._inner, account, index);
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol)
            pragma solidity ^0.8.20;
            import {Strings} from "../Strings.sol";
            /**
             * @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
             *
             * The library provides methods for generating a hash of a message that conforms to the
             * https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
             * specifications.
             */
            library MessageHashUtils {
                /**
                 * @dev Returns the keccak256 digest of an EIP-191 signed data with version
                 * `0x45` (`personal_sign` messages).
                 *
                 * The digest is calculated by prefixing a bytes32 `messageHash` with
                 * `"\\x19Ethereum Signed Message:\
            32"` and hashing the result. It corresponds with the
                 * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
                 *
                 * NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
                 * keccak256, although any bytes32 value can be safely used because the final digest will
                 * be re-hashed.
                 *
                 * See {ECDSA-recover}.
                 */
                function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        mstore(0x00, "\\x19Ethereum Signed Message:\
            32") // 32 is the bytes-length of messageHash
                        mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
                        digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
                    }
                }
                /**
                 * @dev Returns the keccak256 digest of an EIP-191 signed data with version
                 * `0x45` (`personal_sign` messages).
                 *
                 * The digest is calculated by prefixing an arbitrary `message` with
                 * `"\\x19Ethereum Signed Message:\
            " + len(message)` and hashing the result. It corresponds with the
                 * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
                 *
                 * See {ECDSA-recover}.
                 */
                function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
                    return
                        keccak256(bytes.concat("\\x19Ethereum Signed Message:\
            ", bytes(Strings.toString(message.length)), message));
                }
                /**
                 * @dev Returns the keccak256 digest of an EIP-191 signed data with version
                 * `0x00` (data with intended validator).
                 *
                 * The digest is calculated by prefixing an arbitrary `data` with `"\\x19\\x00"` and the intended
                 * `validator` address. Then hashing the result.
                 *
                 * See {ECDSA-recover}.
                 */
                function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
                    return keccak256(abi.encodePacked(hex"19_00", validator, data));
                }
                /**
                 * @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`).
                 *
                 * The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
                 * `\\x19\\x01` and hashing the result. It corresponds to the hash signed by the
                 * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
                 *
                 * See {ECDSA-recover}.
                 */
                function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let ptr := mload(0x40)
                        mstore(ptr, hex"19_01")
                        mstore(add(ptr, 0x02), domainSeparator)
                        mstore(add(ptr, 0x22), structHash)
                        digest := keccak256(ptr, 0x42)
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MerkleProof.sol)
            pragma solidity ^0.8.20;
            /**
             * @dev These functions deal with verification of Merkle Tree proofs.
             *
             * The tree and the proofs can be generated using our
             * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
             * You will find a quickstart guide in the readme.
             *
             * WARNING: You should avoid using leaf values that are 64 bytes long prior to
             * hashing, or use a hash function other than keccak256 for hashing leaves.
             * This is because the concatenation of a sorted pair of internal nodes in
             * the Merkle tree could be reinterpreted as a leaf value.
             * OpenZeppelin's JavaScript library generates Merkle trees that are safe
             * against this attack out of the box.
             */
            library MerkleProof {
                /**
                 *@dev The multiproof provided is not valid.
                 */
                error MerkleProofInvalidMultiproof();
                /**
                 * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
                 * defined by `root`. For this, a `proof` must be provided, containing
                 * sibling hashes on the branch from the leaf to the root of the tree. Each
                 * pair of leaves and each pair of pre-images are assumed to be sorted.
                 */
                function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
                    return processProof(proof, leaf) == root;
                }
                /**
                 * @dev Calldata version of {verify}
                 */
                function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
                    return processProofCalldata(proof, leaf) == root;
                }
                /**
                 * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
                 * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
                 * hash matches the root of the tree. When processing the proof, the pairs
                 * of leafs & pre-images are assumed to be sorted.
                 */
                function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
                    bytes32 computedHash = leaf;
                    for (uint256 i = 0; i < proof.length; i++) {
                        computedHash = _hashPair(computedHash, proof[i]);
                    }
                    return computedHash;
                }
                /**
                 * @dev Calldata version of {processProof}
                 */
                function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
                    bytes32 computedHash = leaf;
                    for (uint256 i = 0; i < proof.length; i++) {
                        computedHash = _hashPair(computedHash, proof[i]);
                    }
                    return computedHash;
                }
                /**
                 * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
                 * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
                 *
                 * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
                 */
                function multiProofVerify(
                    bytes32[] memory proof,
                    bool[] memory proofFlags,
                    bytes32 root,
                    bytes32[] memory leaves
                ) internal pure returns (bool) {
                    return processMultiProof(proof, proofFlags, leaves) == root;
                }
                /**
                 * @dev Calldata version of {multiProofVerify}
                 *
                 * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
                 */
                function multiProofVerifyCalldata(
                    bytes32[] calldata proof,
                    bool[] calldata proofFlags,
                    bytes32 root,
                    bytes32[] memory leaves
                ) internal pure returns (bool) {
                    return processMultiProofCalldata(proof, proofFlags, leaves) == root;
                }
                /**
                 * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
                 * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
                 * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
                 * respectively.
                 *
                 * CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
                 * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
                 * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
                 */
                function processMultiProof(
                    bytes32[] memory proof,
                    bool[] memory proofFlags,
                    bytes32[] memory leaves
                ) internal pure returns (bytes32 merkleRoot) {
                    // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
                    // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
                    // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
                    // the Merkle tree.
                    uint256 leavesLen = leaves.length;
                    uint256 proofLen = proof.length;
                    uint256 totalHashes = proofFlags.length;
                    // Check proof validity.
                    if (leavesLen + proofLen != totalHashes + 1) {
                        revert MerkleProofInvalidMultiproof();
                    }
                    // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
                    // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
                    bytes32[] memory hashes = new bytes32[](totalHashes);
                    uint256 leafPos = 0;
                    uint256 hashPos = 0;
                    uint256 proofPos = 0;
                    // At each step, we compute the next hash using two values:
                    // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
                    //   get the next hash.
                    // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
                    //   `proof` array.
                    for (uint256 i = 0; i < totalHashes; i++) {
                        bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
                        bytes32 b = proofFlags[i]
                            ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
                            : proof[proofPos++];
                        hashes[i] = _hashPair(a, b);
                    }
                    if (totalHashes > 0) {
                        if (proofPos != proofLen) {
                            revert MerkleProofInvalidMultiproof();
                        }
                        unchecked {
                            return hashes[totalHashes - 1];
                        }
                    } else if (leavesLen > 0) {
                        return leaves[0];
                    } else {
                        return proof[0];
                    }
                }
                /**
                 * @dev Calldata version of {processMultiProof}.
                 *
                 * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
                 */
                function processMultiProofCalldata(
                    bytes32[] calldata proof,
                    bool[] calldata proofFlags,
                    bytes32[] memory leaves
                ) internal pure returns (bytes32 merkleRoot) {
                    // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
                    // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
                    // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
                    // the Merkle tree.
                    uint256 leavesLen = leaves.length;
                    uint256 proofLen = proof.length;
                    uint256 totalHashes = proofFlags.length;
                    // Check proof validity.
                    if (leavesLen + proofLen != totalHashes + 1) {
                        revert MerkleProofInvalidMultiproof();
                    }
                    // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
                    // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
                    bytes32[] memory hashes = new bytes32[](totalHashes);
                    uint256 leafPos = 0;
                    uint256 hashPos = 0;
                    uint256 proofPos = 0;
                    // At each step, we compute the next hash using two values:
                    // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
                    //   get the next hash.
                    // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
                    //   `proof` array.
                    for (uint256 i = 0; i < totalHashes; i++) {
                        bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
                        bytes32 b = proofFlags[i]
                            ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
                            : proof[proofPos++];
                        hashes[i] = _hashPair(a, b);
                    }
                    if (totalHashes > 0) {
                        if (proofPos != proofLen) {
                            revert MerkleProofInvalidMultiproof();
                        }
                        unchecked {
                            return hashes[totalHashes - 1];
                        }
                    } else if (leavesLen > 0) {
                        return leaves[0];
                    } else {
                        return proof[0];
                    }
                }
                /**
                 * @dev Sorts the pair (a, b) and hashes the result.
                 */
                function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
                    return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
                }
                /**
                 * @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory.
                 */
                function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        mstore(0x00, a)
                        mstore(0x20, b)
                        value := keccak256(0x00, 0x40)
                    }
                }
            }
            // SPDX-License-Identifier: Unlicense
            /*
             * @title MEE UserOp Hash Lib
             *
             * @dev Calculates userOp hash for the new type of transaction - SuperTransaction (as a part of MEE stack)
             */
            pragma solidity ^0.8.27;
            library MEEUserOpHashLib {
                /**
                 * Calculates userOp hash. Almost works like a regular 4337 userOp hash with few fields added.
                 *
                 * @param userOpHash userOp hash to calculate the hash for
                 * @param lowerBoundTimestamp lower bound timestamp set when constructing userOp
                 * @param upperBoundTimestamp upper bound timestamp set when constructing userOp
                 * Timestamps are used by the MEE node to schedule the execution of the userOps within the superTx
                 */
                function getMEEUserOpHash(bytes32 userOpHash, uint256 lowerBoundTimestamp, uint256 upperBoundTimestamp)
                    internal
                    pure
                    returns (bytes32 meeUserOpHash)
                {
                    meeUserOpHash =
                        keccak256(bytes.concat(keccak256(abi.encode(userOpHash, lowerBoundTimestamp, upperBoundTimestamp))));
                }
            }
            // 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) (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: GPL-3.0
            pragma solidity ^0.8.23;
            /* solhint-disable no-inline-assembly */
             /*
              * For simulation purposes, validateUserOp (and validatePaymasterUserOp)
              * must return this value in case of signature failure, instead of revert.
              */
            uint256 constant SIG_VALIDATION_FAILED = 1;
            /*
             * For simulation purposes, validateUserOp (and validatePaymasterUserOp)
             * return this value on success.
             */
            uint256 constant SIG_VALIDATION_SUCCESS = 0;
            /**
             * Returned data from validateUserOp.
             * validateUserOp returns a uint256, which is created by `_packedValidationData` and
             * parsed by `_parseValidationData`.
             * @param aggregator  - address(0) - The account validated the signature by itself.
             *                      address(1) - The account failed to validate the signature.
             *                      otherwise - This is an address of a signature aggregator that must
             *                                  be used to validate the signature.
             * @param validAfter  - This UserOp is valid only after this timestamp.
             * @param validaUntil - This UserOp is valid only up to this timestamp.
             */
            struct ValidationData {
                address aggregator;
                uint48 validAfter;
                uint48 validUntil;
            }
            /**
             * Extract sigFailed, validAfter, validUntil.
             * Also convert zero validUntil to type(uint48).max.
             * @param validationData - The packed validation data.
             */
            function _parseValidationData(
                uint256 validationData
            ) pure returns (ValidationData memory data) {
                address aggregator = address(uint160(validationData));
                uint48 validUntil = uint48(validationData >> 160);
                if (validUntil == 0) {
                    validUntil = type(uint48).max;
                }
                uint48 validAfter = uint48(validationData >> (48 + 160));
                return ValidationData(aggregator, validAfter, validUntil);
            }
            /**
             * Helper to pack the return value for validateUserOp.
             * @param data - The ValidationData to pack.
             */
            function _packValidationData(
                ValidationData memory data
            ) pure returns (uint256) {
                return
                    uint160(data.aggregator) |
                    (uint256(data.validUntil) << 160) |
                    (uint256(data.validAfter) << (160 + 48));
            }
            /**
             * Helper to pack the return value for validateUserOp, when not using an aggregator.
             * @param sigFailed  - True for signature failure, false for success.
             * @param validUntil - Last timestamp this UserOperation is valid (or zero for infinite).
             * @param validAfter - First timestamp this UserOperation is valid.
             */
            function _packValidationData(
                bool sigFailed,
                uint48 validUntil,
                uint48 validAfter
            ) pure returns (uint256) {
                return
                    (sigFailed ? 1 : 0) |
                    (uint256(validUntil) << 160) |
                    (uint256(validAfter) << (160 + 48));
            }
            /**
             * keccak function over calldata.
             * @dev copy calldata into memory, do keccak and drop allocated memory. Strangely, this is more efficient than letting solidity do it.
             */
                function calldataKeccak(bytes calldata data) pure returns (bytes32 ret) {
                    assembly ("memory-safe") {
                        let mem := mload(0x40)
                        let len := data.length
                        calldatacopy(mem, data.offset, len)
                        ret := keccak256(mem, len)
                    }
                }
            /**
             * The minimum of two numbers.
             * @param a - First number.
             * @param b - Second number.
             */
                function min(uint256 a, uint256 b) pure returns (uint256) {
                    return a < b ? a : b;
                }
            // SPDX-License-Identifier: Apache-2.0
            /*
             * @author Hamdi Allam [email protected]
             * Please reach out with any questions or concerns
             */
            pragma solidity >=0.5.10 <0.9.0;
            library RLPReader {
                uint8 constant STRING_SHORT_START = 0x80;
                uint8 constant STRING_LONG_START = 0xb8;
                uint8 constant LIST_SHORT_START = 0xc0;
                uint8 constant LIST_LONG_START = 0xf8;
                uint8 constant WORD_SIZE = 32;
                struct RLPItem {
                    uint256 len;
                    uint256 memPtr;
                }
                struct Iterator {
                    RLPItem item; // Item that's being iterated over.
                    uint256 nextPtr; // Position of the next item in the list.
                }
                /*
                 * @dev Returns the next element in the iteration. Reverts if it has not next element.
                 * @param self The iterator.
                 * @return The next element in the iteration.
                 */
                function next(Iterator memory self) internal pure returns (RLPItem memory) {
                    require(hasNext(self));
                    uint256 ptr = self.nextPtr;
                    uint256 itemLength = _itemLength(ptr);
                    self.nextPtr = ptr + itemLength;
                    return RLPItem(itemLength, ptr);
                }
                /*
                 * @dev Returns true if the iteration has more elements.
                 * @param self The iterator.
                 * @return true if the iteration has more elements.
                 */
                function hasNext(Iterator memory self) internal pure returns (bool) {
                    RLPItem memory item = self.item;
                    return self.nextPtr < item.memPtr + item.len;
                }
                /*
                 * @param item RLP encoded bytes
                 */
                function toRlpItem(bytes memory item) internal pure returns (RLPItem memory) {
                    uint256 memPtr;
                    assembly {
                        memPtr := add(item, 0x20)
                    }
                    return RLPItem(item.length, memPtr);
                }
                /*
                 * @dev Create an iterator. Reverts if item is not a list.
                 * @param self The RLP item.
                 * @return An 'Iterator' over the item.
                 */
                function iterator(RLPItem memory self) internal pure returns (Iterator memory) {
                    require(isList(self));
                    uint256 ptr = self.memPtr + _payloadOffset(self.memPtr);
                    return Iterator(self, ptr);
                }
                /*
                 * @param the RLP item.
                 */
                function rlpLen(RLPItem memory item) internal pure returns (uint256) {
                    return item.len;
                }
                /*
                 * @param the RLP item.
                 * @return (memPtr, len) pair: location of the item's payload in memory.
                 */
                function payloadLocation(RLPItem memory item) internal pure returns (uint256, uint256) {
                    uint256 offset = _payloadOffset(item.memPtr);
                    uint256 memPtr = item.memPtr + offset;
                    uint256 len = item.len - offset; // data length
                    return (memPtr, len);
                }
                /*
                 * @param the RLP item.
                 */
                function payloadLen(RLPItem memory item) internal pure returns (uint256) {
                    (, uint256 len) = payloadLocation(item);
                    return len;
                }
                /*
                 * @param the RLP item containing the encoded list.
                 */
                function toList(RLPItem memory item) internal pure returns (RLPItem[] memory) {
                    require(isList(item));
                    uint256 items = numItems(item);
                    RLPItem[] memory result = new RLPItem[](items);
                    uint256 memPtr = item.memPtr + _payloadOffset(item.memPtr);
                    uint256 dataLen;
                    for (uint256 i = 0; i < items; i++) {
                        dataLen = _itemLength(memPtr);
                        result[i] = RLPItem(dataLen, memPtr);
                        memPtr = memPtr + dataLen;
                    }
                    require(memPtr - item.memPtr == item.len);
                    return result;
                }
                // @return indicator whether encoded payload is a list. negate this function call for isData.
                function isList(RLPItem memory item) internal pure returns (bool) {
                    if (item.len == 0) return false;
                    uint8 byte0;
                    uint256 memPtr = item.memPtr;
                    assembly {
                        byte0 := byte(0, mload(memPtr))
                    }
                    if (byte0 < LIST_SHORT_START) return false;
                    return true;
                }
                /*
                 * @dev A cheaper version of keccak256(toRlpBytes(item)) that avoids copying memory.
                 * @return keccak256 hash of RLP encoded bytes.
                 */
                function rlpBytesKeccak256(RLPItem memory item) internal pure returns (bytes32) {
                    uint256 ptr = item.memPtr;
                    uint256 len = item.len;
                    bytes32 result;
                    assembly {
                        result := keccak256(ptr, len)
                    }
                    return result;
                }
                /*
                 * @dev A cheaper version of keccak256(toBytes(item)) that avoids copying memory.
                 * @return keccak256 hash of the item payload.
                 */
                function payloadKeccak256(RLPItem memory item) internal pure returns (bytes32) {
                    (uint256 memPtr, uint256 len) = payloadLocation(item);
                    bytes32 result;
                    assembly {
                        result := keccak256(memPtr, len)
                    }
                    return result;
                }
                /** RLPItem conversions into data types **/
                // @returns raw rlp encoding in bytes
                function toRlpBytes(RLPItem memory item) internal pure returns (bytes memory) {
                    bytes memory result = new bytes(item.len);
                    if (result.length == 0) return result;
                    uint256 ptr;
                    assembly {
                        ptr := add(0x20, result)
                    }
                    copy(item.memPtr, ptr, item.len);
                    return result;
                }
                // any non-zero byte except "0x80" is considered true
                function toBoolean(RLPItem memory item) internal pure returns (bool) {
                    require(item.len == 1);
                    uint256 result;
                    uint256 memPtr = item.memPtr;
                    assembly {
                        result := byte(0, mload(memPtr))
                    }
                    // SEE Github Issue #5.
                    // Summary: Most commonly used RLP libraries (i.e Geth) will encode
                    // "0" as "0x80" instead of as "0". We handle this edge case explicitly
                    // here.
                    if (result == 0 || result == STRING_SHORT_START) {
                        return false;
                    } else {
                        return true;
                    }
                }
                function toAddress(RLPItem memory item) internal pure returns (address) {
                    // 1 byte for the length prefix
                    require(item.len == 21);
                    return address(uint160(toUint(item)));
                }
                function toUint(RLPItem memory item) internal pure returns (uint256) {
                    require(item.len > 0 && item.len <= 33);
                    (uint256 memPtr, uint256 len) = payloadLocation(item);
                    uint256 result;
                    assembly {
                        result := mload(memPtr)
                        // shift to the correct location if neccesary
                        if lt(len, 32) {
                            result := div(result, exp(256, sub(32, len)))
                        }
                    }
                    return result;
                }
                // enforces 32 byte length
                function toUintStrict(RLPItem memory item) internal pure returns (uint256) {
                    // one byte prefix
                    require(item.len == 33);
                    uint256 result;
                    uint256 memPtr = item.memPtr + 1;
                    assembly {
                        result := mload(memPtr)
                    }
                    return result;
                }
                function toBytes(RLPItem memory item) internal pure returns (bytes memory) {
                    require(item.len > 0);
                    (uint256 memPtr, uint256 len) = payloadLocation(item);
                    bytes memory result = new bytes(len);
                    uint256 destPtr;
                    assembly {
                        destPtr := add(0x20, result)
                    }
                    copy(memPtr, destPtr, len);
                    return result;
                }
                /*
                 * Private Helpers
                 */
                // @return number of payload items inside an encoded list.
                function numItems(RLPItem memory item) private pure returns (uint256) {
                    if (item.len == 0) return 0;
                    uint256 count = 0;
                    uint256 currPtr = item.memPtr + _payloadOffset(item.memPtr);
                    uint256 endPtr = item.memPtr + item.len;
                    while (currPtr < endPtr) {
                        currPtr = currPtr + _itemLength(currPtr); // skip over an item
                        count++;
                    }
                    return count;
                }
                // @return entire rlp item byte length
                function _itemLength(uint256 memPtr) private pure returns (uint256) {
                    uint256 itemLen;
                    uint256 byte0;
                    assembly {
                        byte0 := byte(0, mload(memPtr))
                    }
                    if (byte0 < STRING_SHORT_START) {
                        itemLen = 1;
                    } else if (byte0 < STRING_LONG_START) {
                        itemLen = byte0 - STRING_SHORT_START + 1;
                    } else if (byte0 < LIST_SHORT_START) {
                        assembly {
                            let byteLen := sub(byte0, 0xb7) // # of bytes the actual length is
                            memPtr := add(memPtr, 1) // skip over the first byte
                            /* 32 byte word size */
                            let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to get the len
                            itemLen := add(dataLen, add(byteLen, 1))
                        }
                    } else if (byte0 < LIST_LONG_START) {
                        itemLen = byte0 - LIST_SHORT_START + 1;
                    } else {
                        assembly {
                            let byteLen := sub(byte0, 0xf7)
                            memPtr := add(memPtr, 1)
                            let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to the correct length
                            itemLen := add(dataLen, add(byteLen, 1))
                        }
                    }
                    return itemLen;
                }
                // @return number of bytes until the data
                function _payloadOffset(uint256 memPtr) private pure returns (uint256) {
                    uint256 byte0;
                    assembly {
                        byte0 := byte(0, mload(memPtr))
                    }
                    if (byte0 < STRING_SHORT_START) {
                        return 0;
                    } else if (byte0 < STRING_LONG_START || (byte0 >= LIST_SHORT_START && byte0 < LIST_LONG_START)) {
                        return 1;
                    } else if (byte0 < LIST_SHORT_START) {
                        // being explicit
                        return byte0 - (STRING_LONG_START - 1) + 1;
                    } else {
                        return byte0 - (LIST_LONG_START - 1) + 1;
                    }
                }
                /*
                 * @param src Pointer to source
                 * @param dest Pointer to destination
                 * @param len Amount of memory to copy from the source
                 */
                function copy(uint256 src, uint256 dest, uint256 len) private pure {
                    if (len == 0) return;
                    // copy as many word sizes as possible
                    for (; len >= WORD_SIZE; len -= WORD_SIZE) {
                        assembly {
                            mstore(dest, mload(src))
                        }
                        src += WORD_SIZE;
                        dest += WORD_SIZE;
                    }
                    if (len > 0) {
                        // left over bytes. Mask is used to remove unwanted bytes from the word
                        uint256 mask = 256**(WORD_SIZE - len) - 1;
                        assembly {
                            let srcpart := and(mload(src), not(mask)) // zero out src
                            let destpart := and(mload(dest), mask) // retrieve the bytes
                            mstore(dest, or(destpart, srcpart))
                        }
                    }
                }
            }
            // SPDX-License-Identifier: AGPL-3.0-only
            pragma solidity ^0.8.27;
            // Had to keep it copypasted as the og lib https://github.com/bakaoh/solidity-rlp-encode has incompatible solc version
            import "byteslib/BytesLib.sol";
            /**
             * @title RLPEncoder
             * @dev A simple RLP encoding library.
             * @author Bakaoh
             */
            library RLPEncoder {
                using BytesLib for bytes;
                /*
                 * Internal functions
                 */
                /**
                 * @dev RLP encodes a byte string.
                 * @param self The byte string to encode.
                 * @return The RLP encoded string in bytes.
                 */
                function encodeBytes(bytes memory self) internal pure returns (bytes memory) {
                    bytes memory encoded;
                    if (self.length == 1 && uint8(self[0]) < 128) {
                        encoded = self;
                    } else {
                        encoded = encodeLength(self.length, 128).concat(self);
                    }
                    return encoded;
                }
                /**
                 * @dev RLP encodes a uint.
                 * @param self The uint to encode.
                 * @return The RLP encoded uint in bytes.
                 */
                function encodeUint(uint256 self) internal pure returns (bytes memory) {
                    return encodeBytes(toBinary(self));
                }
                /**
                 * @dev Encode the first byte, followed by the `len` in binary form if `length` is more than 55.
                 * @param self The length of the string or the payload.
                 * @param offset 128 if item is string, 192 if item is list.
                 * @return RLP encoded bytes.
                 */
                function encodeLength(uint256 self, uint256 offset) internal pure returns (bytes memory) {
                    bytes memory encoded;
                    if (self < 56) {
                        encoded = new bytes(1);
                        encoded[0] = bytes32(self + offset)[31];
                    } else {
                        uint256 lenLen;
                        uint256 i = 1;
                        while (self / i != 0) {
                            lenLen++;
                            i *= 256;
                        }
                        encoded = new bytes(lenLen + 1);
                        encoded[0] = bytes32(lenLen + offset + 55)[31];
                        for (i = 1; i <= lenLen; i++) {
                            encoded[i] = bytes32((self / (256 ** (lenLen - i))) % 256)[31];
                        }
                    }
                    return encoded;
                }
                /*
                 * Private functions
                 */
                /**
                 * @dev Encode integer in big endian binary form with no leading zeroes.
                 * @notice TODO: This should be optimized with assembly to save gas costs.
                 * @param _x The integer to encode.
                 * @return RLP encoded bytes.
                 */
                function toBinary(uint256 _x) private pure returns (bytes memory) {
                    bytes memory b = new bytes(32);
                    assembly {
                        mstore(add(b, 32), _x)
                    }
                    uint256 i;
                    for (i = 0; i < 32; i++) {
                        if (b[i] != 0) {
                            break;
                        }
                    }
                    bytes memory res = new bytes(32 - i);
                    for (uint256 j = 0; j < res.length; j++) {
                        res[j] = b[i++];
                    }
                    return res;
                }
            }
            // SPDX-License-Identifier: Unlicense
            /*
             * @title Solidity Bytes Arrays Utils
             * @author Gonçalo Sá <[email protected]>
             *
             * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.
             *      The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.
             */
            pragma solidity >=0.8.0 <0.9.0;
            library BytesLib {
                function concat(
                    bytes memory _preBytes,
                    bytes memory _postBytes
                )
                    internal
                    pure
                    returns (bytes memory)
                {
                    bytes memory tempBytes;
                    assembly {
                        // Get a location of some free memory and store it in tempBytes as
                        // Solidity does for memory variables.
                        tempBytes := mload(0x40)
                        // Store the length of the first bytes array at the beginning of
                        // the memory for tempBytes.
                        let length := mload(_preBytes)
                        mstore(tempBytes, length)
                        // Maintain a memory counter for the current write location in the
                        // temp bytes array by adding the 32 bytes for the array length to
                        // the starting location.
                        let mc := add(tempBytes, 0x20)
                        // Stop copying when the memory counter reaches the length of the
                        // first bytes array.
                        let end := add(mc, length)
                        for {
                            // Initialize a copy counter to the start of the _preBytes data,
                            // 32 bytes into its memory.
                            let cc := add(_preBytes, 0x20)
                        } lt(mc, end) {
                            // Increase both counters by 32 bytes each iteration.
                            mc := add(mc, 0x20)
                            cc := add(cc, 0x20)
                        } {
                            // Write the _preBytes data into the tempBytes memory 32 bytes
                            // at a time.
                            mstore(mc, mload(cc))
                        }
                        // Add the length of _postBytes to the current length of tempBytes
                        // and store it as the new length in the first 32 bytes of the
                        // tempBytes memory.
                        length := mload(_postBytes)
                        mstore(tempBytes, add(length, mload(tempBytes)))
                        // Move the memory counter back from a multiple of 0x20 to the
                        // actual end of the _preBytes data.
                        mc := end
                        // Stop copying when the memory counter reaches the new combined
                        // length of the arrays.
                        end := add(mc, length)
                        for {
                            let cc := add(_postBytes, 0x20)
                        } lt(mc, end) {
                            mc := add(mc, 0x20)
                            cc := add(cc, 0x20)
                        } {
                            mstore(mc, mload(cc))
                        }
                        // Update the free-memory pointer by padding our last write location
                        // to 32 bytes: add 31 bytes to the end of tempBytes to move to the
                        // next 32 byte block, then round down to the nearest multiple of
                        // 32. If the sum of the length of the two arrays is zero then add
                        // one before rounding down to leave a blank 32 bytes (the length block with 0).
                        mstore(0x40, and(
                          add(add(end, iszero(add(length, mload(_preBytes)))), 31),
                          not(31) // Round down to the nearest 32 bytes.
                        ))
                    }
                    return tempBytes;
                }
                function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {
                    assembly {
                        // Read the first 32 bytes of _preBytes storage, which is the length
                        // of the array. (We don't need to use the offset into the slot
                        // because arrays use the entire slot.)
                        let fslot := sload(_preBytes.slot)
                        // Arrays of 31 bytes or less have an even value in their slot,
                        // while longer arrays have an odd value. The actual length is
                        // the slot divided by two for odd values, and the lowest order
                        // byte divided by two for even values.
                        // If the slot is even, bitwise and the slot with 255 and divide by
                        // two to get the length. If the slot is odd, bitwise and the slot
                        // with -1 and divide by two.
                        let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
                        let mlength := mload(_postBytes)
                        let newlength := add(slength, mlength)
                        // slength can contain both the length and contents of the array
                        // if length < 32 bytes so let's prepare for that
                        // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
                        switch add(lt(slength, 32), lt(newlength, 32))
                        case 2 {
                            // Since the new array still fits in the slot, we just need to
                            // update the contents of the slot.
                            // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length
                            sstore(
                                _preBytes.slot,
                                // all the modifications to the slot are inside this
                                // next block
                                add(
                                    // we can just add to the slot contents because the
                                    // bytes we want to change are the LSBs
                                    fslot,
                                    add(
                                        mul(
                                            div(
                                                // load the bytes from memory
                                                mload(add(_postBytes, 0x20)),
                                                // zero all bytes to the right
                                                exp(0x100, sub(32, mlength))
                                            ),
                                            // and now shift left the number of bytes to
                                            // leave space for the length in the slot
                                            exp(0x100, sub(32, newlength))
                                        ),
                                        // increase length by the double of the memory
                                        // bytes length
                                        mul(mlength, 2)
                                    )
                                )
                            )
                        }
                        case 1 {
                            // The stored value fits in the slot, but the combined value
                            // will exceed it.
                            // get the keccak hash to get the contents of the array
                            mstore(0x0, _preBytes.slot)
                            let sc := add(keccak256(0x0, 0x20), div(slength, 32))
                            // save new length
                            sstore(_preBytes.slot, add(mul(newlength, 2), 1))
                            // The contents of the _postBytes array start 32 bytes into
                            // the structure. Our first read should obtain the `submod`
                            // bytes that can fit into the unused space in the last word
                            // of the stored array. To get this, we read 32 bytes starting
                            // from `submod`, so the data we read overlaps with the array
                            // contents by `submod` bytes. Masking the lowest-order
                            // `submod` bytes allows us to add that value directly to the
                            // stored value.
                            let submod := sub(32, slength)
                            let mc := add(_postBytes, submod)
                            let end := add(_postBytes, mlength)
                            let mask := sub(exp(0x100, submod), 1)
                            sstore(
                                sc,
                                add(
                                    and(
                                        fslot,
                                        0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00
                                    ),
                                    and(mload(mc), mask)
                                )
                            )
                            for {
                                mc := add(mc, 0x20)
                                sc := add(sc, 1)
                            } lt(mc, end) {
                                sc := add(sc, 1)
                                mc := add(mc, 0x20)
                            } {
                                sstore(sc, mload(mc))
                            }
                            mask := exp(0x100, sub(mc, end))
                            sstore(sc, mul(div(mload(mc), mask), mask))
                        }
                        default {
                            // get the keccak hash to get the contents of the array
                            mstore(0x0, _preBytes.slot)
                            // Start copying to the last used word of the stored array.
                            let sc := add(keccak256(0x0, 0x20), div(slength, 32))
                            // save new length
                            sstore(_preBytes.slot, add(mul(newlength, 2), 1))
                            // Copy over the first `submod` bytes of the new data as in
                            // case 1 above.
                            let slengthmod := mod(slength, 32)
                            let mlengthmod := mod(mlength, 32)
                            let submod := sub(32, slengthmod)
                            let mc := add(_postBytes, submod)
                            let end := add(_postBytes, mlength)
                            let mask := sub(exp(0x100, submod), 1)
                            sstore(sc, add(sload(sc), and(mload(mc), mask)))
                            for {
                                sc := add(sc, 1)
                                mc := add(mc, 0x20)
                            } lt(mc, end) {
                                sc := add(sc, 1)
                                mc := add(mc, 0x20)
                            } {
                                sstore(sc, mload(mc))
                            }
                            mask := exp(0x100, sub(mc, end))
                            sstore(sc, mul(div(mload(mc), mask), mask))
                        }
                    }
                }
                function slice(
                    bytes memory _bytes,
                    uint256 _start,
                    uint256 _length
                )
                    internal
                    pure
                    returns (bytes memory)
                {
                    require(_length + 31 >= _length, "slice_overflow");
                    require(_bytes.length >= _start + _length, "slice_outOfBounds");
                    bytes memory tempBytes;
                    assembly {
                        switch iszero(_length)
                        case 0 {
                            // Get a location of some free memory and store it in tempBytes as
                            // Solidity does for memory variables.
                            tempBytes := mload(0x40)
                            // The first word of the slice result is potentially a partial
                            // word read from the original array. To read it, we calculate
                            // the length of that partial word and start copying that many
                            // bytes into the array. The first word we copy will start with
                            // data we don't care about, but the last `lengthmod` bytes will
                            // land at the beginning of the contents of the new array. When
                            // we're done copying, we overwrite the full first word with
                            // the actual length of the slice.
                            let lengthmod := and(_length, 31)
                            // The multiplication in the next line is necessary
                            // because when slicing multiples of 32 bytes (lengthmod == 0)
                            // the following copy loop was copying the origin's length
                            // and then ending prematurely not copying everything it should.
                            let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
                            let end := add(mc, _length)
                            for {
                                // The multiplication in the next line has the same exact purpose
                                // as the one above.
                                let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
                            } lt(mc, end) {
                                mc := add(mc, 0x20)
                                cc := add(cc, 0x20)
                            } {
                                mstore(mc, mload(cc))
                            }
                            mstore(tempBytes, _length)
                            //update free-memory pointer
                            //allocating the array padded to 32 bytes like the compiler does now
                            mstore(0x40, and(add(mc, 31), not(31)))
                        }
                        //if we want a zero-length slice let's just return a zero-length array
                        default {
                            tempBytes := mload(0x40)
                            //zero out the 32 bytes slice we are about to return
                            //we need to do it because Solidity does not garbage collect
                            mstore(tempBytes, 0)
                            mstore(0x40, add(tempBytes, 0x20))
                        }
                    }
                    return tempBytes;
                }
                function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {
                    require(_bytes.length >= _start + 20, "toAddress_outOfBounds");
                    address tempAddress;
                    assembly {
                        tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
                    }
                    return tempAddress;
                }
                function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) {
                    require(_bytes.length >= _start + 1 , "toUint8_outOfBounds");
                    uint8 tempUint;
                    assembly {
                        tempUint := mload(add(add(_bytes, 0x1), _start))
                    }
                    return tempUint;
                }
                function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {
                    require(_bytes.length >= _start + 2, "toUint16_outOfBounds");
                    uint16 tempUint;
                    assembly {
                        tempUint := mload(add(add(_bytes, 0x2), _start))
                    }
                    return tempUint;
                }
                function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {
                    require(_bytes.length >= _start + 4, "toUint32_outOfBounds");
                    uint32 tempUint;
                    assembly {
                        tempUint := mload(add(add(_bytes, 0x4), _start))
                    }
                    return tempUint;
                }
                function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {
                    require(_bytes.length >= _start + 8, "toUint64_outOfBounds");
                    uint64 tempUint;
                    assembly {
                        tempUint := mload(add(add(_bytes, 0x8), _start))
                    }
                    return tempUint;
                }
                function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) {
                    require(_bytes.length >= _start + 12, "toUint96_outOfBounds");
                    uint96 tempUint;
                    assembly {
                        tempUint := mload(add(add(_bytes, 0xc), _start))
                    }
                    return tempUint;
                }
                function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {
                    require(_bytes.length >= _start + 16, "toUint128_outOfBounds");
                    uint128 tempUint;
                    assembly {
                        tempUint := mload(add(add(_bytes, 0x10), _start))
                    }
                    return tempUint;
                }
                function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {
                    require(_bytes.length >= _start + 32, "toUint256_outOfBounds");
                    uint256 tempUint;
                    assembly {
                        tempUint := mload(add(add(_bytes, 0x20), _start))
                    }
                    return tempUint;
                }
                function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {
                    require(_bytes.length >= _start + 32, "toBytes32_outOfBounds");
                    bytes32 tempBytes32;
                    assembly {
                        tempBytes32 := mload(add(add(_bytes, 0x20), _start))
                    }
                    return tempBytes32;
                }
                function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {
                    bool success = true;
                    assembly {
                        let length := mload(_preBytes)
                        // if lengths don't match the arrays are not equal
                        switch eq(length, mload(_postBytes))
                        case 1 {
                            // cb is a circuit breaker in the for loop since there's
                            //  no said feature for inline assembly loops
                            // cb = 1 - don't breaker
                            // cb = 0 - break
                            let cb := 1
                            let mc := add(_preBytes, 0x20)
                            let end := add(mc, length)
                            for {
                                let cc := add(_postBytes, 0x20)
                            // the next line is the loop condition:
                            // while(uint256(mc < end) + cb == 2)
                            } eq(add(lt(mc, end), cb), 2) {
                                mc := add(mc, 0x20)
                                cc := add(cc, 0x20)
                            } {
                                // if any of these checks fails then arrays are not equal
                                if iszero(eq(mload(mc), mload(cc))) {
                                    // unsuccess:
                                    success := 0
                                    cb := 0
                                }
                            }
                        }
                        default {
                            // unsuccess:
                            success := 0
                        }
                    }
                    return success;
                }
                function equalStorage(
                    bytes storage _preBytes,
                    bytes memory _postBytes
                )
                    internal
                    view
                    returns (bool)
                {
                    bool success = true;
                    assembly {
                        // we know _preBytes_offset is 0
                        let fslot := sload(_preBytes.slot)
                        // Decode the length of the stored array like in concatStorage().
                        let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
                        let mlength := mload(_postBytes)
                        // if lengths don't match the arrays are not equal
                        switch eq(slength, mlength)
                        case 1 {
                            // slength can contain both the length and contents of the array
                            // if length < 32 bytes so let's prepare for that
                            // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
                            if iszero(iszero(slength)) {
                                switch lt(slength, 32)
                                case 1 {
                                    // blank the last byte which is the length
                                    fslot := mul(div(fslot, 0x100), 0x100)
                                    if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {
                                        // unsuccess:
                                        success := 0
                                    }
                                }
                                default {
                                    // cb is a circuit breaker in the for loop since there's
                                    //  no said feature for inline assembly loops
                                    // cb = 1 - don't breaker
                                    // cb = 0 - break
                                    let cb := 1
                                    // get the keccak hash to get the contents of the array
                                    mstore(0x0, _preBytes.slot)
                                    let sc := keccak256(0x0, 0x20)
                                    let mc := add(_postBytes, 0x20)
                                    let end := add(mc, mlength)
                                    // the next line is the loop condition:
                                    // while(uint256(mc < end) + cb == 2)
                                    for {} eq(add(lt(mc, end), cb), 2) {
                                        sc := add(sc, 1)
                                        mc := add(mc, 0x20)
                                    } {
                                        if iszero(eq(sload(sc), mload(mc))) {
                                            // unsuccess:
                                            success := 0
                                            cb := 0
                                        }
                                    }
                                }
                            }
                        }
                        default {
                            // unsuccess:
                            success := 0
                        }
                    }
                    return success;
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.4;
            /// @notice Gas optimized ECDSA wrapper.
            /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ECDSA.sol)
            /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ECDSA.sol)
            /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol)
            ///
            /// @dev Note:
            /// - The recovery functions use the ecrecover precompile (0x1).
            /// - As of Solady version 0.0.68, the `recover` variants will revert upon recovery failure.
            ///   This is for more safety by default.
            ///   Use the `tryRecover` variants if you need to get the zero address back
            ///   upon recovery failure instead.
            /// - As of Solady version 0.0.134, all `bytes signature` variants accept both
            ///   regular 65-byte `(r, s, v)` and EIP-2098 `(r, vs)` short form signatures.
            ///   See: https://eips.ethereum.org/EIPS/eip-2098
            ///   This is for calldata efficiency on smart accounts prevalent on L2s.
            ///
            /// WARNING! Do NOT directly use signatures as unique identifiers:
            /// - The recovery operations do NOT check if a signature is non-malleable.
            /// - Use a nonce in the digest to prevent replay attacks on the same contract.
            /// - Use EIP-712 for the digest to prevent replay attacks across different chains and contracts.
            ///   EIP-712 also enables readable signing of typed data for better user safety.
            /// - If you need a unique hash from a signature, please use the `canonicalHash` functions.
            library ECDSA {
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                         CONSTANTS                          */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev The order of the secp256k1 elliptic curve.
                uint256 internal constant N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141;
                /// @dev `N/2 + 1`. Used for checking the malleability of the signature.
                uint256 private constant _HALF_N_PLUS_1 =
                    0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1;
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                        CUSTOM ERRORS                       */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev The signature is invalid.
                error InvalidSignature();
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                    RECOVERY OPERATIONS                     */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
                function recover(bytes32 hash, bytes memory signature) internal view returns (address result) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        for { let m := mload(0x40) } 1 {
                            mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                            revert(0x1c, 0x04)
                        } {
                            switch mload(signature)
                            case 64 {
                                let vs := mload(add(signature, 0x40))
                                mstore(0x20, add(shr(255, vs), 27)) // `v`.
                                mstore(0x60, shr(1, shl(1, vs))) // `s`.
                            }
                            case 65 {
                                mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                                mstore(0x60, mload(add(signature, 0x40))) // `s`.
                            }
                            default { continue }
                            mstore(0x00, hash)
                            mstore(0x40, mload(add(signature, 0x20))) // `r`.
                            result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
                            mstore(0x60, 0) // Restore the zero slot.
                            mstore(0x40, m) // Restore the free memory pointer.
                            // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                            if returndatasize() { break }
                        }
                    }
                }
                /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
                function recoverCalldata(bytes32 hash, bytes calldata signature)
                    internal
                    view
                    returns (address result)
                {
                    /// @solidity memory-safe-assembly
                    assembly {
                        for { let m := mload(0x40) } 1 {
                            mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                            revert(0x1c, 0x04)
                        } {
                            switch signature.length
                            case 64 {
                                let vs := calldataload(add(signature.offset, 0x20))
                                mstore(0x20, add(shr(255, vs), 27)) // `v`.
                                mstore(0x40, calldataload(signature.offset)) // `r`.
                                mstore(0x60, shr(1, shl(1, vs))) // `s`.
                            }
                            case 65 {
                                mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
                                calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
                            }
                            default { continue }
                            mstore(0x00, hash)
                            result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
                            mstore(0x60, 0) // Restore the zero slot.
                            mstore(0x40, m) // Restore the free memory pointer.
                            // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                            if returndatasize() { break }
                        }
                    }
                }
                /// @dev Recovers the signer's address from a message digest `hash`,
                /// and the EIP-2098 short form signature defined by `r` and `vs`.
                function recover(bytes32 hash, bytes32 r, bytes32 vs) internal view returns (address result) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let m := mload(0x40) // Cache the free memory pointer.
                        mstore(0x00, hash)
                        mstore(0x20, add(shr(255, vs), 27)) // `v`.
                        mstore(0x40, r)
                        mstore(0x60, shr(1, shl(1, vs))) // `s`.
                        result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
                        // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                        if iszero(returndatasize()) {
                            mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                            revert(0x1c, 0x04)
                        }
                        mstore(0x60, 0) // Restore the zero slot.
                        mstore(0x40, m) // Restore the free memory pointer.
                    }
                }
                /// @dev Recovers the signer's address from a message digest `hash`,
                /// and the signature defined by `v`, `r`, `s`.
                function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
                    internal
                    view
                    returns (address result)
                {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let m := mload(0x40) // Cache the free memory pointer.
                        mstore(0x00, hash)
                        mstore(0x20, and(v, 0xff))
                        mstore(0x40, r)
                        mstore(0x60, s)
                        result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
                        // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                        if iszero(returndatasize()) {
                            mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                            revert(0x1c, 0x04)
                        }
                        mstore(0x60, 0) // Restore the zero slot.
                        mstore(0x40, m) // Restore the free memory pointer.
                    }
                }
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                   TRY-RECOVER OPERATIONS                   */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                // WARNING!
                // These functions will NOT revert upon recovery failure.
                // Instead, they will return the zero address upon recovery failure.
                // It is critical that the returned address is NEVER compared against
                // a zero address (e.g. an uninitialized address variable).
                /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
                function tryRecover(bytes32 hash, bytes memory signature)
                    internal
                    view
                    returns (address result)
                {
                    /// @solidity memory-safe-assembly
                    assembly {
                        for { let m := mload(0x40) } 1 {} {
                            switch mload(signature)
                            case 64 {
                                let vs := mload(add(signature, 0x40))
                                mstore(0x20, add(shr(255, vs), 27)) // `v`.
                                mstore(0x60, shr(1, shl(1, vs))) // `s`.
                            }
                            case 65 {
                                mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                                mstore(0x60, mload(add(signature, 0x40))) // `s`.
                            }
                            default { break }
                            mstore(0x00, hash)
                            mstore(0x40, mload(add(signature, 0x20))) // `r`.
                            pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20))
                            mstore(0x60, 0) // Restore the zero slot.
                            // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                            result := mload(xor(0x60, returndatasize()))
                            mstore(0x40, m) // Restore the free memory pointer.
                            break
                        }
                    }
                }
                /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
                function tryRecoverCalldata(bytes32 hash, bytes calldata signature)
                    internal
                    view
                    returns (address result)
                {
                    /// @solidity memory-safe-assembly
                    assembly {
                        for { let m := mload(0x40) } 1 {} {
                            switch signature.length
                            case 64 {
                                let vs := calldataload(add(signature.offset, 0x20))
                                mstore(0x20, add(shr(255, vs), 27)) // `v`.
                                mstore(0x40, calldataload(signature.offset)) // `r`.
                                mstore(0x60, shr(1, shl(1, vs))) // `s`.
                            }
                            case 65 {
                                mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
                                calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
                            }
                            default { break }
                            mstore(0x00, hash)
                            pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20))
                            mstore(0x60, 0) // Restore the zero slot.
                            // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                            result := mload(xor(0x60, returndatasize()))
                            mstore(0x40, m) // Restore the free memory pointer.
                            break
                        }
                    }
                }
                /// @dev Recovers the signer's address from a message digest `hash`,
                /// and the EIP-2098 short form signature defined by `r` and `vs`.
                function tryRecover(bytes32 hash, bytes32 r, bytes32 vs)
                    internal
                    view
                    returns (address result)
                {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let m := mload(0x40) // Cache the free memory pointer.
                        mstore(0x00, hash)
                        mstore(0x20, add(shr(255, vs), 27)) // `v`.
                        mstore(0x40, r)
                        mstore(0x60, shr(1, shl(1, vs))) // `s`.
                        pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20))
                        mstore(0x60, 0) // Restore the zero slot.
                        // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                        result := mload(xor(0x60, returndatasize()))
                        mstore(0x40, m) // Restore the free memory pointer.
                    }
                }
                /// @dev Recovers the signer's address from a message digest `hash`,
                /// and the signature defined by `v`, `r`, `s`.
                function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
                    internal
                    view
                    returns (address result)
                {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let m := mload(0x40) // Cache the free memory pointer.
                        mstore(0x00, hash)
                        mstore(0x20, and(v, 0xff))
                        mstore(0x40, r)
                        mstore(0x60, s)
                        pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20))
                        mstore(0x60, 0) // Restore the zero slot.
                        // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                        result := mload(xor(0x60, returndatasize()))
                        mstore(0x40, m) // Restore the free memory pointer.
                    }
                }
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                     HASHING OPERATIONS                     */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev Returns an Ethereum Signed Message, created from a `hash`.
                /// This produces a hash corresponding to the one signed with the
                /// [`eth_sign`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign)
                /// JSON-RPC method as part of EIP-191.
                function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        mstore(0x20, hash) // Store into scratch space for keccak256.
                        mstore(0x00, "\\x00\\x00\\x00\\x00\\x19Ethereum Signed Message:\
            32") // 28 bytes.
                        result := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 0x3c`.
                    }
                }
                /// @dev Returns an Ethereum Signed Message, created from `s`.
                /// This produces a hash corresponding to the one signed with the
                /// [`eth_sign`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign)
                /// JSON-RPC method as part of EIP-191.
                /// Note: Supports lengths of `s` up to 999999 bytes.
                function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let sLength := mload(s)
                        let o := 0x20
                        mstore(o, "\\x19Ethereum Signed Message:\
            ") // 26 bytes, zero-right-padded.
                        mstore(0x00, 0x00)
                        // Convert the `s.length` to ASCII decimal representation: `base10(s.length)`.
                        for { let temp := sLength } 1 {} {
                            o := sub(o, 1)
                            mstore8(o, add(48, mod(temp, 10)))
                            temp := div(temp, 10)
                            if iszero(temp) { break }
                        }
                        let n := sub(0x3a, o) // Header length: `26 + 32 - o`.
                        // Throw an out-of-offset error (consumes all gas) if the header exceeds 32 bytes.
                        returndatacopy(returndatasize(), returndatasize(), gt(n, 0x20))
                        mstore(s, or(mload(0x00), mload(n))) // Temporarily store the header.
                        result := keccak256(add(s, sub(0x20, n)), add(n, sLength))
                        mstore(s, sLength) // Restore the length.
                    }
                }
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                  CANONICAL HASH FUNCTIONS                  */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                // The following functions returns the hash of the signature in it's canonicalized format,
                // which is the 65-byte `abi.encodePacked(r, s, uint8(v))`, where `v` is either 27 or 28.
                // If `s` is greater than `N / 2` then it will be converted to `N - s`
                // and the `v` value will be flipped.
                // If the signature has an invalid length, or if `v` is invalid,
                // a uniquely corrupt hash will be returned.
                // These functions are useful for "poor-mans-VRF".
                /// @dev Returns the canonical hash of `signature`.
                function canonicalHash(bytes memory signature) internal pure returns (bytes32 result) {
                    // @solidity memory-safe-assembly
                    assembly {
                        let l := mload(signature)
                        for {} 1 {} {
                            mstore(0x00, mload(add(signature, 0x20))) // `r`.
                            let s := mload(add(signature, 0x40))
                            let v := mload(add(signature, 0x41))
                            if eq(l, 64) {
                                v := add(shr(255, s), 27)
                                s := shr(1, shl(1, s))
                            }
                            if iszero(lt(s, _HALF_N_PLUS_1)) {
                                v := xor(v, 7)
                                s := sub(N, s)
                            }
                            mstore(0x21, v)
                            mstore(0x20, s)
                            result := keccak256(0x00, 0x41)
                            mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
                            break
                        }
                        // If the length is neither 64 nor 65, return a uniquely corrupted hash.
                        if iszero(lt(sub(l, 64), 2)) {
                            // `bytes4(keccak256("InvalidSignatureLength"))`.
                            result := xor(keccak256(add(signature, 0x20), l), 0xd62f1ab2)
                        }
                    }
                }
                /// @dev Returns the canonical hash of `signature`.
                function canonicalHashCalldata(bytes calldata signature)
                    internal
                    pure
                    returns (bytes32 result)
                {
                    // @solidity memory-safe-assembly
                    assembly {
                        for {} 1 {} {
                            mstore(0x00, calldataload(signature.offset)) // `r`.
                            let s := calldataload(add(signature.offset, 0x20))
                            let v := calldataload(add(signature.offset, 0x21))
                            if eq(signature.length, 64) {
                                v := add(shr(255, s), 27)
                                s := shr(1, shl(1, s))
                            }
                            if iszero(lt(s, _HALF_N_PLUS_1)) {
                                v := xor(v, 7)
                                s := sub(N, s)
                            }
                            mstore(0x21, v)
                            mstore(0x20, s)
                            result := keccak256(0x00, 0x41)
                            mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
                            break
                        }
                        // If the length is neither 64 nor 65, return a uniquely corrupted hash.
                        if iszero(lt(sub(signature.length, 64), 2)) {
                            calldatacopy(mload(0x40), signature.offset, signature.length)
                            // `bytes4(keccak256("InvalidSignatureLength"))`.
                            result := xor(keccak256(mload(0x40), signature.length), 0xd62f1ab2)
                        }
                    }
                }
                /// @dev Returns the canonical hash of `signature`.
                function canonicalHash(bytes32 r, bytes32 vs) internal pure returns (bytes32 result) {
                    // @solidity memory-safe-assembly
                    assembly {
                        mstore(0x00, r) // `r`.
                        let v := add(shr(255, vs), 27)
                        let s := shr(1, shl(1, vs))
                        mstore(0x21, v)
                        mstore(0x20, s)
                        result := keccak256(0x00, 0x41)
                        mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
                    }
                }
                /// @dev Returns the canonical hash of `signature`.
                function canonicalHash(uint8 v, bytes32 r, bytes32 s) internal pure returns (bytes32 result) {
                    // @solidity memory-safe-assembly
                    assembly {
                        mstore(0x00, r) // `r`.
                        if iszero(lt(s, _HALF_N_PLUS_1)) {
                            v := xor(v, 7)
                            s := sub(N, s)
                        }
                        mstore(0x21, v)
                        mstore(0x20, s)
                        result := keccak256(0x00, 0x41)
                        mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
                    }
                }
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                   EMPTY CALLDATA HELPERS                   */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev Returns an empty calldata bytes.
                function emptySignature() internal pure returns (bytes calldata signature) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        signature.length := 0
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)
            pragma solidity ^0.8.20;
            import {Math} from "./math/Math.sol";
            import {SignedMath} from "./math/SignedMath.sol";
            /**
             * @dev String operations.
             */
            library Strings {
                bytes16 private constant HEX_DIGITS = "0123456789abcdef";
                uint8 private constant ADDRESS_LENGTH = 20;
                /**
                 * @dev The `value` string doesn't fit in the specified `length`.
                 */
                error StringsInsufficientHexLength(uint256 value, uint256 length);
                /**
                 * @dev Converts a `uint256` to its ASCII `string` decimal representation.
                 */
                function toString(uint256 value) internal pure returns (string memory) {
                    unchecked {
                        uint256 length = Math.log10(value) + 1;
                        string memory buffer = new string(length);
                        uint256 ptr;
                        /// @solidity memory-safe-assembly
                        assembly {
                            ptr := add(buffer, add(32, length))
                        }
                        while (true) {
                            ptr--;
                            /// @solidity memory-safe-assembly
                            assembly {
                                mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
                            }
                            value /= 10;
                            if (value == 0) break;
                        }
                        return buffer;
                    }
                }
                /**
                 * @dev Converts a `int256` to its ASCII `string` decimal representation.
                 */
                function toStringSigned(int256 value) internal pure returns (string memory) {
                    return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
                }
                /**
                 * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
                 */
                function toHexString(uint256 value) internal pure returns (string memory) {
                    unchecked {
                        return toHexString(value, Math.log256(value) + 1);
                    }
                }
                /**
                 * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
                 */
                function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
                    uint256 localValue = value;
                    bytes memory buffer = new bytes(2 * length + 2);
                    buffer[0] = "0";
                    buffer[1] = "x";
                    for (uint256 i = 2 * length + 1; i > 1; --i) {
                        buffer[i] = HEX_DIGITS[localValue & 0xf];
                        localValue >>= 4;
                    }
                    if (localValue != 0) {
                        revert StringsInsufficientHexLength(value, length);
                    }
                    return string(buffer);
                }
                /**
                 * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
                 * representation.
                 */
                function toHexString(address addr) internal pure returns (string memory) {
                    return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
                }
                /**
                 * @dev Returns true if the two strings are equal.
                 */
                function equal(string memory a, string memory b) internal pure returns (bool) {
                    return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
            pragma solidity ^0.8.20;
            /**
             * @dev Standard math utilities missing in the Solidity language.
             */
            library Math {
                /**
                 * @dev Muldiv operation overflow.
                 */
                error MathOverflowedMulDiv();
                enum Rounding {
                    Floor, // Toward negative infinity
                    Ceil, // Toward positive infinity
                    Trunc, // Toward zero
                    Expand // Away from zero
                }
                /**
                 * @dev Returns the addition of two unsigned integers, with an overflow flag.
                 */
                function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                    unchecked {
                        uint256 c = a + b;
                        if (c < a) return (false, 0);
                        return (true, c);
                    }
                }
                /**
                 * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
                 */
                function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                    unchecked {
                        if (b > a) return (false, 0);
                        return (true, a - b);
                    }
                }
                /**
                 * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
                 */
                function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                    unchecked {
                        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                        // benefit is lost if 'b' is also tested.
                        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                        if (a == 0) return (true, 0);
                        uint256 c = a * b;
                        if (c / a != b) return (false, 0);
                        return (true, c);
                    }
                }
                /**
                 * @dev Returns the division of two unsigned integers, with a division by zero flag.
                 */
                function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                    unchecked {
                        if (b == 0) return (false, 0);
                        return (true, a / b);
                    }
                }
                /**
                 * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
                 */
                function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                    unchecked {
                        if (b == 0) return (false, 0);
                        return (true, a % b);
                    }
                }
                /**
                 * @dev Returns the largest of two numbers.
                 */
                function max(uint256 a, uint256 b) internal pure returns (uint256) {
                    return a > b ? a : b;
                }
                /**
                 * @dev Returns the smallest of two numbers.
                 */
                function min(uint256 a, uint256 b) internal pure returns (uint256) {
                    return a < b ? a : b;
                }
                /**
                 * @dev Returns the average of two numbers. The result is rounded towards
                 * zero.
                 */
                function average(uint256 a, uint256 b) internal pure returns (uint256) {
                    // (a + b) / 2 can overflow.
                    return (a & b) + (a ^ b) / 2;
                }
                /**
                 * @dev Returns the ceiling of the division of two numbers.
                 *
                 * This differs from standard division with `/` in that it rounds towards infinity instead
                 * of rounding towards zero.
                 */
                function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
                    if (b == 0) {
                        // Guarantee the same behavior as in a regular Solidity division.
                        return a / b;
                    }
                    // (a + b - 1) / b can overflow on addition, so we distribute.
                    return a == 0 ? 0 : (a - 1) / b + 1;
                }
                /**
                 * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
                 * denominator == 0.
                 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
                 * Uniswap Labs also under MIT license.
                 */
                function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
                    unchecked {
                        // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                        // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                        // variables such that product = prod1 * 2^256 + prod0.
                        uint256 prod0 = x * y; // Least significant 256 bits of the product
                        uint256 prod1; // Most significant 256 bits of the product
                        assembly {
                            let mm := mulmod(x, y, not(0))
                            prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                        }
                        // Handle non-overflow cases, 256 by 256 division.
                        if (prod1 == 0) {
                            // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                            // The surrounding unchecked block does not change this fact.
                            // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                            return prod0 / denominator;
                        }
                        // Make sure the result is less than 2^256. Also prevents denominator == 0.
                        if (denominator <= prod1) {
                            revert MathOverflowedMulDiv();
                        }
                        ///////////////////////////////////////////////
                        // 512 by 256 division.
                        ///////////////////////////////////////////////
                        // Make division exact by subtracting the remainder from [prod1 prod0].
                        uint256 remainder;
                        assembly {
                            // Compute remainder using mulmod.
                            remainder := mulmod(x, y, denominator)
                            // Subtract 256 bit number from 512 bit number.
                            prod1 := sub(prod1, gt(remainder, prod0))
                            prod0 := sub(prod0, remainder)
                        }
                        // Factor powers of two out of denominator and compute largest power of two divisor of denominator.
                        // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
                        uint256 twos = denominator & (0 - denominator);
                        assembly {
                            // Divide denominator by twos.
                            denominator := div(denominator, twos)
                            // Divide [prod1 prod0] by twos.
                            prod0 := div(prod0, twos)
                            // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                            twos := add(div(sub(0, twos), twos), 1)
                        }
                        // Shift in bits from prod1 into prod0.
                        prod0 |= prod1 * twos;
                        // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                        // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                        // four bits. That is, denominator * inv = 1 mod 2^4.
                        uint256 inverse = (3 * denominator) ^ 2;
                        // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
                        // works in modular arithmetic, doubling the correct bits in each step.
                        inverse *= 2 - denominator * inverse; // inverse mod 2^8
                        inverse *= 2 - denominator * inverse; // inverse mod 2^16
                        inverse *= 2 - denominator * inverse; // inverse mod 2^32
                        inverse *= 2 - denominator * inverse; // inverse mod 2^64
                        inverse *= 2 - denominator * inverse; // inverse mod 2^128
                        inverse *= 2 - denominator * inverse; // inverse mod 2^256
                        // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                        // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                        // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                        // is no longer required.
                        result = prod0 * inverse;
                        return result;
                    }
                }
                /**
                 * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
                 */
                function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
                    uint256 result = mulDiv(x, y, denominator);
                    if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
                        result += 1;
                    }
                    return result;
                }
                /**
                 * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
                 * towards zero.
                 *
                 * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
                 */
                function sqrt(uint256 a) internal pure returns (uint256) {
                    if (a == 0) {
                        return 0;
                    }
                    // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
                    //
                    // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
                    // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
                    //
                    // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
                    // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
                    // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
                    //
                    // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
                    uint256 result = 1 << (log2(a) >> 1);
                    // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
                    // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
                    // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
                    // into the expected uint128 result.
                    unchecked {
                        result = (result + a / result) >> 1;
                        result = (result + a / result) >> 1;
                        result = (result + a / result) >> 1;
                        result = (result + a / result) >> 1;
                        result = (result + a / result) >> 1;
                        result = (result + a / result) >> 1;
                        result = (result + a / result) >> 1;
                        return min(result, a / result);
                    }
                }
                /**
                 * @notice Calculates sqrt(a), following the selected rounding direction.
                 */
                function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
                    unchecked {
                        uint256 result = sqrt(a);
                        return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
                    }
                }
                /**
                 * @dev Return the log in base 2 of a positive value rounded towards zero.
                 * Returns 0 if given 0.
                 */
                function log2(uint256 value) internal pure returns (uint256) {
                    uint256 result = 0;
                    unchecked {
                        if (value >> 128 > 0) {
                            value >>= 128;
                            result += 128;
                        }
                        if (value >> 64 > 0) {
                            value >>= 64;
                            result += 64;
                        }
                        if (value >> 32 > 0) {
                            value >>= 32;
                            result += 32;
                        }
                        if (value >> 16 > 0) {
                            value >>= 16;
                            result += 16;
                        }
                        if (value >> 8 > 0) {
                            value >>= 8;
                            result += 8;
                        }
                        if (value >> 4 > 0) {
                            value >>= 4;
                            result += 4;
                        }
                        if (value >> 2 > 0) {
                            value >>= 2;
                            result += 2;
                        }
                        if (value >> 1 > 0) {
                            result += 1;
                        }
                    }
                    return result;
                }
                /**
                 * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
                 * Returns 0 if given 0.
                 */
                function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
                    unchecked {
                        uint256 result = log2(value);
                        return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
                    }
                }
                /**
                 * @dev Return the log in base 10 of a positive value rounded towards zero.
                 * Returns 0 if given 0.
                 */
                function log10(uint256 value) internal pure returns (uint256) {
                    uint256 result = 0;
                    unchecked {
                        if (value >= 10 ** 64) {
                            value /= 10 ** 64;
                            result += 64;
                        }
                        if (value >= 10 ** 32) {
                            value /= 10 ** 32;
                            result += 32;
                        }
                        if (value >= 10 ** 16) {
                            value /= 10 ** 16;
                            result += 16;
                        }
                        if (value >= 10 ** 8) {
                            value /= 10 ** 8;
                            result += 8;
                        }
                        if (value >= 10 ** 4) {
                            value /= 10 ** 4;
                            result += 4;
                        }
                        if (value >= 10 ** 2) {
                            value /= 10 ** 2;
                            result += 2;
                        }
                        if (value >= 10 ** 1) {
                            result += 1;
                        }
                    }
                    return result;
                }
                /**
                 * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
                 * Returns 0 if given 0.
                 */
                function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
                    unchecked {
                        uint256 result = log10(value);
                        return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
                    }
                }
                /**
                 * @dev Return the log in base 256 of a positive value rounded towards zero.
                 * Returns 0 if given 0.
                 *
                 * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
                 */
                function log256(uint256 value) internal pure returns (uint256) {
                    uint256 result = 0;
                    unchecked {
                        if (value >> 128 > 0) {
                            value >>= 128;
                            result += 16;
                        }
                        if (value >> 64 > 0) {
                            value >>= 64;
                            result += 8;
                        }
                        if (value >> 32 > 0) {
                            value >>= 32;
                            result += 4;
                        }
                        if (value >> 16 > 0) {
                            value >>= 16;
                            result += 2;
                        }
                        if (value >> 8 > 0) {
                            result += 1;
                        }
                    }
                    return result;
                }
                /**
                 * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
                 * Returns 0 if given 0.
                 */
                function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
                    unchecked {
                        uint256 result = log256(value);
                        return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
                    }
                }
                /**
                 * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
                 */
                function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
                    return uint8(rounding) % 2 == 1;
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)
            pragma solidity ^0.8.20;
            /**
             * @dev Standard signed math utilities missing in the Solidity language.
             */
            library SignedMath {
                /**
                 * @dev Returns the largest of two signed numbers.
                 */
                function max(int256 a, int256 b) internal pure returns (int256) {
                    return a > b ? a : b;
                }
                /**
                 * @dev Returns the smallest of two signed numbers.
                 */
                function min(int256 a, int256 b) internal pure returns (int256) {
                    return a < b ? a : b;
                }
                /**
                 * @dev Returns the average of two signed numbers without overflow.
                 * The result is rounded towards zero.
                 */
                function average(int256 a, int256 b) internal pure returns (int256) {
                    // Formula from the book "Hacker's Delight"
                    int256 x = (a & b) + ((a ^ b) >> 1);
                    return x + (int256(uint256(x) >> 255) & (a ^ b));
                }
                /**
                 * @dev Returns the absolute unsigned value of a signed value.
                 */
                function abs(int256 n) internal pure returns (uint256) {
                    unchecked {
                        // must be unchecked in order to support `n = type(int256).min`
                        return uint256(n >= 0 ? n : -n);
                    }
                }
            }