ETH Price: $1,948.51 (-1.25%)

Transaction Decoder

Block:
23174050 at Aug-19-2025 08:53:35 AM +UTC
Transaction Fee:
0.000053673230867698 ETH $0.10
Gas Used:
135,298 Gas / 0.396703801 Gwei

Emitted Events:

596 EntryPoint.BeforeExecution( )
597 TetherToken.Transfer( from=0xD8293ad21678c6F09Da139b4B62D38e514a03B78, to=0xE691cDAbc7dC1CD2138e21F75caF0bd048696133, value=100 )
598 EntryPoint.UserOperationEvent( userOpHash=36A9E0333351B1D5B7DCE8F11C1EE7A863AD44E2879C787E06B614E20F60797F, sender=0xD8293ad21678c6F09Da139b4B62D38e514a03B78, paymaster=AmbirePaymaster, nonce=110, success=True, actualGasCost=53859681654168, actualGasUsed=135768 )

Account State Difference:

  Address   Before After State Difference Code
0x00000000...6f37da032
(Entry Point 0.7.0)
114.5950510443562213 Eth114.594997184674567132 Eth0.000053859681654168
(Titan Builder)
20.012680327551356434 Eth20.012680330516817998 Eth0.000000002965461564
0x77162EB0...B451a5E7B
(Bundler: 0x771...e7b)
0.143542604519329037 Eth
Nonce: 150
0.143542790970115507 Eth
Nonce: 151
0.00000018645078647
0xdAC17F95...13D831ec7

Execution Trace

EntryPoint.handleOps( ops=, beneficiary=0x77162EB08A150E126C20E6E8D0601a3B451a5E7B )
  • 0xd8293ad21678c6f09da139b4b62d38e514a03b78.19822f7c( )
    • Null: 0x000...001.dac86242( )
    • AmbirePaymaster.validatePaymasterUserOp( userOp=[{name:sender, type:address, order:1, indexed:false, value:0xD8293ad21678c6F09Da139b4B62D38e514a03B78, valueString:0xD8293ad21678c6F09Da139b4B62D38e514a03B78}, {name:nonce, type:uint256, order:2, indexed:false, value:110, valueString:110}, {name:initCode, type:bytes, order:3, indexed:false, value:0x, valueString:0x}, {name:callData, type:bytes, order:4, indexed:false, value:0xABC5345E0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000DAC17F958D2EE523A2206206994597C13D831EC7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044A9059CBB000000000000000000000000E691CDABC7DC1CD2138E21F75CAF0BD048696133000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000000000000000000000942F9CE5D9A33A82F88D233AEB3292E6802303480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000E000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000036AB400000000000000000000000000000000000000000000000000000000000000A0000000000000000000000000000000000000000000000000000000000000000767617354616E6B0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000045553444300000000000000000000000000000000000000000000000000000000, valueString:0xABC5345E0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000DAC17F958D2EE523A2206206994597C13D831EC7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044A9059CBB000000000000000000000000E691CDABC7DC1CD2138E21F75CAF0BD048696133000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000000000000000000000942F9CE5D9A33A82F88D233AEB3292E6802303480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000E000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000036AB400000000000000000000000000000000000000000000000000000000000000A0000000000000000000000000000000000000000000000000000000000000000767617354616E6B0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000045553444300000000000000000000000000000000000000000000000000000000}, {name:accountGasLimits, type:bytes32, order:5, indexed:false, value:0000000000000000000000000000C9F20000000000000000000000000000B814, valueString:0000000000000000000000000000C9F20000000000000000000000000000B814}, {name:preVerificationGas, type:uint256, order:6, indexed:false, value:50217, valueString:50217}, {name:gasFees, type:bytes32, order:7, indexed:false, value:0000000000000000000000000000559E0000000000000000000000001C0E87F6, valueString:0000000000000000000000000000559E0000000000000000000000001C0E87F6}, {name:paymasterAndData, type:bytes, order:8, indexed:false, value:0xA8B267C68715FA1DCA055993149F30217B572CF00000000000000000000000000000A410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000068A43C2700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000004298C3BADA633F0309EDC3BF279BC6B647D32E3677654A910B0102C986AF47DAA1679BDBC7C56B4DE09F985E29A93A275C75C25E39097DB698511737B50B37AE741B00000000000000000000000000000000000000000000000000000000000000, valueString:0xA8B267C68715FA1DCA055993149F30217B572CF00000000000000000000000000000A410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000068A43C2700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000004298C3BADA633F0309EDC3BF279BC6B647D32E3677654A910B0102C986AF47DAA1679BDBC7C56B4DE09F985E29A93A275C75C25E39097DB698511737B50B37AE741B00000000000000000000000000000000000000000000000000000000000000}, {name:signature, type:bytes, order:9, indexed:false, value:0xF990E56257441D3019C14F6002826F9A92D2E307E814737E94ECFE714DFBB7F823233274BFF438E831342FF8DCBFC8B7926D2A922FE7F0EF22A28BD655BB4BDB1B00, valueString:0xF990E56257441D3019C14F6002826F9A92D2E307E814737E94ECFE714DFBB7F823233274BFF438E831342FF8DCBFC8B7926D2A922FE7F0EF22A28BD655BB4BDB1B00}], 36A9E0333351B1D5B7DCE8F11C1EE7A863AD44E2879C787E06B614E20F60797F, 89924800237962 ) => ( context=0x, validationData=2565803164958427679680499928749144148715893657021125230592 )
      • Null: 0x000...001.fd415890( )
      • EntryPoint.innerHandleOp( callData=0xABC5345E0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000DAC17F958D2EE523A2206206994597C13D831EC7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044A9059CBB000000000000000000000000E691CDABC7DC1CD2138E21F75CAF0BD048696133000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000000000000000000000942F9CE5D9A33A82F88D233AEB3292E6802303480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000E000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000036AB400000000000000000000000000000000000000000000000000000000000000A0000000000000000000000000000000000000000000000000000000000000000767617354616E6B0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000045553444300000000000000000000000000000000000000000000000000000000, opInfo=[{name:mUserOp, type:tuple, order:1, indexed:false, value:[{name:sender, type:address, order:1, indexed:false, value:0xD8293ad21678c6F09Da139b4B62D38e514a03B78, valueString:0xD8293ad21678c6F09Da139b4B62D38e514a03B78}, {name:nonce, type:uint256, order:2, indexed:false, value:110, valueString:110}, {name:verificationGasLimit, type:uint256, order:3, indexed:false, value:51698, valueString:51698}, {name:callGasLimit, type:uint256, order:4, indexed:false, value:47124, valueString:47124}, {name:paymasterVerificationGasLimit, type:uint256, order:5, indexed:false, value:42000, valueString:42000}, {name:paymasterPostOpGasLimit, type:uint256, order:6, indexed:false, value:0, valueString:0}, {name:preVerificationGas, type:uint256, order:7, indexed:false, value:50217, valueString:50217}, {name:paymaster, type:address, order:8, indexed:false, value:0xA8B267C68715FA1Dca055993149f30217B572Cf0, valueString:0xA8B267C68715FA1Dca055993149f30217B572Cf0}, {name:maxFeePerGas, type:uint256, order:9, indexed:false, value:470714358, valueString:470714358}, {name:maxPriorityFeePerGas, type:uint256, order:10, indexed:false, value:21918, valueString:21918}], valueString:[{name:sender, type:address, order:1, indexed:false, value:0xD8293ad21678c6F09Da139b4B62D38e514a03B78, valueString:0xD8293ad21678c6F09Da139b4B62D38e514a03B78}, {name:nonce, type:uint256, order:2, indexed:false, value:110, valueString:110}, {name:verificationGasLimit, type:uint256, order:3, indexed:false, value:51698, valueString:51698}, {name:callGasLimit, type:uint256, order:4, indexed:false, value:47124, valueString:47124}, {name:paymasterVerificationGasLimit, type:uint256, order:5, indexed:false, value:42000, valueString:42000}, {name:paymasterPostOpGasLimit, type:uint256, order:6, indexed:false, value:0, valueString:0}, {name:preVerificationGas, type:uint256, order:7, indexed:false, value:50217, valueString:50217}, {name:paymaster, type:address, order:8, indexed:false, value:0xA8B267C68715FA1Dca055993149f30217B572Cf0, valueString:0xA8B267C68715FA1Dca055993149f30217B572Cf0}, {name:maxFeePerGas, type:uint256, order:9, indexed:false, value:470714358, valueString:470714358}, {name:maxPriorityFeePerGas, type:uint256, order:10, indexed:false, value:21918, valueString:21918}]}, {name:userOpHash, type:bytes32, order:2, indexed:false, value:36A9E0333351B1D5B7DCE8F11C1EE7A863AD44E2879C787E06B614E20F60797F, valueString:36A9E0333351B1D5B7DCE8F11C1EE7A863AD44E2879C787E06B614E20F60797F}, {name:prefund, type:uint256, order:3, indexed:false, value:89924800237962, valueString:89924800237962}, {name:contextOffset, type:uint256, order:4, indexed:false, value:1216, valueString:1216}, {name:preOpGas, type:uint256, order:5, indexed:false, value:100146, valueString:100146}], context=0x ) => ( actualGasCost=53859681654168 )
        • 0xd8293ad21678c6f09da139b4b62d38e514a03b78.abc5345e( )
          • TetherToken.transfer( _to=0xE691cDAbc7dC1CD2138e21F75caF0bd048696133, _value=100 )
          • Ambire Wallet: Deployer.00000000( )
          • ETH 0.000053859681654168 Bundler: 0x771...e7b.CALL( )
            File 1 of 3: 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 3: TetherToken
            pragma solidity ^0.4.17;
            
            /**
             * @title SafeMath
             * @dev Math operations with safety checks that throw on error
             */
            library SafeMath {
                function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                    if (a == 0) {
                        return 0;
                    }
                    uint256 c = a * b;
                    assert(c / a == b);
                    return c;
                }
            
                function div(uint256 a, uint256 b) internal pure returns (uint256) {
                    // assert(b > 0); // Solidity automatically throws when dividing by 0
                    uint256 c = a / b;
                    // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                    return c;
                }
            
                function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                    assert(b <= a);
                    return a - b;
                }
            
                function add(uint256 a, uint256 b) internal pure returns (uint256) {
                    uint256 c = a + b;
                    assert(c >= a);
                    return c;
                }
            }
            
            /**
             * @title Ownable
             * @dev The Ownable contract has an owner address, and provides basic authorization control
             * functions, this simplifies the implementation of "user permissions".
             */
            contract Ownable {
                address public owner;
            
                /**
                  * @dev The Ownable constructor sets the original `owner` of the contract to the sender
                  * account.
                  */
                function Ownable() public {
                    owner = msg.sender;
                }
            
                /**
                  * @dev Throws if called by any account other than the owner.
                  */
                modifier onlyOwner() {
                    require(msg.sender == owner);
                    _;
                }
            
                /**
                * @dev Allows the current owner to transfer control of the contract to a newOwner.
                * @param newOwner The address to transfer ownership to.
                */
                function transferOwnership(address newOwner) public onlyOwner {
                    if (newOwner != address(0)) {
                        owner = newOwner;
                    }
                }
            
            }
            
            /**
             * @title ERC20Basic
             * @dev Simpler version of ERC20 interface
             * @dev see https://github.com/ethereum/EIPs/issues/20
             */
            contract ERC20Basic {
                uint public _totalSupply;
                function totalSupply() public constant returns (uint);
                function balanceOf(address who) public constant returns (uint);
                function transfer(address to, uint value) public;
                event Transfer(address indexed from, address indexed to, uint value);
            }
            
            /**
             * @title ERC20 interface
             * @dev see https://github.com/ethereum/EIPs/issues/20
             */
            contract ERC20 is ERC20Basic {
                function allowance(address owner, address spender) public constant returns (uint);
                function transferFrom(address from, address to, uint value) public;
                function approve(address spender, uint value) public;
                event Approval(address indexed owner, address indexed spender, uint value);
            }
            
            /**
             * @title Basic token
             * @dev Basic version of StandardToken, with no allowances.
             */
            contract BasicToken is Ownable, ERC20Basic {
                using SafeMath for uint;
            
                mapping(address => uint) public balances;
            
                // additional variables for use if transaction fees ever became necessary
                uint public basisPointsRate = 0;
                uint public maximumFee = 0;
            
                /**
                * @dev Fix for the ERC20 short address attack.
                */
                modifier onlyPayloadSize(uint size) {
                    require(!(msg.data.length < size + 4));
                    _;
                }
            
                /**
                * @dev transfer token for a specified address
                * @param _to The address to transfer to.
                * @param _value The amount to be transferred.
                */
                function transfer(address _to, uint _value) public onlyPayloadSize(2 * 32) {
                    uint fee = (_value.mul(basisPointsRate)).div(10000);
                    if (fee > maximumFee) {
                        fee = maximumFee;
                    }
                    uint sendAmount = _value.sub(fee);
                    balances[msg.sender] = balances[msg.sender].sub(_value);
                    balances[_to] = balances[_to].add(sendAmount);
                    if (fee > 0) {
                        balances[owner] = balances[owner].add(fee);
                        Transfer(msg.sender, owner, fee);
                    }
                    Transfer(msg.sender, _to, sendAmount);
                }
            
                /**
                * @dev Gets the balance of the specified address.
                * @param _owner The address to query the the balance of.
                * @return An uint representing the amount owned by the passed address.
                */
                function balanceOf(address _owner) public constant returns (uint balance) {
                    return balances[_owner];
                }
            
            }
            
            /**
             * @title Standard ERC20 token
             *
             * @dev Implementation of the basic standard token.
             * @dev https://github.com/ethereum/EIPs/issues/20
             * @dev Based oncode by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
             */
            contract StandardToken is BasicToken, ERC20 {
            
                mapping (address => mapping (address => uint)) public allowed;
            
                uint public constant MAX_UINT = 2**256 - 1;
            
                /**
                * @dev Transfer tokens from one address to another
                * @param _from address The address which you want to send tokens from
                * @param _to address The address which you want to transfer to
                * @param _value uint the amount of tokens to be transferred
                */
                function transferFrom(address _from, address _to, uint _value) public onlyPayloadSize(3 * 32) {
                    var _allowance = allowed[_from][msg.sender];
            
                    // Check is not needed because sub(_allowance, _value) will already throw if this condition is not met
                    // if (_value > _allowance) throw;
            
                    uint fee = (_value.mul(basisPointsRate)).div(10000);
                    if (fee > maximumFee) {
                        fee = maximumFee;
                    }
                    if (_allowance < MAX_UINT) {
                        allowed[_from][msg.sender] = _allowance.sub(_value);
                    }
                    uint sendAmount = _value.sub(fee);
                    balances[_from] = balances[_from].sub(_value);
                    balances[_to] = balances[_to].add(sendAmount);
                    if (fee > 0) {
                        balances[owner] = balances[owner].add(fee);
                        Transfer(_from, owner, fee);
                    }
                    Transfer(_from, _to, sendAmount);
                }
            
                /**
                * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
                * @param _spender The address which will spend the funds.
                * @param _value The amount of tokens to be spent.
                */
                function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) {
            
                    // To change the approve amount you first have to reduce the addresses`
                    //  allowance to zero by calling `approve(_spender, 0)` if it is not
                    //  already 0 to mitigate the race condition described here:
                    //  https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                    require(!((_value != 0) && (allowed[msg.sender][_spender] != 0)));
            
                    allowed[msg.sender][_spender] = _value;
                    Approval(msg.sender, _spender, _value);
                }
            
                /**
                * @dev Function to check the amount of tokens than an owner allowed to a spender.
                * @param _owner address The address which owns the funds.
                * @param _spender address The address which will spend the funds.
                * @return A uint specifying the amount of tokens still available for the spender.
                */
                function allowance(address _owner, address _spender) public constant returns (uint remaining) {
                    return allowed[_owner][_spender];
                }
            
            }
            
            
            /**
             * @title Pausable
             * @dev Base contract which allows children to implement an emergency stop mechanism.
             */
            contract Pausable is Ownable {
              event Pause();
              event Unpause();
            
              bool public paused = false;
            
            
              /**
               * @dev Modifier to make a function callable only when the contract is not paused.
               */
              modifier whenNotPaused() {
                require(!paused);
                _;
              }
            
              /**
               * @dev Modifier to make a function callable only when the contract is paused.
               */
              modifier whenPaused() {
                require(paused);
                _;
              }
            
              /**
               * @dev called by the owner to pause, triggers stopped state
               */
              function pause() onlyOwner whenNotPaused public {
                paused = true;
                Pause();
              }
            
              /**
               * @dev called by the owner to unpause, returns to normal state
               */
              function unpause() onlyOwner whenPaused public {
                paused = false;
                Unpause();
              }
            }
            
            contract BlackList is Ownable, BasicToken {
            
                /////// Getters to allow the same blacklist to be used also by other contracts (including upgraded Tether) ///////
                function getBlackListStatus(address _maker) external constant returns (bool) {
                    return isBlackListed[_maker];
                }
            
                function getOwner() external constant returns (address) {
                    return owner;
                }
            
                mapping (address => bool) public isBlackListed;
                
                function addBlackList (address _evilUser) public onlyOwner {
                    isBlackListed[_evilUser] = true;
                    AddedBlackList(_evilUser);
                }
            
                function removeBlackList (address _clearedUser) public onlyOwner {
                    isBlackListed[_clearedUser] = false;
                    RemovedBlackList(_clearedUser);
                }
            
                function destroyBlackFunds (address _blackListedUser) public onlyOwner {
                    require(isBlackListed[_blackListedUser]);
                    uint dirtyFunds = balanceOf(_blackListedUser);
                    balances[_blackListedUser] = 0;
                    _totalSupply -= dirtyFunds;
                    DestroyedBlackFunds(_blackListedUser, dirtyFunds);
                }
            
                event DestroyedBlackFunds(address _blackListedUser, uint _balance);
            
                event AddedBlackList(address _user);
            
                event RemovedBlackList(address _user);
            
            }
            
            contract UpgradedStandardToken is StandardToken{
                // those methods are called by the legacy contract
                // and they must ensure msg.sender to be the contract address
                function transferByLegacy(address from, address to, uint value) public;
                function transferFromByLegacy(address sender, address from, address spender, uint value) public;
                function approveByLegacy(address from, address spender, uint value) public;
            }
            
            contract TetherToken is Pausable, StandardToken, BlackList {
            
                string public name;
                string public symbol;
                uint public decimals;
                address public upgradedAddress;
                bool public deprecated;
            
                //  The contract can be initialized with a number of tokens
                //  All the tokens are deposited to the owner address
                //
                // @param _balance Initial supply of the contract
                // @param _name Token Name
                // @param _symbol Token symbol
                // @param _decimals Token decimals
                function TetherToken(uint _initialSupply, string _name, string _symbol, uint _decimals) public {
                    _totalSupply = _initialSupply;
                    name = _name;
                    symbol = _symbol;
                    decimals = _decimals;
                    balances[owner] = _initialSupply;
                    deprecated = false;
                }
            
                // Forward ERC20 methods to upgraded contract if this one is deprecated
                function transfer(address _to, uint _value) public whenNotPaused {
                    require(!isBlackListed[msg.sender]);
                    if (deprecated) {
                        return UpgradedStandardToken(upgradedAddress).transferByLegacy(msg.sender, _to, _value);
                    } else {
                        return super.transfer(_to, _value);
                    }
                }
            
                // Forward ERC20 methods to upgraded contract if this one is deprecated
                function transferFrom(address _from, address _to, uint _value) public whenNotPaused {
                    require(!isBlackListed[_from]);
                    if (deprecated) {
                        return UpgradedStandardToken(upgradedAddress).transferFromByLegacy(msg.sender, _from, _to, _value);
                    } else {
                        return super.transferFrom(_from, _to, _value);
                    }
                }
            
                // Forward ERC20 methods to upgraded contract if this one is deprecated
                function balanceOf(address who) public constant returns (uint) {
                    if (deprecated) {
                        return UpgradedStandardToken(upgradedAddress).balanceOf(who);
                    } else {
                        return super.balanceOf(who);
                    }
                }
            
                // Forward ERC20 methods to upgraded contract if this one is deprecated
                function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) {
                    if (deprecated) {
                        return UpgradedStandardToken(upgradedAddress).approveByLegacy(msg.sender, _spender, _value);
                    } else {
                        return super.approve(_spender, _value);
                    }
                }
            
                // Forward ERC20 methods to upgraded contract if this one is deprecated
                function allowance(address _owner, address _spender) public constant returns (uint remaining) {
                    if (deprecated) {
                        return StandardToken(upgradedAddress).allowance(_owner, _spender);
                    } else {
                        return super.allowance(_owner, _spender);
                    }
                }
            
                // deprecate current contract in favour of a new one
                function deprecate(address _upgradedAddress) public onlyOwner {
                    deprecated = true;
                    upgradedAddress = _upgradedAddress;
                    Deprecate(_upgradedAddress);
                }
            
                // deprecate current contract if favour of a new one
                function totalSupply() public constant returns (uint) {
                    if (deprecated) {
                        return StandardToken(upgradedAddress).totalSupply();
                    } else {
                        return _totalSupply;
                    }
                }
            
                // Issue a new amount of tokens
                // these tokens are deposited into the owner address
                //
                // @param _amount Number of tokens to be issued
                function issue(uint amount) public onlyOwner {
                    require(_totalSupply + amount > _totalSupply);
                    require(balances[owner] + amount > balances[owner]);
            
                    balances[owner] += amount;
                    _totalSupply += amount;
                    Issue(amount);
                }
            
                // Redeem tokens.
                // These tokens are withdrawn from the owner address
                // if the balance must be enough to cover the redeem
                // or the call will fail.
                // @param _amount Number of tokens to be issued
                function redeem(uint amount) public onlyOwner {
                    require(_totalSupply >= amount);
                    require(balances[owner] >= amount);
            
                    _totalSupply -= amount;
                    balances[owner] -= amount;
                    Redeem(amount);
                }
            
                function setParams(uint newBasisPoints, uint newMaxFee) public onlyOwner {
                    // Ensure transparency by hardcoding limit beyond which fees can never be added
                    require(newBasisPoints < 20);
                    require(newMaxFee < 50);
            
                    basisPointsRate = newBasisPoints;
                    maximumFee = newMaxFee.mul(10**decimals);
            
                    Params(basisPointsRate, maximumFee);
                }
            
                // Called when new token are issued
                event Issue(uint amount);
            
                // Called when tokens are redeemed
                event Redeem(uint amount);
            
                // Called when contract is deprecated
                event Deprecate(address newAddress);
            
                // Called if contract ever adds fees
                event Params(uint feeBasisPoints, uint maxFee);
            }

            File 3 of 3: 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;
            }