ETH Price: $1,949.61 (-1.88%)

Transaction Decoder

Block:
24466317 at Feb-16-2026 02:13:23 AM +UTC
Transaction Fee:
0.00000372539293908 ETH $0.007263
Gas Used:
110,535 Gas / 0.033703288 Gwei

Account State Difference:

  Address   Before After State Difference Code
(quasarbuilder)
15.041317766515021443 Eth15.041317846574416593 Eth0.00000008005939515
0x3cfDc212...3dE6a7a89
(Candide: Bundler 1)
0.274897193844828258 Eth
Nonce: 3554
0.274893468451889178 Eth
Nonce: 3555
0.00000372539293908

Execution Trace

EntryPoint.handleOps( ops=, beneficiary=0x3cfDc212769c890907bcE93D3d8C2c53dE6a7a89 )
  • 0x8b926a22e41af4bb4f16c05b0c980f660ae60eaa.19822f7c( )
    • Null: 0x000...001.bfeb7ca4( )
    • AmbirePaymaster.validatePaymasterUserOp( userOp=[{name:sender, type:address, order:1, indexed:false, value:0x8b926A22E41AF4Bb4f16c05b0C980f660ae60EAa, valueString:0x8b926A22E41AF4Bb4f16c05b0C980f660ae60EAa}, {name:nonce, type:uint256, order:2, indexed:false, value:12, valueString:12}, {name:initCode, type:bytes, order:3, indexed:false, value:0x, valueString:0x}, {name:callData, type:bytes, order:4, indexed:false, value:0xABC5345E00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000760000000000000000000000000DAC17F958D2EE523A2206206994597C13D831EC7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044095EA7B30000000000000000000000001231DEB6F5749EF6CE6943A275A1D3E7486F4EAE0000000000000000000000000000000000000000000000000000000008F0D180000000000000000000000000000000000000000000000000000000000000000000000000000000001231DEB6F5749EF6CE6943A275A1D3E7486F4EAE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000005845FD9AE2E07A62D0903126A840BDCD44C805A91DCDAE4E31740B9DA231D24CF28D727835400000000000000000000000000000000000000000000000000000000000000C000000000000000000000000000000000000000000000000000000000000001000000000000000000000000008B926A22E41AF4BB4F16C05B0C980F660AE60EAA0000000000000000000000000000000000000000000003C2E7A86F18DE11EB8500000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000015616D626972652D657874656E73696F6E2D70726F640000000000000000000000000000000000000000000000000000000000000000000000000000000000002A307830303030303030303030303030303030303030303030303030303030303030303030303030303030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000003EF238C36035880EFBDFA239D218186B79AD1D6F0000000000000000000000003EF238C36035880EFBDFA239D218186B79AD1D6F000000000000000000000000DAC17F958D2EE523A2206206994597C13D831EC7000000000000000000000000DAC17F958D2EE523A2206206994597C13D831EC70000000000000000000000000000000000000000000000000000000008F0D18000000000000000000000000000000000000000000000000000000000000000E000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000084EEDD56E1000000000000000000000000DAC17F958D2EE523A2206206994597C13D831EC7000000000000000000000000000000000000000000000000000000000009BA3C000000000000000000000000000000000000000000000000000000000001B774000000000000000000000000942F9CE5D9A33A82F88D233AEB3292E680230348000000000000000000000000000000000000000000000000000000000000000000000000000000000D05A7D3448512B78FA8A9E46C4872C88C4A0D050000000000000000000000000D05A7D3448512B78FA8A9E46C4872C88C4A0D05000000000000000000000000DAC17F958D2EE523A2206206994597C13D831EC700000000000000000000000088800092FF476844F74DC2FC427974BBEE2794AE0000000000000000000000000000000000000000000000000000000008E55FD000000000000000000000000000000000000000000000000000000000000000E00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011B83BD37F90001DAC17F958D2EE523A2206206994597C13D831EC7000188800092FF476844F74DC2FC427974BBEE2794AE0408E55FD00A03CCA19808C8F5000000028F5C0001365084B05FA7D5028346BD21D842ED0601BAB5B8000000011231DEB6F5749EF6CE6943A275A1D3E7486F4EAE0000000027688555000501030601B557901F030101010102001E000D0200030200040D0101040500FF00724934F5FDD0E938664A39B0B484D055E24F680EDAC17F958D2EE523A2206206994597C13D831EC7C7BBEC68D12A0D1830360F8EC58FA599BA1B0E9B53BBDF4EA397D17A6F904DC882B3FB78A6875A66C02AAA39B223FE8D0A0E5C4F27EAD9083C756CC200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000942F9CE5D9A33A82F88D233AEB3292E6802303480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000E00000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000A0000000000000000000000000000000000000000000000000000000000000000767617354616E6B0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000045553444300000000000000000000000000000000000000000000000000000000, valueString:0xABC5345E00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000760000000000000000000000000DAC17F958D2EE523A2206206994597C13D831EC7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044095EA7B30000000000000000000000001231DEB6F5749EF6CE6943A275A1D3E7486F4EAE0000000000000000000000000000000000000000000000000000000008F0D180000000000000000000000000000000000000000000000000000000000000000000000000000000001231DEB6F5749EF6CE6943A275A1D3E7486F4EAE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000005845FD9AE2E07A62D0903126A840BDCD44C805A91DCDAE4E31740B9DA231D24CF28D727835400000000000000000000000000000000000000000000000000000000000000C000000000000000000000000000000000000000000000000000000000000001000000000000000000000000008B926A22E41AF4BB4F16C05B0C980F660AE60EAA0000000000000000000000000000000000000000000003C2E7A86F18DE11EB8500000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000015616D626972652D657874656E73696F6E2D70726F640000000000000000000000000000000000000000000000000000000000000000000000000000000000002A307830303030303030303030303030303030303030303030303030303030303030303030303030303030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000003EF238C36035880EFBDFA239D218186B79AD1D6F0000000000000000000000003EF238C36035880EFBDFA239D218186B79AD1D6F000000000000000000000000DAC17F958D2EE523A2206206994597C13D831EC7000000000000000000000000DAC17F958D2EE523A2206206994597C13D831EC70000000000000000000000000000000000000000000000000000000008F0D18000000000000000000000000000000000000000000000000000000000000000E000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000084EEDD56E1000000000000000000000000DAC17F958D2EE523A2206206994597C13D831EC7000000000000000000000000000000000000000000000000000000000009BA3C000000000000000000000000000000000000000000000000000000000001B774000000000000000000000000942F9CE5D9A33A82F88D233AEB3292E680230348000000000000000000000000000000000000000000000000000000000000000000000000000000000D05A7D3448512B78FA8A9E46C4872C88C4A0D050000000000000000000000000D05A7D3448512B78FA8A9E46C4872C88C4A0D05000000000000000000000000DAC17F958D2EE523A2206206994597C13D831EC700000000000000000000000088800092FF476844F74DC2FC427974BBEE2794AE0000000000000000000000000000000000000000000000000000000008E55FD000000000000000000000000000000000000000000000000000000000000000E00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011B83BD37F90001DAC17F958D2EE523A2206206994597C13D831EC7000188800092FF476844F74DC2FC427974BBEE2794AE0408E55FD00A03CCA19808C8F5000000028F5C0001365084B05FA7D5028346BD21D842ED0601BAB5B8000000011231DEB6F5749EF6CE6943A275A1D3E7486F4EAE0000000027688555000501030601B557901F030101010102001E000D0200030200040D0101040500FF00724934F5FDD0E938664A39B0B484D055E24F680EDAC17F958D2EE523A2206206994597C13D831EC7C7BBEC68D12A0D1830360F8EC58FA599BA1B0E9B53BBDF4EA397D17A6F904DC882B3FB78A6875A66C02AAA39B223FE8D0A0E5C4F27EAD9083C756CC200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000942F9CE5D9A33A82F88D233AEB3292E6802303480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000E00000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000A0000000000000000000000000000000000000000000000000000000000000000767617354616E6B0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000045553444300000000000000000000000000000000000000000000000000000000}, {name:accountGasLimits, type:bytes32, order:5, indexed:false, value:000000000000000000000000000169F50000000000000000000000000006E815, valueString:000000000000000000000000000169F50000000000000000000000000006E815}, {name:preVerificationGas, type:uint256, order:6, indexed:false, value:84244, valueString:84244}, {name:gasFees, type:bytes32, order:7, indexed:false, value:00000000000000000000000000000012000000000000000000000000026AE94C, valueString:00000000000000000000000000000012000000000000000000000000026AE94C}, {name:paymasterAndData, type:bytes, order:8, indexed:false, value:0xA8B267C68715FA1DCA055993149F30217B572CF00000000000000000000000000000A410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000069927D250000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000428E97763CAB31798B8B78B1407F3E216463E9EDD19FFA6615F21CEAD3303BBA4D21F5AA8D13DE890044BD8E2E25F1CA3499EDCE639830E900C805ACE73C650B061B00000000000000000000000000000000000000000000000000000000000000, valueString:0xA8B267C68715FA1DCA055993149F30217B572CF00000000000000000000000000000A410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000069927D250000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000428E97763CAB31798B8B78B1407F3E216463E9EDD19FFA6615F21CEAD3303BBA4D21F5AA8D13DE890044BD8E2E25F1CA3499EDCE639830E900C805ACE73C650B061B00000000000000000000000000000000000000000000000000000000000000}, {name:signature, type:bytes, order:9, indexed:false, value:0x276D49430237C00B2A128CD0D579EF8EF6E7F327AD0B6F5158C5C681B38367A609B270A450D7648E894434C06E3EACFB4DF77E746ED81CB9FE4EBB4649B2A7511B00, valueString:0x276D49430237C00B2A128CD0D579EF8EF6E7F327AD0B6F5158C5C681B38367A609B270A450D7648E894434C06E3EACFB4DF77E746ED81CB9FE4EBB4649B2A7511B00}], D8A1CA8D7A433377E3C534F7F47DBE75B24E049FB15383AE6F7DC54A003A3BA7, 27238071771048 ) => ( context=0x, validationData=2588623352593049688011333413686251731339103436869256347648 )
      • Null: 0x000...001.d0a0dbc2( )
        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: AmbirePaymaster
        // SPDX-License-Identifier: agpl-3.0
        pragma solidity 0.8.19;
        import './libs/SignatureValidator.sol';
        import './ExternalSigValidator.sol';
        import './libs/erc4337/PackedUserOperation.sol';
        import './libs/erc4337/UserOpHelper.sol';
        import './deployless/IAmbireAccount.sol';
        /**
         * @notice  A validator that performs DKIM signature recovery
         * @dev     All external/public functions (that are not view/pure) use `payable` because AmbireAccount
         * is a wallet contract, and any ETH sent to it is not lost, but on the other hand not having `payable`
         * makes the Solidity compiler add an extra check for `msg.value`, which in this case is wasted gas
         */
        contract AmbireAccount is IAmbireAccount {
        \t// @dev We do not have a constructor. This contract cannot be initialized with any valid `privileges` by itself!
        \t// The intended use case is to deploy one base implementation contract, and create a minimal proxy for each user wallet, by
        \t// using our own code generation to insert SSTOREs to initialize `privileges` (it was previously called IdentityProxyDeploy.js, now src/libs/proxyDeploy/deploy.ts)
        \taddress private constant FALLBACK_HANDLER_SLOT = address(0x6969);
        \t// @dev This is how we understand if msg.sender is the entry point
        \tbytes32 private constant ENTRY_POINT_MARKER = 0x0000000000000000000000000000000000000000000000000000000000007171;
        \t// Externally validated signatures
        \tuint8 private constant SIGMODE_EXTERNALLY_VALIDATED = 255;
        \t// Variables
        \tmapping(address => bytes32) public privileges;
        \tuint256 public nonce;
        \t// Events
        \tevent LogPrivilegeChanged(address indexed addr, bytes32 priv);
        \tevent LogErr(address indexed to, uint256 value, bytes data, bytes returnData); // only used in tryCatch
        \t// This contract can accept ETH without calldata
        \treceive() external payable {}
        \t/**
        \t * @dev     To support EIP 721 and EIP 1155, we need to respond to those methods with their own method signature
        \t * @return  bytes4  onERC721Received function selector
        \t */
        \tfunction onERC721Received(address, address, uint256, bytes calldata) external pure returns (bytes4) {
        \t\treturn this.onERC721Received.selector;
        \t}
        \t/**
        \t * @dev     To support EIP 721 and EIP 1155, we need to respond to those methods with their own method signature
        \t * @return  bytes4  onERC1155Received function selector
        \t */
        \tfunction onERC1155Received(address, address, uint256, uint256, bytes calldata) external pure returns (bytes4) {
        \t\treturn this.onERC1155Received.selector;
        \t}
        \t/**
        \t * @dev     To support EIP 721 and EIP 1155, we need to respond to those methods with their own method signature
        \t * @return  bytes4  onERC1155Received function selector
        \t */
        \tfunction onERC1155BatchReceived(
        \t\taddress,
        \t\taddress,
        \t\tuint256[] calldata,
        \t\tuint256[] calldata,
        \t\tbytes calldata
        \t) external pure returns (bytes4) {
        \t\treturn this.onERC1155BatchReceived.selector;
        \t}
        \t/**
        \t * @notice  fallback method: currently used to call the fallback handler
        \t * which is set by the user and can be changed
        \t * @dev     this contract can accept ETH with calldata, hence payable
        \t */
        \tfallback() external payable {
        \t\t// We store the fallback handler at this magic slot
        \t\taddress fallbackHandler = address(uint160(uint(privileges[FALLBACK_HANDLER_SLOT])));
        \t\tif (fallbackHandler == address(0)) return;
        \t\tassembly {
        \t\t\t// we can use addr 0 because logic is taking full control of the
        \t\t\t// execution making sure it returns itself and does not
        \t\t\t// rely on any further Solidity code.
        \t\t\tcalldatacopy(0, 0, calldatasize())
        \t\t\tlet result := delegatecall(gas(), fallbackHandler, 0, calldatasize(), 0, 0)
        \t\t\tlet size := returndatasize()
        \t\t\treturndatacopy(0, 0, size)
        \t\t\tif eq(result, 0) {
        \t\t\t\trevert(0, size)
        \t\t\t}
        \t\t\treturn(0, size)
        \t\t}
        \t}
        \t/**
        \t * @notice  used to set the privilege of a key (by `addr`)
        \t * @dev     normal signatures will be considered valid if the
        \t * `addr` they are signed with has non-zero (not 0x000..000) privilege set; we can set the privilege to
        \t * a hash of the recovery keys and timelock (see `RecoveryInfo`) to enable recovery signatures
        \t * @param   addr  the address to give privs to
        \t * @param   priv  the privs to give
        \t */
        \tfunction setAddrPrivilege(address addr, bytes32 priv) external payable {
        \t\trequire(msg.sender == address(this), 'ONLY_ACCOUNT_CAN_CALL');
        \t\tprivileges[addr] = priv;
        \t\temit LogPrivilegeChanged(addr, priv);
        \t}
        \t/**
        \t * @notice  Useful when we need to do multiple operations but ignore failures in some of them
        \t * @param   to  address we're sending value to
        \t * @param   value  the amount
        \t * @param   data  callData
        \t */
        \tfunction tryCatch(address to, uint256 value, bytes calldata data) external payable {
        \t\trequire(msg.sender == address(this), 'ONLY_ACCOUNT_CAN_CALL');
        \t\tuint256 gasBefore = gasleft();
        \t\t(bool success, bytes memory returnData) = to.call{ value: value, gas: gasBefore }(data);
        \t\trequire(gasleft() > gasBefore / 64, 'TRYCATCH_OOG');
        \t\tif (!success) emit LogErr(to, value, data, returnData);
        \t}
        \t/**
        \t * @notice  same as `tryCatch` but with a gas limit
        \t * @param   to  address we're sending value to
        \t * @param   value  the amount
        \t * @param   data  callData
        \t * @param   gasLimit  how much gas is allowed
        \t */
        \tfunction tryCatchLimit(address to, uint256 value, bytes calldata data, uint256 gasLimit) external payable {
        \t\trequire(msg.sender == address(this), 'ONLY_ACCOUNT_CAN_CALL');
        \t\tuint256 gasBefore = gasleft();
        \t\t(bool success, bytes memory returnData) = to.call{ value: value, gas: gasLimit }(data);
        \t\trequire(gasleft() > gasBefore / 64, 'TRYCATCH_OOG');
        \t\tif (!success) emit LogErr(to, value, data, returnData);
        \t}
        \t/**
        \t * @notice  execute: this method is used to execute a single bundle of calls that are signed with a key
        \t * that is authorized to execute on this account (in `privileges`)
        \t * @dev     WARNING: if the signature of this is changed, we have to change AmbireAccountFactory
        \t * @param   calls  the transaction we're executing. They may not execute
        \t * if specific cases. One such is when setting a timelock
        \t * @param   signature  the signature for the transactions
        \t */
        \tfunction execute(Transaction[] calldata calls, bytes calldata signature) public payable {
        \t\taddress signerKey;
        \t\tuint8 sigMode = uint8(signature[signature.length - 1]);
        \t\tuint256 currentNonce = nonce;
        \t\t// we increment the nonce here (not using `nonce++` to save some gas)
        \t\tnonce = currentNonce + 1;
        \t\tif (sigMode == SIGMODE_EXTERNALLY_VALIDATED) {
        \t\t\tbool isValidSig;
        \t\t\tuint256 timestampValidAfter;
        \t\t\t(signerKey, isValidSig, timestampValidAfter) = validateExternalSig(calls, signature);
        \t\t\tif (!isValidSig) {
        \t\t\t\trequire(block.timestamp >= timestampValidAfter, 'SIGNATURE_VALIDATION_TIMELOCK');
        \t\t\t\trevert('SIGNATURE_VALIDATION_FAIL');
        \t\t\t}
        \t\t} else {
        \t\t\tsignerKey = SignatureValidator.recoverAddr(
        \t\t\t\tkeccak256(abi.encode(address(this), block.chainid, currentNonce, calls)),
        \t\t\t\tsignature,
        \t\t\t\ttrue
        \t\t\t);
        \t\t\trequire(privileges[signerKey] != bytes32(0), 'INSUFFICIENT_PRIVILEGE');
        \t\t}
        \t\texecuteBatch(calls);
        \t\t// The actual anti-bricking mechanism - do not allow a signerKey to drop their own privileges
        \t\trequire(privileges[signerKey] != bytes32(0), 'PRIVILEGE_NOT_DOWNGRADED');
        \t}
        \t/**
        \t * @notice  allows executing multiple bundles of calls (batch together multiple executes)
        \t * @param   toExec  an array of execute function parameters
        \t */
        \tfunction executeMultiple(ExecuteArgs[] calldata toExec) external payable {
        \t\tfor (uint256 i = 0; i != toExec.length; i++) execute(toExec[i].calls, toExec[i].signature);
        \t}
        \t/**
        \t * @notice  Allows executing calls if the caller itself is authorized
        \t * @dev     no need for nonce management here cause we're not dealing with sigs
        \t * @param   calls  the transaction we're executing
        \t */
        \tfunction executeBySender(Transaction[] calldata calls) external payable {
        \t\trequire(privileges[msg.sender] != bytes32(0), 'INSUFFICIENT_PRIVILEGE');
        \t\texecuteBatch(calls);
        \t\t// again, anti-bricking
        \t\trequire(privileges[msg.sender] != bytes32(0), 'PRIVILEGE_NOT_DOWNGRADED');
        \t}
        \t/**
        \t * @notice  allows the contract itself to execute a batch of calls
        \t * self-calling is useful in cases like wanting to do multiple things in a tryCatchLimit
        \t * @param   calls  the calls we're executing
        \t */
        \tfunction executeBySelf(Transaction[] calldata calls) external payable {
        \t\trequire(msg.sender == address(this), 'ONLY_ACCOUNT_CAN_CALL');
        \t\texecuteBatch(calls);
        \t}
        \t/**
        \t * @notice  allows the contract itself to execute a single calls
        \t * self-calling is useful when you want to workaround the executeBatch()
        \t * protection of not being able to call address(0)
        \t * @param   call  the call we're executing
        \t */
        \tfunction executeBySelfSingle(Transaction calldata call) external payable {
        \t\trequire(msg.sender == address(this), 'ONLY_ACCOUNT_CAN_CALL');
        \t\texecuteCall(call.to, call.value, call.data);
        \t}
        \t/**
        \t * @notice  Execute a batch of transactions
        \t * @param   calls  the transaction we're executing
        \t */
        \tfunction executeBatch(Transaction[] memory calls) internal {
        \t\tuint256 len = calls.length;
        \t\tfor (uint256 i = 0; i < len; i++) {
        \t\t\tTransaction memory call = calls[i];
        \t\t\tif (call.to != address(0)) executeCall(call.to, call.value, call.data);
        \t\t}
        \t}
        \t/**
        \t * @notice  Execute a signle transaction
        \t * @dev     we shouldn't use address.call(), cause: https://github.com/ethereum/solidity/issues/2884
        \t * @param   to  the address we're sending to
        \t * @param   value  the amount we're sending
        \t * @param   data  callData
        \t */
        \tfunction executeCall(address to, uint256 value, bytes memory data) internal {
        \t\tassembly {
        \t\t\tlet result := call(gas(), to, value, add(data, 0x20), mload(data), 0, 0)
        \t\t\tif eq(result, 0) {
        \t\t\t\tlet size := returndatasize()
        \t\t\t\tlet ptr := mload(0x40)
        \t\t\t\treturndatacopy(ptr, 0, size)
        \t\t\t\trevert(ptr, size)
        \t\t\t}
        \t\t}
        \t}
        \t/**
        \t * @notice  EIP-1271 implementation
        \t * @dev     see https://eips.ethereum.org/EIPS/eip-1271
        \t * @param   hash  the signed hash
        \t * @param   signature  the signature for the signed hash
        \t * @return  bytes4  is it a success or a failure
        \t */
        \tfunction isValidSignature(bytes32 hash, bytes calldata signature) external view returns (bytes4) {
        \t\t(address recovered, bool usedUnprotected) = SignatureValidator.recoverAddrAllowUnprotected(hash, signature, false);
        \t\tif (uint256(privileges[recovered]) > (usedUnprotected ? 1 : 0)) {
        \t\t\t// bytes4(keccak256("isValidSignature(bytes32,bytes)")
        \t\t\treturn 0x1626ba7e;
        \t\t} else {
        \t\t\treturn 0xffffffff;
        \t\t}
        \t}
        \t/**
        \t * @notice  EIP-1155 implementation
        \t * we pretty much only need to signal that we support the interface for 165, but for 1155 we also need the fallback function
        \t * @param   interfaceID  the interface we're signaling support for
        \t * @return  bool  do we support the interface or not
        \t */
        \tfunction supportsInterface(bytes4 interfaceID) external view returns (bool) {
        \t\tbool supported = interfaceID == 0x01ffc9a7 || // ERC-165 support (i.e. `bytes4(keccak256('supportsInterface(bytes4)'))`).
        \t\t\tinterfaceID == 0x150b7a02 || // ERC721TokenReceiver
        \t\t\tinterfaceID == 0x4e2312e0 || // ERC-1155 `ERC1155TokenReceiver` support (i.e. `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)")) ^ bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`).
        \t\t\tinterfaceID == 0x0a417632; // used for checking whether the account is v2 or not
        \t\tif (supported) return true;
        \t\taddress payable fallbackHandler = payable(address(uint160(uint256(privileges[FALLBACK_HANDLER_SLOT]))));
        \t\tif (fallbackHandler == address(0)) return false;
        \t\treturn AmbireAccount(fallbackHandler).supportsInterface(interfaceID);
        \t}
        \t//
        \t// EIP-4337 implementation
        \t//
        \t// return value in case of signature failure, with no time-range.
        \t// equivalent to packSigTimeRange(true,0,0);
        \tuint256 constant internal SIG_VALIDATION_FAILED = 1;
        \t// equivalent to packSigTimeRange(false,0,0);
        \tuint256 constant internal SIG_VALIDATION_SUCCESS = 0;
        \t/**
        \t * @notice  EIP-4337 implementation
        \t * @dev     We have an edge case for enabling ERC-4337 in the first if statement.
        \t * If the function call is to execute, we do not perform an userOp sig validation.
        \t * We require a one time hash nonce commitment from the paymaster for the given
        \t * req. We use this to give permissions to the entry point on the fly
        \t * and enable ERC-4337
        \t * @param   op  the PackedUserOperation we're executing
        \t * @param   userOpHash  the hash we've committed to
        \t * @param   missingAccountFunds  the funds the account needs to pay
        \t * @return  uint256  0 for success, 1 for signature failure, and a uint256
        \t * packed timestamp for a future valid signature:
        \t * address aggregator, uint48 validUntil, uint48 validAfter
        \t */
        \tfunction validateUserOp(PackedUserOperation calldata op, bytes32 userOpHash, uint256 missingAccountFunds)
        \texternal payable returns (uint256)
        \t{
        \t\t// enable running executeMultiple operation through the entryPoint if
        \t\t// a paymaster sponsors it with a commitment one-time nonce.
        \t\t// two use cases:
        \t\t// 1) enable 4337 on a network by giving privileges to the entryPoint
        \t\t// 2) key recovery. If the key is lost, we cannot sign the userOp,
        \t\t// so we have to go to `execute` to trigger the recovery logic
        \t\t// Why executeMultiple but not execute?
        \t\t// executeMultiple allows us to combine recovery + fee payment calls.
        \t\t// The fee payment call will be with a signature from the new key
        \t\tif (op.callData.length >= 4 && bytes4(op.callData[0:4]) == this.executeMultiple.selector) {
        \t\t\t// Require a paymaster, otherwise this mode can be used by anyone to get the user to spend their deposit
        \t\t\t// @estimation-no-revert
        \t\t\tif (op.signature.length != 0) return SIG_VALIDATION_FAILED;
        \t\t\trequire(
        \t\t\t\top.paymasterAndData.length >= UserOpHelper.PAYMASTER_DATA_OFFSET &&
        \t\t\t\tbytes20(op.paymasterAndData[:UserOpHelper.PAYMASTER_ADDR_OFFSET]) != bytes20(0),
        \t\t\t\t'validateUserOp: paymaster required in execute() mode'
        \t\t\t);
        \t\t\t// hashing in everything except sender (nonces are scoped by sender anyway), nonce, signature
        \t\t\tuint256 targetNonce = uint256(keccak256(
        \t\t\t\tabi.encode(op.initCode, op.callData, op.accountGasLimits, op.preVerificationGas, op.gasFees, op.paymasterAndData)
        \t\t\t)) << 64;
        \t\t\t// @estimation-no-revert
        \t\t\tif (op.nonce != targetNonce) return SIG_VALIDATION_FAILED;
        \t\t\treturn SIG_VALIDATION_SUCCESS;
        \t\t}
        \t\trequire(privileges[msg.sender] == ENTRY_POINT_MARKER, 'validateUserOp: not from entryPoint');
        \t\t// @estimation
        \t\t// paying should happen even if signature validation fails
        \t\tif (missingAccountFunds > 0) {
        \t\t\t// NOTE: MAY pay more than the minimum, to deposit for future transactions
        \t\t\t(bool success,) = msg.sender.call{value : missingAccountFunds}('');
        \t\t\t// ignore failure (its EntryPoint's job to verify, not account.)
        \t\t\t(success);
        \t\t}
        \t\t// this is replay-safe because userOpHash is retrieved like this: keccak256(abi.encode(userOp.hash(), address(this), block.chainid))
        \t\taddress signer = SignatureValidator.recoverAddr(userOpHash, op.signature, true);
        \t\tif (privileges[signer] == bytes32(0)) return SIG_VALIDATION_FAILED;
        \t\treturn SIG_VALIDATION_SUCCESS;
        \t}
        \tfunction validateExternalSig(Transaction[] memory calls, bytes calldata signature)
        \tinternal returns(address signerKey, bool isValidSig, uint256 timestampValidAfter) {
        \t\t(bytes memory sig, ) = SignatureValidator.splitSignature(signature);
        \t\t// the address of the validator we're using for this validation
        \t\taddress validatorAddr;
        \t\t// all the data needed by the validator to execute the validation.
        \t\t// In the case of DKIMRecoverySigValidator, this is AccInfo:
        \t\t// abi.encode {string emailFrom; string emailTo; string domainName;
        \t\t// bytes dkimPubKeyModulus; bytes dkimPubKeyExponent; address secondaryKey;
        \t\t// bool acceptUnknownSelectors; uint32 waitUntilAcceptAdded;
        \t\t// uint32 waitUntilAcceptRemoved; bool acceptEmptyDKIMSig;
        \t\t// bool acceptEmptySecondSig;uint32 onlyOneSigTimelock;}
        \t\t// The struct is declared in DKIMRecoverySigValidator
        \t\tbytes memory validatorData;
        \t\t// the signature data needed by the external validator.
        \t\t// In the case of DKIMRecoverySigValidator, this is abi.encode(
        \t\t// SignatureMeta memory sigMeta, bytes memory dkimSig, bytes memory secondSig
        \t\t// ).
        \t\tbytes memory innerSig;
        \t\t// the signerKey in this case is an arbitrary value that does
        \t\t// not have any specific purpose other than representing
        \t\t// the privileges key
        \t\t(signerKey, validatorAddr, validatorData, innerSig) = abi.decode(sig, (address, address, bytes, bytes));
        \t\trequire(
        \t\t\tprivileges[signerKey] == keccak256(abi.encode(validatorAddr, validatorData)),
        \t\t\t'EXTERNAL_VALIDATION_NOT_SET'
        \t\t);
        \t\t// The sig validator itself should throw when a signature isn't validated successfully
        \t\t// the return value just indicates whether we want to execute the current calls
        \t\t(isValidSig, timestampValidAfter) = ExternalSigValidator(validatorAddr).validateSig(validatorData, innerSig, calls);
        \t}
        }
        // SPDX-License-Identifier: agpl-3.0
        pragma solidity 0.8.19;
        import './deployless/IAmbireAccount.sol';
        import './libs/Transaction.sol';
        /**
         * @notice  A contract used for deploying AmbireAccount.sol
         * @dev     We use create2 to get the AmbireAccount address. It's deterministic:
         * if the same data is passed to it, the same address will pop out.
         */
        contract AmbireFactory {
        \tevent LogDeployed(address addr, uint256 salt);
        \taddress public immutable allowedToDrain;
        \tconstructor(address allowed) {
        \t\tallowedToDrain = allowed;
        \t}
        \t/**
        \t * @notice  Allows anyone to deploy any contracft with a specific code/salt
        \t * @dev     This is safe because it's CREATE2 deployment
        \t * @param   code  the code to be deployed
        \t * @param   salt  the salt to shuffle the computed address
        \t * @return  address  the deployed address
        \t */
        \tfunction deploy(bytes calldata code, uint256 salt) external returns(address) {
        \t\treturn deploySafe(code, salt);
        \t}
        \t
        \t/**
        \t * @notice  Call this when you want to deploy the contract and execute calls
        \t * @dev     When the relayer needs to act upon an /identity/:addr/submit call, it'll either call execute on the AmbireAccount directly
        \t * if it's already deployed, or call `deployAndExecute` if the account is still counterfactual
        \t * we can't have deployAndExecuteBySender, because the sender will be the factory
        \t * @param   code  the code to be deployed
        \t * @param   salt  the salt to shuffle the computed address
        \t * @param   txns  the txns the are going to be executed
        \t * @param   signature  the signature for the txns
        \t * @return  address  the deployed address
        \t */
        \tfunction deployAndExecute(
        \t\tbytes calldata code,
        \t\tuint256 salt,
        \t\tTransaction[] calldata txns,
        \t\tbytes calldata signature
        \t) external returns (address){
        \t\taddress payable addr = payable(deploySafe(code, salt));
        \t\tIAmbireAccount(addr).execute(txns, signature);
        \t\treturn addr;
        \t}
        \t
        \t/**
        \t * @notice  Call this when you want to deploy the contract and call executeMultiple
        \t * @dev     when the relayer needs to act upon an /identity/:addr/submit call, 
        \t * it'll either call execute on the AmbireAccount directly. If it's already
        \t * deployed, or call `deployAndExecuteMultiple` if the account is still
        \t * counterfactual but there are multiple accountOps to send
        \t * @param   code  the code to be deployed
        \t * @param   salt  the salt to shuffle the computed address
        \t * @param   toExec  [txns, signature] execute parameters
        \t * @return  address  the deployed address
        \t */
        \tfunction deployAndExecuteMultiple(
        \t\tbytes calldata code,
        \t\tuint256 salt,
        \t\tIAmbireAccount.ExecuteArgs[] calldata toExec
        \t) external returns (address){
        \t\taddress payable addr = payable(deploySafe(code, salt));
        \t\tIAmbireAccount(addr).executeMultiple(toExec);
        \t\treturn addr;
        \t}
        \t/**
        \t * @notice  This method can be used to withdraw stuck tokens or airdrops
        \t * @dev     Only allowedToDrain can do the call
        \t * @param   to  receiver
        \t * @param   value  how much to be sent
        \t * @param   data  if a token has airdropped, code to send it
        \t * @param   gas  maximum gas willing to spend
        \t */
        \tfunction call(address to, uint256 value, bytes calldata data, uint256 gas) external {
        \t\trequire(msg.sender == allowedToDrain, 'ONLY_AUTHORIZED');
        \t\t(bool success, bytes memory err) = to.call{ gas: gas, value: value }(data);
        \t\trequire(success, string(err));
        \t}
        \t
        \t/**
        \t * @dev     This is done to mitigate possible frontruns where, for example,
        \t * where deploying the same code/salt via deploy() would make a pending
        \t * deployAndExecute fail. The way we mitigate that is by checking if the
        \t * contract is already deployed and if so, we continue execution
        \t * @param   code  the code to be deployed
        \t * @param   salt  the salt to shuffle the computed address
        \t * @return  address  the deployed address
        \t */
        \tfunction deploySafe(bytes memory code, uint256 salt) internal returns (address) {
        \t\taddress expectedAddr = address(
        \t\t\tuint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, keccak256(code)))))
        \t\t);
        \t\tuint256 size;
        \t\tassembly {
        \t\t\tsize := extcodesize(expectedAddr)
        \t\t}
        \t\t// If there is code at that address, we can assume it's the one we were about to deploy,
        \t\t// because of how CREATE2 and keccak256 works
        \t\tif (size == 0) {
        \t\t\taddress addr;
        \t\t\tassembly {
        \t\t\t\taddr := create2(0, add(code, 0x20), mload(code), salt)
        \t\t\t}
        \t\t\trequire(addr != address(0), 'FAILED_DEPLOYING');
        \t\t\trequire(addr == expectedAddr, 'FAILED_MATCH');
        \t\t\temit LogDeployed(addr, salt);
        \t\t}
        \t\treturn expectedAddr;
        \t}
        }
        // SPDX-License-Identifier: agpl-3.0
        pragma solidity 0.8.19;
        import './deployless/IAmbireAccount.sol';
        import './libs/erc4337/IPaymaster.sol';
        import './libs/SignatureValidator.sol';
        import './libs/erc4337/UserOpHelper.sol';
        contract AmbirePaymaster is IPaymaster {
        \taddress immutable public relayer;
        \tconstructor(address _relayer) {
        \t\trelayer = _relayer;
        \t}
        \t/**
        \t * @notice  This method can be used to withdraw stuck tokens or airdrops
        \t *
        \t * @param   to  The address we're calling
        \t * @param   value  The value in the call
        \t * @param\tdata\tthe call data
        \t * @param\tgas\tthe call gas
        \t */
        \tfunction call(address to, uint256 value, bytes calldata data, uint256 gas) external payable {
        \t\trequire(msg.sender == relayer, 'call: not relayer');
        \t\t(bool success, bytes memory err) = to.call{ gas: gas, value: value }(data);
        \t\trequire(success, string(err));
        \t}
        \t/**
        \t * @notice  Validate user operations the paymaster has signed
        \t * We do not need to send funds to the EntryPoint because we rely on pre-existing deposit.
        \t * Requests are chain specific to prevent signature reuse.
        \t * @dev     We have two use cases for the paymaster:
        \t * - normal erc-4337. Everything is per ERC-4337 standard, the nonce is sequential.
        \t * - an executeMultiple call. If the calldata is executeMultiple, we've hardcoded
        \t * a 0 nonce. That's what's called a one-time hash nonce and its key is actually
        \t * the commitment. Check EntryPoint -> NonceManager for more information.
        \t *
        \t * @param   userOp  the UserOperation we're executing
        \t * @return  context  context is returned in the postOp and called by the
        \t * EntryPoint. But we're not using postOp is context is always emtpy
        \t * @return  validationData  This consists of:
        \t * - an aggregator address: address(uint160(validationData)). This is used
        \t * when you want an outer contract to determine whether the signature is valid.
        \t * In our case, this is always 0 (address 0) for valid signatures and
        \t * 1 (address 1) for invalid. This is what the entry point expects and
        \t * in those two cases, an outer contract is obviously not called.
        \t * - a uint48 validUntil: uint48(validationData >> 160)
        \t * A Paymaster signature can be signed at time "x" but delayed intentionally
        \t * until time "y" when a fee payment's price has dropped significantly or
        \t * some other issue. validUntil sets a time validity for the signature
             * - a uint48 validAfter: uint48(validationData >> (48 + 160))
        \t * If the signature should be valid only after a period of time,
        \t * we tweak the validAfter property.
        \t * For more information, check EntryPoint -> _getValidationData()
        \t */
        \tfunction validatePaymasterUserOp(PackedUserOperation calldata userOp, bytes32, uint256)
        \t\texternal
        \t\tview
        \t\treturns (bytes memory context, uint256 validationData)
        \t{
        \t\t(uint48 validUntil, uint48 validAfter, bytes memory signature) = abi.decode(
        \t\t\tuserOp.paymasterAndData[UserOpHelper.PAYMASTER_DATA_OFFSET:],
        \t\t\t(uint48, uint48, bytes)
        \t\t);
        \t\tbytes memory callData = userOp.callData;
        \t\tbytes32 hash = keccak256(abi.encode(
        \t\t\tblock.chainid,
        \t\t\taddress(this),
        \t\t\t// entry point
        \t\t\tmsg.sender,
        \t\t\tvalidUntil,
        \t\t\tvalidAfter,
        \t\t\t// everything except paymasterAndData and signature
        \t\t\tuserOp.sender,
        \t\t\t// for the nonce we have an exception case: one-time nonces depend on paymasterAndData, which is generated by the relayer
        \t\t\t// we can't have this as part of the sig cuz we create a cyclical dep
        \t\t\t// the nonce can only be used once, so one cannot replay the gas payment
        \t\t\tcallData.length >= 4 && bytes4(userOp.callData[0:4]) == IAmbireAccount.executeMultiple.selector ? 0 : userOp.nonce,
        \t\t\tuserOp.initCode,
        \t\t\tcallData,
        \t\t\tuserOp.accountGasLimits,
        \t\t\tuserOp.preVerificationGas,
        \t\t\tuserOp.gasFees
        \t\t));
        \t\t(address recovered, ) = SignatureValidator.recoverAddrAllowUnprotected(hash, signature, true);
        \t\tbool isValidSig = recovered == relayer;
        \t\t// see _packValidationData: https://github.com/eth-infinitism/account-abstraction/blob/f2b09e60a92d5b3177c68d9f382912ccac19e8db/contracts/core/Helpers.sol#L73-L80
        \t\treturn ("", uint160(isValidSig ? 0 : 1) | (uint256(validUntil) << 160) | (uint256(validAfter) << 208));
        \t}
        \t/**
        \t * @notice  No-op, won't be used because we don't return a context
        \t * @param   mode  .
        \t * @param   context  .
        \t * @param   actualGasCost  .
        \t */
        \tfunction postOp(PostOpMode mode, bytes calldata context, uint256 actualGasCost) external {
        \t\t// No-op, won't be used because we don't return a context
        \t}
        }
        // SPDX-License-Identifier: agpl-3.0
        pragma solidity 0.8.19;
        import './libs/Transaction.sol';
        /**
         * @title   ExternalSigValidator
         * @notice  A way to add custom recovery to AmbireAccount.
         * address accountAddr is the Ambire account address
         * bytes calldata data is all the data needed by the ExternalSigValidator.
         * It could be anything and it's validator specific.
         * bytes calldata sig is the signature we're validating. Notice its not
         * bytes32 so there could be cases where its not only the signature. It's
         * validator specific
         * uint256 nonce - the Ambire account nonce
         * Transaction[] calldata calls - the txns that are going to be executed
         * if the validation is successful
         * @dev     Not all passed properties necessarily need to be used.
         */
        abstract contract ExternalSigValidator {
        \tfunction validateSig(
        \t\tbytes calldata data,
        \t\tbytes calldata sig,
        \t\tTransaction[] calldata calls
        \t) external virtual returns (bool isValidSignature, uint256 timestampValidAfter);
        }// SPDX-License-Identifier: agpl-3.0
        pragma solidity ^0.8.7;
        import '../libs/Transaction.sol';
        interface IAmbireAccount {
        \tfunction privileges(address addr) external returns (bytes32);
        \tfunction nonce() external returns (uint);
        \tstruct RecoveryInfo {
        \t\taddress[] keys;
        \t\tuint timelock;
        \t}
        \tstruct ExecuteArgs {
        \t\tTransaction[] calls;
        \t\tbytes signature;
        \t}
        \tfunction setAddrPrivilege(address addr, bytes32 priv) external payable;
        \tfunction tryCatch(address to, uint value, bytes calldata data) external payable;
        \tfunction tryCatchLimit(address to, uint value, bytes calldata data, uint gasLimit) external payable;
        \tfunction execute(Transaction[] calldata txns, bytes calldata signature) external payable;
        \tfunction executeBySender(Transaction[] calldata txns) external payable;
        \tfunction executeBySelf(Transaction[] calldata txns) external payable;
        \tfunction executeMultiple(ExecuteArgs[] calldata toExec) external payable;
        \t// EIP 1271 implementation
        \t// see https://eips.ethereum.org/EIPS/eip-1271
        \tfunction isValidSignature(bytes32 hash, bytes calldata signature) external view returns (bytes4);
        \tfunction supportsInterface(bytes4 interfaceID) external view returns (bool);
        }
        // SPDX-License-Identifier: agpl-3.0
        pragma solidity 0.8.19;
        library Bytes {
        \tfunction trimToSize(bytes memory b, uint256 newLen) internal pure {
        \t\trequire(b.length > newLen, 'BytesLib: only shrinking');
        \t\tassembly {
        \t\t\tmstore(b, newLen)
        \t\t}
        \t}
        \t/***********************************|
        \t|        Read Bytes Functions       |
        \t|__________________________________*/
        \t/**
        \t * @dev Reads a bytes32 value from a position in a byte array.
        \t * @param b Byte array containing a bytes32 value.
        \t * @param index Index in byte array of bytes32 value.
        \t * @return result bytes32 value from byte array.
        \t */
        \tfunction readBytes32(bytes memory b, uint256 index) internal pure returns (bytes32 result) {
        \t\t// Arrays are prefixed by a 256 bit length parameter
        \t\tindex += 32;
        \t\trequire(b.length >= index, 'BytesLib: length');
        \t\t// Read the bytes32 from array memory
        \t\tassembly {
        \t\t\tresult := mload(add(b, index))
        \t\t}
        \t\treturn result;
        \t}
        }
        // SPDX-License-Identifier: agpl-3.0
        pragma solidity 0.8.19;
        import './Bytes.sol';
        interface IERC1271Wallet {
        \tfunction isValidSignature(bytes32 hash, bytes calldata signature) external view returns (bytes4 magicValue);
        }
        library SignatureValidator {
        \tusing Bytes for bytes;
        \tenum SignatureMode {
        \t\t// the first mode Unprotected is used in combination with EIP-1271 signature verification to do
        \t\t// EIP-712 verifications, as well as "Ethereum signed message:" message verifications
        \t\t// The caveat with this is that we need to ensure that the signer key used for it isn't reused, or the message body
        \t\t// itself contains context about the wallet (such as it's address)
        \t\t// We do this, rather than applying the prefix on-chain, because if we do you won't be able to see the message
        \t\t// when signing on a hardware wallet (you'll only see the hash) - since `isValidSignature` can only receive the hash -
        \t\t// if the prefix is applied on-chain you can never match it - it's hash(prefix+hash(msg)) vs hash(prefix+msg)
        \t\t// As for transactions (`execute()`), those can be signed with any of the modes
        \t\t// Otherwise, if it's reused, we MUST use `Standard` mode which always wraps the final digest hash, but unfortnately this means
        \t\t// you can't preview the full message when signing on a HW wallet
        \t\tUnprotected,
        \t\tStandard,
        \t\tSmartWallet,
        \t\tSpoof,
        \t\tSchnorr,
        \t\tMultisig,
        \t\t// WARNING: Signature modes should not be more than 26 as the "v"
        \t\t// value for standard ecrecover is 27/28
        \t\t// WARNING: must always be last
        \t\tLastUnused
        \t}
        \t// bytes4(keccak256("isValidSignature(bytes32,bytes)"))
        \tbytes4 internal constant ERC1271_MAGICVALUE_BYTES32 = 0x1626ba7e;
        \t// secp256k1 group order
        \tuint256 internal constant Q = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141;
        \tfunction splitSignature(bytes memory sig) internal pure returns (bytes memory, uint8) {
        \t\tuint8 modeRaw;
        \t\tunchecked {
        \t\t\tmodeRaw = uint8(sig[sig.length - 1]);
        \t\t}
        \t\tsig.trimToSize(sig.length - 1);
        \t\treturn (sig, modeRaw);
        \t}
        \tfunction recoverAddr(bytes32 hash, bytes memory sig, bool allowSpoofing) internal view returns (address) {
        \t\t(address recovered, bool usedUnprotected) = recoverAddrAllowUnprotected(hash, sig, allowSpoofing);
        \t\trequire(!usedUnprotected, 'SV_USED_UNBOUND');
        \t\treturn recovered;
        \t}
        \tfunction recoverAddrAllowUnprotected(bytes32 hash, bytes memory sig, bool allowSpoofing) internal view returns (address, bool) {
        \t\trequire(sig.length != 0, 'SV_SIGLEN');
        \t\tuint8 modeRaw;
        \t\tunchecked {
        \t\t\tmodeRaw = uint8(sig[sig.length - 1]);
        \t\t}
        \t\t// Ensure we're in bounds for mode; Solidity does this as well but it will just silently blow up rather than showing a decent error
        \t\tif (modeRaw >= uint8(SignatureMode.LastUnused)) {
        \t\t\tif (sig.length == 65) modeRaw = uint8(SignatureMode.Unprotected);
        \t\t\telse revert('SV_SIGMODE');
        \t\t}
        \t\tSignatureMode mode = SignatureMode(modeRaw);
        \t\t// the address of the key we are gonna be returning
        \t\taddress signerKey;
        \t\t// wrap in the EIP712 wrapping if it's not unbound
        \t\t// multisig gets an exception because each inner sig will have to apply this logic
        \t\t// @TODO should spoofing be removed from this?
        \t\tbool isUnprotected = mode == SignatureMode.Unprotected || mode == SignatureMode.Multisig;
        \t\tif (!isUnprotected) {
        \t\t\tbytes32 DOMAIN_SEPARATOR = keccak256(abi.encode(
        \t\t\t\tkeccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)'),
        \t\t\t\tkeccak256(bytes('Ambire')),
        \t\t\t\tkeccak256(bytes('1')),
        \t\t\t\tblock.chainid,
        \t\t\t\taddress(this),
        \t\t\t\tbytes32(0)
        \t\t\t));
        \t\t\thash = keccak256(abi.encodePacked(
        \t\t\t\t'\\x19\\x01',
        \t\t\t\tDOMAIN_SEPARATOR,
        \t\t\t\tkeccak256(abi.encode(
        \t\t\t\t\tkeccak256(bytes('AmbireOperation(address account,bytes32 hash)')),
        \t\t\t\t\taddress(this),
        \t\t\t\t\thash
        \t\t\t\t))
        \t\t\t));
        \t\t}
        \t\t// {r}{s}{v}{mode}
        \t\tif (mode == SignatureMode.Unprotected || mode == SignatureMode.Standard) {
        \t\t\trequire(sig.length == 65 || sig.length == 66, 'SV_LEN');
        \t\t\tbytes32 r = sig.readBytes32(0);
        \t\t\tbytes32 s = sig.readBytes32(32);
        \t\t\tuint8 v = uint8(sig[64]);
        \t\t\tsignerKey = ecrecover(hash, v, r, s);
        \t\t// {sig}{verifier}{mode}
        \t\t} else if (mode == SignatureMode.Schnorr) {
        \t\t\t// Based on https://hackmd.io/@nZ-twauPRISEa6G9zg3XRw/SyjJzSLt9
        \t\t\t// You can use this library to produce signatures: https://github.com/borislav-itskov/schnorrkel.js
        \t\t\t// px := public key x-coord
        \t\t\t// e := schnorr signature challenge
        \t\t\t// s := schnorr signature
        \t\t\t// parity := public key y-coord parity (27 or 28)
        \t\t\t// last uint8 is for the Ambire sig mode - it's ignored
        \t\t\tsig.trimToSize(sig.length - 1);
        \t\t\t(bytes32 px, bytes32 e, bytes32 s, uint8 parity) = abi.decode(sig, (bytes32, bytes32, bytes32, uint8));
        \t\t\t// ecrecover = (m, v, r, s);
        \t\t\tbytes32 sp = bytes32(Q - mulmod(uint256(s), uint256(px), Q));
        \t\t\tbytes32 ep = bytes32(Q - mulmod(uint256(e), uint256(px), Q));
        \t\t\trequire(sp != bytes32(Q));
        \t\t\t// the ecrecover precompile implementation checks that the `r` and `s`
        \t\t\t// inputs are non-zero (in this case, `px` and `ep`), thus we don't need to
        \t\t\t// check if they're zero.
        \t\t\taddress R = ecrecover(sp, parity, px, ep);
        \t\t\trequire(R != address(0), 'SV_ZERO_SIG');
        \t\t\trequire(e == keccak256(abi.encodePacked(R, uint8(parity), px, hash)), 'SV_SCHNORR_FAILED');
        \t\t\tsignerKey = address(uint160(uint256(keccak256(abi.encodePacked('SCHNORR', px)))));
        \t\t} else if (mode == SignatureMode.Multisig) {
        \t\t\tsig.trimToSize(sig.length - 1);
        \t\t\tbytes[] memory signatures = abi.decode(sig, (bytes[]));
        \t\t\t// since we're in a multisig, we care if any of the inner sigs are unbound
        \t\t\tisUnprotected = false;
        \t\t\tfor (uint256 i = 0; i != signatures.length; i++) {
        \t\t\t\t(address inner, bool isInnerUnprotected) = recoverAddrAllowUnprotected(hash, signatures[i], false);
        \t\t\t\tif (isInnerUnprotected) isUnprotected = true;
        \t\t\t\tsignerKey = address(
        \t\t\t\t\tuint160(uint256(keccak256(abi.encodePacked(signerKey, inner))))
        \t\t\t\t);
        \t\t\t}
        \t\t} else if (mode == SignatureMode.SmartWallet) {
        \t\t\t// 32 bytes for the addr, 1 byte for the type = 33
        \t\t\trequire(sig.length > 33, 'SV_LEN_WALLET');
        \t\t\tuint256 newLen;
        \t\t\tunchecked {
        \t\t\t\tnewLen = sig.length - 33;
        \t\t\t}
        \t\t\tIERC1271Wallet wallet = IERC1271Wallet(address(uint160(uint256(sig.readBytes32(newLen)))));
        \t\t\tsig.trimToSize(newLen);
        \t\t\trequire(ERC1271_MAGICVALUE_BYTES32 == wallet.isValidSignature(hash, sig), 'SV_WALLET_INVALID');
        \t\t\tsignerKey = address(wallet);
        \t\t// {address}{mode}; the spoof mode is used when simulating calls
        \t\t} else if (mode == SignatureMode.Spoof && allowSpoofing) {
        \t\t\t// This is safe cause it's specifically intended for spoofing sigs in simulation conditions, where tx.origin can be controlled
        \t\t\t// We did not choose 0x00..00 because in future network upgrades tx.origin may be nerfed or there may be edge cases in which
        \t\t\t// it is zero, such as native account abstraction
        \t\t\t// slither-disable-next-line tx-origin
        \t\t\trequire(tx.origin == address(1) || tx.origin == address(6969), 'SV_SPOOF_ORIGIN');
        \t\t\trequire(sig.length == 33, 'SV_SPOOF_LEN');
        \t\t\tsig.trimToSize(32);
        \t\t\t// To simulate the gas usage; check is just to silence unused warning
        \t\t\trequire(ecrecover(0, 0, 0, 0) != address(6969));
        \t\t\tsignerKey = abi.decode(sig, (address));
        \t\t} else {
        \t\t\trevert('SV_TYPE');
        \t\t}
        \t\trequire(signerKey != address(0), 'SV_ZERO_SIG');
        \t\treturn (signerKey, isUnprotected);
        \t}
        }
        // SPDX-License-Identifier: agpl-3.0
        pragma solidity 0.8.19;
        // Transaction structure
        // we handle replay protection separately by requiring (address(this), chainID, nonce) as part of the sig
        // @dev a better name for this would be `Call`, but we are keeping `Transaction` for backwards compatibility
        struct Transaction {
            address to;
            uint256 value;
            bytes data;
        }
        // SPDX-License-Identifier: GPL-3.0
        pragma solidity ^0.8.12;
        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 {
                opSucceeded, // user op succeeded
                opReverted, // user op reverted. still has to pay for gas.
                postOpReverted //user op succeeded, but caused postOp to revert. Now it's a 2nd call, after user's op was deliberately reverted.
            }
            /**
             * 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,
             *         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
             *      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. still has to pay for gas.
             *      postOpReverted - user op succeeded, but caused postOp (in mode=opSucceeded) to revert.
             *                       Now this is the 2nd call, after user's op was deliberately reverted.
             * @param context - the context value returned by validatePaymasterUserOp
             * @param actualGasCost - actual gas used so far (without this postOp call).
             */
            function postOp(PostOpMode mode, bytes calldata context, uint256 actualGasCost) external;
        }
        // SPDX-License-Identifier: agpl-3.0
        pragma solidity 0.8.19;
        /**
         * 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;
          // callGasLimit + verificationGasLimit
          bytes32 accountGasLimits;
          uint256 preVerificationGas;
          // maxFeePerGas + maxPriorityFeePerGas
          bytes32 gasFees;
          bytes paymasterAndData;
          bytes signature;
        }
        // SPDX-License-Identifier: agpl-3.0
        pragma solidity 0.8.19;
        library UserOpHelper {
        \tuint256 public constant PAYMASTER_ADDR_OFFSET = 20;
          // 52 = 20 address + 16 paymasterVerificationGasLimit + 16 paymasterPostOpGasLimit
        \tuint256 public constant PAYMASTER_DATA_OFFSET = 52;
        }