ETH Price: $1,980.16 (+0.24%)
Gas: 0.04 Gwei

Transaction Decoder

Block:
24509984 at Feb-22-2026 04:21:47 AM +UTC
Transaction Fee:
0.000006114247905051 ETH $0.01
Gas Used:
210,457 Gas / 0.029052243 Gwei

Emitted Events:

434 EntryPoint.BeforeExecution( )
435 CHNStaking.ClaimRewardFromVault( userAddress=0x93105E585832D9294271Ca1Caa4505d8490648FA, pid=0 )
436 Chain.Transfer( from=CHNReward, to=0x93105E585832D9294271Ca1Caa4505d8490648FA, value=19976786569423995129 )
437 CHNReward.Claim( user=0x93105E585832D9294271Ca1Caa4505d8490648FA, pid=0, reward=19976786569423995129 )
438 0x6442f4729ee8c890d310ef92845d43184a12ee6a.0x23299aca2c857a349e47cc7c62543207e23fc21bea9be69488ce2eb90095acdc( 0x23299aca2c857a349e47cc7c62543207e23fc21bea9be69488ce2eb90095acdc, bb0014b6fc9795f060f04a9cd85c8c5d7f8d41bcdfb3c12cbb8ee5df0613a567, 0000000000000000000000000000000000000000000000000000000000000040, 000000000000000000000000000000000000000000000000000000000000003c, 7465616d5f636d616530666f7a6d303179643973306b65763470743466732d70, 726a5f636d6273376535356e303061793736306b356e6b316172797400000000 )
439 EntryPoint.UserOperationEvent( userOpHash=BB0014B6FC9795F060F04A9CD85C8C5D7F8D41BCDFB3C12CBB8EE5DF0613A567, sender=0x93105E585832D9294271Ca1Caa4505d8490648FA, paymaster=0x6442f4729ee8c890d310ef92845d43184a12ee6a, nonce=98417521096507359529816647548905699162319630852386525827198289280978248531968, success=True, actualGasCost=7201049888851, actualGasUsed=248071 )

Account State Difference:

  Address   Before After State Difference Code
0x23445c63...06D0e19A9
(Onyx: Staking)
0x5FF137D4...a026d2789
(Entry Point 0.6.0)
319.692043453872197023 Eth319.692036252822308172 Eth0.000007201049888851
0x62733E85...d58E63906
(Bundler: 0x627...906)
0.079399727366328661 Eth
Nonce: 70227
0.079400814168312461 Eth
Nonce: 70228
0.0000010868019838
0xA2cd3D43...1ED94fb18
(BuilderNet)
133.055994330185420295 Eth133.055994344162290579 Eth0.000000013976870284

Execution Trace

EntryPoint.handleOps( ops=, beneficiary=0x62733E856163a8B95b637Eca317319ed58E63906 )
  • 0x93105e585832d9294271ca1caa4505d8490648fa.3a871cdd( )
    • 0xf22175c80c6e074c171811c59c6c0087e2a6a346.3a871cdd( )
      • Null: 0x000...001.2abb802a( )
      • 0x6442f4729ee8c890d310ef92845d43184a12ee6a.f465c77e( )
        • Null: 0x000...001.5c5b59d0( )
        • EntryPoint.innerHandleOp( callData=0xB61D27F600000000000000000000000028CA9CAAE31602D0312EBF6466C9DD57FCA5DA93000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000024AE169A50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, opInfo=[{name:mUserOp, type:tuple, order:1, indexed:false, value:[{name:sender, type:address, order:1, indexed:false, value:0x93105E585832D9294271Ca1Caa4505d8490648FA, valueString:0x93105E585832D9294271Ca1Caa4505d8490648FA}, {name:nonce, type:uint256, order:2, indexed:false, value:98417521096507359529816647548905699162319630852386525827198289280978248531968, valueString:98417521096507359529816647548905699162319630852386525827198289280978248531968}, {name:callGasLimit, type:uint256, order:3, indexed:false, value:161621, valueString:161621}, {name:verificationGasLimit, type:uint256, order:4, indexed:false, value:112012, valueString:112012}, {name:preVerificationGas, type:uint256, order:5, indexed:false, value:83336, valueString:83336}, {name:paymaster, type:address, order:6, indexed:false, value:0x6442f4729Ee8c890D310EF92845d43184A12ee6a, valueString:0x6442f4729Ee8c890D310EF92845d43184A12ee6a}, {name:maxFeePerGas, type:uint256, order:7, indexed:false, value:45778894, valueString:45778894}, {name:maxPriorityFeePerGas, type:uint256, order:8, indexed:false, value:42350, valueString:42350}], valueString:[{name:sender, type:address, order:1, indexed:false, value:0x93105E585832D9294271Ca1Caa4505d8490648FA, valueString:0x93105E585832D9294271Ca1Caa4505d8490648FA}, {name:nonce, type:uint256, order:2, indexed:false, value:98417521096507359529816647548905699162319630852386525827198289280978248531968, valueString:98417521096507359529816647548905699162319630852386525827198289280978248531968}, {name:callGasLimit, type:uint256, order:3, indexed:false, value:161621, valueString:161621}, {name:verificationGasLimit, type:uint256, order:4, indexed:false, value:112012, valueString:112012}, {name:preVerificationGas, type:uint256, order:5, indexed:false, value:83336, valueString:83336}, {name:paymaster, type:address, order:6, indexed:false, value:0x6442f4729Ee8c890D310EF92845d43184A12ee6a, valueString:0x6442f4729Ee8c890D310EF92845d43184A12ee6a}, {name:maxFeePerGas, type:uint256, order:7, indexed:false, value:45778894, valueString:45778894}, {name:maxPriorityFeePerGas, type:uint256, order:8, indexed:false, value:42350, valueString:42350}]}, {name:userOpHash, type:bytes32, order:2, indexed:false, value:BB0014B6FC9795F060F04A9CD85C8C5D7F8D41BCDFB3C12CBB8EE5DF0613A567, valueString:BB0014B6FC9795F060F04A9CD85C8C5D7F8D41BCDFB3C12CBB8EE5DF0613A567}, {name:prefund, type:uint256, order:3, indexed:false, value:26597216961742, valueString:26597216961742}, {name:contextOffset, type:uint256, order:4, indexed:false, value:1376, valueString:1376}, {name:preOpGas, type:uint256, order:5, indexed:false, value:157362, valueString:157362}], context=0xBB0014B6FC9795F060F04A9CD85C8C5D7F8D41BCDFB3C12CBB8EE5DF0613A5670000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000003C7465616D5F636D616530666F7A6D303179643973306B65763470743466732D70726A5F636D6273376535356E303061793736306B356E6B316172797400000000 ) => ( actualGasCost=7201049888851 )
          • 0x93105e585832d9294271ca1caa4505d8490648fa.b61d27f6( )
            • 0xf22175c80c6e074c171811c59c6c0087e2a6a346.b61d27f6( )
              • AccountFactory.isRegistered( _account=0x93105E585832D9294271Ca1Caa4505d8490648FA ) => ( True )
              • CHNReward.claimReward( pid=0 )
                • CHNStaking.claimRewardFromVault( userAddress=0x93105E585832D9294271Ca1Caa4505d8490648FA, pid=0 ) => ( 19976786569423995129 )
                • Chain.transfer( to=0x93105E585832D9294271Ca1Caa4505d8490648FA, value=19976786569423995129 ) => ( True )
                • 0x6442f4729ee8c890d310ef92845d43184a12ee6a.a9a23409( )
                • ETH 0.000007201049888851 Bundler: 0x627...906.CALL( )
                  handleOps[EntryPoint (ln:137)]
                  File 1 of 5: EntryPoint
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v4.8.0) (security/ReentrancyGuard.sol)
                  pragma solidity ^0.8.0;
                  /**
                   * @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;
                      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
                          require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
                          // 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;
                      }
                  }
                  /**
                   ** Account-Abstraction (EIP-4337) singleton EntryPoint implementation.
                   ** Only one instance required on each chain.
                   **/
                  // SPDX-License-Identifier: GPL-3.0
                  pragma solidity ^0.8.12;
                  /* solhint-disable avoid-low-level-calls */
                  /* solhint-disable no-inline-assembly */
                  import "../interfaces/IAccount.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 "@openzeppelin/contracts/security/ReentrancyGuard.sol";
                  contract EntryPoint is IEntryPoint, StakeManager, NonceManager, ReentrancyGuard {
                      using UserOperationLib for UserOperation;
                      SenderCreator private immutable senderCreator = new SenderCreator();
                      // internal value used during simulation: need to query aggregator.
                      address private constant SIMULATE_FIND_AGGREGATOR = address(1);
                      // marker for inner call revert on out of gas
                      bytes32 private constant INNER_OUT_OF_GAS = hex'deaddead';
                      uint256 private constant REVERT_REASON_MAX_LEN = 2048;
                      /**
                       * for simulation purposes, validateUserOp (and validatePaymasterUserOp) must return this value
                       * in case of signature failure, instead of revert.
                       */
                      uint256 public constant SIG_VALIDATION_FAILED = 1;
                      /**
                       * 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 op
                       * @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, UserOperation calldata userOp, UserOpInfo memory opInfo) private returns (uint256 collected) {
                          uint256 preGas = gasleft();
                          bytes memory context = getMemoryBytesFromOffset(opInfo.contextOffset);
                          try this.innerHandleOp(userOp.callData, opInfo, context) returns (uint256 _actualGasCost) {
                              collected = _actualGasCost;
                          } catch {
                              bytes32 innerRevertCode;
                              assembly {
                                  returndatacopy(0, 0, 32)
                                  innerRevertCode := mload(0)
                              }
                              // handleOps was called with gas limit too low. abort entire bundle.
                              if (innerRevertCode == INNER_OUT_OF_GAS) {
                                  //report paymaster, since if it is not deliberately caused by the bundler,
                                  // it must be a revert caused by paymaster.
                                  revert FailedOp(opIndex, "AA95 out of gas");
                              }
                              uint256 actualGas = preGas - gasleft() + opInfo.preOpGas;
                              collected = _handlePostOp(opIndex, IPaymaster.PostOpMode.postOpReverted, opInfo, context, actualGas);
                          }
                      }
                      /**
                       * 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(UserOperation[] 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);
                      } //unchecked
                      }
                      /**
                       * 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
                      ) public nonReentrant {
                          uint256 opasLen = opsPerAggregator.length;
                          uint256 totalOps = 0;
                          for (uint256 i = 0; i < opasLen; i++) {
                              UserOpsPerAggregator calldata opa = opsPerAggregator[i];
                              UserOperation[] 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);
                          emit BeforeExecution();
                          uint256 opIndex = 0;
                          for (uint256 a = 0; a < opasLen; a++) {
                              UserOpsPerAggregator calldata opa = opsPerAggregator[a];
                              UserOperation[] 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++;
                              }
                          }
                          uint256 collected = 0;
                          opIndex = 0;
                          for (uint256 a = 0; a < opasLen; a++) {
                              UserOpsPerAggregator calldata opa = opsPerAggregator[a];
                              emit SignatureAggregatorChanged(address(opa.aggregator));
                              UserOperation[] 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);
                      }
                      /// @inheritdoc IEntryPoint
                      function simulateHandleOp(UserOperation calldata op, address target, bytes calldata targetCallData) external override {
                          UserOpInfo memory opInfo;
                          _simulationOnlyValidations(op);
                          (uint256 validationData, uint256 paymasterValidationData) = _validatePrepayment(0, op, opInfo);
                          ValidationData memory data = _intersectTimeRange(validationData, paymasterValidationData);
                          numberMarker();
                          uint256 paid = _executeUserOp(0, op, opInfo);
                          numberMarker();
                          bool targetSuccess;
                          bytes memory targetResult;
                          if (target != address(0)) {
                              (targetSuccess, targetResult) = target.call(targetCallData);
                          }
                          revert ExecutionResult(opInfo.preOpGas, paid, data.validAfter, data.validUntil, targetSuccess, targetResult);
                      }
                      // A memory copy of UserOp static fields only.
                      // Excluding: callData, initCode and signature. Replacing paymasterAndData with paymaster.
                      struct MemoryUserOp {
                          address sender;
                          uint256 nonce;
                          uint256 callGasLimit;
                          uint256 verificationGasLimit;
                          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.
                       */
                      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;
                          uint callGasLimit = mUserOp.callGasLimit;
                      unchecked {
                          // handleOps was called with gas limit too low. abort entire bundle.
                          if (gasleft() < callGasLimit + mUserOp.verificationGasLimit + 5000) {
                              assembly {
                                  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;
                          //note: opIndex is ignored (relevant only if mode==postOpReverted, which is only possible outside of innerHandleOp)
                          return _handlePostOp(0, mode, opInfo, context, actualGas);
                      }
                      }
                      /**
                       * 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.
                       */
                      function getUserOpHash(UserOperation 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.
                       */
                      function _copyUserOpToMemory(UserOperation calldata userOp, MemoryUserOp memory mUserOp) internal pure {
                          mUserOp.sender = userOp.sender;
                          mUserOp.nonce = userOp.nonce;
                          mUserOp.callGasLimit = userOp.callGasLimit;
                          mUserOp.verificationGasLimit = userOp.verificationGasLimit;
                          mUserOp.preVerificationGas = userOp.preVerificationGas;
                          mUserOp.maxFeePerGas = userOp.maxFeePerGas;
                          mUserOp.maxPriorityFeePerGas = userOp.maxPriorityFeePerGas;
                          bytes calldata paymasterAndData = userOp.paymasterAndData;
                          if (paymasterAndData.length > 0) {
                              require(paymasterAndData.length >= 20, "AA93 invalid paymasterAndData");
                              mUserOp.paymaster = address(bytes20(paymasterAndData[: 20]));
                          } else {
                              mUserOp.paymaster = address(0);
                          }
                      }
                      /**
                       * Simulate a call to account.validateUserOp and paymaster.validatePaymasterUserOp.
                       * @dev this method always revert. Successful result is ValidationResult error. other errors are failures.
                       * @dev The node must also verify it doesn't use banned opcodes, and that it doesn't reference storage outside the account's data.
                       * @param userOp the user operation to validate.
                       */
                      function simulateValidation(UserOperation calldata userOp) external {
                          UserOpInfo memory outOpInfo;
                          _simulationOnlyValidations(userOp);
                          (uint256 validationData, uint256 paymasterValidationData) = _validatePrepayment(0, userOp, outOpInfo);
                          StakeInfo memory paymasterInfo = _getStakeInfo(outOpInfo.mUserOp.paymaster);
                          StakeInfo memory senderInfo = _getStakeInfo(outOpInfo.mUserOp.sender);
                          StakeInfo memory factoryInfo;
                          {
                              bytes calldata initCode = userOp.initCode;
                              address factory = initCode.length >= 20 ? address(bytes20(initCode[0 : 20])) : address(0);
                              factoryInfo = _getStakeInfo(factory);
                          }
                          ValidationData memory data = _intersectTimeRange(validationData, paymasterValidationData);
                          address aggregator = data.aggregator;
                          bool sigFailed = aggregator == address(1);
                          ReturnInfo memory returnInfo = ReturnInfo(outOpInfo.preOpGas, outOpInfo.prefund,
                              sigFailed, data.validAfter, data.validUntil, getMemoryBytesFromOffset(outOpInfo.contextOffset));
                          if (aggregator != address(0) && aggregator != address(1)) {
                              AggregatorStakeInfo memory aggregatorInfo = AggregatorStakeInfo(aggregator, _getStakeInfo(aggregator));
                              revert ValidationResultWithAggregation(returnInfo, senderInfo, factoryInfo, paymasterInfo, aggregatorInfo);
                          }
                          revert ValidationResult(returnInfo, senderInfo, factoryInfo, paymasterInfo);
                      }
                      function _getRequiredPrefund(MemoryUserOp memory mUserOp) internal pure returns (uint256 requiredPrefund) {
                      unchecked {
                          //when using a Paymaster, the verificationGasLimit is used also to as a limit for the postOp call.
                          // our security model might call postOp eventually twice
                          uint256 mul = mUserOp.paymaster != address(0) ? 3 : 1;
                          uint256 requiredGas = mUserOp.callGasLimit + mUserOp.verificationGasLimit * mul + mUserOp.preVerificationGas;
                          requiredPrefund = requiredGas * mUserOp.maxFeePerGas;
                      }
                      }
                      // create the sender's contract if needed.
                      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);
                          }
                      }
                      /**
                       * 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 calldata initCode) public {
                          address sender = senderCreator.createSender(initCode);
                          revert SenderAddressResult(sender);
                      }
                      function _simulationOnlyValidations(UserOperation calldata userOp) internal view {
                          // solhint-disable-next-line no-empty-blocks
                          try this._validateSenderAndPaymaster(userOp.initCode, userOp.sender, userOp.paymasterAndData) {}
                          catch Error(string memory revertReason) {
                              if (bytes(revertReason).length != 0) {
                                  revert FailedOp(0, revertReason);
                              }
                          }
                      }
                      /**
                      * Called only during simulation.
                      * This function always reverts to prevent warm/cold storage differentiation in simulation vs execution.
                      */
                      function _validateSenderAndPaymaster(bytes calldata initCode, address sender, bytes calldata paymasterAndData) external view {
                          if (initCode.length == 0 && sender.code.length == 0) {
                              // it would revert anyway. but give a meaningful message
                              revert("AA20 account not deployed");
                          }
                          if (paymasterAndData.length >= 20) {
                              address paymaster = address(bytes20(paymasterAndData[0 : 20]));
                              if (paymaster.code.length == 0) {
                                  // it would revert anyway. but give a meaningful message
                                  revert("AA30 paymaster not deployed");
                              }
                          }
                          // always revert
                          revert("");
                      }
                      /**
                       * call account.validateUserOp.
                       * revert (with FailedOp) in case validateUserOp reverts, or account didn't send required prefund.
                       * decrement account's deposit if needed
                       */
                      function _validateAccountPrepayment(uint256 opIndex, UserOperation calldata op, UserOpInfo memory opInfo, uint256 requiredPrefund)
                      internal returns (uint256 gasUsedByValidateAccountPrepayment, uint256 validationData) {
                      unchecked {
                          uint256 preGas = gasleft();
                          MemoryUserOp memory mUserOp = opInfo.mUserOp;
                          address sender = mUserOp.sender;
                          _createSenderIfNeeded(opIndex, opInfo, op.initCode);
                          address paymaster = mUserOp.paymaster;
                          numberMarker();
                          uint256 missingAccountFunds = 0;
                          if (paymaster == address(0)) {
                              uint256 bal = balanceOf(sender);
                              missingAccountFunds = bal > requiredPrefund ? 0 : requiredPrefund - bal;
                          }
                          try IAccount(sender).validateUserOp{gas : mUserOp.verificationGasLimit}(op, opInfo.userOpHash, missingAccountFunds)
                          returns (uint256 _validationData) {
                              validationData = _validationData;
                          } catch Error(string memory revertReason) {
                              revert FailedOp(opIndex, string.concat("AA23 reverted: ", revertReason));
                          } catch {
                              revert FailedOp(opIndex, "AA23 reverted (or OOG)");
                          }
                          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 = uint112(deposit - requiredPrefund);
                          }
                          gasUsedByValidateAccountPrepayment = preGas - gasleft();
                      }
                      }
                      /**
                       * 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
                       */
                      function _validatePaymasterPrepayment(uint256 opIndex, UserOperation calldata op, UserOpInfo memory opInfo, uint256 requiredPreFund, uint256 gasUsedByValidateAccountPrepayment)
                      internal returns (bytes memory context, uint256 validationData) {
                      unchecked {
                          MemoryUserOp memory mUserOp = opInfo.mUserOp;
                          uint256 verificationGasLimit = mUserOp.verificationGasLimit;
                          require(verificationGasLimit > gasUsedByValidateAccountPrepayment, "AA41 too little verificationGas");
                          uint256 gas = verificationGasLimit - gasUsedByValidateAccountPrepayment;
                          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 = uint112(deposit - requiredPreFund);
                          try IPaymaster(paymaster).validatePaymasterUserOp{gas : gas}(op, opInfo.userOpHash, requiredPreFund) returns (bytes memory _context, uint256 _validationData){
                              context = _context;
                              validationData = _validationData;
                          } catch Error(string memory revertReason) {
                              revert FailedOp(opIndex, string.concat("AA33 reverted: ", revertReason));
                          } catch {
                              revert FailedOp(opIndex, "AA33 reverted (or OOG)");
                          }
                      }
                      }
                      /**
                       * revert if either account validationData or paymaster validationData is expired
                       */
                      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");
                          }
                      }
                      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).
                       * 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, UserOperation calldata userOp, UserOpInfo memory outOpInfo)
                      private 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 maxGasValues = mUserOp.preVerificationGas | mUserOp.verificationGasLimit | mUserOp.callGasLimit |
                          userOp.maxFeePerGas | userOp.maxPriorityFeePerGas;
                          require(maxGasValues <= type(uint120).max, "AA94 gas values overflow");
                          uint256 gasUsedByValidateAccountPrepayment;
                          (uint256 requiredPreFund) = _getRequiredPrefund(mUserOp);
                          (gasUsedByValidateAccountPrepayment, validationData) = _validateAccountPrepayment(opIndex, userOp, outOpInfo, requiredPreFund);
                          if (!_validateAndUpdateNonce(mUserOp.sender, mUserOp.nonce)) {
                              revert FailedOp(opIndex, "AA25 invalid account nonce");
                          }
                          //a "marker" where account opcode validation is done and paymaster opcode validation is about to start
                          // (used only by off-chain simulateValidation)
                          numberMarker();
                          bytes memory context;
                          if (mUserOp.paymaster != address(0)) {
                              (context, paymasterValidationData) = _validatePaymasterPrepayment(opIndex, userOp, outOpInfo, requiredPreFund, gasUsedByValidateAccountPrepayment);
                          }
                      unchecked {
                          uint256 gasUsed = preGas - gasleft();
                          if (userOp.verificationGasLimit < gasUsed) {
                              revert FailedOp(opIndex, "AA40 over verificationGasLimit");
                          }
                          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 opIndex index in the batch
                       * @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 _handlePostOp(uint256 opIndex, 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) {
                                      IPaymaster(paymaster).postOp{gas : mUserOp.verificationGasLimit}(mode, context, actualGasCost);
                                  } else {
                                      // solhint-disable-next-line no-empty-blocks
                                      try IPaymaster(paymaster).postOp{gas : mUserOp.verificationGasLimit}(mode, context, actualGasCost) {}
                                      catch Error(string memory reason) {
                                          revert FailedOp(opIndex, string.concat("AA50 postOp reverted: ", reason));
                                      }
                                      catch {
                                          revert FailedOp(opIndex, "AA50 postOp revert");
                                      }
                                  }
                              }
                          }
                          actualGas += preGas - gasleft();
                          actualGasCost = actualGas * gasPrice;
                          if (opInfo.prefund < actualGasCost) {
                              revert FailedOp(opIndex, "AA51 prefund below actualGasCost");
                          }
                          uint256 refund = opInfo.prefund - actualGasCost;
                          _incrementDeposit(refundAddress, refund);
                          bool success = mode == IPaymaster.PostOpMode.opSucceeded;
                          emit UserOperationEvent(opInfo.userOpHash, mUserOp.sender, mUserOp.paymaster, mUserOp.nonce, 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
                       */
                      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);
                      }
                      }
                      function min(uint256 a, uint256 b) internal pure returns (uint256) {
                          return a < b ? a : b;
                      }
                      function getOffsetOfMemoryBytes(bytes memory data) internal pure returns (uint256 offset) {
                          assembly {offset := data}
                      }
                      function getMemoryBytesFromOffset(uint256 offset) internal pure returns (bytes memory data) {
                          assembly {data := offset}
                      }
                      //place the NUMBER opcode in the code.
                      // this is used as a marker during simulation, as this OP is completely banned from the simulated code of the
                      // account and paymaster.
                      function numberMarker() internal view {
                          assembly {mstore(0, number())}
                      }
                  }
                  // SPDX-License-Identifier: GPL-3.0
                  pragma solidity ^0.8.12;
                  /* solhint-disable no-inline-assembly */
                  /**
                   * returned data from validateUserOp.
                   * validateUserOp returns a uint256, with 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
                      function _parseValidationData(uint 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);
                      }
                  // intersect account and paymaster ranges.
                      function _intersectTimeRange(uint256 validationData, uint256 paymasterValidationData) pure returns (ValidationData memory) {
                          ValidationData memory accountValidationData = _parseValidationData(validationData);
                          ValidationData memory pmValidationData = _parseValidationData(paymasterValidationData);
                          address aggregator = accountValidationData.aggregator;
                          if (aggregator == address(0)) {
                              aggregator = pmValidationData.aggregator;
                          }
                          uint48 validAfter = accountValidationData.validAfter;
                          uint48 validUntil = accountValidationData.validUntil;
                          uint48 pmValidAfter = pmValidationData.validAfter;
                          uint48 pmValidUntil = pmValidationData.validUntil;
                          if (validAfter < pmValidAfter) validAfter = pmValidAfter;
                          if (validUntil > pmValidUntil) validUntil = pmValidUntil;
                          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 {
                              let mem := mload(0x40)
                              let len := data.length
                              calldatacopy(mem, data.offset, len)
                              ret := keccak256(mem, len)
                          }
                      }
                  // SPDX-License-Identifier: GPL-3.0
                  pragma solidity ^0.8.12;
                  import "../interfaces/IEntryPoint.sol";
                  /**
                   * nonce management functionality
                   */
                  contract NonceManager is INonceManager {
                      /**
                       * The next valid sequence number for a given nonce key.
                       */
                      mapping(address => mapping(uint192 => uint256)) public nonceSequenceNumber;
                      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()
                       */
                      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.12;
                  /**
                   * 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 {
                              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.12;
                  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
                      function _getStakeInfo(address addr) internal view returns (StakeInfo memory info) {
                          DepositInfo storage depositInfo = deposits[addr];
                          info.stake = depositInfo.stake;
                          info.unstakeDelaySec = depositInfo.unstakeDelaySec;
                      }
                      /// return the deposit (for gas payment) of the account
                      function balanceOf(address account) public view returns (uint256) {
                          return deposits[account].deposit;
                      }
                      receive() external payable {
                          depositTo(msg.sender);
                      }
                      function _incrementDeposit(address account, uint256 amount) internal {
                          DepositInfo storage info = deposits[account];
                          uint256 newAmount = info.deposit + amount;
                          require(newAmount <= type(uint112).max, "deposit overflow");
                          info.deposit = uint112(newAmount);
                      }
                      /**
                       * add to the deposit of the given account
                       */
                      function depositTo(address account) public payable {
                          _incrementDeposit(account, msg.value);
                          DepositInfo storage info = deposits[account];
                          emit Deposited(account, info.deposit);
                      }
                      /**
                       * 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 = uint112(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.12;
                  import "./UserOperation.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(UserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds)
                      external returns (uint256 validationData);
                  }
                  // SPDX-License-Identifier: GPL-3.0
                  pragma solidity ^0.8.12;
                  import "./UserOperation.sol";
                  /**
                   * Aggregated Signatures validator.
                   */
                  interface IAggregator {
                      /**
                       * validate aggregated signature.
                       * revert if the aggregated signature does not match the given list of operations.
                       */
                      function validateSignatures(UserOperation[] calldata userOps, bytes calldata signature) external view;
                      /**
                       * validate signature of a single userOp
                       * This method is should be called by bundler after EntryPoint.simulateValidation() returns (reverts) with ValidationResultWithAggregation
                       * 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(UserOperation 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(UserOperation[] 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.8.12;
                  /* solhint-disable avoid-low-level-calls */
                  /* solhint-disable no-inline-assembly */
                  /* solhint-disable reason-string */
                  import "./UserOperation.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 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.
                       */
                      event SignatureAggregatorChanged(address indexed aggregator);
                      /**
                       * a custom revert error of handleOps, to identify the offending op.
                       *  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.
                       *   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.
                       */
                      error FailedOp(uint256 opIndex, string reason);
                      /**
                       * error case when a signature aggregator fails to verify the aggregated signature it had created.
                       */
                      error SignatureValidationFailed(address aggregator);
                      /**
                       * Successful result from simulateValidation.
                       * @param returnInfo gas and time-range returned values
                       * @param senderInfo stake information about the sender
                       * @param factoryInfo stake information about the factory (if any)
                       * @param paymasterInfo stake information about the paymaster (if any)
                       */
                      error ValidationResult(ReturnInfo returnInfo,
                          StakeInfo senderInfo, StakeInfo factoryInfo, StakeInfo paymasterInfo);
                      /**
                       * Successful result from simulateValidation, if the account returns a signature aggregator
                       * @param returnInfo gas and time-range returned values
                       * @param senderInfo stake information about the sender
                       * @param factoryInfo stake information about the factory (if any)
                       * @param paymasterInfo stake information about the paymaster (if any)
                       * @param aggregatorInfo signature aggregation info (if the account requires signature aggregator)
                       *      bundler MUST use it to verify the signature, or reject the UserOperation
                       */
                      error ValidationResultWithAggregation(ReturnInfo returnInfo,
                          StakeInfo senderInfo, StakeInfo factoryInfo, StakeInfo paymasterInfo,
                          AggregatorStakeInfo aggregatorInfo);
                      /**
                       * return value of getSenderAddress
                       */
                      error SenderAddressResult(address sender);
                      /**
                       * return value of simulateHandleOp
                       */
                      error ExecutionResult(uint256 preOpGas, uint256 paid, uint48 validAfter, uint48 validUntil, bool targetSuccess, bytes targetResult);
                      //UserOps handled, per aggregator
                      struct UserOpsPerAggregator {
                          UserOperation[] userOps;
                          // aggregator address
                          IAggregator aggregator;
                          // aggregated signature
                          bytes signature;
                      }
                      /**
                       * Execute a batch of UserOperation.
                       * 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(UserOperation[] 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.
                       */
                      function getUserOpHash(UserOperation calldata userOp) external view returns (bytes32);
                      /**
                       * Simulate a call to account.validateUserOp and paymaster.validatePaymasterUserOp.
                       * @dev this method always revert. Successful result is ValidationResult error. other errors are failures.
                       * @dev The node must also verify it doesn't use banned opcodes, and that it doesn't reference storage outside the account's data.
                       * @param userOp the user operation to validate.
                       */
                      function simulateValidation(UserOperation calldata userOp) external;
                      /**
                       * gas and return values during simulation
                       * @param preOpGas the gas used for validation (including preValidationGas)
                       * @param prefund the required prefund for this operation
                       * @param sigFailed validateUserOp's (or paymaster's) signature check failed
                       * @param validAfter - first timestamp this UserOp is valid (merging account and paymaster time-range)
                       * @param validUntil - last timestamp this UserOp is valid (merging account and paymaster time-range)
                       * @param paymasterContext returned by validatePaymasterUserOp (to be passed into postOp)
                       */
                      struct ReturnInfo {
                          uint256 preOpGas;
                          uint256 prefund;
                          bool sigFailed;
                          uint48 validAfter;
                          uint48 validUntil;
                          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;
                      /**
                       * simulate full execution of a UserOperation (including both validation and target execution)
                       * this method will always revert with "ExecutionResult".
                       * it performs full validation of the UserOperation, but ignores signature error.
                       * an optional target address is called after the userop succeeds, and its value is returned
                       * (before the entire call is reverted)
                       * Note that in order to collect the the success/failure of the target call, it must be executed
                       * with trace enabled to track the emitted events.
                       * @param op the UserOperation to simulate
                       * @param target if nonzero, a target address to call after userop simulation. If called, the targetSuccess and targetResult
                       *        are set to the return from that call.
                       * @param targetCallData callData to pass to target address
                       */
                      function simulateHandleOp(UserOperation calldata op, address target, bytes calldata targetCallData) external;
                  }
                  // SPDX-License-Identifier: GPL-3.0
                  pragma solidity ^0.8.12;
                  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.8.12;
                  import "./UserOperation.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(UserOperation 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: GPL-3.0-only
                  pragma solidity ^0.8.12;
                  /**
                   * 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,staked, stake) fit into one cell (used during handleOps)
                       *    and the rest fit into a 2nd cell.
                       *    112 bit allows for 10^15 eth
                       *    48 bit for full timestamp
                       *    32 bit allows 150 years for unstake delay
                       */
                      struct DepositInfo {
                          uint112 deposit;
                          bool staked;
                          uint112 stake;
                          uint32 unstakeDelaySec;
                          uint48 withdrawTime;
                      }
                      //API struct used by getStakeInfo and simulateValidation
                      struct StakeInfo {
                          uint256 stake;
                          uint256 unstakeDelaySec;
                      }
                      /// @return info - full deposit information of given account
                      function getDepositInfo(address account) external view returns (DepositInfo memory info);
                      /// @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
                       */
                      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.8.12;
                  /* solhint-disable no-inline-assembly */
                  import {calldataKeccak} from "../core/Helpers.sol";
                  /**
                   * 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 callGasLimit the gas limit passed to the callData method call.
                       * @param verificationGasLimit gas used for validateUserOp and validatePaymasterUserOp.
                       * @param preVerificationGas gas not calculated by the handleOps method, but added to the gas paid. Covers batch overhead.
                       * @param maxFeePerGas same as EIP-1559 gas parameter.
                       * @param maxPriorityFeePerGas same as EIP-1559 gas parameter.
                       * @param paymasterAndData if set, this field holds the paymaster address and paymaster-specific 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 UserOperation {
                          address sender;
                          uint256 nonce;
                          bytes initCode;
                          bytes callData;
                          uint256 callGasLimit;
                          uint256 verificationGasLimit;
                          uint256 preVerificationGas;
                          uint256 maxFeePerGas;
                          uint256 maxPriorityFeePerGas;
                          bytes paymasterAndData;
                          bytes signature;
                      }
                  /**
                   * Utility functions helpful when working with UserOperation structs.
                   */
                  library UserOperationLib {
                      function getSender(UserOperation 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.
                      function gasPrice(UserOperation calldata userOp) internal view returns (uint256) {
                      unchecked {
                          uint256 maxFeePerGas = userOp.maxFeePerGas;
                          uint256 maxPriorityFeePerGas = userOp.maxPriorityFeePerGas;
                          if (maxFeePerGas == maxPriorityFeePerGas) {
                              //legacy mode (for networks that don't support basefee opcode)
                              return maxFeePerGas;
                          }
                          return min(maxFeePerGas, maxPriorityFeePerGas + block.basefee);
                      }
                      }
                      function pack(UserOperation 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);
                          uint256 callGasLimit = userOp.callGasLimit;
                          uint256 verificationGasLimit = userOp.verificationGasLimit;
                          uint256 preVerificationGas = userOp.preVerificationGas;
                          uint256 maxFeePerGas = userOp.maxFeePerGas;
                          uint256 maxPriorityFeePerGas = userOp.maxPriorityFeePerGas;
                          bytes32 hashPaymasterAndData = calldataKeccak(userOp.paymasterAndData);
                          return abi.encode(
                              sender, nonce,
                              hashInitCode, hashCallData,
                              callGasLimit, verificationGasLimit, preVerificationGas,
                              maxFeePerGas, maxPriorityFeePerGas,
                              hashPaymasterAndData
                          );
                      }
                      function hash(UserOperation calldata userOp) internal pure returns (bytes32) {
                          return keccak256(pack(userOp));
                      }
                      function min(uint256 a, uint256 b) internal pure returns (uint256) {
                          return a < b ? a : b;
                      }
                  }
                  // SPDX-License-Identifier: LGPL-3.0-only
                  pragma solidity >=0.7.5 <0.9.0;
                  // 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 {
                              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 {
                              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 {
                              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 {
                              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 {
                              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 5: CHNStaking
                  // File: @openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol
                  
                  
                  // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)
                  
                  pragma solidity ^0.8.1;
                  
                  /**
                   * @dev Collection of functions related to the address type
                   */
                  library AddressUpgradeable {
                      /**
                       * @dev Returns true if `account` is a contract.
                       *
                       * [IMPORTANT]
                       * ====
                       * It is unsafe to assume that an address for which this function returns
                       * false is an externally-owned account (EOA) and not a contract.
                       *
                       * Among others, `isContract` will return false for the following
                       * types of addresses:
                       *
                       *  - an externally-owned account
                       *  - a contract in construction
                       *  - an address where a contract will be created
                       *  - an address where a contract lived, but was destroyed
                       * ====
                       *
                       * [IMPORTANT]
                       * ====
                       * You shouldn't rely on `isContract` to protect against flash loan attacks!
                       *
                       * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                       * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                       * constructor.
                       * ====
                       */
                      function isContract(address account) internal view returns (bool) {
                          // This method relies on extcodesize/address.code.length, which returns 0
                          // for contracts in construction, since the code is only stored at the end
                          // of the constructor execution.
                  
                          return account.code.length > 0;
                      }
                  
                      /**
                       * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                       * `recipient`, forwarding all available gas and reverting on errors.
                       *
                       * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                       * of certain opcodes, possibly making contracts go over the 2300 gas limit
                       * imposed by `transfer`, making them unable to receive funds via
                       * `transfer`. {sendValue} removes this limitation.
                       *
                       * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                       *
                       * IMPORTANT: because control is transferred to `recipient`, care must be
                       * taken to not create reentrancy vulnerabilities. Consider using
                       * {ReentrancyGuard} or the
                       * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                       */
                      function sendValue(address payable recipient, uint256 amount) internal {
                          require(address(this).balance >= amount, "Address: insufficient balance");
                  
                          (bool success, ) = recipient.call{value: amount}("");
                          require(success, "Address: unable to send value, recipient may have reverted");
                      }
                  
                      /**
                       * @dev Performs a Solidity function call using a low level `call`. A
                       * plain `call` is an unsafe replacement for a function call: use this
                       * function instead.
                       *
                       * If `target` reverts with a revert reason, it is bubbled up by this
                       * function (like regular Solidity function calls).
                       *
                       * Returns the raw returned data. To convert to the expected return value,
                       * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                       *
                       * Requirements:
                       *
                       * - `target` must be a contract.
                       * - calling `target` with `data` must not revert.
                       *
                       * _Available since v3.1._
                       */
                      function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                          return functionCall(target, data, "Address: low-level call failed");
                      }
                  
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                       * `errorMessage` as a fallback revert reason when `target` reverts.
                       *
                       * _Available since v3.1._
                       */
                      function functionCall(
                          address target,
                          bytes memory data,
                          string memory errorMessage
                      ) internal returns (bytes memory) {
                          return functionCallWithValue(target, data, 0, errorMessage);
                      }
                  
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                       * but also transferring `value` wei to `target`.
                       *
                       * Requirements:
                       *
                       * - the calling contract must have an ETH balance of at least `value`.
                       * - the called Solidity function must be `payable`.
                       *
                       * _Available since v3.1._
                       */
                      function functionCallWithValue(
                          address target,
                          bytes memory data,
                          uint256 value
                      ) internal returns (bytes memory) {
                          return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                      }
                  
                      /**
                       * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                       * with `errorMessage` as a fallback revert reason when `target` reverts.
                       *
                       * _Available since v3.1._
                       */
                      function functionCallWithValue(
                          address target,
                          bytes memory data,
                          uint256 value,
                          string memory errorMessage
                      ) internal returns (bytes memory) {
                          require(address(this).balance >= value, "Address: insufficient balance for call");
                          require(isContract(target), "Address: call to non-contract");
                  
                          (bool success, bytes memory returndata) = target.call{value: value}(data);
                          return verifyCallResult(success, returndata, errorMessage);
                      }
                  
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                       * but performing a static call.
                       *
                       * _Available since v3.3._
                       */
                      function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                          return functionStaticCall(target, data, "Address: low-level static call failed");
                      }
                  
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                       * but performing a static call.
                       *
                       * _Available since v3.3._
                       */
                      function functionStaticCall(
                          address target,
                          bytes memory data,
                          string memory errorMessage
                      ) internal view returns (bytes memory) {
                          require(isContract(target), "Address: static call to non-contract");
                  
                          (bool success, bytes memory returndata) = target.staticcall(data);
                          return verifyCallResult(success, returndata, errorMessage);
                      }
                  
                      /**
                       * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
                       * revert reason using the provided one.
                       *
                       * _Available since v4.3._
                       */
                      function verifyCallResult(
                          bool success,
                          bytes memory returndata,
                          string memory errorMessage
                      ) internal pure returns (bytes memory) {
                          if (success) {
                              return returndata;
                          } else {
                              // Look for revert reason and bubble it up if present
                              if (returndata.length > 0) {
                                  // The easiest way to bubble the revert reason is using memory via assembly
                  
                                  assembly {
                                      let returndata_size := mload(returndata)
                                      revert(add(32, returndata), returndata_size)
                                  }
                              } else {
                                  revert(errorMessage);
                              }
                          }
                      }
                  }
                  
                  // File: @openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol
                  
                  
                  // OpenZeppelin Contracts (last updated v4.5.0) (proxy/utils/Initializable.sol)
                  
                  pragma solidity ^0.8.0;
                  
                  
                  /**
                   * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
                   * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
                   * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
                   * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
                   *
                   * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
                   * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
                   *
                   * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
                   * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
                   *
                   * [CAUTION]
                   * ====
                   * Avoid leaving a contract uninitialized.
                   *
                   * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
                   * contract, which may impact the proxy. To initialize the implementation contract, you can either invoke the
                   * initializer manually, or you can include a constructor to automatically mark it as initialized when it is deployed:
                   *
                   * [.hljs-theme-light.nopadding]
                   * ```
                   * /// @custom:oz-upgrades-unsafe-allow constructor
                   * constructor() initializer {}
                   * ```
                   * ====
                   */
                  abstract contract Initializable {
                      /**
                       * @dev Indicates that the contract has been initialized.
                       */
                      bool private _initialized;
                  
                      /**
                       * @dev Indicates that the contract is in the process of being initialized.
                       */
                      bool private _initializing;
                  
                      /**
                       * @dev Modifier to protect an initializer function from being invoked twice.
                       */
                      modifier initializer() {
                          // If the contract is initializing we ignore whether _initialized is set in order to support multiple
                          // inheritance patterns, but we only do this in the context of a constructor, because in other contexts the
                          // contract may have been reentered.
                          require(_initializing ? _isConstructor() : !_initialized, "Initializable: contract is already initialized");
                  
                          bool isTopLevelCall = !_initializing;
                          if (isTopLevelCall) {
                              _initializing = true;
                              _initialized = true;
                          }
                  
                          _;
                  
                          if (isTopLevelCall) {
                              _initializing = false;
                          }
                      }
                  
                      /**
                       * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
                       * {initializer} modifier, directly or indirectly.
                       */
                      modifier onlyInitializing() {
                          require(_initializing, "Initializable: contract is not initializing");
                          _;
                      }
                  
                      function _isConstructor() private view returns (bool) {
                          return !AddressUpgradeable.isContract(address(this));
                      }
                  }
                  
                  // File: @openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol
                  
                  
                  // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
                  
                  pragma solidity ^0.8.0;
                  
                  
                  /**
                   * @dev Provides information about the current execution context, including the
                   * sender of the transaction and its data. While these are generally available
                   * via msg.sender and msg.data, they should not be accessed in such a direct
                   * manner, since when dealing with meta-transactions the account sending and
                   * paying for execution may not be the actual sender (as far as an application
                   * is concerned).
                   *
                   * This contract is only required for intermediate, library-like contracts.
                   */
                  abstract contract ContextUpgradeable is Initializable {
                      function __Context_init() internal onlyInitializing {
                      }
                  
                      function __Context_init_unchained() internal onlyInitializing {
                      }
                      function _msgSender() internal view virtual returns (address) {
                          return msg.sender;
                      }
                  
                      function _msgData() internal view virtual returns (bytes calldata) {
                          return msg.data;
                      }
                  
                      /**
                       * @dev This empty reserved space is put in place to allow future versions to add new
                       * variables without shifting down storage in the inheritance chain.
                       * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                       */
                      uint256[50] private __gap;
                  }
                  
                  // File: @openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol
                  
                  
                  // OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)
                  
                  pragma solidity ^0.8.0;
                  
                  
                  
                  /**
                   * @dev Contract module which provides a basic access control mechanism, where
                   * there is an account (an owner) that can be granted exclusive access to
                   * specific functions.
                   *
                   * By default, the owner account will be the one that deploys the contract. This
                   * can later be changed with {transferOwnership}.
                   *
                   * This module is used through inheritance. It will make available the modifier
                   * `onlyOwner`, which can be applied to your functions to restrict their use to
                   * the owner.
                   */
                  abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
                      address private _owner;
                  
                      event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                  
                      /**
                       * @dev Initializes the contract setting the deployer as the initial owner.
                       */
                      function __Ownable_init() internal onlyInitializing {
                          __Ownable_init_unchained();
                      }
                  
                      function __Ownable_init_unchained() internal onlyInitializing {
                          _transferOwnership(_msgSender());
                      }
                  
                      /**
                       * @dev Returns the address of the current owner.
                       */
                      function owner() public view virtual returns (address) {
                          return _owner;
                      }
                  
                      /**
                       * @dev Throws if called by any account other than the owner.
                       */
                      modifier onlyOwner() {
                          require(owner() == _msgSender(), "Ownable: caller is not the owner");
                          _;
                      }
                  
                      /**
                       * @dev Leaves the contract without owner. It will not be possible to call
                       * `onlyOwner` functions anymore. Can only be called by the current owner.
                       *
                       * NOTE: Renouncing ownership will leave the contract without an owner,
                       * thereby removing any functionality that is only available to the owner.
                       */
                      function renounceOwnership() public virtual onlyOwner {
                          _transferOwnership(address(0));
                      }
                  
                      /**
                       * @dev Transfers ownership of the contract to a new account (`newOwner`).
                       * Can only be called by the current owner.
                       */
                      function transferOwnership(address newOwner) public virtual onlyOwner {
                          require(newOwner != address(0), "Ownable: new owner is the zero address");
                          _transferOwnership(newOwner);
                      }
                  
                      /**
                       * @dev Transfers ownership of the contract to a new account (`newOwner`).
                       * Internal function without access restriction.
                       */
                      function _transferOwnership(address newOwner) internal virtual {
                          address oldOwner = _owner;
                          _owner = newOwner;
                          emit OwnershipTransferred(oldOwner, newOwner);
                      }
                  
                      /**
                       * @dev This empty reserved space is put in place to allow future versions to add new
                       * variables without shifting down storage in the inheritance chain.
                       * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                       */
                      uint256[49] private __gap;
                  }
                  
                  // File: @openzeppelin/contracts/utils/math/SafeMath.sol
                  
                  
                  // OpenZeppelin Contracts v4.4.1 (utils/math/SafeMath.sol)
                  
                  pragma solidity ^0.8.0;
                  
                  // CAUTION
                  // This version of SafeMath should only be used with Solidity 0.8 or later,
                  // because it relies on the compiler's built in overflow checks.
                  
                  /**
                   * @dev Wrappers over Solidity's arithmetic operations.
                   *
                   * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler
                   * now has built in overflow checking.
                   */
                  library SafeMath {
                      /**
                       * @dev Returns the addition of two unsigned integers, with an overflow flag.
                       *
                       * _Available since v3.4._
                       */
                      function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                          unchecked {
                              uint256 c = a + b;
                              if (c < a) return (false, 0);
                              return (true, c);
                          }
                      }
                  
                      /**
                       * @dev Returns the substraction of two unsigned integers, with an overflow flag.
                       *
                       * _Available since v3.4._
                       */
                      function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                          unchecked {
                              if (b > a) return (false, 0);
                              return (true, a - b);
                          }
                      }
                  
                      /**
                       * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
                       *
                       * _Available since v3.4._
                       */
                      function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                          unchecked {
                              // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                              // benefit is lost if 'b' is also tested.
                              // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                              if (a == 0) return (true, 0);
                              uint256 c = a * b;
                              if (c / a != b) return (false, 0);
                              return (true, c);
                          }
                      }
                  
                      /**
                       * @dev Returns the division of two unsigned integers, with a division by zero flag.
                       *
                       * _Available since v3.4._
                       */
                      function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                          unchecked {
                              if (b == 0) return (false, 0);
                              return (true, a / b);
                          }
                      }
                  
                      /**
                       * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
                       *
                       * _Available since v3.4._
                       */
                      function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                          unchecked {
                              if (b == 0) return (false, 0);
                              return (true, a % b);
                          }
                      }
                  
                      /**
                       * @dev Returns the addition of two unsigned integers, reverting on
                       * overflow.
                       *
                       * Counterpart to Solidity's `+` operator.
                       *
                       * Requirements:
                       *
                       * - Addition cannot overflow.
                       */
                      function add(uint256 a, uint256 b) internal pure returns (uint256) {
                          return a + b;
                      }
                  
                      /**
                       * @dev Returns the subtraction of two unsigned integers, reverting on
                       * overflow (when the result is negative).
                       *
                       * Counterpart to Solidity's `-` operator.
                       *
                       * Requirements:
                       *
                       * - Subtraction cannot overflow.
                       */
                      function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                          return a - b;
                      }
                  
                      /**
                       * @dev Returns the multiplication of two unsigned integers, reverting on
                       * overflow.
                       *
                       * Counterpart to Solidity's `*` operator.
                       *
                       * Requirements:
                       *
                       * - Multiplication cannot overflow.
                       */
                      function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                          return a * b;
                      }
                  
                      /**
                       * @dev Returns the integer division of two unsigned integers, reverting on
                       * division by zero. The result is rounded towards zero.
                       *
                       * Counterpart to Solidity's `/` operator.
                       *
                       * Requirements:
                       *
                       * - The divisor cannot be zero.
                       */
                      function div(uint256 a, uint256 b) internal pure returns (uint256) {
                          return a / b;
                      }
                  
                      /**
                       * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                       * reverting when dividing by zero.
                       *
                       * Counterpart to Solidity's `%` operator. This function uses a `revert`
                       * opcode (which leaves remaining gas untouched) while Solidity uses an
                       * invalid opcode to revert (consuming all remaining gas).
                       *
                       * Requirements:
                       *
                       * - The divisor cannot be zero.
                       */
                      function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                          return a % b;
                      }
                  
                      /**
                       * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
                       * overflow (when the result is negative).
                       *
                       * CAUTION: This function is deprecated because it requires allocating memory for the error
                       * message unnecessarily. For custom revert reasons use {trySub}.
                       *
                       * Counterpart to Solidity's `-` operator.
                       *
                       * Requirements:
                       *
                       * - Subtraction cannot overflow.
                       */
                      function sub(
                          uint256 a,
                          uint256 b,
                          string memory errorMessage
                      ) internal pure returns (uint256) {
                          unchecked {
                              require(b <= a, errorMessage);
                              return a - b;
                          }
                      }
                  
                      /**
                       * @dev Returns the integer division of two unsigned integers, reverting with custom message on
                       * division by zero. The result is rounded towards zero.
                       *
                       * Counterpart to Solidity's `/` operator. Note: this function uses a
                       * `revert` opcode (which leaves remaining gas untouched) while Solidity
                       * uses an invalid opcode to revert (consuming all remaining gas).
                       *
                       * Requirements:
                       *
                       * - The divisor cannot be zero.
                       */
                      function div(
                          uint256 a,
                          uint256 b,
                          string memory errorMessage
                      ) internal pure returns (uint256) {
                          unchecked {
                              require(b > 0, errorMessage);
                              return a / b;
                          }
                      }
                  
                      /**
                       * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                       * reverting with custom message when dividing by zero.
                       *
                       * CAUTION: This function is deprecated because it requires allocating memory for the error
                       * message unnecessarily. For custom revert reasons use {tryMod}.
                       *
                       * Counterpart to Solidity's `%` operator. This function uses a `revert`
                       * opcode (which leaves remaining gas untouched) while Solidity uses an
                       * invalid opcode to revert (consuming all remaining gas).
                       *
                       * Requirements:
                       *
                       * - The divisor cannot be zero.
                       */
                      function mod(
                          uint256 a,
                          uint256 b,
                          string memory errorMessage
                      ) internal pure returns (uint256) {
                          unchecked {
                              require(b > 0, errorMessage);
                              return a % b;
                          }
                      }
                  }
                  
                  // File: @openzeppelin/contracts/utils/Address.sol
                  
                  
                  // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)
                  
                  pragma solidity ^0.8.1;
                  
                  /**
                   * @dev Collection of functions related to the address type
                   */
                  library Address {
                      /**
                       * @dev Returns true if `account` is a contract.
                       *
                       * [IMPORTANT]
                       * ====
                       * It is unsafe to assume that an address for which this function returns
                       * false is an externally-owned account (EOA) and not a contract.
                       *
                       * Among others, `isContract` will return false for the following
                       * types of addresses:
                       *
                       *  - an externally-owned account
                       *  - a contract in construction
                       *  - an address where a contract will be created
                       *  - an address where a contract lived, but was destroyed
                       * ====
                       *
                       * [IMPORTANT]
                       * ====
                       * You shouldn't rely on `isContract` to protect against flash loan attacks!
                       *
                       * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                       * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                       * constructor.
                       * ====
                       */
                      function isContract(address account) internal view returns (bool) {
                          // This method relies on extcodesize/address.code.length, which returns 0
                          // for contracts in construction, since the code is only stored at the end
                          // of the constructor execution.
                  
                          return account.code.length > 0;
                      }
                  
                      /**
                       * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                       * `recipient`, forwarding all available gas and reverting on errors.
                       *
                       * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                       * of certain opcodes, possibly making contracts go over the 2300 gas limit
                       * imposed by `transfer`, making them unable to receive funds via
                       * `transfer`. {sendValue} removes this limitation.
                       *
                       * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                       *
                       * IMPORTANT: because control is transferred to `recipient`, care must be
                       * taken to not create reentrancy vulnerabilities. Consider using
                       * {ReentrancyGuard} or the
                       * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                       */
                      function sendValue(address payable recipient, uint256 amount) internal {
                          require(address(this).balance >= amount, "Address: insufficient balance");
                  
                          (bool success, ) = recipient.call{value: amount}("");
                          require(success, "Address: unable to send value, recipient may have reverted");
                      }
                  
                      /**
                       * @dev Performs a Solidity function call using a low level `call`. A
                       * plain `call` is an unsafe replacement for a function call: use this
                       * function instead.
                       *
                       * If `target` reverts with a revert reason, it is bubbled up by this
                       * function (like regular Solidity function calls).
                       *
                       * Returns the raw returned data. To convert to the expected return value,
                       * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                       *
                       * Requirements:
                       *
                       * - `target` must be a contract.
                       * - calling `target` with `data` must not revert.
                       *
                       * _Available since v3.1._
                       */
                      function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                          return functionCall(target, data, "Address: low-level call failed");
                      }
                  
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                       * `errorMessage` as a fallback revert reason when `target` reverts.
                       *
                       * _Available since v3.1._
                       */
                      function functionCall(
                          address target,
                          bytes memory data,
                          string memory errorMessage
                      ) internal returns (bytes memory) {
                          return functionCallWithValue(target, data, 0, errorMessage);
                      }
                  
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                       * but also transferring `value` wei to `target`.
                       *
                       * Requirements:
                       *
                       * - the calling contract must have an ETH balance of at least `value`.
                       * - the called Solidity function must be `payable`.
                       *
                       * _Available since v3.1._
                       */
                      function functionCallWithValue(
                          address target,
                          bytes memory data,
                          uint256 value
                      ) internal returns (bytes memory) {
                          return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                      }
                  
                      /**
                       * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                       * with `errorMessage` as a fallback revert reason when `target` reverts.
                       *
                       * _Available since v3.1._
                       */
                      function functionCallWithValue(
                          address target,
                          bytes memory data,
                          uint256 value,
                          string memory errorMessage
                      ) internal returns (bytes memory) {
                          require(address(this).balance >= value, "Address: insufficient balance for call");
                          require(isContract(target), "Address: call to non-contract");
                  
                          (bool success, bytes memory returndata) = target.call{value: value}(data);
                          return verifyCallResult(success, returndata, errorMessage);
                      }
                  
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                       * but performing a static call.
                       *
                       * _Available since v3.3._
                       */
                      function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                          return functionStaticCall(target, data, "Address: low-level static call failed");
                      }
                  
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                       * but performing a static call.
                       *
                       * _Available since v3.3._
                       */
                      function functionStaticCall(
                          address target,
                          bytes memory data,
                          string memory errorMessage
                      ) internal view returns (bytes memory) {
                          require(isContract(target), "Address: static call to non-contract");
                  
                          (bool success, bytes memory returndata) = target.staticcall(data);
                          return verifyCallResult(success, returndata, errorMessage);
                      }
                  
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                       * but performing a delegate call.
                       *
                       * _Available since v3.4._
                       */
                      function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                          return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                      }
                  
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                       * but performing a delegate call.
                       *
                       * _Available since v3.4._
                       */
                      function functionDelegateCall(
                          address target,
                          bytes memory data,
                          string memory errorMessage
                      ) internal returns (bytes memory) {
                          require(isContract(target), "Address: delegate call to non-contract");
                  
                          (bool success, bytes memory returndata) = target.delegatecall(data);
                          return verifyCallResult(success, returndata, errorMessage);
                      }
                  
                      /**
                       * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
                       * revert reason using the provided one.
                       *
                       * _Available since v4.3._
                       */
                      function verifyCallResult(
                          bool success,
                          bytes memory returndata,
                          string memory errorMessage
                      ) internal pure returns (bytes memory) {
                          if (success) {
                              return returndata;
                          } else {
                              // Look for revert reason and bubble it up if present
                              if (returndata.length > 0) {
                                  // The easiest way to bubble the revert reason is using memory via assembly
                  
                                  assembly {
                                      let returndata_size := mload(returndata)
                                      revert(add(32, returndata), returndata_size)
                                  }
                              } else {
                                  revert(errorMessage);
                              }
                          }
                      }
                  }
                  
                  // File: @openzeppelin/contracts/token/ERC20/IERC20.sol
                  
                  
                  // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)
                  
                  pragma solidity ^0.8.0;
                  
                  /**
                   * @dev Interface of the ERC20 standard as defined in the EIP.
                   */
                  interface IERC20 {
                      /**
                       * @dev Returns the amount of tokens in existence.
                       */
                      function totalSupply() external view returns (uint256);
                  
                      /**
                       * @dev Returns the amount of tokens owned by `account`.
                       */
                      function balanceOf(address account) external view returns (uint256);
                  
                      /**
                       * @dev Moves `amount` tokens from the caller's account to `to`.
                       *
                       * Returns a boolean value indicating whether the operation succeeded.
                       *
                       * Emits a {Transfer} event.
                       */
                      function transfer(address to, uint256 amount) external returns (bool);
                  
                      /**
                       * @dev Returns the remaining number of tokens that `spender` will be
                       * allowed to spend on behalf of `owner` through {transferFrom}. This is
                       * zero by default.
                       *
                       * This value changes when {approve} or {transferFrom} are called.
                       */
                      function allowance(address owner, address spender) external view returns (uint256);
                  
                      /**
                       * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                       *
                       * Returns a boolean value indicating whether the operation succeeded.
                       *
                       * IMPORTANT: Beware that changing an allowance with this method brings the risk
                       * that someone may use both the old and the new allowance by unfortunate
                       * transaction ordering. One possible solution to mitigate this race
                       * condition is to first reduce the spender's allowance to 0 and set the
                       * desired value afterwards:
                       * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                       *
                       * Emits an {Approval} event.
                       */
                      function approve(address spender, uint256 amount) external returns (bool);
                  
                      /**
                       * @dev Moves `amount` tokens from `from` to `to` using the
                       * allowance mechanism. `amount` is then deducted from the caller's
                       * allowance.
                       *
                       * Returns a boolean value indicating whether the operation succeeded.
                       *
                       * Emits a {Transfer} event.
                       */
                      function transferFrom(
                          address from,
                          address to,
                          uint256 amount
                      ) external returns (bool);
                  
                      /**
                       * @dev Emitted when `value` tokens are moved from one account (`from`) to
                       * another (`to`).
                       *
                       * Note that `value` may be zero.
                       */
                      event Transfer(address indexed from, address indexed to, uint256 value);
                  
                      /**
                       * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                       * a call to {approve}. `value` is the new allowance.
                       */
                      event Approval(address indexed owner, address indexed spender, uint256 value);
                  }
                  
                  // File: @openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
                  
                  
                  // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)
                  
                  pragma solidity ^0.8.0;
                  
                  
                  
                  /**
                   * @title SafeERC20
                   * @dev Wrappers around ERC20 operations that throw on failure (when the token
                   * contract returns false). Tokens that return no value (and instead revert or
                   * throw on failure) are also supported, non-reverting calls are assumed to be
                   * successful.
                   * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
                   * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
                   */
                  library SafeERC20 {
                      using Address for address;
                  
                      function safeTransfer(
                          IERC20 token,
                          address to,
                          uint256 value
                      ) internal {
                          _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                      }
                  
                      function safeTransferFrom(
                          IERC20 token,
                          address from,
                          address to,
                          uint256 value
                      ) internal {
                          _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
                      }
                  
                      /**
                       * @dev Deprecated. This function has issues similar to the ones found in
                       * {IERC20-approve}, and its usage is discouraged.
                       *
                       * Whenever possible, use {safeIncreaseAllowance} and
                       * {safeDecreaseAllowance} instead.
                       */
                      function safeApprove(
                          IERC20 token,
                          address spender,
                          uint256 value
                      ) internal {
                          // safeApprove should only be called when setting an initial allowance,
                          // or when resetting it to zero. To increase and decrease it, use
                          // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                          require(
                              (value == 0) || (token.allowance(address(this), spender) == 0),
                              "SafeERC20: approve from non-zero to non-zero allowance"
                          );
                          _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                      }
                  
                      function safeIncreaseAllowance(
                          IERC20 token,
                          address spender,
                          uint256 value
                      ) internal {
                          uint256 newAllowance = token.allowance(address(this), spender) + value;
                          _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                      }
                  
                      function safeDecreaseAllowance(
                          IERC20 token,
                          address spender,
                          uint256 value
                      ) internal {
                          unchecked {
                              uint256 oldAllowance = token.allowance(address(this), spender);
                              require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                              uint256 newAllowance = oldAllowance - value;
                              _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                          }
                      }
                  
                      /**
                       * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                       * on the return value: the return value is optional (but if data is returned, it must not be false).
                       * @param token The token targeted by the call.
                       * @param data The call data (encoded using abi.encode or one of its variants).
                       */
                      function _callOptionalReturn(IERC20 token, bytes memory data) private {
                          // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                          // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
                          // the target address contains contract code and also asserts for success in the low-level call.
                  
                          bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                          if (returndata.length > 0) {
                              // Return data is optional
                              require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                          }
                      }
                  }
                  
                  // File: CHNStaking.sol
                  
                  
                  
                  pragma solidity ^0.8.3;
                  
                  
                  
                  
                  
                  contract CHNStaking is OwnableUpgradeable {
                      using SafeMath for uint256;
                      using SafeERC20 for IERC20;
                      // Info of each user.
                      struct UserInfo {
                          uint256 amount;
                          uint256 rewardDebt;
                          uint256 pendingTokenReward;
                      }
                      // Info of each pool.
                      struct PoolInfo {
                          IERC20 stakeToken;
                          uint256 allocPoint;
                          uint256 lastRewardBlock;
                          uint256 accCHNPerShare;
                          uint256 totalAmountStake;
                      }
                  
                      event Add(address indexed stakToken, uint256 indexed allocPoint);
                      event Set(uint256 indexed pid, uint256 indexed allocPoint);
                      event Stake(address indexed user, uint256 indexed pid, uint256 amount);
                      event Withdraw(address indexed user, uint256 indexed pid, uint256 amount, uint256 reward);
                      event EmergencyWithdraw(
                          address indexed user,
                          uint256 indexed pid,
                          uint256 amount
                      );
                      event ClaimRewardFromVault(address indexed userAddress, uint256 indexed pid);
                      
                      /// @notice An event thats emitted when a delegate account's vote balance changes
                      event DelegateVotesChanged(address indexed delegate, uint previousBalance, uint newBalance);
                      
                      IERC20 public rewardToken;
                      uint256 public rewardPerBlock;
                      PoolInfo[] public poolInfo;
                      mapping(uint256 => mapping(address => UserInfo)) public userInfo;
                      mapping(address => bool) public poolTokens;
                      uint256 public totalAllocPoint = 0;
                      uint256 public startBlock;
                      uint256 public bonusEndBlock;
                      uint256 public BONUS_MULTIPLIER;
                      address public rewardVault;
                  
                      /// @notice A checkpoint for marking number of votes from a given block
                      struct Checkpoint {
                          uint32 fromBlock;
                          uint256 votes;
                      }
                  
                      /// @notice A record of votes checkpoints for each account, by index
                      mapping (uint256 => mapping (address => mapping (uint32 => Checkpoint))) public checkpoints;
                  
                      /// @notice The number of checkpoints for each account
                      mapping (uint256 => mapping (address => uint32)) public numCheckpoints;
                  
                      modifier validatePoolByPid(uint256 _pid) {
                          require(_pid < poolInfo.length, "Pool does not exist");
                          _;
                      }
                  
                      function initialize(
                          IERC20 _rewardToken,
                          uint256 _rewardPerBlock,
                          uint256 _startBlock,
                          uint256 _bonusEndBlock,
                          uint256 _multiplier,
                          address _rewardVault
                      ) public initializer {
                          require(_rewardVault != address(0) && address(_rewardToken) != address(0), "Zero address validation");
                          require(_startBlock < _bonusEndBlock, "Start block lower than bonus end block");
                          require(_rewardPerBlock < _rewardToken.totalSupply(), "Reward per block bigger than reward token total supply");
                          require(BONUS_MULTIPLIER < 100, "Bonus multipler bigger than 100x reward bonus");
                          __Ownable_init();
                          rewardToken = _rewardToken;
                          rewardPerBlock = _rewardPerBlock;
                          startBlock = _startBlock;
                          bonusEndBlock = _bonusEndBlock;
                          BONUS_MULTIPLIER = _multiplier;
                          rewardVault = _rewardVault;
                      }
                  
                      function poolLength() external view returns (uint256) {
                          return poolInfo.length;
                      }
                  
                      function getStakingAmount(uint256 pid, address user) public view returns (uint256) {
                          UserInfo memory info = userInfo[pid][user];
                          return info.amount;
                      }
                  
                      // Add a new stake to the pool. Can only be called by the Timelock and DAO.
                      // XXX DO NOT add the same stake token more than once. Rewards will be messed up if you do.
                      // This function can be only called by Timelock and DAO with voting power
                      function add(
                          uint256 _allocPoint,
                          IERC20 _stakeToken
                      ) public onlyOwner {
                          require(!poolTokens[address(_stakeToken)], "Stake token already exist");
                          massUpdatePools();
                          uint256 lastRewardBlock =
                              block.number > startBlock ? block.number : startBlock;
                          totalAllocPoint = totalAllocPoint.add(_allocPoint);
                          poolTokens[address(_stakeToken)] = true;
                          poolInfo.push(
                              PoolInfo({
                                  stakeToken: _stakeToken,
                                  allocPoint: _allocPoint,
                                  lastRewardBlock: lastRewardBlock,
                                  accCHNPerShare: 0,
                                  totalAmountStake: 0
                              })
                          );
                          emit Add(address(_stakeToken), _allocPoint);
                      }
                  
                      // Update the given pool's XCN allocation point. Can only be called by the Timelock and DAO.
                      // This function can be only called by Timelock and DAO with voting power
                      function set(
                          uint256 _pid,
                          uint256 _allocPoint
                      ) public onlyOwner validatePoolByPid(_pid) {
                          massUpdatePools();
                          totalAllocPoint = totalAllocPoint.sub(poolInfo[_pid].allocPoint).add(
                              _allocPoint
                          );
                          poolInfo[_pid].allocPoint = _allocPoint;
                          emit Set(_pid, _allocPoint);
                      }
                  
                      // Update reward per block by the Timelock and DAO
                      function setRewardPerblock(uint256 speed)
                          public
                          onlyOwner {
                              rewardPerBlock = speed;
                      }
                  
                      // Return reward multiplier over the given _from to _to block.
                      function getMultiplier(uint256 _from, uint256 _to)
                          public
                          view
                          returns (uint256)
                      {
                          require(_from >= startBlock, "from block number bigger than start block");
                          if (_to <= bonusEndBlock) {
                              return _to.sub(_from).mul(BONUS_MULTIPLIER);
                          } else if (_from >= bonusEndBlock) {
                              return _to.sub(_from);
                          } else {
                              return
                                  bonusEndBlock.sub(_from).mul(BONUS_MULTIPLIER).add(
                                      _to.sub(bonusEndBlock)
                                  );
                          }
                      }
                  
                      function pendingReward(uint256 _pid, address _user)
                          external
                          view
                          validatePoolByPid(_pid)
                          returns (uint256)
                      {
                          PoolInfo storage pool = poolInfo[_pid];
                          UserInfo storage user = userInfo[_pid][_user];
                          uint256 accCHNPerShare = pool.accCHNPerShare;
                          uint256 supply = pool.totalAmountStake;
                          if (block.number > pool.lastRewardBlock && supply != 0) {
                              uint256 multiplier =
                                  getMultiplier(pool.lastRewardBlock, block.number);
                              uint256 reward =
                                  multiplier.mul(rewardPerBlock).mul(pool.allocPoint).div(
                                      totalAllocPoint
                                  );
                              accCHNPerShare = accCHNPerShare.add(
                                  reward.mul(1e12).div(supply)
                              );
                          }
                          return user.amount.mul(accCHNPerShare).div(1e12).add(user.pendingTokenReward).sub(user.rewardDebt);
                      }
                  
                      // Update reward vairables for all pools. Be careful of gas spending!
                      function massUpdatePools() public {
                          uint256 length = poolInfo.length;
                          for (uint256 pid = 0; pid < length; ++pid) {
                              updatePool(pid);
                          }
                      }
                  
                      // Update reward variables of the given pool to be up-to-date.
                      function updatePool(uint256 _pid) public validatePoolByPid(_pid) {
                          PoolInfo storage pool = poolInfo[_pid];
                          if (block.number <= pool.lastRewardBlock) {
                              return;
                          }
                          uint256 supply = pool.totalAmountStake;
                          if (supply == 0) {
                              pool.lastRewardBlock = block.number;
                              return;
                          }
                          uint256 multiplier = getMultiplier(pool.lastRewardBlock, block.number);
                          uint256 reward =
                              multiplier.mul(rewardPerBlock).mul(pool.allocPoint).div(
                                  totalAllocPoint
                              );
                          pool.accCHNPerShare = pool.accCHNPerShare.add(
                              reward.mul(1e12).div(supply)
                          );
                          pool.lastRewardBlock = block.number;
                      }
                  
                      function _moveDelegates(uint256 _pid, address dstRep, uint256 amount, bool stake) internal {
                          if (amount > 0) {
                              if (dstRep != address(0)) {
                                  uint32 dstRepNum = numCheckpoints[_pid][dstRep];
                                  uint256 dstRepOld = dstRepNum > 0 ? checkpoints[_pid][dstRep][dstRepNum - 1].votes : 0;
                                  if (stake) {
                                      uint256 dstRepNew = dstRepOld.add(amount);
                                      _writeCheckpoint(_pid, dstRep, dstRepNum, dstRepOld, dstRepNew);
                                  } else {
                                      uint256 dstRepNew = dstRepOld.sub(amount);
                                      _writeCheckpoint(_pid, dstRep, dstRepNum, dstRepOld, dstRepNew);
                                  }
                              }
                          }
                      }
                  
                      // Only support non-deflationary tokens staking
                      function stake(uint256 _pid, uint256 _amount) public validatePoolByPid(_pid) {
                          PoolInfo storage pool = poolInfo[_pid];
                          UserInfo storage user = userInfo[_pid][msg.sender];
                          updatePool(_pid);
                          if (user.amount > 0) {
                              uint256 pending =
                                  user.amount.mul(pool.accCHNPerShare).div(1e12).sub(
                                      user.rewardDebt
                                  );
                              user.pendingTokenReward = user.pendingTokenReward.add(pending);
                          }
                          pool.totalAmountStake = pool.totalAmountStake.add(_amount);
                          pool.stakeToken.safeTransferFrom(
                              address(msg.sender),
                              address(this),
                              _amount
                          );
                          user.amount = user.amount.add(_amount);
                          user.rewardDebt = user.amount.mul(pool.accCHNPerShare).div(1e12);
                  
                          _moveDelegates(_pid, msg.sender, _amount, true);
                          emit Stake(msg.sender, _pid, _amount);
                      }
                  
                      function withdraw(uint256 _pid, uint256 _amount) public validatePoolByPid(_pid) {
                          PoolInfo storage pool = poolInfo[_pid];
                          UserInfo storage user = userInfo[_pid][msg.sender];
                          require(user.amount >= _amount, "withdraw: not good");
                          updatePool(_pid);
                          uint256 pending =
                              user.amount.mul(pool.accCHNPerShare).div(1e12).sub(
                                  user.rewardDebt
                              );
                          // pending = pending.add(user.pendingTokenReward);
                          // pool.stakeToken.safeTransfer(address(msg.sender), pending);
                          user.pendingTokenReward = user.pendingTokenReward + pending;
                          user.amount = user.amount.sub(_amount);
                          pool.totalAmountStake = pool.totalAmountStake.sub(_amount);
                          user.rewardDebt = user.amount.mul(pool.accCHNPerShare).div(1e12);
                          pool.stakeToken.safeTransfer(address(msg.sender), _amount);
                  
                          // Remove delegates from staking user
                          _moveDelegates(_pid, msg.sender, _amount, false);
                          emit Withdraw(msg.sender, _pid, _amount, 0);
                      }
                  
                      // Withdraw without caring about rewards. EMERGENCY ONLY.
                      function emergencyWithdraw(uint256 _pid) public validatePoolByPid(_pid) {
                          PoolInfo storage pool = poolInfo[_pid];
                          UserInfo storage user = userInfo[_pid][msg.sender];
                          uint256 userAmount = user.amount;
                          user.amount = 0;
                          user.rewardDebt = 0;
                          user.pendingTokenReward = 0;
                          pool.totalAmountStake = pool.totalAmountStake.sub(userAmount);
                          pool.stakeToken.safeTransfer(address(msg.sender), userAmount);
                          // Remove delegates from staking user
                          _moveDelegates(_pid, msg.sender, userAmount, false);
                          emit EmergencyWithdraw(msg.sender, _pid, userAmount);
                      }
                  
                      function claimRewardFromVault(address userAddress, uint256 pid) public validatePoolByPid(pid) returns (uint256) {
                          require(msg.sender == rewardVault, "Ownable: only reward vault");
                          PoolInfo storage pool = poolInfo[pid];
                          UserInfo storage user = userInfo[pid][userAddress];
                          updatePool(pid);
                          uint256 pending =
                              user.amount.mul(pool.accCHNPerShare).div(1e12).sub(
                                  user.rewardDebt
                              );
                          pending = pending + user.pendingTokenReward;
                          user.pendingTokenReward = 0;
                          user.rewardDebt = user.amount.mul(pool.accCHNPerShare).div(1e12);
                          emit ClaimRewardFromVault(userAddress, pid);
                          return pending;
                      }
                  
                      /**
                       * @notice Determine the prior number of votes for an account as of a block number
                       * @dev Block number must be a finalized block or else this function will revert to prevent misinformation.
                       * @param account The address of the account to check
                       * @param blockNumber The block number to get the vote balance at
                       * @return The number of votes the account had as of the given block
                       */
                      function getPriorVotes(uint256 _pid, address account, uint blockNumber) public view returns (uint256) {
                          require(blockNumber < block.number, "Comp::getPriorVotes: not yet determined");
                  
                          uint32 nCheckpoints = numCheckpoints[_pid][account];
                          if (nCheckpoints == 0) {
                              return 0;
                          }
                  
                          // First check most recent balance
                          if (checkpoints[_pid][account][nCheckpoints - 1].fromBlock <= blockNumber) {
                              return checkpoints[_pid][account][nCheckpoints - 1].votes;
                          }
                  
                          // Next check implicit zero balance
                          if (checkpoints[_pid][account][0].fromBlock > blockNumber) {
                              return 0;
                          }
                  
                          uint32 lower = 0;
                          uint32 upper = nCheckpoints - 1;
                          while (upper > lower) {
                              uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow
                              Checkpoint memory cp = checkpoints[_pid][account][center];
                              if (cp.fromBlock == blockNumber) {
                                  return cp.votes;
                              } else if (cp.fromBlock < blockNumber) {
                                  lower = center;
                              } else {
                                  upper = center - 1;
                              }
                          }
                          return checkpoints[_pid][account][lower].votes;
                      }
                  
                      function _writeCheckpoint(uint256 _pid, address delegatee, uint32 nCheckpoints, uint256 oldVotes, uint256 newVotes) internal {
                        uint32 blockNumber = safe32(block.number, "Comp::_writeCheckpoint: block number exceeds 32 bits");
                  
                        if (nCheckpoints > 0 && checkpoints[_pid][delegatee][nCheckpoints - 1].fromBlock == blockNumber) {
                            checkpoints[_pid][delegatee][nCheckpoints - 1].votes = newVotes;
                        } else {
                            checkpoints[_pid][delegatee][nCheckpoints] = Checkpoint(blockNumber, newVotes);
                            numCheckpoints[_pid][delegatee] = nCheckpoints + 1;
                        }
                  
                        emit DelegateVotesChanged(delegatee, oldVotes, newVotes);
                      }
                      
                      function safe32(uint n, string memory errorMessage) internal pure returns (uint32) {
                          require(n < 2**32, errorMessage);
                          return uint32(n);
                      }
                  }

                  File 3 of 5: CHNReward
                  // File: @openzeppelin/contracts/utils/Context.sol
                  
                  
                  // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
                  
                  pragma solidity ^0.8.0;
                  
                  /**
                   * @dev Provides information about the current execution context, including the
                   * sender of the transaction and its data. While these are generally available
                   * via msg.sender and msg.data, they should not be accessed in such a direct
                   * manner, since when dealing with meta-transactions the account sending and
                   * paying for execution may not be the actual sender (as far as an application
                   * is concerned).
                   *
                   * This contract is only required for intermediate, library-like contracts.
                   */
                  abstract contract Context {
                      function _msgSender() internal view virtual returns (address) {
                          return msg.sender;
                      }
                  
                      function _msgData() internal view virtual returns (bytes calldata) {
                          return msg.data;
                      }
                  }
                  
                  // File: @openzeppelin/contracts/access/Ownable.sol
                  
                  
                  // OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)
                  
                  pragma solidity ^0.8.0;
                  
                  
                  /**
                   * @dev Contract module which provides a basic access control mechanism, where
                   * there is an account (an owner) that can be granted exclusive access to
                   * specific functions.
                   *
                   * By default, the owner account will be the one that deploys the contract. This
                   * can later be changed with {transferOwnership}.
                   *
                   * This module is used through inheritance. It will make available the modifier
                   * `onlyOwner`, which can be applied to your functions to restrict their use to
                   * the owner.
                   */
                  abstract contract Ownable is Context {
                      address private _owner;
                  
                      event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                  
                      /**
                       * @dev Initializes the contract setting the deployer as the initial owner.
                       */
                      constructor() {
                          _transferOwnership(_msgSender());
                      }
                  
                      /**
                       * @dev Returns the address of the current owner.
                       */
                      function owner() public view virtual returns (address) {
                          return _owner;
                      }
                  
                      /**
                       * @dev Throws if called by any account other than the owner.
                       */
                      modifier onlyOwner() {
                          require(owner() == _msgSender(), "Ownable: caller is not the owner");
                          _;
                      }
                  
                      /**
                       * @dev Leaves the contract without owner. It will not be possible to call
                       * `onlyOwner` functions anymore. Can only be called by the current owner.
                       *
                       * NOTE: Renouncing ownership will leave the contract without an owner,
                       * thereby removing any functionality that is only available to the owner.
                       */
                      function renounceOwnership() public virtual onlyOwner {
                          _transferOwnership(address(0));
                      }
                  
                      /**
                       * @dev Transfers ownership of the contract to a new account (`newOwner`).
                       * Can only be called by the current owner.
                       */
                      function transferOwnership(address newOwner) public virtual onlyOwner {
                          require(newOwner != address(0), "Ownable: new owner is the zero address");
                          _transferOwnership(newOwner);
                      }
                  
                      /**
                       * @dev Transfers ownership of the contract to a new account (`newOwner`).
                       * Internal function without access restriction.
                       */
                      function _transferOwnership(address newOwner) internal virtual {
                          address oldOwner = _owner;
                          _owner = newOwner;
                          emit OwnershipTransferred(oldOwner, newOwner);
                      }
                  }
                  
                  // File: @openzeppelin/contracts/utils/Address.sol
                  
                  
                  // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)
                  
                  pragma solidity ^0.8.1;
                  
                  /**
                   * @dev Collection of functions related to the address type
                   */
                  library Address {
                      /**
                       * @dev Returns true if `account` is a contract.
                       *
                       * [IMPORTANT]
                       * ====
                       * It is unsafe to assume that an address for which this function returns
                       * false is an externally-owned account (EOA) and not a contract.
                       *
                       * Among others, `isContract` will return false for the following
                       * types of addresses:
                       *
                       *  - an externally-owned account
                       *  - a contract in construction
                       *  - an address where a contract will be created
                       *  - an address where a contract lived, but was destroyed
                       * ====
                       *
                       * [IMPORTANT]
                       * ====
                       * You shouldn't rely on `isContract` to protect against flash loan attacks!
                       *
                       * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                       * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                       * constructor.
                       * ====
                       */
                      function isContract(address account) internal view returns (bool) {
                          // This method relies on extcodesize/address.code.length, which returns 0
                          // for contracts in construction, since the code is only stored at the end
                          // of the constructor execution.
                  
                          return account.code.length > 0;
                      }
                  
                      /**
                       * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                       * `recipient`, forwarding all available gas and reverting on errors.
                       *
                       * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                       * of certain opcodes, possibly making contracts go over the 2300 gas limit
                       * imposed by `transfer`, making them unable to receive funds via
                       * `transfer`. {sendValue} removes this limitation.
                       *
                       * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                       *
                       * IMPORTANT: because control is transferred to `recipient`, care must be
                       * taken to not create reentrancy vulnerabilities. Consider using
                       * {ReentrancyGuard} or the
                       * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                       */
                      function sendValue(address payable recipient, uint256 amount) internal {
                          require(address(this).balance >= amount, "Address: insufficient balance");
                  
                          (bool success, ) = recipient.call{value: amount}("");
                          require(success, "Address: unable to send value, recipient may have reverted");
                      }
                  
                      /**
                       * @dev Performs a Solidity function call using a low level `call`. A
                       * plain `call` is an unsafe replacement for a function call: use this
                       * function instead.
                       *
                       * If `target` reverts with a revert reason, it is bubbled up by this
                       * function (like regular Solidity function calls).
                       *
                       * Returns the raw returned data. To convert to the expected return value,
                       * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                       *
                       * Requirements:
                       *
                       * - `target` must be a contract.
                       * - calling `target` with `data` must not revert.
                       *
                       * _Available since v3.1._
                       */
                      function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                          return functionCall(target, data, "Address: low-level call failed");
                      }
                  
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                       * `errorMessage` as a fallback revert reason when `target` reverts.
                       *
                       * _Available since v3.1._
                       */
                      function functionCall(
                          address target,
                          bytes memory data,
                          string memory errorMessage
                      ) internal returns (bytes memory) {
                          return functionCallWithValue(target, data, 0, errorMessage);
                      }
                  
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                       * but also transferring `value` wei to `target`.
                       *
                       * Requirements:
                       *
                       * - the calling contract must have an ETH balance of at least `value`.
                       * - the called Solidity function must be `payable`.
                       *
                       * _Available since v3.1._
                       */
                      function functionCallWithValue(
                          address target,
                          bytes memory data,
                          uint256 value
                      ) internal returns (bytes memory) {
                          return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                      }
                  
                      /**
                       * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                       * with `errorMessage` as a fallback revert reason when `target` reverts.
                       *
                       * _Available since v3.1._
                       */
                      function functionCallWithValue(
                          address target,
                          bytes memory data,
                          uint256 value,
                          string memory errorMessage
                      ) internal returns (bytes memory) {
                          require(address(this).balance >= value, "Address: insufficient balance for call");
                          require(isContract(target), "Address: call to non-contract");
                  
                          (bool success, bytes memory returndata) = target.call{value: value}(data);
                          return verifyCallResult(success, returndata, errorMessage);
                      }
                  
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                       * but performing a static call.
                       *
                       * _Available since v3.3._
                       */
                      function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                          return functionStaticCall(target, data, "Address: low-level static call failed");
                      }
                  
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                       * but performing a static call.
                       *
                       * _Available since v3.3._
                       */
                      function functionStaticCall(
                          address target,
                          bytes memory data,
                          string memory errorMessage
                      ) internal view returns (bytes memory) {
                          require(isContract(target), "Address: static call to non-contract");
                  
                          (bool success, bytes memory returndata) = target.staticcall(data);
                          return verifyCallResult(success, returndata, errorMessage);
                      }
                  
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                       * but performing a delegate call.
                       *
                       * _Available since v3.4._
                       */
                      function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                          return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                      }
                  
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                       * but performing a delegate call.
                       *
                       * _Available since v3.4._
                       */
                      function functionDelegateCall(
                          address target,
                          bytes memory data,
                          string memory errorMessage
                      ) internal returns (bytes memory) {
                          require(isContract(target), "Address: delegate call to non-contract");
                  
                          (bool success, bytes memory returndata) = target.delegatecall(data);
                          return verifyCallResult(success, returndata, errorMessage);
                      }
                  
                      /**
                       * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
                       * revert reason using the provided one.
                       *
                       * _Available since v4.3._
                       */
                      function verifyCallResult(
                          bool success,
                          bytes memory returndata,
                          string memory errorMessage
                      ) internal pure returns (bytes memory) {
                          if (success) {
                              return returndata;
                          } else {
                              // Look for revert reason and bubble it up if present
                              if (returndata.length > 0) {
                                  // The easiest way to bubble the revert reason is using memory via assembly
                  
                                  assembly {
                                      let returndata_size := mload(returndata)
                                      revert(add(32, returndata), returndata_size)
                                  }
                              } else {
                                  revert(errorMessage);
                              }
                          }
                      }
                  }
                  
                  // File: @openzeppelin/contracts/token/ERC20/IERC20.sol
                  
                  
                  // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)
                  
                  pragma solidity ^0.8.0;
                  
                  /**
                   * @dev Interface of the ERC20 standard as defined in the EIP.
                   */
                  interface IERC20 {
                      /**
                       * @dev Returns the amount of tokens in existence.
                       */
                      function totalSupply() external view returns (uint256);
                  
                      /**
                       * @dev Returns the amount of tokens owned by `account`.
                       */
                      function balanceOf(address account) external view returns (uint256);
                  
                      /**
                       * @dev Moves `amount` tokens from the caller's account to `to`.
                       *
                       * Returns a boolean value indicating whether the operation succeeded.
                       *
                       * Emits a {Transfer} event.
                       */
                      function transfer(address to, uint256 amount) external returns (bool);
                  
                      /**
                       * @dev Returns the remaining number of tokens that `spender` will be
                       * allowed to spend on behalf of `owner` through {transferFrom}. This is
                       * zero by default.
                       *
                       * This value changes when {approve} or {transferFrom} are called.
                       */
                      function allowance(address owner, address spender) external view returns (uint256);
                  
                      /**
                       * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                       *
                       * Returns a boolean value indicating whether the operation succeeded.
                       *
                       * IMPORTANT: Beware that changing an allowance with this method brings the risk
                       * that someone may use both the old and the new allowance by unfortunate
                       * transaction ordering. One possible solution to mitigate this race
                       * condition is to first reduce the spender's allowance to 0 and set the
                       * desired value afterwards:
                       * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                       *
                       * Emits an {Approval} event.
                       */
                      function approve(address spender, uint256 amount) external returns (bool);
                  
                      /**
                       * @dev Moves `amount` tokens from `from` to `to` using the
                       * allowance mechanism. `amount` is then deducted from the caller's
                       * allowance.
                       *
                       * Returns a boolean value indicating whether the operation succeeded.
                       *
                       * Emits a {Transfer} event.
                       */
                      function transferFrom(
                          address from,
                          address to,
                          uint256 amount
                      ) external returns (bool);
                  
                      /**
                       * @dev Emitted when `value` tokens are moved from one account (`from`) to
                       * another (`to`).
                       *
                       * Note that `value` may be zero.
                       */
                      event Transfer(address indexed from, address indexed to, uint256 value);
                  
                      /**
                       * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                       * a call to {approve}. `value` is the new allowance.
                       */
                      event Approval(address indexed owner, address indexed spender, uint256 value);
                  }
                  
                  // File: @openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
                  
                  
                  // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)
                  
                  pragma solidity ^0.8.0;
                  
                  
                  
                  /**
                   * @title SafeERC20
                   * @dev Wrappers around ERC20 operations that throw on failure (when the token
                   * contract returns false). Tokens that return no value (and instead revert or
                   * throw on failure) are also supported, non-reverting calls are assumed to be
                   * successful.
                   * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
                   * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
                   */
                  library SafeERC20 {
                      using Address for address;
                  
                      function safeTransfer(
                          IERC20 token,
                          address to,
                          uint256 value
                      ) internal {
                          _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                      }
                  
                      function safeTransferFrom(
                          IERC20 token,
                          address from,
                          address to,
                          uint256 value
                      ) internal {
                          _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
                      }
                  
                      /**
                       * @dev Deprecated. This function has issues similar to the ones found in
                       * {IERC20-approve}, and its usage is discouraged.
                       *
                       * Whenever possible, use {safeIncreaseAllowance} and
                       * {safeDecreaseAllowance} instead.
                       */
                      function safeApprove(
                          IERC20 token,
                          address spender,
                          uint256 value
                      ) internal {
                          // safeApprove should only be called when setting an initial allowance,
                          // or when resetting it to zero. To increase and decrease it, use
                          // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                          require(
                              (value == 0) || (token.allowance(address(this), spender) == 0),
                              "SafeERC20: approve from non-zero to non-zero allowance"
                          );
                          _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                      }
                  
                      function safeIncreaseAllowance(
                          IERC20 token,
                          address spender,
                          uint256 value
                      ) internal {
                          uint256 newAllowance = token.allowance(address(this), spender) + value;
                          _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                      }
                  
                      function safeDecreaseAllowance(
                          IERC20 token,
                          address spender,
                          uint256 value
                      ) internal {
                          unchecked {
                              uint256 oldAllowance = token.allowance(address(this), spender);
                              require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                              uint256 newAllowance = oldAllowance - value;
                              _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                          }
                      }
                  
                      /**
                       * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                       * on the return value: the return value is optional (but if data is returned, it must not be false).
                       * @param token The token targeted by the call.
                       * @param data The call data (encoded using abi.encode or one of its variants).
                       */
                      function _callOptionalReturn(IERC20 token, bytes memory data) private {
                          // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                          // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
                          // the target address contains contract code and also asserts for success in the low-level call.
                  
                          bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                          if (returndata.length > 0) {
                              // Return data is optional
                              require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                          }
                      }
                  }
                  
                  // File: CHNReward.sol
                  
                  
                  
                  pragma solidity ^0.8.3;
                  
                  
                  
                  
                  interface CHNStakingInterface {
                      function claimRewardFromVault(address userAddress, uint256 pid) external returns (uint256);
                  }
                  
                  contract CHNReward is Ownable {
                      using SafeERC20 for IERC20;
                  
                      event Claim(address indexed user, uint256 indexed pid, uint256 reward);
                      event GrantDAO(address indexed user, uint256 amount);
                      event ChangeStaking(address indexed stake);
                  
                      IERC20 public rewardToken;
                      CHNStakingInterface public staking;
                  
                      constructor(IERC20 _rewardToken) Ownable() {
                          rewardToken = _rewardToken;
                      }
                  
                      function changeStakingAdderss(address _staking) public onlyOwner {
                          staking = CHNStakingInterface(_staking);
                          emit ChangeStaking(_staking);
                      }
                  
                      function claimReward(uint256 pid) public {
                          uint256 rewardAmount = staking.claimRewardFromVault(msg.sender, pid);
                          rewardToken.safeTransfer(address(msg.sender), rewardAmount);
                          emit Claim(msg.sender, pid, rewardAmount);
                      }
                  
                      function grantDAO(address user, uint256 amount) public onlyOwner {
                          rewardToken.safeTransfer(user, amount);
                          emit GrantDAO(user, amount);
                      }
                  }

                  File 4 of 5: Chain
                  pragma solidity 0.5.16;
                  
                  /**
                   * @title Roles
                   * @dev Library for managing addresses assigned to a Role.
                   */
                  library Roles {
                      struct Role {
                          mapping (address => bool) bearer;
                      }
                  
                      /**
                       * @dev give an account access to this role
                       */
                      function add(Role storage role, address account) internal {
                          require(account != address(0));
                          require(!has(role, account));
                  
                          role.bearer[account] = true;
                      }
                  
                      /**
                       * @dev remove an account's access to this role
                       */
                      function remove(Role storage role, address account) internal {
                          require(account != address(0));
                          require(has(role, account));
                  
                          role.bearer[account] = false;
                      }
                  
                      /**
                       * @dev check if an account has this role
                       * @return bool
                       */
                      function has(Role storage role, address account) internal view returns (bool) {
                          require(account != address(0));
                          return role.bearer[account];
                      }
                  }
                  
                  contract MinterRole {
                      using Roles for Roles.Role;
                  
                      event MinterAdded(address indexed account);
                      event MinterRemoved(address indexed account);
                  
                      Roles.Role private _minters;
                  
                      constructor () internal {
                          _addMinter(msg.sender);
                      }
                  
                      modifier onlyMinter() {
                          require(isMinter(msg.sender));
                          _;
                      }
                  
                      function isMinter(address account) public view returns (bool) {
                          return _minters.has(account);
                      }
                  
                      function addMinter(address account) public onlyMinter {
                          _addMinter(account);
                      }
                  
                      function renounceMinter() public {
                          _removeMinter(msg.sender);
                      }
                  
                      function _addMinter(address account) internal {
                          _minters.add(account);
                          emit MinterAdded(account);
                      }
                  
                      function _removeMinter(address account) internal {
                          _minters.remove(account);
                          emit MinterRemoved(account);
                      }
                  }
                  
                  /**
                   * @title SafeMath
                   * @dev Unsigned math operations with safety checks that revert on error
                   */
                  library SafeMath {
                      /**
                       * @dev Multiplies two unsigned integers, reverts on overflow.
                       */
                      function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                          // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                          // benefit is lost if 'b' is also tested.
                          // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
                          if (a == 0) {
                              return 0;
                          }
                  
                          uint256 c = a * b;
                          require(c / a == b);
                  
                          return c;
                      }
                  
                      /**
                       * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero.
                       */
                      function div(uint256 a, uint256 b) internal pure returns (uint256) {
                          // Solidity only automatically asserts when dividing by 0
                          require(b > 0);
                          uint256 c = a / b;
                          // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                  
                          return c;
                      }
                  
                      /**
                       * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend).
                       */
                      function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                          require(b <= a);
                          uint256 c = a - b;
                  
                          return c;
                      }
                  
                      /**
                       * @dev Adds two unsigned integers, reverts on overflow.
                       */
                      function add(uint256 a, uint256 b) internal pure returns (uint256) {
                          uint256 c = a + b;
                          require(c >= a);
                  
                          return c;
                      }
                  
                      /**
                       * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo),
                       * reverts when dividing by zero.
                       */
                      function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                          require(b != 0);
                          return a % b;
                      }
                  }
                  
                  /**
                   * @title SafeMath96
                   * @dev Unsigned math operations with safety checks that revert on error with 96 bit unsiged integer
                   */
                  library SafeMath96 {
                      function safe32(uint n, string memory errorMessage) internal pure returns (uint32) {
                          require(n < 2**32, errorMessage);
                          return uint32(n);
                      }
                  
                      function safe96(uint n, string memory errorMessage) internal pure returns (uint96) {
                          require(n < 2**96, errorMessage);
                          return uint96(n);
                      }
                  
                      function add96(uint96 a, uint96 b, string memory errorMessage) internal pure returns (uint96) {
                          uint96 c = a + b;
                          require(c >= a, errorMessage);
                          return c;
                      }
                  
                      function sub96(uint96 a, uint96 b, string memory errorMessage) internal pure returns (uint96) {
                          require(b <= a, errorMessage);
                          return a - b;
                      }
                  }
                  /**
                   * @title ERC20 interface
                   * @dev see https://eips.ethereum.org/EIPS/eip-20
                   */
                  interface IERC20 {
                      function transfer(address to, uint256 value) external returns (bool);
                  
                      function approve(address spender, uint256 value) external returns (bool);
                  
                      function transferFrom(address from, address to, uint256 value) external returns (bool);
                  
                      function totalSupply() external view returns (uint256);
                  
                      function balanceOf(address who) external view returns (uint256);
                  
                      function allowance(address owner, address spender) external view returns (uint256);
                  
                      event Transfer(address indexed from, address indexed to, uint256 value);
                  
                      event Approval(address indexed owner, address indexed spender, uint256 value);
                  }
                  
                  /**
                   * @title CHN interface
                   * @dev see https://github.com/chain/chain-token/blob/main/ChainToken.sol
                   */
                  interface CHNInterface {
                      function transfer(address to, uint256 value) external returns (bool);
                  
                      function approve(address spender, uint256 value) external returns (bool);
                  
                      function transferFrom(address from, address to, uint256 value) external returns (bool);
                  
                      function totalSupply() external view returns (uint256);
                  
                      function balanceOf(address who) external view returns (uint256);
                  
                      function allowance(address owner, address spender) external view returns (uint256);
                  
                      function burn(uint256 _value) external;
                  
                      event Transfer(address indexed from, address indexed to, uint256 value);
                  
                      event Approval(address indexed owner, address indexed spender, uint256 value);
                  
                      event Burn(address indexed burner, uint256 value);
                  }
                  
                  /**
                   * @title Standard ERC20 token
                   *
                   * @dev Implementation of the basic standard token.
                   * https://eips.ethereum.org/EIPS/eip-20
                   * Originally based on code by FirstBlood:
                   * https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
                   *
                   * This implementation emits additional Approval events, allowing applications to reconstruct the allowance status for
                   * all accounts just by listening to said events. Note that this isn't required by the specification, and other
                   * compliant implementations may not do it.
                   */
                  contract ERC20 is IERC20 {
                      using SafeMath for uint256;
                  
                      mapping (address => uint256) private _balances;
                  
                      mapping (address => mapping (address => uint256)) private _allowed;
                  
                      uint256 private _totalSupply;
                  
                      /**
                       * @dev Total number of tokens in existence
                       */
                      function totalSupply() public view returns (uint256) {
                          return _totalSupply;
                      }
                  
                      /**
                       * @dev Gets the balance of the specified address.
                       * @param owner The address to query the balance of.
                       * @return An uint256 representing the amount owned by the passed address.
                       */
                      function balanceOf(address owner) public view returns (uint256) {
                          return _balances[owner];
                      }
                  
                      /**
                       * @dev Function to check the amount of tokens that 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 uint256 specifying the amount of tokens still available for the spender.
                       */
                      function allowance(address owner, address spender) public view returns (uint256) {
                          return _allowed[owner][spender];
                      }
                  
                      /**
                       * @dev Transfer token to a specified address
                       * @param to The address to transfer to.
                       * @param value The amount to be transferred.
                       */
                      function transfer(address to, uint256 value) public returns (bool) {
                          _transfer(msg.sender, to, value);
                          return true;
                      }
                  
                      /**
                       * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
                       * Beware that changing an allowance with this method brings the risk that someone may use both the old
                       * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
                       * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
                       * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                       * @param spender The address which will spend the funds.
                       * @param value The amount of tokens to be spent.
                       */
                      function approve(address spender, uint256 value) public returns (bool) {
                          _approve(msg.sender, spender, value);
                          return true;
                      }
                  
                      /**
                       * @dev Transfer tokens from one address to another.
                       * Note that while this function emits an Approval event, this is not required as per the specification,
                       * and other compliant implementations may not emit the event.
                       * @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 uint256 the amount of tokens to be transferred
                       */
                      function transferFrom(address from, address to, uint256 value) public returns (bool) {
                          _transfer(from, to, value);
                          _approve(from, msg.sender, _allowed[from][msg.sender].sub(value));
                          return true;
                      }
                  
                      /**
                       * @dev Increase the amount of tokens that an owner allowed to a spender.
                       * approve should be called when _allowed[msg.sender][spender] == 0. To increment
                       * allowed value is better to use this function to avoid 2 calls (and wait until
                       * the first transaction is mined)
                       * From MonolithDAO Token.sol
                       * Emits an Approval event.
                       * @param spender The address which will spend the funds.
                       * @param addedValue The amount of tokens to increase the allowance by.
                       */
                      function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
                          _approve(msg.sender, spender, _allowed[msg.sender][spender].add(addedValue));
                          return true;
                      }
                  
                      /**
                       * @dev Decrease the amount of tokens that an owner allowed to a spender.
                       * approve should be called when _allowed[msg.sender][spender] == 0. To decrement
                       * allowed value is better to use this function to avoid 2 calls (and wait until
                       * the first transaction is mined)
                       * From MonolithDAO Token.sol
                       * Emits an Approval event.
                       * @param spender The address which will spend the funds.
                       * @param subtractedValue The amount of tokens to decrease the allowance by.
                       */
                      function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
                          _approve(msg.sender, spender, _allowed[msg.sender][spender].sub(subtractedValue));
                          return true;
                      }
                  
                      /**
                       * @dev Transfer token for a specified addresses
                       * @param from The address to transfer from.
                       * @param to The address to transfer to.
                       * @param value The amount to be transferred.
                       */
                      function _transfer(address from, address to, uint256 value) internal {
                          require(to != address(0));
                  
                          _balances[from] = _balances[from].sub(value);
                          _balances[to] = _balances[to].add(value);
                          emit Transfer(from, to, value);
                      }
                  
                      /**
                       * @dev Internal function that mints an amount of the token and assigns it to
                       * an account. This encapsulates the modification of balances such that the
                       * proper events are emitted.
                       * @param account The account that will receive the created tokens.
                       * @param value The amount that will be created.
                       */
                      function _mint(address account, uint256 value) internal {
                          require(account != address(0));
                  
                          _totalSupply = _totalSupply.add(value);
                          _balances[account] = _balances[account].add(value);
                          emit Transfer(address(0), account, value);
                      }
                  
                      /**
                       * @dev Internal function that burns an amount of the token of a given
                       * account.
                       * @param account The account whose tokens will be burnt.
                       * @param value The amount that will be burnt.
                       */
                      function _burn(address account, uint256 value) internal {
                          require(account != address(0));
                  
                          _totalSupply = _totalSupply.sub(value);
                          _balances[account] = _balances[account].sub(value);
                          emit Transfer(account, address(0), value);
                      }
                  
                      /**
                       * @dev Approve an address to spend another addresses' tokens.
                       * @param owner The address that owns the tokens.
                       * @param spender The address that will spend the tokens.
                       * @param value The number of tokens that can be spent.
                       */
                      function _approve(address owner, address spender, uint256 value) internal {
                          require(spender != address(0));
                          require(owner != address(0));
                  
                          _allowed[owner][spender] = value;
                          emit Approval(owner, spender, value);
                      }
                  
                      /**
                       * @dev Internal function that burns an amount of the token of a given
                       * account, deducting from the sender's allowance for said account. Uses the
                       * internal burn function.
                       * Emits an Approval event (reflecting the reduced allowance).
                       * @param account The account whose tokens will be burnt.
                       * @param value The amount that will be burnt.
                       */
                      function _burnFrom(address account, uint256 value) internal {
                          _burn(account, value);
                          _approve(account, msg.sender, _allowed[account][msg.sender].sub(value));
                      }
                  }
                  
                  /**
                   * @title ERC20Mintable
                   * @dev ERC20 minting logic
                   */
                  contract ERC20Mintable is ERC20, MinterRole {
                      address private MINT_BASE_TOKEN;
                      uint256 private MAX_SUPPLY_AMOUNT;
                  
                      constructor (address mintBaseToken, uint256 MAX_SUPPLY) public {
                          MINT_BASE_TOKEN = mintBaseToken;
                          MAX_SUPPLY_AMOUNT = MAX_SUPPLY;
                      }
                  
                      /**
                       * @dev Function to mint tokens
                       * @param to The address that will receive the minted tokens.
                       * @param value The amount of tokens to mint.
                       * @return A boolean that indicates if the operation was successful.
                       */
                      function mint(address to, uint256 value) public returns (bool) {
                          require(CHNInterface(MINT_BASE_TOKEN).balanceOf(msg.sender) >= value, "Mint Base Token Insufficient");
                          require(totalSupply().add(value.mul(1000)) < MAX_SUPPLY_AMOUNT, "Mint limited max supply");
                          IERC20(MINT_BASE_TOKEN).transferFrom(msg.sender, address(this), value);
                          CHNInterface(MINT_BASE_TOKEN).burn(value);
                          _mint(to, value.mul(1000));
                          return true;
                      }
                  }
                  
                  /**
                   * @title ERC20Detailed token
                   * @dev The decimals are only for visualization purposes.
                   * All the operations are done using the smallest and indivisible token unit,
                   * just as on Ethereum all the operations are done in wei.
                   */
                  contract ERC20Detailed is IERC20 {
                      string private _name;
                      string private _symbol;
                      uint8 private _decimals;
                  
                      constructor (string memory name, string memory symbol, uint8 decimals) public {
                          _name = name;
                          _symbol = symbol;
                          _decimals = decimals;
                      }
                  
                      /**
                       * @return the name of the token.
                       */
                      function name() public view returns (string memory) {
                          return _name;
                      }
                  
                      /**
                       * @return the symbol of the token.
                       */
                      function symbol() public view returns (string memory) {
                          return _symbol;
                      }
                  
                      /**
                       * @return the number of decimals of the token.
                       */
                      function decimals() public view returns (uint8) {
                          return _decimals;
                      }
                  }
                  
                  contract Chain is ERC20Mintable, ERC20Detailed {
                      using SafeMath96 for uint96;
                  
                      uint8 public constant DECIMALS = 18;
                      uint256 public constant INITIAL_SUPPLY = 21537311000 * (10 ** uint256(DECIMALS));
                      uint256 public constant MAX_SUPPLY = 68895442185 * (10 ** uint256(DECIMALS));
                      address public constant MINT_BASE = 0x41C37A4683d6a05adB31c39D71348A8403B13Ca9;
                  
                      /// @notice A record of each accounts delegate
                      mapping (address => address) public delegates;
                  
                      /// @notice A checkpoint for marking number of votes from a given block
                      struct Checkpoint {
                          uint32 fromBlock;
                          uint256 votes;
                      }
                  
                      /// @notice A record of votes checkpoints for each account, by index
                      mapping (address => mapping (uint32 => Checkpoint)) public checkpoints;
                  
                      /// @notice The number of checkpoints for each account
                      mapping (address => uint32) public numCheckpoints;
                  
                      /// @notice The EIP-712 typehash for the contract's domain
                      bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)");
                  
                      /// @notice The EIP-712 typehash for the delegation struct used by the contract
                      bytes32 public constant DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");
                  
                      /// @notice A record of states for signing / validating signatures
                      mapping (address => uint) public nonces;
                  
                      /// @notice An event thats emitted when an account changes its delegate
                      event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);
                  
                      /// @notice An event thats emitted when a delegate account's vote balance changes
                      event DelegateVotesChanged(address indexed delegate, uint previousBalance, uint newBalance);
                  
                      /**
                       * @dev Constructor that gives msg.sender all of existing tokens.
                       */
                      constructor () public ERC20Detailed("Chain", "XCN", DECIMALS) ERC20Mintable(MINT_BASE, MAX_SUPPLY) {
                          _mint(msg.sender, INITIAL_SUPPLY);
                      }
                  
                      function transfer(address to, uint256 value) public returns (bool) {
                          _transfer(msg.sender, to, value);
                          _moveDelegates(delegates[msg.sender], delegates[to], value);
                          return true;
                      }
                  
                  
                      function transferFrom(address from, address to, uint256 value) public returns (bool) {
                          _transfer(from, to, value);
                          _approve(from, msg.sender, allowance(from, msg.sender).sub(value));
                          _moveDelegates(delegates[msg.sender], delegates[to], value);
                          return true;
                      }
                  
                      /**
                       * @notice Delegate votes from `msg.sender` to `delegatee`
                       * @param delegatee The address to delegate votes to
                       */
                      function delegate(address delegatee) public {
                          return _delegate(msg.sender, delegatee);
                      }
                  
                      /**
                       * @notice Delegates votes from signatory to `delegatee`
                       * @param delegatee The address to delegate votes to
                       * @param nonce The contract state required to match the signature
                       * @param expiry The time at which to expire the signature
                       * @param v The recovery byte of the signature
                       * @param r Half of the ECDSA signature pair
                       * @param s Half of the ECDSA signature pair
                       */
                      function delegateBySig(address delegatee, uint nonce, uint expiry, uint8 v, bytes32 r, bytes32 s) public {
                          bytes32 domainSeparator = keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name())), getChainId(), address(this)));
                          bytes32 structHash = keccak256(abi.encode(DELEGATION_TYPEHASH, delegatee, nonce, expiry));
                          bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
                          address signatory = ecrecover(digest, v, r, s);
                          require(signatory != address(0), "Xcn::delegateBySig: invalid signature");
                          require(nonce == nonces[signatory]++, "Xcn::delegateBySig: invalid nonce");
                          require(now <= expiry, "Xcn::delegateBySig: signature expired");
                          return _delegate(signatory, delegatee);
                      }
                  
                      /**
                       * @notice Gets the current votes balance for `account`
                       * @param account The address to get votes balance
                       * @return The number of current votes for `account`
                       */
                      function getCurrentVotes(address account) external view returns (uint256) {
                          uint32 nCheckpoints = numCheckpoints[account];
                          return nCheckpoints > 0 ? checkpoints[account][nCheckpoints - 1].votes : 0;
                      }
                  
                      /**
                       * @notice Determine the prior number of votes for an account as of a block number
                       * @dev Block number must be a finalized block or else this function will revert to prevent misinformation.
                       * @param account The address of the account to check
                       * @param blockNumber The block number to get the vote balance at
                       * @return The number of votes the account had as of the given block
                       */
                      function getPriorVotes(address account, uint blockNumber) public view returns (uint256) {
                          require(blockNumber < block.number, "Xcn::getPriorVotes: not yet determined");
                  
                          uint32 nCheckpoints = numCheckpoints[account];
                          if (nCheckpoints == 0) {
                              return 0;
                          }
                  
                          // First check most recent balance
                          if (checkpoints[account][nCheckpoints - 1].fromBlock <= blockNumber) {
                              return checkpoints[account][nCheckpoints - 1].votes;
                          }
                  
                          // Next check implicit zero balance
                          if (checkpoints[account][0].fromBlock > blockNumber) {
                              return 0;
                          }
                  
                          uint32 lower = 0;
                          uint32 upper = nCheckpoints - 1;
                          while (upper > lower) {
                              uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow
                              Checkpoint memory cp = checkpoints[account][center];
                              if (cp.fromBlock == blockNumber) {
                                  return cp.votes;
                              } else if (cp.fromBlock < blockNumber) {
                                  lower = center;
                              } else {
                                  upper = center - 1;
                              }
                          }
                          return checkpoints[account][lower].votes;
                      }
                  
                      function _delegate(address delegator, address delegatee) internal {
                          address currentDelegate = delegates[delegator];
                          uint256 delegatorBalance = balanceOf(delegator);
                          delegates[delegator] = delegatee;
                  
                          emit DelegateChanged(delegator, currentDelegate, delegatee);
                  
                          _moveDelegates(currentDelegate, delegatee, delegatorBalance);
                      }
                  
                      function _moveDelegates(address srcRep, address dstRep, uint256 amount) internal {
                          if (srcRep != dstRep && amount > 0) {
                              if (srcRep != address(0)) {
                                  uint32 srcRepNum = numCheckpoints[srcRep];
                                  uint256 srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].votes : 0;
                                  uint256 srcRepNew = srcRepOld.sub(amount);
                                  _writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew);
                              }
                  
                              if (dstRep != address(0)) {
                                  uint32 dstRepNum = numCheckpoints[dstRep];
                                  uint256 dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].votes : 0;
                                  uint256 dstRepNew = dstRepOld.add(amount);
                                  _writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew);
                              }
                          }
                      }
                  
                      function _writeCheckpoint(address delegatee, uint32 nCheckpoints, uint256 oldVotes, uint256 newVotes) internal {
                        uint32 blockNumber = safe32(block.number, "Xcn::_writeCheckpoint: block number exceeds 32 bits");
                  
                        if (nCheckpoints > 0 && checkpoints[delegatee][nCheckpoints - 1].fromBlock == blockNumber) {
                            checkpoints[delegatee][nCheckpoints - 1].votes = newVotes;
                        } else {
                            checkpoints[delegatee][nCheckpoints] = Checkpoint(blockNumber, newVotes);
                            numCheckpoints[delegatee] = nCheckpoints + 1;
                        }
                  
                        emit DelegateVotesChanged(delegatee, oldVotes, newVotes);
                      }
                      
                      function safe32(uint n, string memory errorMessage) internal pure returns (uint32) {
                          require(n < 2**32, errorMessage);
                          return uint32(n);
                      }
                  
                      function getChainId() internal pure returns (uint) {
                          uint256 chainId;
                          assembly { chainId := chainid() }
                          return chainId;
                      }
                  }

                  File 5 of 5: AccountFactory
                  // SPDX-License-Identifier: Apache 2.0
                  pragma solidity ^0.8.0;
                  abstract contract ERC1271 {
                      // bytes4(keccak256("isValidSignature(bytes32,bytes)")
                      bytes4 internal constant MAGICVALUE = 0x1626ba7e;
                      /**
                       * @dev Should return whether the signature provided is valid for the provided hash
                       * @param _hash      Hash of the data to be signed
                       * @param _signature Signature byte array associated with _hash
                       *
                       * MUST return the bytes4 magic value 0x1626ba7e when function passes.
                       * MUST NOT modify state (using STATICCALL for solc < 0.5, view modifier for solc > 0.5)
                       * MUST allow external calls
                       */
                      function isValidSignature(bytes32 _hash, bytes memory _signature) public view virtual returns (bytes4 magicValue);
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
                  pragma solidity ^0.8.0;
                  import "./interface/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);
                   * }
                   * ```
                   *
                   * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
                   */
                  abstract contract ERC165 is IERC165 {
                      /**
                       * @dev See {IERC165-supportsInterface}.
                       */
                      function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                          return interfaceId == type(IERC165).interfaceId;
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
                  pragma solidity ^0.8.0;
                  /**
                   * @dev Interface of the ERC165 standard, as defined in the
                   * [EIP](https://eips.ethereum.org/EIPS/eip-165).
                   *
                   * 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
                       * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
                       * 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: Apache 2.0
                  pragma solidity ^0.8.0;
                  /// @author thirdweb
                  import "../lib/Address.sol";
                  import "./interface/IMulticall.sol";
                  /**
                   * @dev Provides a function to batch together multiple calls in a single external call.
                   *
                   * _Available since v4.1._
                   */
                  contract Multicall is IMulticall {
                      /**
                       *  @notice Receives and executes a batch of function calls on this contract.
                       *  @dev Receives and executes a batch of function calls on this contract.
                       *
                       *  @param data The bytes data that makes up the batch of function calls to execute.
                       *  @return results The bytes data that makes up the result of the batch of function calls executed.
                       */
                      function multicall(bytes[] calldata data) external returns (bytes[] memory results) {
                          results = new bytes[](data.length);
                          address sender = _msgSender();
                          bool isForwarder = msg.sender != sender;
                          for (uint256 i = 0; i < data.length; i++) {
                              if (isForwarder) {
                                  results[i] = Address.functionDelegateCall(address(this), abi.encodePacked(data[i], sender));
                              } else {
                                  results[i] = Address.functionDelegateCall(address(this), data[i]);
                              }
                          }
                          return results;
                      }
                      /// @notice Returns the sender in the given execution context.
                      function _msgSender() internal view virtual returns (address) {
                          return msg.sender;
                      }
                  }
                  // SPDX-License-Identifier: Apache-2.0
                  pragma solidity ^0.8.0;
                  /// @author thirdweb
                  interface IAccountPermissions {
                      /*///////////////////////////////////////////////////////////////
                                                  Types
                      //////////////////////////////////////////////////////////////*/
                      /**
                       *  @notice The payload that must be signed by an authorized wallet to set permissions for a signer to use the smart wallet.
                       *
                       *  @param signer The addres of the signer to give permissions.
                       *  @param approvedTargets The list of approved targets that a role holder can call using the smart wallet.
                       *  @param nativeTokenLimitPerTransaction The maximum value that can be transferred by a role holder in a single transaction.
                       *  @param permissionStartTimestamp The UNIX timestamp at and after which a signer has permission to use the smart wallet.
                       *  @param permissionEndTimestamp The UNIX timestamp at and after which a signer no longer has permission to use the smart wallet.
                       *  @param reqValidityStartTimestamp The UNIX timestamp at and after which a signature is valid.
                       *  @param reqValidityEndTimestamp The UNIX timestamp at and after which a signature is invalid/expired.
                       *  @param uid A unique non-repeatable ID for the payload.
                       *  @param isAdmin Whether the signer should be an admin.
                       */
                      struct SignerPermissionRequest {
                          address signer;
                          uint8 isAdmin;
                          address[] approvedTargets;
                          uint256 nativeTokenLimitPerTransaction;
                          uint128 permissionStartTimestamp;
                          uint128 permissionEndTimestamp;
                          uint128 reqValidityStartTimestamp;
                          uint128 reqValidityEndTimestamp;
                          bytes32 uid;
                      }
                      /**
                       *  @notice The permissions that a signer has to use the smart wallet.
                       *
                       *  @param signer The address of the signer.
                       *  @param approvedTargets The list of approved targets that a role holder can call using the smart wallet.
                       *  @param nativeTokenLimitPerTransaction The maximum value that can be transferred by a role holder in a single transaction.
                       *  @param startTimestamp The UNIX timestamp at and after which a signer has permission to use the smart wallet.
                       *  @param endTimestamp The UNIX timestamp at and after which a signer no longer has permission to use the smart wallet.
                       */
                      struct SignerPermissions {
                          address signer;
                          address[] approvedTargets;
                          uint256 nativeTokenLimitPerTransaction;
                          uint128 startTimestamp;
                          uint128 endTimestamp;
                      }
                      /**
                       *  @notice Internal struct for storing permissions for a signer (without approved targets).
                       *
                       *  @param nativeTokenLimitPerTransaction The maximum value that can be transferred by a role holder in a single transaction.
                       *  @param startTimestamp The UNIX timestamp at and after which a signer has permission to use the smart wallet.
                       *  @param endTimestamp The UNIX timestamp at and after which a signer no longer has permission to use the smart wallet.
                       */
                      struct SignerPermissionsStatic {
                          uint256 nativeTokenLimitPerTransaction;
                          uint128 startTimestamp;
                          uint128 endTimestamp;
                      }
                      /*///////////////////////////////////////////////////////////////
                                                  Events
                      //////////////////////////////////////////////////////////////*/
                      /// @notice Emitted when permissions for a signer are updated.
                      event SignerPermissionsUpdated(
                          address indexed authorizingSigner,
                          address indexed targetSigner,
                          SignerPermissionRequest permissions
                      );
                      /// @notice Emitted when an admin is set or removed.
                      event AdminUpdated(address indexed signer, bool isAdmin);
                      /*///////////////////////////////////////////////////////////////
                                              View functions
                      //////////////////////////////////////////////////////////////*/
                      /// @notice Returns whether the given account is an admin.
                      function isAdmin(address signer) external view returns (bool);
                      /// @notice Returns whether the given account is an active signer on the account.
                      function isActiveSigner(address signer) external view returns (bool);
                      /// @notice Returns the restrictions under which a signer can use the smart wallet.
                      function getPermissionsForSigner(address signer) external view returns (SignerPermissions memory permissions);
                      /// @notice Returns all active and inactive signers of the account.
                      function getAllSigners() external view returns (SignerPermissions[] memory signers);
                      /// @notice Returns all signers with active permissions to use the account.
                      function getAllActiveSigners() external view returns (SignerPermissions[] memory signers);
                      /// @notice Returns all admins of the account.
                      function getAllAdmins() external view returns (address[] memory admins);
                      /// @dev Verifies that a request is signed by an authorized account.
                      function verifySignerPermissionRequest(
                          SignerPermissionRequest calldata req,
                          bytes calldata signature
                      ) external view returns (bool success, address signer);
                      /*///////////////////////////////////////////////////////////////
                                              External functions
                      //////////////////////////////////////////////////////////////*/
                      /// @notice Sets the permissions for a given signer.
                      function setPermissionsForSigner(SignerPermissionRequest calldata req, bytes calldata signature) external;
                  }
                  // SPDX-License-Identifier: Apache-2.0
                  pragma solidity ^0.8.0;
                  /// @author thirdweb
                  /**
                   *  Thirdweb's `ContractMetadata` is a contract extension for any base contracts. It lets you set a metadata URI
                   *  for you contract.
                   *
                   *  Additionally, `ContractMetadata` is necessary for NFT contracts that want royalties to get distributed on OpenSea.
                   */
                  interface IContractMetadata {
                      /// @dev Returns the metadata URI of the contract.
                      function contractURI() external view returns (string memory);
                      /**
                       *  @dev Sets contract URI for the storefront-level metadata of the contract.
                       *       Only module admin can call this function.
                       */
                      function setContractURI(string calldata _uri) external;
                      /// @dev Emitted when the contract URI is updated.
                      event ContractURIUpdated(string prevURI, string newURI);
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity ^0.8.0;
                  /// @author thirdweb
                  /**
                   * @dev Provides a function to batch together multiple calls in a single external call.
                   *
                   * _Available since v4.1._
                   */
                  interface IMulticall {
                      /**
                       * @dev Receives and executes a batch of function calls on this contract.
                       */
                      function multicall(bytes[] calldata data) external returns (bytes[] memory results);
                  }
                  // SPDX-License-Identifier: Apache-2.0
                  pragma solidity ^0.8.0;
                  /// @author thirdweb
                  /**
                   * @dev External interface of AccessControl declared to support ERC165 detection.
                   */
                  interface IPermissions {
                      /**
                       * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
                       *
                       * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
                       * {RoleAdminChanged} not being emitted signaling this.
                       *
                       * _Available since v3.1._
                       */
                      event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
                      /**
                       * @dev Emitted when `account` is granted `role`.
                       *
                       * `sender` is the account that originated the contract call, an admin role
                       * bearer except when using {AccessControl-_setupRole}.
                       */
                      event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
                      /**
                       * @dev Emitted when `account` is revoked `role`.
                       *
                       * `sender` is the account that originated the contract call:
                       *   - if using `revokeRole`, it is the admin role bearer
                       *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
                       */
                      event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
                      /**
                       * @dev Returns `true` if `account` has been granted `role`.
                       */
                      function hasRole(bytes32 role, address account) external view returns (bool);
                      /**
                       * @dev Returns the admin role that controls `role`. See {grantRole} and
                       * {revokeRole}.
                       *
                       * To change a role's admin, use {AccessControl-_setRoleAdmin}.
                       */
                      function getRoleAdmin(bytes32 role) external view returns (bytes32);
                      /**
                       * @dev Grants `role` to `account`.
                       *
                       * If `account` had not been already granted `role`, emits a {RoleGranted}
                       * event.
                       *
                       * Requirements:
                       *
                       * - the caller must have ``role``'s admin role.
                       */
                      function grantRole(bytes32 role, address account) external;
                      /**
                       * @dev Revokes `role` from `account`.
                       *
                       * If `account` had been granted `role`, emits a {RoleRevoked} event.
                       *
                       * Requirements:
                       *
                       * - the caller must have ``role``'s admin role.
                       */
                      function revokeRole(bytes32 role, address account) external;
                      /**
                       * @dev Revokes `role` from the calling account.
                       *
                       * Roles are often managed via {grantRole} and {revokeRole}: this function's
                       * purpose is to provide a mechanism for accounts to lose their privileges
                       * if they are compromised (such as when a trusted device is misplaced).
                       *
                       * If the calling account had been granted `role`, emits a {RoleRevoked}
                       * event.
                       *
                       * Requirements:
                       *
                       * - the caller must be `account`.
                       */
                      function renounceRole(bytes32 role, address account) external;
                  }
                  // SPDX-License-Identifier: Apache-2.0
                  pragma solidity ^0.8.0;
                  /// @author thirdweb
                  import "./IPermissions.sol";
                  /**
                   * @dev External interface of AccessControlEnumerable declared to support ERC165 detection.
                   */
                  interface IPermissionsEnumerable is IPermissions {
                      /**
                       * @dev Returns one of the accounts that have `role`. `index` must be a
                       * value between 0 and {getRoleMemberCount}, non-inclusive.
                       *
                       * Role bearers are not sorted in any particular way, and their ordering may
                       * change at any point.
                       *
                       * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
                       * you perform all queries on the same block. See the following
                       * [forum post](https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296)
                       * for more information.
                       */
                      function getRoleMember(bytes32 role, uint256 index) external view returns (address);
                      /**
                       * @dev Returns the number of accounts that have `role`. Can be used
                       * together with {getRoleMember} to enumerate all bearers of a role.
                       */
                      function getRoleMemberCount(bytes32 role) external view returns (uint256);
                  }
                  // SPDX-License-Identifier: Apache-2.0
                  pragma solidity ^0.8.0;
                  /// @author thirdweb
                  import "../interface/IAccountPermissions.sol";
                  import "../../external-deps/openzeppelin/utils/cryptography/EIP712.sol";
                  import "../../external-deps/openzeppelin/utils/structs/EnumerableSet.sol";
                  library AccountPermissionsStorage {
                      /// @custom:storage-location erc7201:account.permissions.storage
                      /// @dev keccak256(abi.encode(uint256(keccak256("account.permissions.storage")) - 1)) & ~bytes32(uint256(0xff))
                      bytes32 public constant ACCOUNT_PERMISSIONS_STORAGE_POSITION =
                          0x3181e78fc1b109bc611fd2406150bf06e33faa75f71cba12c3e1fd670f2def00;
                      struct Data {
                          /// @dev The set of all admins of the wallet.
                          EnumerableSet.AddressSet allAdmins;
                          /// @dev The set of all signers with permission to use the account.
                          EnumerableSet.AddressSet allSigners;
                          /// @dev Map from address => whether the address is an admin.
                          mapping(address => bool) isAdmin;
                          /// @dev Map from signer address => active restrictions for that signer.
                          mapping(address => IAccountPermissions.SignerPermissionsStatic) signerPermissions;
                          /// @dev Map from signer address => approved target the signer can call using the account contract.
                          mapping(address => EnumerableSet.AddressSet) approvedTargets;
                          /// @dev Mapping from a signed request UID => whether the request is processed.
                          mapping(bytes32 => bool) executed;
                      }
                      function data() internal pure returns (Data storage data_) {
                          bytes32 position = ACCOUNT_PERMISSIONS_STORAGE_POSITION;
                          assembly {
                              data_.slot := position
                          }
                      }
                  }
                  abstract contract AccountPermissions is IAccountPermissions, EIP712 {
                      using ECDSA for bytes32;
                      using EnumerableSet for EnumerableSet.AddressSet;
                      bytes32 private constant TYPEHASH =
                          keccak256(
                              "SignerPermissionRequest(address signer,uint8 isAdmin,address[] approvedTargets,uint256 nativeTokenLimitPerTransaction,uint128 permissionStartTimestamp,uint128 permissionEndTimestamp,uint128 reqValidityStartTimestamp,uint128 reqValidityEndTimestamp,bytes32 uid)"
                          );
                      function _onlyAdmin() internal virtual {
                          require(isAdmin(msg.sender), "!admin");
                      }
                      /*///////////////////////////////////////////////////////////////
                                              External functions
                      //////////////////////////////////////////////////////////////*/
                      /// @notice Sets the permissions for a given signer.
                      function setPermissionsForSigner(SignerPermissionRequest calldata _req, bytes calldata _signature) external {
                          address targetSigner = _req.signer;
                          require(
                              _req.reqValidityStartTimestamp <= block.timestamp && block.timestamp < _req.reqValidityEndTimestamp,
                              "!period"
                          );
                          (bool success, address signer) = verifySignerPermissionRequest(_req, _signature);
                          require(success, "!sig");
                          _accountPermissionsStorage().executed[_req.uid] = true;
                          //isAdmin > 0, set admin or remove admin
                          if (_req.isAdmin > 0) {
                              //isAdmin = 1, set admin
                              //isAdmin > 1, remove admin
                              bool _isAdmin = _req.isAdmin == 1;
                              _setAdmin(targetSigner, _isAdmin);
                              return;
                          }
                          require(!isAdmin(targetSigner), "admin");
                          _accountPermissionsStorage().allSigners.add(targetSigner);
                          _accountPermissionsStorage().signerPermissions[targetSigner] = SignerPermissionsStatic(
                              _req.nativeTokenLimitPerTransaction,
                              _req.permissionStartTimestamp,
                              _req.permissionEndTimestamp
                          );
                          address[] memory currentTargets = _accountPermissionsStorage().approvedTargets[targetSigner].values();
                          uint256 len = currentTargets.length;
                          for (uint256 i = 0; i < len; i += 1) {
                              _accountPermissionsStorage().approvedTargets[targetSigner].remove(currentTargets[i]);
                          }
                          len = _req.approvedTargets.length;
                          for (uint256 i = 0; i < len; i += 1) {
                              _accountPermissionsStorage().approvedTargets[targetSigner].add(_req.approvedTargets[i]);
                          }
                          _afterSignerPermissionsUpdate(_req);
                          emit SignerPermissionsUpdated(signer, targetSigner, _req);
                      }
                      /*///////////////////////////////////////////////////////////////
                                              View functions
                      //////////////////////////////////////////////////////////////*/
                      /// @notice Returns whether the given account is an admin.
                      function isAdmin(address _account) public view virtual returns (bool) {
                          return _accountPermissionsStorage().isAdmin[_account];
                      }
                      /// @notice Returns whether the given account is an active signer on the account.
                      function isActiveSigner(address signer) public view returns (bool) {
                          SignerPermissionsStatic memory permissions = _accountPermissionsStorage().signerPermissions[signer];
                          return
                              permissions.startTimestamp <= block.timestamp &&
                              block.timestamp < permissions.endTimestamp &&
                              _accountPermissionsStorage().approvedTargets[signer].length() > 0;
                      }
                      /// @notice Returns the restrictions under which a signer can use the smart wallet.
                      function getPermissionsForSigner(address signer) external view returns (SignerPermissions memory) {
                          SignerPermissionsStatic memory permissions = _accountPermissionsStorage().signerPermissions[signer];
                          return
                              SignerPermissions(
                                  signer,
                                  _accountPermissionsStorage().approvedTargets[signer].values(),
                                  permissions.nativeTokenLimitPerTransaction,
                                  permissions.startTimestamp,
                                  permissions.endTimestamp
                              );
                      }
                      /// @dev Verifies that a request is signed by an authorized account.
                      function verifySignerPermissionRequest(
                          SignerPermissionRequest calldata req,
                          bytes calldata signature
                      ) public view virtual returns (bool success, address signer) {
                          signer = _recoverAddress(_encodeRequest(req), signature);
                          success = !_accountPermissionsStorage().executed[req.uid] && isAdmin(signer);
                      }
                      /// @notice Returns all active and inactive signers of the account.
                      function getAllSigners() external view returns (SignerPermissions[] memory signers) {
                          address[] memory allSigners = _accountPermissionsStorage().allSigners.values();
                          uint256 len = allSigners.length;
                          signers = new SignerPermissions[](len);
                          for (uint256 i = 0; i < len; i += 1) {
                              address signer = allSigners[i];
                              SignerPermissionsStatic memory permissions = _accountPermissionsStorage().signerPermissions[signer];
                              signers[i] = SignerPermissions(
                                  signer,
                                  _accountPermissionsStorage().approvedTargets[signer].values(),
                                  permissions.nativeTokenLimitPerTransaction,
                                  permissions.startTimestamp,
                                  permissions.endTimestamp
                              );
                          }
                      }
                      /// @notice Returns all signers with active permissions to use the account.
                      function getAllActiveSigners() external view returns (SignerPermissions[] memory signers) {
                          address[] memory allSigners = _accountPermissionsStorage().allSigners.values();
                          uint256 len = allSigners.length;
                          uint256 numOfActiveSigners = 0;
                          for (uint256 i = 0; i < len; i += 1) {
                              if (isActiveSigner(allSigners[i])) {
                                  numOfActiveSigners++;
                              } else {
                                  allSigners[i] = address(0);
                              }
                          }
                          signers = new SignerPermissions[](numOfActiveSigners);
                          uint256 index = 0;
                          for (uint256 i = 0; i < len; i += 1) {
                              if (allSigners[i] != address(0)) {
                                  address signer = allSigners[i];
                                  SignerPermissionsStatic memory permissions = _accountPermissionsStorage().signerPermissions[signer];
                                  signers[index++] = SignerPermissions(
                                      signer,
                                      _accountPermissionsStorage().approvedTargets[signer].values(),
                                      permissions.nativeTokenLimitPerTransaction,
                                      permissions.startTimestamp,
                                      permissions.endTimestamp
                                  );
                              }
                          }
                      }
                      /// @notice Returns all admins of the account.
                      function getAllAdmins() external view returns (address[] memory) {
                          return _accountPermissionsStorage().allAdmins.values();
                      }
                      /*///////////////////////////////////////////////////////////////
                                          Internal functions
                      //////////////////////////////////////////////////////////////*/
                      /// @notice Runs after every `changeRole` run.
                      function _afterSignerPermissionsUpdate(SignerPermissionRequest calldata _req) internal virtual;
                      /// @notice Makes the given account an admin.
                      function _setAdmin(address _account, bool _isAdmin) internal virtual {
                          _accountPermissionsStorage().isAdmin[_account] = _isAdmin;
                          if (_isAdmin) {
                              _accountPermissionsStorage().allAdmins.add(_account);
                          } else {
                              _accountPermissionsStorage().allAdmins.remove(_account);
                          }
                          emit AdminUpdated(_account, _isAdmin);
                      }
                      /// @dev Returns the address of the signer of the request.
                      function _recoverAddress(bytes memory _encoded, bytes calldata _signature) internal view virtual returns (address) {
                          return _hashTypedDataV4(keccak256(_encoded)).recover(_signature);
                      }
                      /// @dev Encodes a request for recovery of the signer in `recoverAddress`.
                      function _encodeRequest(SignerPermissionRequest calldata _req) internal pure virtual returns (bytes memory) {
                          return
                              abi.encode(
                                  TYPEHASH,
                                  _req.signer,
                                  _req.isAdmin,
                                  keccak256(abi.encodePacked(_req.approvedTargets)),
                                  _req.nativeTokenLimitPerTransaction,
                                  _req.permissionStartTimestamp,
                                  _req.permissionEndTimestamp,
                                  _req.reqValidityStartTimestamp,
                                  _req.reqValidityEndTimestamp,
                                  _req.uid
                              );
                      }
                      /// @dev Returns the AccountPermissions storage.
                      function _accountPermissionsStorage() internal pure returns (AccountPermissionsStorage.Data storage data) {
                          data = AccountPermissionsStorage.data();
                      }
                  }
                  // SPDX-License-Identifier: Apache-2.0
                  pragma solidity ^0.8.0;
                  /// @author thirdweb
                  import "../interface/IContractMetadata.sol";
                  /**
                   *  @author  thirdweb.com
                   *
                   *  @title   Contract Metadata
                   *  @notice  Thirdweb's `ContractMetadata` is a contract extension for any base contracts. It lets you set a metadata URI
                   *           for you contract.
                   *           Additionally, `ContractMetadata` is necessary for NFT contracts that want royalties to get distributed on OpenSea.
                   */
                  library ContractMetadataStorage {
                      /// @custom:storage-location erc7201:contract.metadata.storage
                      /// @dev keccak256(abi.encode(uint256(keccak256("contract.metadata.storage")) - 1)) & ~bytes32(uint256(0xff))
                      bytes32 public constant CONTRACT_METADATA_STORAGE_POSITION =
                          0x4bc804ba64359c0e35e5ed5d90ee596ecaa49a3a930ddcb1470ea0dd625da900;
                      struct Data {
                          /// @notice Returns the contract metadata URI.
                          string contractURI;
                      }
                      function data() internal pure returns (Data storage data_) {
                          bytes32 position = CONTRACT_METADATA_STORAGE_POSITION;
                          assembly {
                              data_.slot := position
                          }
                      }
                  }
                  abstract contract ContractMetadata is IContractMetadata {
                      /**
                       *  @notice         Lets a contract admin set the URI for contract-level metadata.
                       *  @dev            Caller should be authorized to setup contractURI, e.g. contract admin.
                       *                  See {_canSetContractURI}.
                       *                  Emits {ContractURIUpdated Event}.
                       *
                       *  @param _uri     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                       */
                      function setContractURI(string memory _uri) external override {
                          if (!_canSetContractURI()) {
                              revert("Not authorized");
                          }
                          _setupContractURI(_uri);
                      }
                      /// @dev Lets a contract admin set the URI for contract-level metadata.
                      function _setupContractURI(string memory _uri) internal {
                          string memory prevURI = _contractMetadataStorage().contractURI;
                          _contractMetadataStorage().contractURI = _uri;
                          emit ContractURIUpdated(prevURI, _uri);
                      }
                      /// @notice Returns the contract metadata URI.
                      function contractURI() public view virtual override returns (string memory) {
                          return _contractMetadataStorage().contractURI;
                      }
                      /// @dev Returns the AccountPermissions storage.
                      function _contractMetadataStorage() internal pure returns (ContractMetadataStorage.Data storage data) {
                          data = ContractMetadataStorage.data();
                      }
                      /// @dev Returns whether contract metadata can be set in the given execution context.
                      function _canSetContractURI() internal view virtual returns (bool);
                  }
                  // SPDX-License-Identifier: Apache 2.0
                  pragma solidity ^0.8.0;
                  import "../../lib/Address.sol";
                  library InitStorage {
                      /// @custom:storage-location erc7201:init.storage
                      /// @dev keccak256(abi.encode(uint256(keccak256("init.storage")) - 1)) & ~bytes32(uint256(0xff))
                      bytes32 constant INIT_STORAGE_POSITION = 0x322cf19c484104d3b1a9c2982ebae869ede3fa5f6c4703ca41b9a48c76ee0300;
                      /// @dev Layout of the entrypoint contract's storage.
                      struct Data {
                          uint8 initialized;
                          bool initializing;
                      }
                      /// @dev Returns the entrypoint contract's data at the relevant storage location.
                      function data() internal pure returns (Data storage data_) {
                          bytes32 position = INIT_STORAGE_POSITION;
                          assembly {
                              data_.slot := position
                          }
                      }
                  }
                  abstract contract Initializable {
                      /**
                       * @dev Triggered when the contract has been initialized or reinitialized.
                       */
                      event Initialized(uint8 version);
                      /**
                       * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
                       * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`.
                       */
                      modifier initializer() {
                          uint8 _initialized = _initStorage().initialized;
                          bool _initializing = _initStorage().initializing;
                          bool isTopLevelCall = !_initializing;
                          require(
                              (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1),
                              "Initializable: contract is already initialized"
                          );
                          _initStorage().initialized = 1;
                          if (isTopLevelCall) {
                              _initStorage().initializing = true;
                          }
                          _;
                          if (isTopLevelCall) {
                              _initStorage().initializing = false;
                              emit Initialized(1);
                          }
                      }
                      /**
                       * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
                       * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
                       * used to initialize parent contracts.
                       *
                       * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original
                       * initialization step. This is essential to configure modules that are added through upgrades and that require
                       * initialization.
                       *
                       * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
                       * a contract, executing them in the right order is up to the developer or operator.
                       */
                      modifier reinitializer(uint8 version) {
                          uint8 _initialized = _initStorage().initialized;
                          bool _initializing = _initStorage().initializing;
                          require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
                          _initStorage().initialized = version;
                          _initStorage().initializing = true;
                          _;
                          _initStorage().initializing = false;
                          emit Initialized(version);
                      }
                      /**
                       * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
                       * {initializer} and {reinitializer} modifiers, directly or indirectly.
                       */
                      modifier onlyInitializing() {
                          require(_initStorage().initializing, "Initializable: contract is not initializing");
                          _;
                      }
                      /**
                       * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
                       * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
                       * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
                       * through proxies.
                       */
                      function _disableInitializers() internal virtual {
                          uint8 _initialized = _initStorage().initialized;
                          bool _initializing = _initStorage().initializing;
                          require(!_initializing, "Initializable: contract is initializing");
                          if (_initialized < type(uint8).max) {
                              _initStorage().initialized = type(uint8).max;
                              emit Initialized(type(uint8).max);
                          }
                      }
                      /// @dev Returns the InitStorage storage.
                      function _initStorage() internal pure returns (InitStorage.Data storage data) {
                          data = InitStorage.data();
                      }
                  }
                  // SPDX-License-Identifier: Apache-2.0
                  pragma solidity ^0.8.0;
                  /// @author thirdweb
                  import "../interface/IPermissions.sol";
                  import "../../lib/Strings.sol";
                  /**
                   *  @title   Permissions
                   *  @dev     This contracts provides extending-contracts with role-based access control mechanisms
                   */
                  library PermissionsStorage {
                      /// @custom:storage-location erc7201:permissions.storage
                      /// @dev keccak256(abi.encode(uint256(keccak256("permissions.storage")) - 1)) & ~bytes32(uint256(0xff))
                      bytes32 public constant PERMISSIONS_STORAGE_POSITION =
                          0x0a7b0f5c59907924802379ebe98cdc23e2ee7820f63d30126e10b3752010e500;
                      struct Data {
                          /// @dev Map from keccak256 hash of a role => a map from address => whether address has role.
                          mapping(bytes32 => mapping(address => bool)) _hasRole;
                          /// @dev Map from keccak256 hash of a role to role admin. See {getRoleAdmin}.
                          mapping(bytes32 => bytes32) _getRoleAdmin;
                      }
                      function data() internal pure returns (Data storage data_) {
                          bytes32 position = PERMISSIONS_STORAGE_POSITION;
                          assembly {
                              data_.slot := position
                          }
                      }
                  }
                  contract Permissions is IPermissions {
                      /// @dev Default admin role for all roles. Only accounts with this role can grant/revoke other roles.
                      bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
                      /// @dev Modifier that checks if an account has the specified role; reverts otherwise.
                      modifier onlyRole(bytes32 role) {
                          _checkRole(role, _msgSender());
                          _;
                      }
                      /**
                       *  @notice         Checks whether an account has a particular role.
                       *  @dev            Returns `true` if `account` has been granted `role`.
                       *
                       *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                       *  @param account  Address of the account for which the role is being checked.
                       */
                      function hasRole(bytes32 role, address account) public view override returns (bool) {
                          return _permissionsStorage()._hasRole[role][account];
                      }
                      /**
                       *  @notice         Checks whether an account has a particular role;
                       *                  role restrictions can be swtiched on and off.
                       *
                       *  @dev            Returns `true` if `account` has been granted `role`.
                       *                  Role restrictions can be swtiched on and off:
                       *                      - If address(0) has ROLE, then the ROLE restrictions
                       *                        don't apply.
                       *                      - If address(0) does not have ROLE, then the ROLE
                       *                        restrictions will apply.
                       *
                       *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                       *  @param account  Address of the account for which the role is being checked.
                       */
                      function hasRoleWithSwitch(bytes32 role, address account) public view returns (bool) {
                          if (!_permissionsStorage()._hasRole[role][address(0)]) {
                              return _permissionsStorage()._hasRole[role][account];
                          }
                          return true;
                      }
                      /**
                       *  @notice         Returns the admin role that controls the specified role.
                       *  @dev            See {grantRole} and {revokeRole}.
                       *                  To change a role's admin, use {_setRoleAdmin}.
                       *
                       *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                       */
                      function getRoleAdmin(bytes32 role) external view override returns (bytes32) {
                          return _permissionsStorage()._getRoleAdmin[role];
                      }
                      /**
                       *  @notice         Grants a role to an account, if not previously granted.
                       *  @dev            Caller must have admin role for the `role`.
                       *                  Emits {RoleGranted Event}.
                       *
                       *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                       *  @param account  Address of the account to which the role is being granted.
                       */
                      function grantRole(bytes32 role, address account) public virtual override {
                          _checkRole(_permissionsStorage()._getRoleAdmin[role], _msgSender());
                          if (_permissionsStorage()._hasRole[role][account]) {
                              revert("Can only grant to non holders");
                          }
                          _setupRole(role, account);
                      }
                      /**
                       *  @notice         Revokes role from an account.
                       *  @dev            Caller must have admin role for the `role`.
                       *                  Emits {RoleRevoked Event}.
                       *
                       *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                       *  @param account  Address of the account from which the role is being revoked.
                       */
                      function revokeRole(bytes32 role, address account) public virtual override {
                          _checkRole(_permissionsStorage()._getRoleAdmin[role], _msgSender());
                          _revokeRole(role, account);
                      }
                      /**
                       *  @notice         Revokes role from the account.
                       *  @dev            Caller must have the `role`, with caller being the same as `account`.
                       *                  Emits {RoleRevoked Event}.
                       *
                       *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                       *  @param account  Address of the account from which the role is being revoked.
                       */
                      function renounceRole(bytes32 role, address account) public virtual override {
                          if (_msgSender() != account) {
                              revert("Can only renounce for self");
                          }
                          _revokeRole(role, account);
                      }
                      /// @dev Sets `adminRole` as `role`'s admin role.
                      function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
                          bytes32 previousAdminRole = _permissionsStorage()._getRoleAdmin[role];
                          _permissionsStorage()._getRoleAdmin[role] = adminRole;
                          emit RoleAdminChanged(role, previousAdminRole, adminRole);
                      }
                      /// @dev Sets up `role` for `account`
                      function _setupRole(bytes32 role, address account) internal virtual {
                          _permissionsStorage()._hasRole[role][account] = true;
                          emit RoleGranted(role, account, _msgSender());
                      }
                      /// @dev Revokes `role` from `account`
                      function _revokeRole(bytes32 role, address account) internal virtual {
                          _checkRole(role, account);
                          delete _permissionsStorage()._hasRole[role][account];
                          emit RoleRevoked(role, account, _msgSender());
                      }
                      /// @dev Checks `role` for `account`. Reverts with a message including the required role.
                      function _checkRole(bytes32 role, address account) internal view virtual {
                          if (!_permissionsStorage()._hasRole[role][account]) {
                              revert(
                                  string(
                                      abi.encodePacked(
                                          "Permissions: account ",
                                          Strings.toHexString(uint160(account), 20),
                                          " is missing role ",
                                          Strings.toHexString(uint256(role), 32)
                                      )
                                  )
                              );
                          }
                      }
                      /// @dev Checks `role` for `account`. Reverts with a message including the required role.
                      function _checkRoleWithSwitch(bytes32 role, address account) internal view virtual {
                          if (!hasRoleWithSwitch(role, account)) {
                              revert(
                                  string(
                                      abi.encodePacked(
                                          "Permissions: account ",
                                          Strings.toHexString(uint160(account), 20),
                                          " is missing role ",
                                          Strings.toHexString(uint256(role), 32)
                                      )
                                  )
                              );
                          }
                      }
                      function _msgSender() internal view virtual returns (address sender) {
                          return msg.sender;
                      }
                      function _msgData() internal view virtual returns (bytes calldata) {
                          return msg.data;
                      }
                      /// @dev Returns the Permissions storage.
                      function _permissionsStorage() internal pure returns (PermissionsStorage.Data storage data) {
                          data = PermissionsStorage.data();
                      }
                  }
                  // SPDX-License-Identifier: Apache-2.0
                  pragma solidity ^0.8.0;
                  /// @author thirdweb
                  import "../interface/IPermissionsEnumerable.sol";
                  import "./Permissions.sol";
                  /**
                   *  @title   PermissionsEnumerable
                   *  @dev     This contracts provides extending-contracts with role-based access control mechanisms.
                   *           Also provides interfaces to view all members with a given role, and total count of members.
                   */
                  library PermissionsEnumerableStorage {
                      /// @custom:storage-location erc7201:extension.manager.storage
                      bytes32 public constant PERMISSIONS_ENUMERABLE_STORAGE_POSITION =
                          keccak256(abi.encode(uint256(keccak256("permissions.enumerable.storage")) - 1)) & ~bytes32(uint256(0xff));
                      /**
                       *  @notice A data structure to store data of members for a given role.
                       *
                       *  @param index    Current index in the list of accounts that have a role.
                       *  @param members  map from index => address of account that has a role
                       *  @param indexOf  map from address => index which the account has.
                       */
                      struct RoleMembers {
                          uint256 index;
                          mapping(uint256 => address) members;
                          mapping(address => uint256) indexOf;
                      }
                      struct Data {
                          /// @dev map from keccak256 hash of a role to its members' data. See {RoleMembers}.
                          mapping(bytes32 => RoleMembers) roleMembers;
                      }
                      function data() internal pure returns (Data storage data_) {
                          bytes32 position = PERMISSIONS_ENUMERABLE_STORAGE_POSITION;
                          assembly {
                              data_.slot := position
                          }
                      }
                  }
                  contract PermissionsEnumerable is IPermissionsEnumerable, Permissions {
                      /**
                       *  @notice         Returns the role-member from a list of members for a role,
                       *                  at a given index.
                       *  @dev            Returns `member` who has `role`, at `index` of role-members list.
                       *                  See struct {RoleMembers}, and mapping {roleMembers}
                       *
                       *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                       *  @param index    Index in list of current members for the role.
                       *
                       *  @return member  Address of account that has `role`
                       */
                      function getRoleMember(bytes32 role, uint256 index) external view override returns (address member) {
                          uint256 currentIndex = _permissionsEnumerableStorage().roleMembers[role].index;
                          uint256 check;
                          for (uint256 i = 0; i < currentIndex; i += 1) {
                              if (_permissionsEnumerableStorage().roleMembers[role].members[i] != address(0)) {
                                  if (check == index) {
                                      member = _permissionsEnumerableStorage().roleMembers[role].members[i];
                                      return member;
                                  }
                                  check += 1;
                              } else if (
                                  hasRole(role, address(0)) && i == _permissionsEnumerableStorage().roleMembers[role].indexOf[address(0)]
                              ) {
                                  check += 1;
                              }
                          }
                      }
                      /**
                       *  @notice         Returns total number of accounts that have a role.
                       *  @dev            Returns `count` of accounts that have `role`.
                       *                  See struct {RoleMembers}, and mapping {roleMembers}
                       *
                       *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                       *
                       *  @return count   Total number of accounts that have `role`
                       */
                      function getRoleMemberCount(bytes32 role) external view override returns (uint256 count) {
                          uint256 currentIndex = _permissionsEnumerableStorage().roleMembers[role].index;
                          for (uint256 i = 0; i < currentIndex; i += 1) {
                              if (_permissionsEnumerableStorage().roleMembers[role].members[i] != address(0)) {
                                  count += 1;
                              }
                          }
                          if (hasRole(role, address(0))) {
                              count += 1;
                          }
                      }
                      /// @dev Revokes `role` from `account`, and removes `account` from {roleMembers}
                      ///      See {_removeMember}
                      function _revokeRole(bytes32 role, address account) internal virtual override {
                          super._revokeRole(role, account);
                          _removeMember(role, account);
                      }
                      /// @dev Grants `role` to `account`, and adds `account` to {roleMembers}
                      ///      See {_addMember}
                      function _setupRole(bytes32 role, address account) internal virtual override {
                          super._setupRole(role, account);
                          _addMember(role, account);
                      }
                      /// @dev adds `account` to {roleMembers}, for `role`
                      function _addMember(bytes32 role, address account) internal {
                          uint256 idx = _permissionsEnumerableStorage().roleMembers[role].index;
                          _permissionsEnumerableStorage().roleMembers[role].index += 1;
                          _permissionsEnumerableStorage().roleMembers[role].members[idx] = account;
                          _permissionsEnumerableStorage().roleMembers[role].indexOf[account] = idx;
                      }
                      /// @dev removes `account` from {roleMembers}, for `role`
                      function _removeMember(bytes32 role, address account) internal {
                          uint256 idx = _permissionsEnumerableStorage().roleMembers[role].indexOf[account];
                          delete _permissionsEnumerableStorage().roleMembers[role].members[idx];
                          delete _permissionsEnumerableStorage().roleMembers[role].indexOf[account];
                      }
                      /// @dev Returns the PermissionsEnumerable storage.
                      function _permissionsEnumerableStorage() internal pure returns (PermissionsEnumerableStorage.Data storage data) {
                          data = PermissionsEnumerableStorage.data();
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v4.8.0) (proxy/Clones.sol)
                  pragma solidity ^0.8.0;
                  /**
                   * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
                   * deploying minimal proxy contracts, also known as "clones".
                   *
                   * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
                   * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
                   *
                   * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
                   * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
                   * deterministic method.
                   *
                   * _Available since v3.4._
                   */
                  library Clones {
                      /**
                       * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
                       *
                       * This function uses the create opcode, which should never revert.
                       */
                      function clone(address implementation) internal returns (address instance) {
                          /// @solidity memory-safe-assembly
                          assembly {
                              // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
                              // of the `implementation` address with the bytecode before the address.
                              mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
                              // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
                              mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
                              instance := create(0, 0x09, 0x37)
                          }
                          require(instance != address(0), "ERC1167: create failed");
                      }
                      /**
                       * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
                       *
                       * This function uses the create2 opcode and a `salt` to deterministically deploy
                       * the clone. Using the same `implementation` and `salt` multiple time will revert, since
                       * the clones cannot be deployed twice at the same address.
                       */
                      function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
                          /// @solidity memory-safe-assembly
                          assembly {
                              // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
                              // of the `implementation` address with the bytecode before the address.
                              mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
                              // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
                              mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
                              instance := create2(0, 0x09, 0x37, salt)
                          }
                          require(instance != address(0), "ERC1167: create2 failed");
                      }
                      /**
                       * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
                       */
                      function predictDeterministicAddress(
                          address implementation,
                          bytes32 salt,
                          address deployer
                      ) internal pure returns (address predicted) {
                          /// @solidity memory-safe-assembly
                          assembly {
                              let ptr := mload(0x40)
                              mstore(add(ptr, 0x38), deployer)
                              mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
                              mstore(add(ptr, 0x14), implementation)
                              mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
                              mstore(add(ptr, 0x58), salt)
                              mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
                              predicted := keccak256(add(ptr, 0x43), 0x55)
                          }
                      }
                      /**
                       * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
                       */
                      function predictDeterministicAddress(
                          address implementation,
                          bytes32 salt
                      ) internal view returns (address predicted) {
                          return predictDeterministicAddress(implementation, salt, address(this));
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol)
                  pragma solidity ^0.8.0;
                  import "../../../../eip/interface/IERC165.sol";
                  /**
                   * @dev _Available since v3.1._
                   */
                  interface IERC1155Receiver is IERC165 {
                      /**
                       * @dev Handles the receipt of a single ERC1155 token type. This function is
                       * called at the end of a `safeTransferFrom` after the balance has been updated.
                       *
                       * NOTE: To accept the transfer, this must return
                       * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
                       * (i.e. 0xf23a6e61, or its own function selector).
                       *
                       * @param operator The address which initiated the transfer (i.e. msg.sender)
                       * @param from The address which previously owned the token
                       * @param id The ID of the token being transferred
                       * @param value The amount of tokens being transferred
                       * @param data Additional data with no specified format
                       * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
                       */
                      function onERC1155Received(
                          address operator,
                          address from,
                          uint256 id,
                          uint256 value,
                          bytes calldata data
                      ) external returns (bytes4);
                      /**
                       * @dev Handles the receipt of a multiple ERC1155 token types. This function
                       * is called at the end of a `safeBatchTransferFrom` after the balances have
                       * been updated.
                       *
                       * NOTE: To accept the transfer(s), this must return
                       * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
                       * (i.e. 0xbc197c81, or its own function selector).
                       *
                       * @param operator The address which initiated the batch transfer (i.e. msg.sender)
                       * @param from The address which previously owned the token
                       * @param ids An array containing ids of each token being transferred (order and length must match values array)
                       * @param values An array containing amounts of each token being transferred (order and length must match ids array)
                       * @param data Additional data with no specified format
                       * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
                       */
                      function onERC1155BatchReceived(
                          address operator,
                          address from,
                          uint256[] calldata ids,
                          uint256[] calldata values,
                          bytes calldata data
                      ) external returns (bytes4);
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/utils/ERC1155Holder.sol)
                  pragma solidity ^0.8.0;
                  import "./ERC1155Receiver.sol";
                  /**
                   * Simple implementation of `ERC1155Receiver` that will allow a contract to hold ERC1155 tokens.
                   *
                   * IMPORTANT: When inheriting this contract, you must include a way to use the received tokens, otherwise they will be
                   * stuck.
                   *
                   * @dev _Available since v3.1._
                   */
                  contract ERC1155Holder is ERC1155Receiver {
                      function onERC1155Received(
                          address,
                          address,
                          uint256,
                          uint256,
                          bytes memory
                      ) public virtual override returns (bytes4) {
                          return this.onERC1155Received.selector;
                      }
                      function onERC1155BatchReceived(
                          address,
                          address,
                          uint256[] memory,
                          uint256[] memory,
                          bytes memory
                      ) public virtual override returns (bytes4) {
                          return this.onERC1155BatchReceived.selector;
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts v4.4.1 (token/ERC1155/utils/ERC1155Receiver.sol)
                  pragma solidity ^0.8.0;
                  import "../IERC1155Receiver.sol";
                  import "../../../../../eip/ERC165.sol";
                  /**
                   * @dev _Available since v3.1._
                   */
                  abstract contract ERC1155Receiver is ERC165, IERC1155Receiver {
                      /**
                       * @dev See {IERC165-supportsInterface}.
                       */
                      function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
                          return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId);
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721Receiver.sol)
                  pragma solidity ^0.8.0;
                  /**
                   * @title ERC721 token receiver interface
                   * @dev Interface for any contract that wants to support safeTransfers
                   * from ERC721 asset contracts.
                   */
                  interface IERC721Receiver {
                      /**
                       * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
                       * by `operator` from `from`, this function is called.
                       *
                       * It must return its Solidity selector to confirm the token transfer.
                       * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
                       *
                       * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
                       */
                      function onERC721Received(
                          address operator,
                          address from,
                          uint256 tokenId,
                          bytes calldata data
                      ) external returns (bytes4);
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts v4.4.1 (token/ERC721/utils/ERC721Holder.sol)
                  pragma solidity ^0.8.0;
                  import "../IERC721Receiver.sol";
                  /**
                   * @dev Implementation of the {IERC721Receiver} interface.
                   *
                   * Accepts all token transfers.
                   * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or {IERC721-setApprovalForAll}.
                   */
                  contract ERC721Holder is IERC721Receiver {
                      /**
                       * @dev See {IERC721Receiver-onERC721Received}.
                       *
                       * Always returns `IERC721Receiver.onERC721Received.selector`.
                       */
                      function onERC721Received(address, address, uint256, bytes memory) public virtual override returns (bytes4) {
                          return this.onERC721Received.selector;
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/ECDSA.sol)
                  pragma solidity ^0.8.0;
                  import "../../../../lib/Strings.sol";
                  /**
                   * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
                   *
                   * These functions can be used to verify that a message was signed by the holder
                   * of the private keys of a given address.
                   */
                  library ECDSA {
                      enum RecoverError {
                          NoError,
                          InvalidSignature,
                          InvalidSignatureLength,
                          InvalidSignatureS,
                          InvalidSignatureV // Deprecated in v4.8
                      }
                      function _throwError(RecoverError error) private pure {
                          if (error == RecoverError.NoError) {
                              return; // no error: do nothing
                          } else if (error == RecoverError.InvalidSignature) {
                              revert("ECDSA: invalid signature");
                          } else if (error == RecoverError.InvalidSignatureLength) {
                              revert("ECDSA: invalid signature length");
                          } else if (error == RecoverError.InvalidSignatureS) {
                              revert("ECDSA: invalid signature 's' value");
                          }
                      }
                      /**
                       * @dev Returns the address that signed a hashed message (`hash`) with
                       * `signature` or error string. This address can then be used for verification purposes.
                       *
                       * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
                       * this function rejects them by requiring the `s` value to be in the lower
                       * half order, and the `v` value to be either 27 or 28.
                       *
                       * IMPORTANT: `hash` _must_ be the result of a hash operation for the
                       * verification to be secure: it is possible to craft signatures that
                       * recover to arbitrary addresses for non-hashed data. A safe way to ensure
                       * this is by receiving a hash of the original message (which may otherwise
                       * be too long), and then calling {toEthSignedMessageHash} on it.
                       *
                       * Documentation for signature generation:
                       * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
                       * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
                       *
                       * _Available since v4.3._
                       */
                      function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
                          if (signature.length == 65) {
                              bytes32 r;
                              bytes32 s;
                              uint8 v;
                              // ecrecover takes the signature parameters, and the only way to get them
                              // currently is to use assembly.
                              /// @solidity memory-safe-assembly
                              assembly {
                                  r := mload(add(signature, 0x20))
                                  s := mload(add(signature, 0x40))
                                  v := byte(0, mload(add(signature, 0x60)))
                              }
                              return tryRecover(hash, v, r, s);
                          } else {
                              return (address(0), RecoverError.InvalidSignatureLength);
                          }
                      }
                      /**
                       * @dev Returns the address that signed a hashed message (`hash`) with
                       * `signature`. This address can then be used for verification purposes.
                       *
                       * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
                       * this function rejects them by requiring the `s` value to be in the lower
                       * half order, and the `v` value to be either 27 or 28.
                       *
                       * IMPORTANT: `hash` _must_ be the result of a hash operation for the
                       * verification to be secure: it is possible to craft signatures that
                       * recover to arbitrary addresses for non-hashed data. A safe way to ensure
                       * this is by receiving a hash of the original message (which may otherwise
                       * be too long), and then calling {toEthSignedMessageHash} on it.
                       */
                      function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
                          (address recovered, RecoverError error) = tryRecover(hash, signature);
                          _throwError(error);
                          return recovered;
                      }
                      /**
                       * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
                       *
                       * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
                       *
                       * _Available since v4.3._
                       */
                      function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) {
                          bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
                          uint8 v = uint8((uint256(vs) >> 255) + 27);
                          return tryRecover(hash, v, r, s);
                      }
                      /**
                       * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
                       *
                       * _Available since v4.2._
                       */
                      function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
                          (address recovered, RecoverError error) = tryRecover(hash, r, vs);
                          _throwError(error);
                          return recovered;
                      }
                      /**
                       * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
                       * `r` and `s` signature fields separately.
                       *
                       * _Available since v4.3._
                       */
                      function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) {
                          // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
                          // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
                          // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
                          // signatures from current libraries generate a unique signature with an s-value in the lower half order.
                          //
                          // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
                          // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
                          // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
                          // these malleable signatures as well.
                          if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
                              return (address(0), RecoverError.InvalidSignatureS);
                          }
                          // If the signature is valid (and not malleable), return the signer address
                          address signer = ecrecover(hash, v, r, s);
                          if (signer == address(0)) {
                              return (address(0), RecoverError.InvalidSignature);
                          }
                          return (signer, RecoverError.NoError);
                      }
                      /**
                       * @dev Overload of {ECDSA-recover} that receives the `v`,
                       * `r` and `s` signature fields separately.
                       */
                      function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
                          (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
                          _throwError(error);
                          return recovered;
                      }
                      /**
                       * @dev Returns an Ethereum Signed Message, created from a `hash`. This
                       * produces hash corresponding to the one signed with the
                       * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
                       * JSON-RPC method as part of EIP-191.
                       *
                       * See {recover}.
                       */
                      function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) {
                          // 32 is the length in bytes of hash,
                          // enforced by the type signature above
                          /// @solidity memory-safe-assembly
                          assembly {
                              mstore(0x00, "\\x19Ethereum Signed Message:\
                  32")
                              mstore(0x1c, hash)
                              message := keccak256(0x00, 0x3c)
                          }
                      }
                      /**
                       * @dev Returns an Ethereum Signed Message, created from `s`. This
                       * produces hash corresponding to the one signed with the
                       * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
                       * JSON-RPC method as part of EIP-191.
                       *
                       * See {recover}.
                       */
                      function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
                          return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
                  ", Strings.toString(s.length), s));
                      }
                      /**
                       * @dev Returns an Ethereum Signed Typed Data, created from a
                       * `domainSeparator` and a `structHash`. This produces hash corresponding
                       * to the one signed with the
                       * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
                       * JSON-RPC method as part of EIP-712.
                       *
                       * See {recover}.
                       */
                      function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) {
                          /// @solidity memory-safe-assembly
                          assembly {
                              let ptr := mload(0x40)
                              mstore(ptr, "\\x19\\x01")
                              mstore(add(ptr, 0x02), domainSeparator)
                              mstore(add(ptr, 0x22), structHash)
                              data := keccak256(ptr, 0x42)
                          }
                      }
                      /**
                       * @dev Returns an Ethereum Signed Data with intended validator, created from a
                       * `validator` and `data` according to the version 0 of EIP-191.
                       *
                       * See {recover}.
                       */
                      function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
                          return keccak256(abi.encodePacked("\\x19\\x00", validator, data));
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts v4.4.1 (utils/cryptography/draft-EIP712.sol)
                  pragma solidity ^0.8.0;
                  import "./ECDSA.sol";
                  /**
                   * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
                   *
                   * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
                   * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
                   * they need in their contracts using a combination of `abi.encode` and `keccak256`.
                   *
                   * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
                   * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
                   * ({_hashTypedDataV4}).
                   *
                   * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
                   * the chain id to protect against replay attacks on an eventual fork of the chain.
                   *
                   * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
                   * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
                   *
                   * _Available since v3.4._
                   */
                  abstract contract EIP712 {
                      /* solhint-disable var-name-mixedcase */
                      // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
                      // invalidate the cached domain separator if the chain id changes.
                      bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
                      uint256 private immutable _CACHED_CHAIN_ID;
                      address private immutable _CACHED_THIS;
                      bytes32 private immutable _HASHED_NAME;
                      bytes32 private immutable _HASHED_VERSION;
                      bytes32 private immutable _TYPE_HASH;
                      /* solhint-enable var-name-mixedcase */
                      /**
                       * @dev Initializes the domain separator and parameter caches.
                       *
                       * The meaning of `name` and `version` is specified in
                       * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
                       *
                       * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
                       * - `version`: the current major version of the signing domain.
                       *
                       * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
                       * contract upgrade].
                       */
                      constructor(string memory name, string memory version) {
                          bytes32 hashedName = keccak256(bytes(name));
                          bytes32 hashedVersion = keccak256(bytes(version));
                          bytes32 typeHash = keccak256(
                              "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
                          );
                          _HASHED_NAME = hashedName;
                          _HASHED_VERSION = hashedVersion;
                          _CACHED_CHAIN_ID = block.chainid;
                          _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);
                          _CACHED_THIS = address(this);
                          _TYPE_HASH = typeHash;
                      }
                      /**
                       * @dev Returns the domain separator for the current chain.
                       */
                      function _domainSeparatorV4() internal view returns (bytes32) {
                          if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) {
                              return _CACHED_DOMAIN_SEPARATOR;
                          } else {
                              return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
                          }
                      }
                      function _buildDomainSeparator(
                          bytes32 typeHash,
                          bytes32 nameHash,
                          bytes32 versionHash
                      ) private view returns (bytes32) {
                          return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
                      }
                      /**
                       * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
                       * function returns the hash of the fully encoded EIP712 message for this domain.
                       *
                       * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
                       *
                       * ```solidity
                       * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
                       *     keccak256("Mail(address to,string contents)"),
                       *     mailTo,
                       *     keccak256(bytes(mailContents))
                       * )));
                       * address signer = ECDSA.recover(digest, signature);
                       * ```
                       */
                      function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
                          return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol)
                  pragma solidity ^0.8.0;
                  /**
                   * @dev Library for managing
                   * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
                   * types.
                   *
                   * Sets have the following properties:
                   *
                   * - Elements are added, removed, and checked for existence in constant time
                   * (O(1)).
                   * - Elements are enumerated in O(n). No guarantees are made on the ordering.
                   *
                   * ```
                   * contract Example {
                   *     // Add the library methods
                   *     using EnumerableSet for EnumerableSet.AddressSet;
                   *
                   *     // Declare a set state variable
                   *     EnumerableSet.AddressSet private mySet;
                   * }
                   * ```
                   *
                   * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
                   * and `uint256` (`UintSet`) are supported.
                   *
                   * [WARNING]
                   * ====
                   *  Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable.
                   *  See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
                   *
                   *  In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet.
                   * ====
                   */
                  library EnumerableSet {
                      // To implement this library for multiple types with as little code
                      // repetition as possible, we write it in terms of a generic Set type with
                      // bytes32 values.
                      // The Set implementation uses private functions, and user-facing
                      // implementations (such as AddressSet) are just wrappers around the
                      // underlying Set.
                      // This means that we can only create new EnumerableSets for types that fit
                      // in bytes32.
                      struct Set {
                          // Storage of set values
                          bytes32[] _values;
                          // Position of the value in the `values` array, plus 1 because index 0
                          // means a value is not in the set.
                          mapping(bytes32 => uint256) _indexes;
                      }
                      /**
                       * @dev Add a value to a set. O(1).
                       *
                       * Returns true if the value was added to the set, that is if it was not
                       * already present.
                       */
                      function _add(Set storage set, bytes32 value) private returns (bool) {
                          if (!_contains(set, value)) {
                              set._values.push(value);
                              // The value is stored at length-1, but we add 1 to all indexes
                              // and use 0 as a sentinel value
                              set._indexes[value] = set._values.length;
                              return true;
                          } else {
                              return false;
                          }
                      }
                      /**
                       * @dev Removes a value from a set. O(1).
                       *
                       * Returns true if the value was removed from the set, that is if it was
                       * present.
                       */
                      function _remove(Set storage set, bytes32 value) private returns (bool) {
                          // We read and store the value's index to prevent multiple reads from the same storage slot
                          uint256 valueIndex = set._indexes[value];
                          if (valueIndex != 0) {
                              // Equivalent to contains(set, value)
                              // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
                              // the array, and then remove the last element (sometimes called as 'swap and pop').
                              // This modifies the order of the array, as noted in {at}.
                              uint256 toDeleteIndex = valueIndex - 1;
                              uint256 lastIndex = set._values.length - 1;
                              if (lastIndex != toDeleteIndex) {
                                  bytes32 lastValue = set._values[lastIndex];
                                  // Move the last value to the index where the value to delete is
                                  set._values[toDeleteIndex] = lastValue;
                                  // Update the index for the moved value
                                  set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
                              }
                              // Delete the slot where the moved value was stored
                              set._values.pop();
                              // Delete the index for the deleted slot
                              delete set._indexes[value];
                              return true;
                          } else {
                              return false;
                          }
                      }
                      /**
                       * @dev Returns true if the value is in the set. O(1).
                       */
                      function _contains(Set storage set, bytes32 value) private view returns (bool) {
                          return set._indexes[value] != 0;
                      }
                      /**
                       * @dev Returns the number of values on the set. O(1).
                       */
                      function _length(Set storage set) private view returns (uint256) {
                          return set._values.length;
                      }
                      /**
                       * @dev Returns the value stored at position `index` in the set. O(1).
                       *
                       * Note that there are no guarantees on the ordering of values inside the
                       * array, and it may change when more values are added or removed.
                       *
                       * Requirements:
                       *
                       * - `index` must be strictly less than {length}.
                       */
                      function _at(Set storage set, uint256 index) private view returns (bytes32) {
                          return set._values[index];
                      }
                      /**
                       * @dev Return the entire set in an array
                       *
                       * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
                       * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
                       * this function has an unbounded cost, and using it as part of a state-changing function may render the function
                       * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
                       */
                      function _values(Set storage set) private view returns (bytes32[] memory) {
                          return set._values;
                      }
                      // Bytes32Set
                      struct Bytes32Set {
                          Set _inner;
                      }
                      /**
                       * @dev Add a value to a set. O(1).
                       *
                       * Returns true if the value was added to the set, that is if it was not
                       * already present.
                       */
                      function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
                          return _add(set._inner, value);
                      }
                      /**
                       * @dev Removes a value from a set. O(1).
                       *
                       * Returns true if the value was removed from the set, that is if it was
                       * present.
                       */
                      function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
                          return _remove(set._inner, value);
                      }
                      /**
                       * @dev Returns true if the value is in the set. O(1).
                       */
                      function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
                          return _contains(set._inner, value);
                      }
                      /**
                       * @dev Returns the number of values in the set. O(1).
                       */
                      function length(Bytes32Set storage set) internal view returns (uint256) {
                          return _length(set._inner);
                      }
                      /**
                       * @dev Returns the value stored at position `index` in the set. O(1).
                       *
                       * Note that there are no guarantees on the ordering of values inside the
                       * array, and it may change when more values are added or removed.
                       *
                       * Requirements:
                       *
                       * - `index` must be strictly less than {length}.
                       */
                      function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
                          return _at(set._inner, index);
                      }
                      /**
                       * @dev Return the entire set in an array
                       *
                       * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
                       * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
                       * this function has an unbounded cost, and using it as part of a state-changing function may render the function
                       * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
                       */
                      function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
                          return _values(set._inner);
                      }
                      // AddressSet
                      struct AddressSet {
                          Set _inner;
                      }
                      /**
                       * @dev Add a value to a set. O(1).
                       *
                       * Returns true if the value was added to the set, that is if it was not
                       * already present.
                       */
                      function add(AddressSet storage set, address value) internal returns (bool) {
                          return _add(set._inner, bytes32(uint256(uint160(value))));
                      }
                      /**
                       * @dev Removes a value from a set. O(1).
                       *
                       * Returns true if the value was removed from the set, that is if it was
                       * present.
                       */
                      function remove(AddressSet storage set, address value) internal returns (bool) {
                          return _remove(set._inner, bytes32(uint256(uint160(value))));
                      }
                      /**
                       * @dev Returns true if the value is in the set. O(1).
                       */
                      function contains(AddressSet storage set, address value) internal view returns (bool) {
                          return _contains(set._inner, bytes32(uint256(uint160(value))));
                      }
                      /**
                       * @dev Returns the number of values in the set. O(1).
                       */
                      function length(AddressSet storage set) internal view returns (uint256) {
                          return _length(set._inner);
                      }
                      /**
                       * @dev Returns the value stored at position `index` in the set. O(1).
                       *
                       * Note that there are no guarantees on the ordering of values inside the
                       * array, and it may change when more values are added or removed.
                       *
                       * Requirements:
                       *
                       * - `index` must be strictly less than {length}.
                       */
                      function at(AddressSet storage set, uint256 index) internal view returns (address) {
                          return address(uint160(uint256(_at(set._inner, index))));
                      }
                      /**
                       * @dev Return the entire set in an array
                       *
                       * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
                       * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
                       * this function has an unbounded cost, and using it as part of a state-changing function may render the function
                       * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
                       */
                      function values(AddressSet storage set) internal view returns (address[] memory) {
                          bytes32[] memory store = _values(set._inner);
                          address[] memory result;
                          /// @solidity memory-safe-assembly
                          assembly {
                              result := store
                          }
                          return result;
                      }
                      // UintSet
                      struct UintSet {
                          Set _inner;
                      }
                      /**
                       * @dev Add a value to a set. O(1).
                       *
                       * Returns true if the value was added to the set, that is if it was not
                       * already present.
                       */
                      function add(UintSet storage set, uint256 value) internal returns (bool) {
                          return _add(set._inner, bytes32(value));
                      }
                      /**
                       * @dev Removes a value from a set. O(1).
                       *
                       * Returns true if the value was removed from the set, that is if it was
                       * present.
                       */
                      function remove(UintSet storage set, uint256 value) internal returns (bool) {
                          return _remove(set._inner, bytes32(value));
                      }
                      /**
                       * @dev Returns true if the value is in the set. O(1).
                       */
                      function contains(UintSet storage set, uint256 value) internal view returns (bool) {
                          return _contains(set._inner, bytes32(value));
                      }
                      /**
                       * @dev Returns the number of values on the set. O(1).
                       */
                      function length(UintSet storage set) internal view returns (uint256) {
                          return _length(set._inner);
                      }
                      /**
                       * @dev Returns the value stored at position `index` in the set. O(1).
                       *
                       * Note that there are no guarantees on the ordering of values inside the
                       * array, and it may change when more values are added or removed.
                       *
                       * Requirements:
                       *
                       * - `index` must be strictly less than {length}.
                       */
                      function at(UintSet storage set, uint256 index) internal view returns (uint256) {
                          return uint256(_at(set._inner, index));
                      }
                      /**
                       * @dev Return the entire set in an array
                       *
                       * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
                       * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
                       * this function has an unbounded cost, and using it as part of a state-changing function may render the function
                       * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
                       */
                      function values(UintSet storage set) internal view returns (uint256[] memory) {
                          bytes32[] memory store = _values(set._inner);
                          uint256[] memory result;
                          /// @solidity memory-safe-assembly
                          assembly {
                              result := store
                          }
                          return result;
                      }
                  }
                  // SPDX-License-Identifier: Apache-2.0
                  pragma solidity ^0.8.1;
                  /// @author thirdweb, OpenZeppelin Contracts (v4.9.0)
                  /**
                   * @dev Collection of functions related to the address type
                   */
                  library Address {
                      /**
                       * @dev Returns true if `account` is a contract.
                       *
                       * [IMPORTANT]
                       * ====
                       * It is unsafe to assume that an address for which this function returns
                       * false is an externally-owned account (EOA) and not a contract.
                       *
                       * Among others, `isContract` will return false for the following
                       * types of addresses:
                       *
                       *  - an externally-owned account
                       *  - a contract in construction
                       *  - an address where a contract will be created
                       *  - an address where a contract lived, but was destroyed
                       *
                       * Furthermore, `isContract` will also return true if the target contract within
                       * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
                       * which only has an effect at the end of a transaction.
                       * ====
                       *
                       * [IMPORTANT]
                       * ====
                       * You shouldn't rely on `isContract` to protect against flash loan attacks!
                       *
                       * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                       * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                       * constructor.
                       * ====
                       */
                      function isContract(address account) internal view returns (bool) {
                          // This method relies on extcodesize/address.code.length, which returns 0
                          // for contracts in construction, since the code is only stored at the end
                          // of the constructor execution.
                          return account.code.length > 0;
                      }
                      /**
                       * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                       * `recipient`, forwarding all available gas and reverting on errors.
                       *
                       * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                       * of certain opcodes, possibly making contracts go over the 2300 gas limit
                       * imposed by `transfer`, making them unable to receive funds via
                       * `transfer`. {sendValue} removes this limitation.
                       *
                       * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                       *
                       * IMPORTANT: because control is transferred to `recipient`, care must be
                       * taken to not create reentrancy vulnerabilities. Consider using
                       * {ReentrancyGuard} or the
                       * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                       */
                      function sendValue(address payable recipient, uint256 amount) internal {
                          require(address(this).balance >= amount, "Address: insufficient balance");
                          (bool success, ) = recipient.call{ value: amount }("");
                          require(success, "Address: unable to send value, recipient may have reverted");
                      }
                      /**
                       * @dev Performs a Solidity function call using a low level `call`. A
                       * plain `call` is an unsafe replacement for a function call: use this
                       * function instead.
                       *
                       * If `target` reverts with a revert reason, it is bubbled up by this
                       * function (like regular Solidity function calls).
                       *
                       * Returns the raw returned data. To convert to the expected return value,
                       * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                       *
                       * Requirements:
                       *
                       * - `target` must be a contract.
                       * - calling `target` with `data` must not revert.
                       *
                       * _Available since v3.1._
                       */
                      function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                          return functionCallWithValue(target, data, 0, "Address: low-level call failed");
                      }
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                       * `errorMessage` as a fallback revert reason when `target` reverts.
                       *
                       * _Available since v3.1._
                       */
                      function functionCall(
                          address target,
                          bytes memory data,
                          string memory errorMessage
                      ) internal returns (bytes memory) {
                          return functionCallWithValue(target, data, 0, errorMessage);
                      }
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                       * but also transferring `value` wei to `target`.
                       *
                       * Requirements:
                       *
                       * - the calling contract must have an ETH balance of at least `value`.
                       * - the called Solidity function must be `payable`.
                       *
                       * _Available since v3.1._
                       */
                      function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                          return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                      }
                      /**
                       * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                       * with `errorMessage` as a fallback revert reason when `target` reverts.
                       *
                       * _Available since v3.1._
                       */
                      function functionCallWithValue(
                          address target,
                          bytes memory data,
                          uint256 value,
                          string memory errorMessage
                      ) internal returns (bytes memory) {
                          require(address(this).balance >= value, "Address: insufficient balance for call");
                          (bool success, bytes memory returndata) = target.call{ value: value }(data);
                          return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                      }
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                       * but performing a static call.
                       *
                       * _Available since v3.3._
                       */
                      function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                          return functionStaticCall(target, data, "Address: low-level static call failed");
                      }
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                       * but performing a static call.
                       *
                       * _Available since v3.3._
                       */
                      function functionStaticCall(
                          address target,
                          bytes memory data,
                          string memory errorMessage
                      ) internal view returns (bytes memory) {
                          (bool success, bytes memory returndata) = target.staticcall(data);
                          return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                      }
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                       * but performing a delegate call.
                       *
                       * _Available since v3.4._
                       */
                      function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                          return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                      }
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                       * but performing a delegate call.
                       *
                       * _Available since v3.4._
                       */
                      function functionDelegateCall(
                          address target,
                          bytes memory data,
                          string memory errorMessage
                      ) internal returns (bytes memory) {
                          (bool success, bytes memory returndata) = target.delegatecall(data);
                          return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                      }
                      /**
                       * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
                       * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
                       *
                       * _Available since v4.8._
                       */
                      function verifyCallResultFromTarget(
                          address target,
                          bool success,
                          bytes memory returndata,
                          string memory errorMessage
                      ) internal view returns (bytes memory) {
                          if (success) {
                              if (returndata.length == 0) {
                                  // only check isContract if the call was successful and the return data is empty
                                  // otherwise we already know that it was a contract
                                  require(isContract(target), "Address: call to non-contract");
                              }
                              return returndata;
                          } else {
                              _revert(returndata, errorMessage);
                          }
                      }
                      /**
                       * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
                       * revert reason or using the provided one.
                       *
                       * _Available since v4.3._
                       */
                      function verifyCallResult(
                          bool success,
                          bytes memory returndata,
                          string memory errorMessage
                      ) internal pure returns (bytes memory) {
                          if (success) {
                              return returndata;
                          } else {
                              _revert(returndata, errorMessage);
                          }
                      }
                      function _revert(bytes memory returndata, string memory errorMessage) private pure {
                          // Look for revert reason and bubble it up if present
                          if (returndata.length > 0) {
                              // The easiest way to bubble the revert reason is using memory via assembly
                              /// @solidity memory-safe-assembly
                              assembly {
                                  let returndata_size := mload(returndata)
                                  revert(add(32, returndata), returndata_size)
                              }
                          } else {
                              revert(errorMessage);
                          }
                      }
                  }
                  // SPDX-License-Identifier: Apache-2.0
                  pragma solidity ^0.8.0;
                  /// @author thirdweb
                  /// Credits: https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol
                  library BytesLib {
                      function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {
                          require(_bytes.length >= _start + 20, "toAddress_outOfBounds");
                          address tempAddress;
                          assembly {
                              tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
                          }
                          return tempAddress;
                      }
                  }
                  // SPDX-License-Identifier: Apache-2.0
                  pragma solidity ^0.8.0;
                  /// @author thirdweb
                  /**
                   * @dev String operations.
                   */
                  library Strings {
                      bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
                      /**
                       * @dev Converts a `uint256` to its ASCII `string` decimal representation.
                       */
                      function toString(uint256 value) internal pure returns (string memory) {
                          // Inspired by OraclizeAPI's implementation - MIT licence
                          // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
                          if (value == 0) {
                              return "0";
                          }
                          uint256 temp = value;
                          uint256 digits;
                          while (temp != 0) {
                              digits++;
                              temp /= 10;
                          }
                          bytes memory buffer = new bytes(digits);
                          while (value != 0) {
                              digits -= 1;
                              buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
                              value /= 10;
                          }
                          return string(buffer);
                      }
                      /**
                       * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
                       */
                      function toHexString(uint256 value) internal pure returns (string memory) {
                          if (value == 0) {
                              return "0x00";
                          }
                          uint256 temp = value;
                          uint256 length = 0;
                          while (temp != 0) {
                              length++;
                              temp >>= 8;
                          }
                          return toHexString(value, length);
                      }
                      /**
                       * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
                       */
                      function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
                          bytes memory buffer = new bytes(2 * length + 2);
                          buffer[0] = "0";
                          buffer[1] = "x";
                          for (uint256 i = 2 * length + 1; i > 1; --i) {
                              buffer[i] = _HEX_SYMBOLS[value & 0xf];
                              value >>= 4;
                          }
                          require(value == 0, "Strings: hex length insufficient");
                          return string(buffer);
                      }
                      /// @dev Returns the hexadecimal representation of `value`.
                      /// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte,
                      /// and the alphabets are capitalized conditionally according to
                      /// https://eips.ethereum.org/EIPS/eip-55
                      function toHexStringChecksummed(address value) internal pure returns (string memory str) {
                          str = toHexString(value);
                          /// @solidity memory-safe-assembly
                          assembly {
                              let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`
                              let o := add(str, 0x22)
                              let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... `
                              let t := shl(240, 136) // `0b10001000 << 240`
                              for {
                                  let i := 0
                              } 1 {
                              } {
                                  mstore(add(i, i), mul(t, byte(i, hashed)))
                                  i := add(i, 1)
                                  if eq(i, 20) {
                                      break
                                  }
                              }
                              mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask)))))
                              o := add(o, 0x20)
                              mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask)))))
                          }
                      }
                      /// @dev Returns the hexadecimal representation of `value`.
                      /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
                      function toHexString(address value) internal pure returns (string memory str) {
                          str = toHexStringNoPrefix(value);
                          /// @solidity memory-safe-assembly
                          assembly {
                              let strLength := add(mload(str), 2) // Compute the length.
                              mstore(str, 0x3078) // Write the "0x" prefix.
                              str := sub(str, 2) // Move the pointer.
                              mstore(str, strLength) // Write the length.
                          }
                      }
                      /// @dev Returns the hexadecimal representation of `value`.
                      /// The output is encoded using 2 hexadecimal digits per byte.
                      function toHexStringNoPrefix(address value) internal pure returns (string memory str) {
                          /// @solidity memory-safe-assembly
                          assembly {
                              str := mload(0x40)
                              // Allocate the memory.
                              // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
                              // 0x02 bytes for the prefix, and 0x28 bytes for the digits.
                              // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.
                              mstore(0x40, add(str, 0x80))
                              // Store "0123456789abcdef" in scratch space.
                              mstore(0x0f, 0x30313233343536373839616263646566)
                              str := add(str, 2)
                              mstore(str, 40)
                              let o := add(str, 0x20)
                              mstore(add(o, 40), 0)
                              value := shl(96, value)
                              // We write the string from rightmost digit to leftmost digit.
                              // The following is essentially a do-while loop that also handles the zero case.
                              for {
                                  let i := 0
                              } 1 {
                              } {
                                  let p := add(o, add(i, i))
                                  let temp := byte(i, value)
                                  mstore8(add(p, 1), mload(and(temp, 15)))
                                  mstore8(p, mload(shr(4, temp)))
                                  i := add(i, 1)
                                  if eq(i, 20) {
                                      break
                                  }
                              }
                          }
                      }
                      /// @dev Returns the hex encoded string from the raw bytes.
                      /// The output is encoded using 2 hexadecimal digits per byte.
                      function toHexString(bytes memory raw) internal pure returns (string memory str) {
                          str = toHexStringNoPrefix(raw);
                          /// @solidity memory-safe-assembly
                          assembly {
                              let strLength := add(mload(str), 2) // Compute the length.
                              mstore(str, 0x3078) // Write the "0x" prefix.
                              str := sub(str, 2) // Move the pointer.
                              mstore(str, strLength) // Write the length.
                          }
                      }
                      /// @dev Returns the hex encoded string from the raw bytes.
                      /// The output is encoded using 2 hexadecimal digits per byte.
                      function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) {
                          /// @solidity memory-safe-assembly
                          assembly {
                              let length := mload(raw)
                              str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.
                              mstore(str, add(length, length)) // Store the length of the output.
                              // Store "0123456789abcdef" in scratch space.
                              mstore(0x0f, 0x30313233343536373839616263646566)
                              let o := add(str, 0x20)
                              let end := add(raw, length)
                              for {
                              } iszero(eq(raw, end)) {
                              } {
                                  raw := add(raw, 1)
                                  mstore8(add(o, 1), mload(and(mload(raw), 15)))
                                  mstore8(o, mload(and(shr(4, mload(raw)), 15)))
                                  o := add(o, 2)
                              }
                              mstore(o, 0) // Zeroize the slot after the string.
                              mstore(0x40, add(o, 0x20)) // Allocate the memory.
                          }
                      }
                  }
                  // SPDX-License-Identifier: GPL-3.0
                  pragma solidity ^0.8.12;
                  import "../utils/UserOperation.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(
                          UserOperation calldata userOp,
                          bytes32 userOpHash,
                          uint256 missingAccountFunds
                      ) external returns (uint256 validationData);
                  }
                  // SPDX-License-Identifier: Apache 2.0
                  pragma solidity ^0.8.12;
                  import "./IAccount.sol";
                  import "../../../extension/interface/IAccountPermissions.sol";
                  import "../../../extension/interface/IMulticall.sol";
                  interface IAccountCore is IAccount, IAccountPermissions, IMulticall {
                      /// @dev Returns the address of the factory from which the account was created.
                      function factory() external view returns (address);
                  }
                  // SPDX-License-Identifier: GPL-3.0
                  pragma solidity ^0.8.12;
                  import "./IAccountFactoryCore.sol";
                  interface IAccountFactory is IAccountFactoryCore {
                      /*///////////////////////////////////////////////////////////////
                                          Callback Functions
                      //////////////////////////////////////////////////////////////*/
                      /// @notice Callback function for an Account to register its signers.
                      function onSignerAdded(address signer, bytes32 salt) external;
                      /// @notice Callback function for an Account to un-register its signers.
                      function onSignerRemoved(address signer, bytes32 salt) external;
                  }
                  // SPDX-License-Identifier: GPL-3.0
                  pragma solidity ^0.8.12;
                  interface IAccountFactoryCore {
                      /*///////////////////////////////////////////////////////////////
                                                  Events
                      //////////////////////////////////////////////////////////////*/
                      /// @notice Emitted when a new Account is created.
                      event AccountCreated(address indexed account, address indexed accountAdmin);
                      /// @notice Emitted when a new signer is added to an Account.
                      event SignerAdded(address indexed account, address indexed signer);
                      /// @notice Emitted when a new signer is added to an Account.
                      event SignerRemoved(address indexed account, address indexed signer);
                      /*///////////////////////////////////////////////////////////////
                                          Extension Functions
                      //////////////////////////////////////////////////////////////*/
                      /// @notice Deploys a new Account for admin.
                      function createAccount(address admin, bytes calldata _data) external returns (address account);
                      /*///////////////////////////////////////////////////////////////
                                              View Functions
                      //////////////////////////////////////////////////////////////*/
                      /// @notice Returns the address of the Account implementation.
                      function accountImplementation() external view returns (address);
                      /// @notice Returns all accounts created on the factory.
                      function getAllAccounts() external view returns (address[] memory);
                      /// @notice Returns the address of an Account that would be deployed with the given admin signer.
                      function getAddress(address adminSigner, bytes calldata data) external view returns (address);
                      /// @notice Returns all accounts on which a signer has (active or inactive) permissions.
                      function getAccountsOfSigner(address signer) external view returns (address[] memory accounts);
                  }
                  // SPDX-License-Identifier: GPL-3.0
                  pragma solidity ^0.8.12;
                  import "../utils/UserOperation.sol";
                  /**
                   * Aggregated Signatures validator.
                   */
                  interface IAggregator {
                      /**
                       * validate aggregated signature.
                       * revert if the aggregated signature does not match the given list of operations.
                       */
                      function validateSignatures(UserOperation[] calldata userOps, bytes calldata signature) external view;
                      /**
                       * validate signature of a single userOp
                       * This method is should be called by bundler after EntryPoint.simulateValidation() returns (reverts) with ValidationResultWithAggregation
                       * 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(UserOperation 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(
                          UserOperation[] 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.8.12;
                  /* solhint-disable avoid-low-level-calls */
                  /* solhint-disable no-inline-assembly */
                  /* solhint-disable reason-string */
                  import "../utils/UserOperation.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 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.
                       */
                      event SignatureAggregatorChanged(address indexed aggregator);
                      /**
                       * a custom revert error of handleOps, to identify the offending op.
                       *  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.
                       *   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.
                       */
                      error FailedOp(uint256 opIndex, string reason);
                      /**
                       * error case when a signature aggregator fails to verify the aggregated signature it had created.
                       */
                      error SignatureValidationFailed(address aggregator);
                      /**
                       * Successful result from simulateValidation.
                       * @param returnInfo gas and time-range returned values
                       * @param senderInfo stake information about the sender
                       * @param factoryInfo stake information about the factory (if any)
                       * @param paymasterInfo stake information about the paymaster (if any)
                       */
                      error ValidationResult(ReturnInfo returnInfo, StakeInfo senderInfo, StakeInfo factoryInfo, StakeInfo paymasterInfo);
                      /**
                       * Successful result from simulateValidation, if the account returns a signature aggregator
                       * @param returnInfo gas and time-range returned values
                       * @param senderInfo stake information about the sender
                       * @param factoryInfo stake information about the factory (if any)
                       * @param paymasterInfo stake information about the paymaster (if any)
                       * @param aggregatorInfo signature aggregation info (if the account requires signature aggregator)
                       *      bundler MUST use it to verify the signature, or reject the UserOperation
                       */
                      error ValidationResultWithAggregation(
                          ReturnInfo returnInfo,
                          StakeInfo senderInfo,
                          StakeInfo factoryInfo,
                          StakeInfo paymasterInfo,
                          AggregatorStakeInfo aggregatorInfo
                      );
                      /**
                       * return value of getSenderAddress
                       */
                      error SenderAddressResult(address sender);
                      /**
                       * return value of simulateHandleOp
                       */
                      error ExecutionResult(
                          uint256 preOpGas,
                          uint256 paid,
                          uint48 validAfter,
                          uint48 validUntil,
                          bool targetSuccess,
                          bytes targetResult
                      );
                      //UserOps handled, per aggregator
                      struct UserOpsPerAggregator {
                          UserOperation[] userOps;
                          // aggregator address
                          IAggregator aggregator;
                          // aggregated signature
                          bytes signature;
                      }
                      /**
                       * Execute a batch of UserOperation.
                       * 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(UserOperation[] 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.
                       */
                      function getUserOpHash(UserOperation calldata userOp) external view returns (bytes32);
                      /**
                       * Simulate a call to account.validateUserOp and paymaster.validatePaymasterUserOp.
                       * @dev this method always revert. Successful result is ValidationResult error. other errors are failures.
                       * @dev The node must also verify it doesn't use banned opcodes, and that it doesn't reference storage outside the account's data.
                       * @param userOp the user operation to validate.
                       */
                      function simulateValidation(UserOperation calldata userOp) external;
                      /**
                       * gas and return values during simulation
                       * @param preOpGas the gas used for validation (including preValidationGas)
                       * @param prefund the required prefund for this operation
                       * @param sigFailed validateUserOp's (or paymaster's) signature check failed
                       * @param validAfter - first timestamp this UserOp is valid (merging account and paymaster time-range)
                       * @param validUntil - last timestamp this UserOp is valid (merging account and paymaster time-range)
                       * @param paymasterContext returned by validatePaymasterUserOp (to be passed into postOp)
                       */
                      struct ReturnInfo {
                          uint256 preOpGas;
                          uint256 prefund;
                          bool sigFailed;
                          uint48 validAfter;
                          uint48 validUntil;
                          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;
                      /**
                       * simulate full execution of a UserOperation (including both validation and target execution)
                       * this method will always revert with "ExecutionResult".
                       * it performs full validation of the UserOperation, but ignores signature error.
                       * an optional target address is called after the userop succeeds, and its value is returned
                       * (before the entire call is reverted)
                       * Note that in order to collect the success/failure of the target call, it must be executed
                       * with trace enabled to track the emitted events.
                       * @param op the UserOperation to simulate
                       * @param target if nonzero, a target address to call after userop simulation. If called, the targetSuccess and targetResult
                       *        are set to the return from that call.
                       * @param targetCallData callData to pass to target address
                       */
                      function simulateHandleOp(UserOperation calldata op, address target, bytes calldata targetCallData) external;
                  }
                  // SPDX-License-Identifier: GPL-3.0
                  pragma solidity ^0.8.12;
                  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-only
                  pragma solidity ^0.8.12;
                  /**
                   * 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,staked, stake) fit into one cell (used during handleOps)
                       *    and the rest fit into a 2nd cell.
                       *    112 bit allows for 10^15 eth
                       *    48 bit for full timestamp
                       *    32 bit allows 150 years for unstake delay
                       */
                      struct DepositInfo {
                          uint112 deposit;
                          bool staked;
                          uint112 stake;
                          uint32 unstakeDelaySec;
                          uint48 withdrawTime;
                      }
                      //API struct used by getStakeInfo and simulateValidation
                      struct StakeInfo {
                          uint256 stake;
                          uint256 unstakeDelaySec;
                      }
                      /// @return info - full deposit information of given account
                      function getDepositInfo(address account) external view returns (DepositInfo memory info);
                      /// @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
                       */
                      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: Apache-2.0
                  pragma solidity ^0.8.11;
                  /* solhint-disable avoid-low-level-calls */
                  /* solhint-disable no-inline-assembly */
                  /* solhint-disable reason-string */
                  // Base
                  import "../utils/BaseAccount.sol";
                  // Extensions
                  import "../utils/AccountCore.sol";
                  import "../../../extension/upgradeable/ContractMetadata.sol";
                  import "../../../external-deps/openzeppelin/token/ERC721/utils/ERC721Holder.sol";
                  import "../../../external-deps/openzeppelin/token/ERC1155/utils/ERC1155Holder.sol";
                  // Utils
                  import "../../../eip/ERC1271.sol";
                  import "../utils/Helpers.sol";
                  import "../../../external-deps/openzeppelin/utils/cryptography/ECDSA.sol";
                  import "../utils/BaseAccountFactory.sol";
                  //   $$\\     $$\\       $$\\                 $$\\                         $$\\
                  //   $$ |    $$ |      \\__|                $$ |                        $$ |
                  // $$$$$$\\   $$$$$$$\\  $$\\  $$$$$$\\   $$$$$$$ |$$\\  $$\\  $$\\  $$$$$$\\  $$$$$$$\\
                  // \\_$$  _|  $$  __$$\\ $$ |$$  __$$\\ $$  __$$ |$$ | $$ | $$ |$$  __$$\\ $$  __$$\\
                  //   $$ |    $$ |  $$ |$$ |$$ |  \\__|$$ /  $$ |$$ | $$ | $$ |$$$$$$$$ |$$ |  $$ |
                  //   $$ |$$\\ $$ |  $$ |$$ |$$ |      $$ |  $$ |$$ | $$ | $$ |$$   ____|$$ |  $$ |
                  //   \\$$$$  |$$ |  $$ |$$ |$$ |      \\$$$$$$$ |\\$$$$$\\$$$$  |\\$$$$$$$\\ $$$$$$$  |
                  //    \\____/ \\__|  \\__|\\__|\\__|       \\_______| \\_____\\____/  \\_______|\\_______/
                  contract Account is AccountCore, ContractMetadata, ERC1271, ERC721Holder, ERC1155Holder {
                      using ECDSA for bytes32;
                      using EnumerableSet for EnumerableSet.AddressSet;
                      bytes32 private constant MSG_TYPEHASH = keccak256("AccountMessage(bytes message)");
                      /*///////////////////////////////////////////////////////////////
                                      Constructor, Initializer, Modifiers
                      //////////////////////////////////////////////////////////////*/
                      constructor(IEntryPoint _entrypoint, address _factory) AccountCore(_entrypoint, _factory) {}
                      /// @notice Checks whether the caller is the EntryPoint contract or the admin.
                      modifier onlyAdminOrEntrypoint() virtual {
                          require(msg.sender == address(entryPoint()) || isAdmin(msg.sender), "Account: not admin or EntryPoint.");
                          _;
                      }
                      /// @notice Lets the account receive native tokens.
                      receive() external payable {}
                      /*///////////////////////////////////////////////////////////////
                                              View functions
                      //////////////////////////////////////////////////////////////*/
                      /// @notice See {IERC165-supportsInterface}.
                      function supportsInterface(bytes4 interfaceId) public view virtual override(ERC1155Receiver) returns (bool) {
                          return
                              interfaceId == type(IERC1155Receiver).interfaceId ||
                              interfaceId == type(IERC721Receiver).interfaceId ||
                              super.supportsInterface(interfaceId);
                      }
                      /**
                       *  @notice See EIP-1271
                       *
                       *  @param _hash The original message hash of the data to sign (before mixing this contract's domain separator)
                       *  @param _signature The signature produced on signing the typed data hash (result of `getMessageHash(abi.encode(rawData))`)
                       */
                      function isValidSignature(
                          bytes32 _hash,
                          bytes memory _signature
                      ) public view virtual override returns (bytes4 magicValue) {
                          bytes32 targetDigest = getMessageHash(_hash);
                          address signer = targetDigest.recover(_signature);
                          if (isAdmin(signer)) {
                              return MAGICVALUE;
                          }
                          address caller = msg.sender;
                          EnumerableSet.AddressSet storage approvedTargets = _accountPermissionsStorage().approvedTargets[signer];
                          require(
                              approvedTargets.contains(caller) || (approvedTargets.length() == 1 && approvedTargets.at(0) == address(0)),
                              "Account: caller not approved target."
                          );
                          if (isActiveSigner(signer)) {
                              magicValue = MAGICVALUE;
                          }
                      }
                      /**
                       * @notice Returns the hash of message that should be signed for EIP1271 verification.
                       * @param _hash The message hash to sign for the EIP-1271 origin verifying contract.
                       * @return messageHash The digest to sign for EIP-1271 verification.
                       */
                      function getMessageHash(bytes32 _hash) public view returns (bytes32) {
                          bytes32 messageHash = keccak256(abi.encode(_hash));
                          bytes32 typedDataHash = keccak256(abi.encode(MSG_TYPEHASH, messageHash));
                          return keccak256(abi.encodePacked("\\x19\\x01", _domainSeparatorV4(), typedDataHash));
                      }
                      /*///////////////////////////////////////////////////////////////
                                              External functions
                      //////////////////////////////////////////////////////////////*/
                      /// @notice Executes a transaction (called directly from an admin, or by entryPoint)
                      function execute(address _target, uint256 _value, bytes calldata _calldata) external virtual onlyAdminOrEntrypoint {
                          _registerOnFactory();
                          _call(_target, _value, _calldata);
                      }
                      /// @notice Executes a sequence transaction (called directly from an admin, or by entryPoint)
                      function executeBatch(
                          address[] calldata _target,
                          uint256[] calldata _value,
                          bytes[] calldata _calldata
                      ) external virtual onlyAdminOrEntrypoint {
                          _registerOnFactory();
                          require(_target.length == _calldata.length && _target.length == _value.length, "Account: wrong array lengths.");
                          for (uint256 i = 0; i < _target.length; i++) {
                              _call(_target[i], _value[i], _calldata[i]);
                          }
                      }
                      /// @notice Deposit funds for this account in Entrypoint.
                      function addDeposit() public payable {
                          entryPoint().depositTo{ value: msg.value }(address(this));
                      }
                      /// @notice Withdraw funds for this account from Entrypoint.
                      function withdrawDepositTo(address payable withdrawAddress, uint256 amount) public {
                          _onlyAdmin();
                          entryPoint().withdrawTo(withdrawAddress, amount);
                      }
                      /*///////////////////////////////////////////////////////////////
                                          Internal functions
                      //////////////////////////////////////////////////////////////*/
                      /// @dev Registers the account on the factory if it hasn't been registered yet.
                      function _registerOnFactory() internal virtual {
                          BaseAccountFactory factoryContract = BaseAccountFactory(factory);
                          if (!factoryContract.isRegistered(address(this))) {
                              factoryContract.onRegister(AccountCoreStorage.data().creationSalt);
                          }
                      }
                      /// @dev Calls a target contract and reverts if it fails.
                      function _call(
                          address _target,
                          uint256 value,
                          bytes memory _calldata
                      ) internal virtual returns (bytes memory result) {
                          bool success;
                          (success, result) = _target.call{ value: value }(_calldata);
                          if (!success) {
                              assembly {
                                  revert(add(result, 32), mload(result))
                              }
                          }
                      }
                      /// @dev Returns whether contract metadata can be set in the given execution context.
                      function _canSetContractURI() internal view virtual override returns (bool) {
                          return isAdmin(msg.sender) || msg.sender == address(this);
                      }
                  }
                  // SPDX-License-Identifier: GPL-3.0
                  pragma solidity ^0.8.12;
                  // Utils
                  import "../utils/BaseAccountFactory.sol";
                  import "../utils/BaseAccount.sol";
                  import "../../../external-deps/openzeppelin/proxy/Clones.sol";
                  // Extensions
                  import "../../../extension/upgradeable//PermissionsEnumerable.sol";
                  import "../../../extension/upgradeable//ContractMetadata.sol";
                  // Interface
                  import "../interface/IEntrypoint.sol";
                  // Smart wallet implementation
                  import { Account } from "./Account.sol";
                  //   $$\\     $$\\       $$\\                 $$\\                         $$\\
                  //   $$ |    $$ |      \\__|                $$ |                        $$ |
                  // $$$$$$\\   $$$$$$$\\  $$\\  $$$$$$\\   $$$$$$$ |$$\\  $$\\  $$\\  $$$$$$\\  $$$$$$$\\
                  // \\_$$  _|  $$  __$$\\ $$ |$$  __$$\\ $$  __$$ |$$ | $$ | $$ |$$  __$$\\ $$  __$$\\
                  //   $$ |    $$ |  $$ |$$ |$$ |  \\__|$$ /  $$ |$$ | $$ | $$ |$$$$$$$$ |$$ |  $$ |
                  //   $$ |$$\\ $$ |  $$ |$$ |$$ |      $$ |  $$ |$$ | $$ | $$ |$$   ____|$$ |  $$ |
                  //   \\$$$$  |$$ |  $$ |$$ |$$ |      \\$$$$$$$ |\\$$$$$\\$$$$  |\\$$$$$$$\\ $$$$$$$  |
                  //    \\____/ \\__|  \\__|\\__|\\__|       \\_______| \\_____\\____/  \\_______|\\_______/
                  contract AccountFactory is BaseAccountFactory, ContractMetadata, PermissionsEnumerable {
                      /*///////////////////////////////////////////////////////////////
                                              Constructor
                      //////////////////////////////////////////////////////////////*/
                      constructor(
                          address _defaultAdmin,
                          IEntryPoint _entrypoint
                      ) BaseAccountFactory(address(new Account(_entrypoint, address(this))), address(_entrypoint)) {
                          _setupRole(DEFAULT_ADMIN_ROLE, _defaultAdmin);
                      }
                      /*///////////////////////////////////////////////////////////////
                                          Internal functions
                      //////////////////////////////////////////////////////////////*/
                      /// @dev Called in `createAccount`. Initializes the account contract created in `createAccount`.
                      function _initializeAccount(address _account, address _admin, bytes calldata _data) internal override {
                          Account(payable(_account)).initialize(_admin, _data);
                      }
                      /// @dev Returns whether contract metadata can be set in the given execution context.
                      function _canSetContractURI() internal view virtual override returns (bool) {
                          return hasRole(DEFAULT_ADMIN_ROLE, msg.sender);
                      }
                      /// @notice Returns the sender in the given execution context.
                      function _msgSender() internal view override(Multicall, Permissions) returns (address) {
                          return msg.sender;
                      }
                  }
                  // SPDX-License-Identifier: Apache-2.0
                  pragma solidity ^0.8.11;
                  /* solhint-disable avoid-low-level-calls */
                  /* solhint-disable no-inline-assembly */
                  /* solhint-disable reason-string */
                  // Base
                  import "./../utils/BaseAccount.sol";
                  // Fixed Extensions
                  import "../../../extension/Multicall.sol";
                  import "../../../extension/upgradeable/Initializable.sol";
                  import "../../../extension/upgradeable/AccountPermissions.sol";
                  // Utils
                  import "./Helpers.sol";
                  import "./AccountCoreStorage.sol";
                  import "./BaseAccountFactory.sol";
                  import { AccountExtension } from "./AccountExtension.sol";
                  import "../../../external-deps/openzeppelin/utils/cryptography/ECDSA.sol";
                  import "../interface/IAccountCore.sol";
                  //   $$\\     $$\\       $$\\                 $$\\                         $$\\
                  //   $$ |    $$ |      \\__|                $$ |                        $$ |
                  // $$$$$$\\   $$$$$$$\\  $$\\  $$$$$$\\   $$$$$$$ |$$\\  $$\\  $$\\  $$$$$$\\  $$$$$$$\\
                  // \\_$$  _|  $$  __$$\\ $$ |$$  __$$\\ $$  __$$ |$$ | $$ | $$ |$$  __$$\\ $$  __$$\\
                  //   $$ |    $$ |  $$ |$$ |$$ |  \\__|$$ /  $$ |$$ | $$ | $$ |$$$$$$$$ |$$ |  $$ |
                  //   $$ |$$\\ $$ |  $$ |$$ |$$ |      $$ |  $$ |$$ | $$ | $$ |$$   ____|$$ |  $$ |
                  //   \\$$$$  |$$ |  $$ |$$ |$$ |      \\$$$$$$$ |\\$$$$$\\$$$$  |\\$$$$$$$\\ $$$$$$$  |
                  //    \\____/ \\__|  \\__|\\__|\\__|       \\_______| \\_____\\____/  \\_______|\\_______/
                  contract AccountCore is IAccountCore, Initializable, Multicall, BaseAccount, AccountPermissions {
                      using ECDSA for bytes32;
                      using EnumerableSet for EnumerableSet.AddressSet;
                      /*///////////////////////////////////////////////////////////////
                                                  State
                      //////////////////////////////////////////////////////////////*/
                      /// @notice EIP 4337 factory for this contract.
                      address public immutable factory;
                      /// @notice EIP 4337 Entrypoint contract.
                      IEntryPoint private immutable entrypointContract;
                      /*///////////////////////////////////////////////////////////////
                                      Constructor, Initializer, Modifiers
                      //////////////////////////////////////////////////////////////*/
                      constructor(IEntryPoint _entrypoint, address _factory) EIP712("Account", "1") {
                          _disableInitializers();
                          factory = _factory;
                          entrypointContract = _entrypoint;
                      }
                      /// @notice Initializes the smart contract wallet.
                      function initialize(address _defaultAdmin, bytes calldata _data) public virtual initializer {
                          // This is passed as data in the `_registerOnFactory()` call in `AccountExtension` / `Account`.
                          AccountCoreStorage.data().creationSalt = _generateSalt(_defaultAdmin, _data);
                          _setAdmin(_defaultAdmin, true);
                      }
                      /*///////////////////////////////////////////////////////////////
                                              View functions
                      //////////////////////////////////////////////////////////////*/
                      /// @notice Returns the EIP 4337 entrypoint contract.
                      function entryPoint() public view virtual override returns (IEntryPoint) {
                          address entrypointOverride = AccountCoreStorage.data().entrypointOverride;
                          if (address(entrypointOverride) != address(0)) {
                              return IEntryPoint(entrypointOverride);
                          }
                          return entrypointContract;
                      }
                      /** 
                      @notice Returns whether a signer is authorized to perform transactions using the account.
                              Validity of the signature is based upon signer permission start/end timestamps, txn target, and txn value.
                              Account admins will always return true, and signers with address(0) as the only approved target will skip target checks.
                      @param _signer The signer to check.
                      @param _userOp The user operation to check.
                      @return Whether the signer is authorized to perform the transaction.
                      */
                      /* solhint-disable*/
                      function isValidSigner(address _signer, UserOperation calldata _userOp) public view virtual returns (bool) {
                          // First, check if the signer is an admin.
                          if (_accountPermissionsStorage().isAdmin[_signer]) {
                              return true;
                          }
                          SignerPermissionsStatic memory permissions = _accountPermissionsStorage().signerPermissions[_signer];
                          EnumerableSet.AddressSet storage approvedTargets = _accountPermissionsStorage().approvedTargets[_signer];
                          // If not an admin, check if the signer is active.
                          if (
                              permissions.startTimestamp > block.timestamp ||
                              block.timestamp >= permissions.endTimestamp ||
                              approvedTargets.length() == 0
                          ) {
                              // Account: no active permissions.
                              return false;
                          }
                          // Extract the function signature from the userOp calldata and check whether the signer is attempting to call `execute` or `executeBatch`.
                          bytes4 sig = getFunctionSignature(_userOp.callData);
                          // if address(0) is the only approved target, set isWildCard to true (wildcard approved).
                          bool isWildCard = approvedTargets.length() == 1 && approvedTargets.at(0) == address(0);
                          // checking target and value for `execute`
                          if (sig == AccountExtension.execute.selector) {
                              // Extract the `target` and `value` arguments from the calldata for `execute`.
                              (address target, uint256 value) = decodeExecuteCalldata(_userOp.callData);
                              // if wildcard target is not approved, check that the target is in the approvedTargets set.
                              if (!isWildCard) {
                                  // Check if the target is approved.
                                  if (!approvedTargets.contains(target)) {
                                      // Account: target not approved.
                                      return false;
                                  }
                              }
                              // Check if the value is within the allowed range.
                              if (permissions.nativeTokenLimitPerTransaction < value) {
                                  // Account: value too high OR Account: target not approved.
                                  return false;
                              }
                          }
                          // checking target and value for `executeBatch`
                          else if (sig == AccountExtension.executeBatch.selector) {
                              // Extract the `target` and `value` array arguments from the calldata for `executeBatch`.
                              (address[] memory targets, uint256[] memory values, ) = decodeExecuteBatchCalldata(_userOp.callData);
                              // if wildcard target is not approved, check that the targets are in the approvedTargets set.
                              if (!isWildCard) {
                                  for (uint256 i = 0; i < targets.length; i++) {
                                      if (!approvedTargets.contains(targets[i])) {
                                          // If any target is not approved, break the loop.
                                          return false;
                                      }
                                  }
                              }
                              // For each target+value pair, check if the value is within the allowed range.
                              for (uint256 i = 0; i < targets.length; i++) {
                                  if (permissions.nativeTokenLimitPerTransaction < values[i]) {
                                      // Account: value too high OR Account: target not approved.
                                      return false;
                                  }
                              }
                          } else {
                              // Account: calling invalid fn.
                              return false;
                          }
                          return true;
                      }
                      /* solhint-enable */
                      /*///////////////////////////////////////////////////////////////
                                              External functions
                      //////////////////////////////////////////////////////////////*/
                      /// @notice Overrides the Entrypoint contract being used.
                      function setEntrypointOverride(IEntryPoint _entrypointOverride) public virtual {
                          _onlyAdmin();
                          AccountCoreStorage.data().entrypointOverride = address(_entrypointOverride);
                      }
                      /*///////////////////////////////////////////////////////////////
                                          Internal functions
                      //////////////////////////////////////////////////////////////*/
                      /// @dev Returns the salt used when deploying an Account.
                      function _generateSalt(address _admin, bytes memory _data) internal view virtual returns (bytes32) {
                          return keccak256(abi.encode(_admin, _data));
                      }
                      function getFunctionSignature(bytes calldata data) internal pure returns (bytes4 functionSelector) {
                          require(data.length >= 4, "!Data");
                          return bytes4(data[:4]);
                      }
                      function decodeExecuteCalldata(bytes calldata data) internal pure returns (address _target, uint256 _value) {
                          require(data.length >= 4 + 32 + 32, "!Data");
                          // Decode the address, which is bytes 4 to 35
                          _target = abi.decode(data[4:36], (address));
                          // Decode the value, which is bytes 36 to 68
                          _value = abi.decode(data[36:68], (uint256));
                      }
                      function decodeExecuteBatchCalldata(
                          bytes calldata data
                      ) internal pure returns (address[] memory _targets, uint256[] memory _values, bytes[] memory _callData) {
                          require(data.length >= 4 + 32 + 32 + 32, "!Data");
                          (_targets, _values, _callData) = abi.decode(data[4:], (address[], uint256[], bytes[]));
                      }
                      /// @notice Validates the signature of a user operation.
                      function _validateSignature(
                          UserOperation calldata userOp,
                          bytes32 userOpHash
                      ) internal virtual override returns (uint256 validationData) {
                          bytes32 hash = userOpHash.toEthSignedMessageHash();
                          address signer = hash.recover(userOp.signature);
                          if (!isValidSigner(signer, userOp)) return SIG_VALIDATION_FAILED;
                          SignerPermissionsStatic memory permissions = _accountPermissionsStorage().signerPermissions[signer];
                          uint48 validAfter = uint48(permissions.startTimestamp);
                          uint48 validUntil = uint48(permissions.endTimestamp);
                          return _packValidationData(ValidationData(address(0), validAfter, validUntil));
                      }
                      /// @notice Makes the given account an admin.
                      function _setAdmin(address _account, bool _isAdmin) internal virtual override {
                          super._setAdmin(_account, _isAdmin);
                          if (factory.code.length > 0) {
                              if (_isAdmin) {
                                  BaseAccountFactory(factory).onSignerAdded(_account, AccountCoreStorage.data().creationSalt);
                              } else {
                                  BaseAccountFactory(factory).onSignerRemoved(_account, AccountCoreStorage.data().creationSalt);
                              }
                          }
                      }
                      /// @notice Runs after every `changeRole` run.
                      function _afterSignerPermissionsUpdate(SignerPermissionRequest calldata _req) internal virtual override {
                          if (factory.code.length > 0) {
                              BaseAccountFactory(factory).onSignerAdded(_req.signer, AccountCoreStorage.data().creationSalt);
                          }
                      }
                  }
                  // SPDX-License-Identifier: Apache-2.0
                  pragma solidity ^0.8.11;
                  library AccountCoreStorage {
                      /// @custom:storage-location erc7201:account.core.storage
                      /// @dev keccak256(abi.encode(uint256(keccak256("account.core.storage")) - 1)) & ~bytes32(uint256(0xff))
                      bytes32 public constant ACCOUNT_CORE_STORAGE_POSITION =
                          0x036f52c1827dab135f7fd44ca0bddde297e2f659c710e0ec53e975f22b548300;
                      struct Data {
                          address entrypointOverride;
                          bytes32 creationSalt;
                      }
                      function data() internal pure returns (Data storage acountCoreData) {
                          bytes32 position = ACCOUNT_CORE_STORAGE_POSITION;
                          assembly {
                              acountCoreData.slot := position
                          }
                      }
                  }
                  // SPDX-License-Identifier: Apache-2.0
                  pragma solidity ^0.8.11;
                  /* solhint-disable avoid-low-level-calls */
                  /* solhint-disable no-inline-assembly */
                  /* solhint-disable reason-string */
                  // Extensions
                  import "../../../extension/upgradeable/AccountPermissions.sol";
                  import "../../../extension/upgradeable/ContractMetadata.sol";
                  import "../../../external-deps/openzeppelin/token/ERC721/utils/ERC721Holder.sol";
                  import "../../../external-deps/openzeppelin/token/ERC1155/utils/ERC1155Holder.sol";
                  // Utils
                  import "../../../eip/ERC1271.sol";
                  import "../../../external-deps/openzeppelin/utils/cryptography/ECDSA.sol";
                  import "../../../external-deps/openzeppelin/utils/structs/EnumerableSet.sol";
                  import "./BaseAccountFactory.sol";
                  import "./AccountCore.sol";
                  import "./AccountCoreStorage.sol";
                  //   $$\\     $$\\       $$\\                 $$\\                         $$\\
                  //   $$ |    $$ |      \\__|                $$ |                        $$ |
                  // $$$$$$\\   $$$$$$$\\  $$\\  $$$$$$\\   $$$$$$$ |$$\\  $$\\  $$\\  $$$$$$\\  $$$$$$$\\
                  // \\_$$  _|  $$  __$$\\ $$ |$$  __$$\\ $$  __$$ |$$ | $$ | $$ |$$  __$$\\ $$  __$$\\
                  //   $$ |    $$ |  $$ |$$ |$$ |  \\__|$$ /  $$ |$$ | $$ | $$ |$$$$$$$$ |$$ |  $$ |
                  //   $$ |$$\\ $$ |  $$ |$$ |$$ |      $$ |  $$ |$$ | $$ | $$ |$$   ____|$$ |  $$ |
                  //   \\$$$$  |$$ |  $$ |$$ |$$ |      \\$$$$$$$ |\\$$$$$\\$$$$  |\\$$$$$$$\\ $$$$$$$  |
                  //    \\____/ \\__|  \\__|\\__|\\__|       \\_______| \\_____\\____/  \\_______|\\_______/
                  contract AccountExtension is ContractMetadata, ERC1271, AccountPermissions, ERC721Holder, ERC1155Holder {
                      using ECDSA for bytes32;
                      using EnumerableSet for EnumerableSet.AddressSet;
                      bytes32 private constant MSG_TYPEHASH = keccak256("AccountMessage(bytes message)");
                      /*///////////////////////////////////////////////////////////////
                                      Constructor, Initializer, Modifiers
                      //////////////////////////////////////////////////////////////*/
                      /// @notice Checks whether the caller is the EntryPoint contract or the admin.
                      modifier onlyAdminOrEntrypoint() virtual {
                          require(
                              msg.sender == address(AccountCore(payable(address(this))).entryPoint()) || isAdmin(msg.sender),
                              "Account: not admin or EntryPoint."
                          );
                          _;
                      }
                      // solhint-disable-next-line no-empty-blocks
                      receive() external payable virtual {}
                      constructor() EIP712("Account", "1") {}
                      /*///////////////////////////////////////////////////////////////
                                              View functions
                      //////////////////////////////////////////////////////////////*/
                      /// @notice See {IERC165-supportsInterface}.
                      function supportsInterface(bytes4 interfaceId) public view virtual override(ERC1155Receiver) returns (bool) {
                          return
                              interfaceId == type(IERC1155Receiver).interfaceId ||
                              interfaceId == type(IERC721Receiver).interfaceId ||
                              super.supportsInterface(interfaceId);
                      }
                      /**
                       *  @notice See EIP-1271
                       *
                       *  @param _hash The original message hash of the data to sign (before mixing this contract's domain separator)
                       *  @param _signature The signature produced on signing the typed data hash (result of `getMessageHash(abi.encode(rawData))`)
                       */
                      function isValidSignature(
                          bytes32 _hash,
                          bytes memory _signature
                      ) public view virtual override returns (bytes4 magicValue) {
                          bytes32 targetDigest = getMessageHash(_hash);
                          address signer = targetDigest.recover(_signature);
                          if (isAdmin(signer)) {
                              return MAGICVALUE;
                          }
                          address caller = msg.sender;
                          EnumerableSet.AddressSet storage approvedTargets = _accountPermissionsStorage().approvedTargets[signer];
                          require(
                              approvedTargets.contains(caller) || (approvedTargets.length() == 1 && approvedTargets.at(0) == address(0)),
                              "Account: caller not approved target."
                          );
                          if (isActiveSigner(signer)) {
                              magicValue = MAGICVALUE;
                          }
                      }
                      /**
                       * @notice Returns the hash of message that should be signed for EIP1271 verification.
                       * @param _hash The message hash to sign for the EIP-1271 origin verifying contract.
                       * @return messageHash The digest to sign for EIP-1271 verification.
                       */
                      function getMessageHash(bytes32 _hash) public view returns (bytes32) {
                          bytes32 messageHash = keccak256(abi.encode(_hash));
                          bytes32 typedDataHash = keccak256(abi.encode(MSG_TYPEHASH, messageHash));
                          return keccak256(abi.encodePacked("\\x19\\x01", _domainSeparatorV4(), typedDataHash));
                      }
                      /*///////////////////////////////////////////////////////////////
                                              External functions
                      //////////////////////////////////////////////////////////////*/
                      /// @notice Executes a transaction (called directly from an admin, or by entryPoint)
                      function execute(address _target, uint256 _value, bytes calldata _calldata) external virtual onlyAdminOrEntrypoint {
                          _registerOnFactory();
                          _call(_target, _value, _calldata);
                      }
                      /// @notice Executes a sequence transaction (called directly from an admin, or by entryPoint)
                      function executeBatch(
                          address[] calldata _target,
                          uint256[] calldata _value,
                          bytes[] calldata _calldata
                      ) external virtual onlyAdminOrEntrypoint {
                          _registerOnFactory();
                          require(_target.length == _calldata.length && _target.length == _value.length, "Account: wrong array lengths.");
                          for (uint256 i = 0; i < _target.length; i++) {
                              _call(_target[i], _value[i], _calldata[i]);
                          }
                      }
                      /// @notice Deposit funds for this account in Entrypoint.
                      function addDeposit() public payable {
                          AccountCore(payable(address(this))).entryPoint().depositTo{ value: msg.value }(address(this));
                      }
                      /// @notice Withdraw funds for this account from Entrypoint.
                      function withdrawDepositTo(address payable withdrawAddress, uint256 amount) public {
                          _onlyAdmin();
                          AccountCore(payable(address(this))).entryPoint().withdrawTo(withdrawAddress, amount);
                      }
                      /*///////////////////////////////////////////////////////////////
                                          Internal functions
                      //////////////////////////////////////////////////////////////*/
                      /// @dev Registers the account on the factory if it hasn't been registered yet.
                      function _registerOnFactory() internal virtual {
                          address factory = AccountCore(payable(address(this))).factory();
                          BaseAccountFactory factoryContract = BaseAccountFactory(factory);
                          if (!factoryContract.isRegistered(address(this))) {
                              factoryContract.onRegister(AccountCoreStorage.data().creationSalt);
                          }
                      }
                      /// @dev Calls a target contract and reverts if it fails.
                      function _call(address _target, uint256 value, bytes memory _calldata) internal returns (bytes memory result) {
                          bool success;
                          (success, result) = _target.call{ value: value }(_calldata);
                          if (!success) {
                              assembly {
                                  revert(add(result, 32), mload(result))
                              }
                          }
                      }
                      /// @dev Returns whether contract metadata can be set in the given execution context.
                      function _canSetContractURI() internal view virtual override returns (bool) {
                          return isAdmin(msg.sender) || msg.sender == address(this);
                      }
                      function _afterSignerPermissionsUpdate(SignerPermissionRequest calldata _req) internal virtual override {}
                  }
                  // SPDX-License-Identifier: GPL-3.0
                  pragma solidity ^0.8.12;
                  /* solhint-disable avoid-low-level-calls */
                  /* solhint-disable no-empty-blocks */
                  import "../interface/IAccount.sol";
                  import "../interface/IEntrypoint.sol";
                  import "./Helpers.sol";
                  /**
                   * Basic account implementation.
                   * this contract provides the basic logic for implementing the IAccount interface  - validateUserOp
                   * specific account implementation should inherit it and provide the account-specific logic
                   */
                  abstract contract BaseAccount is IAccount {
                      using UserOperationLib for UserOperation;
                      //return value in case of signature failure, with no time-range.
                      // equivalent to _packValidationData(true,0,0);
                      uint256 internal constant SIG_VALIDATION_FAILED = 1;
                      /**
                       * Return the account nonce.
                       * This method returns the next sequential nonce.
                       * For a nonce of a specific key, use `entrypoint.getNonce(account, key)`
                       */
                      function getNonce() public view virtual returns (uint256) {
                          return entryPoint().getNonce(address(this), 0);
                      }
                      /**
                       * return the entryPoint used by this account.
                       * subclass should return the current entryPoint used by this account.
                       */
                      function entryPoint() public view virtual returns (IEntryPoint);
                      /**
                       * Validate user's signature and nonce.
                       * subclass doesn't need to override this method. Instead, it should override the specific internal validation methods.
                       */
                      function validateUserOp(
                          UserOperation calldata userOp,
                          bytes32 userOpHash,
                          uint256 missingAccountFunds
                      ) external virtual override returns (uint256 validationData) {
                          _requireFromEntryPoint();
                          validationData = _validateSignature(userOp, userOpHash);
                          _validateNonce(userOp.nonce);
                          _payPrefund(missingAccountFunds);
                      }
                      /**
                       * ensure the request comes from the known entrypoint.
                       */
                      function _requireFromEntryPoint() internal view virtual {
                          require(msg.sender == address(entryPoint()), "account: not from EntryPoint");
                      }
                      /**
                       * validate the signature is valid for this message.
                       * @param userOp validate the userOp.signature field
                       * @param userOpHash convenient field: the hash of the request, to check the signature against
                       *          (also hashes the entrypoint and chain id)
                       * @return validationData signature and time-range of this operation
                       *      <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 the 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 _validateSignature(
                          UserOperation calldata userOp,
                          bytes32 userOpHash
                      ) internal virtual returns (uint256 validationData);
                      /**
                       * Validate the nonce of the UserOperation.
                       * This method may validate the nonce requirement of this account.
                       * e.g.
                       * To limit the nonce to use sequenced UserOps only (no "out of order" UserOps):
                       *      `require(nonce < type(uint64).max)`
                       * For a hypothetical account that *requires* the nonce to be out-of-order:
                       *      `require(nonce & type(uint64).max == 0)`
                       *
                       * The actual nonce uniqueness is managed by the EntryPoint, and thus no other
                       * action is needed by the account itself.
                       *
                       * @param nonce to validate
                       *
                       * solhint-disable-next-line no-empty-blocks
                       */
                      function _validateNonce(uint256 nonce) internal view virtual {}
                      /**
                       * sends to the entrypoint (msg.sender) the missing funds for this transaction.
                       * subclass MAY override this method for better funds management
                       * (e.g. send to the entryPoint more than the minimum required, so that in future transactions
                       * it will not be required to send again)
                       * @param missingAccountFunds the minimum value this method should send the entrypoint.
                       *  this value MAY be zero, in case there is enough deposit, or the userOp has a paymaster.
                       */
                      function _payPrefund(uint256 missingAccountFunds) internal virtual {
                          if (missingAccountFunds != 0) {
                              (bool success, ) = payable(msg.sender).call{ value: missingAccountFunds, gas: type(uint256).max }("");
                              (success);
                              //ignore failure (its EntryPoint's job to verify, not account.)
                          }
                      }
                  }
                  // SPDX-License-Identifier: GPL-3.0
                  pragma solidity ^0.8.12;
                  // Utils
                  import "../../../extension/Multicall.sol";
                  import "../../../external-deps/openzeppelin/proxy/Clones.sol";
                  import "../../../external-deps/openzeppelin/utils/structs/EnumerableSet.sol";
                  import "../utils/BaseAccount.sol";
                  import "../../../extension/interface/IAccountPermissions.sol";
                  import "../../../lib/BytesLib.sol";
                  // Interface
                  import "../interface/IEntrypoint.sol";
                  import "../interface/IAccountFactory.sol";
                  //   $$\\     $$\\       $$\\                 $$\\                         $$\\
                  //   $$ |    $$ |      \\__|                $$ |                        $$ |
                  // $$$$$$\\   $$$$$$$\\  $$\\  $$$$$$\\   $$$$$$$ |$$\\  $$\\  $$\\  $$$$$$\\  $$$$$$$\\
                  // \\_$$  _|  $$  __$$\\ $$ |$$  __$$\\ $$  __$$ |$$ | $$ | $$ |$$  __$$\\ $$  __$$\\
                  //   $$ |    $$ |  $$ |$$ |$$ |  \\__|$$ /  $$ |$$ | $$ | $$ |$$$$$$$$ |$$ |  $$ |
                  //   $$ |$$\\ $$ |  $$ |$$ |$$ |      $$ |  $$ |$$ | $$ | $$ |$$   ____|$$ |  $$ |
                  //   \\$$$$  |$$ |  $$ |$$ |$$ |      \\$$$$$$$ |\\$$$$$\\$$$$  |\\$$$$$$$\\ $$$$$$$  |
                  //    \\____/ \\__|  \\__|\\__|\\__|       \\_______| \\_____\\____/  \\_______|\\_______/
                  abstract contract BaseAccountFactory is IAccountFactory, Multicall {
                      using EnumerableSet for EnumerableSet.AddressSet;
                      /*///////////////////////////////////////////////////////////////
                                                  State
                      //////////////////////////////////////////////////////////////*/
                      address public immutable accountImplementation;
                      address public immutable entrypoint;
                      EnumerableSet.AddressSet private allAccounts;
                      mapping(address => EnumerableSet.AddressSet) internal accountsOfSigner;
                      /*///////////////////////////////////////////////////////////////
                                              Constructor
                      //////////////////////////////////////////////////////////////*/
                      constructor(address _accountImpl, address _entrypoint) {
                          accountImplementation = _accountImpl;
                          entrypoint = _entrypoint;
                      }
                      /*///////////////////////////////////////////////////////////////
                                          External functions
                      //////////////////////////////////////////////////////////////*/
                      /// @notice Deploys a new Account for admin.
                      function createAccount(address _admin, bytes calldata _data) external virtual override returns (address) {
                          address impl = accountImplementation;
                          bytes32 salt = _generateSalt(_admin, _data);
                          address account = Clones.predictDeterministicAddress(impl, salt);
                          if (account.code.length > 0) {
                              return account;
                          }
                          account = Clones.cloneDeterministic(impl, salt);
                          if (msg.sender != entrypoint) {
                              require(allAccounts.add(account), "AccountFactory: account already registered");
                          }
                          _initializeAccount(account, _admin, _data);
                          emit AccountCreated(account, _admin);
                          return account;
                      }
                      /// @notice Callback function for an Account to register itself on the factory.
                      function onRegister(bytes32 _salt) external {
                          address account = msg.sender;
                          require(_isAccountOfFactory(account, _salt), "AccountFactory: not an account.");
                          require(allAccounts.add(account), "AccountFactory: account already registered");
                      }
                      function onSignerAdded(address _signer, bytes32 _salt) external {
                          address account = msg.sender;
                          require(_isAccountOfFactory(account, _salt), "AccountFactory: not an account.");
                          bool isNewSigner = accountsOfSigner[_signer].add(account);
                          if (isNewSigner) {
                              emit SignerAdded(account, _signer);
                          }
                      }
                      /// @notice Callback function for an Account to un-register its signers.
                      function onSignerRemoved(address _signer, bytes32 _salt) external {
                          address account = msg.sender;
                          require(_isAccountOfFactory(account, _salt), "AccountFactory: not an account.");
                          bool isAccount = accountsOfSigner[_signer].remove(account);
                          if (isAccount) {
                              emit SignerRemoved(account, _signer);
                          }
                      }
                      /*///////////////////////////////////////////////////////////////
                                              View functions
                      //////////////////////////////////////////////////////////////*/
                      /// @notice Returns whether an account is registered on this factory.
                      function isRegistered(address _account) external view returns (bool) {
                          return allAccounts.contains(_account);
                      }
                      /// @notice Returns the total number of accounts.
                      function totalAccounts() external view returns (uint256) {
                          return allAccounts.length();
                      }
                      /// @notice Returns all accounts between the given indices.
                      function getAccounts(uint256 _start, uint256 _end) external view returns (address[] memory accounts) {
                          require(_start < _end && _end <= allAccounts.length(), "BaseAccountFactory: invalid indices");
                          uint256 len = _end - _start;
                          accounts = new address[](_end - _start);
                          for (uint256 i = 0; i < len; i += 1) {
                              accounts[i] = allAccounts.at(i + _start);
                          }
                      }
                      /// @notice Returns all accounts created on the factory.
                      function getAllAccounts() external view returns (address[] memory) {
                          return allAccounts.values();
                      }
                      /// @notice Returns the address of an Account that would be deployed with the given admin signer.
                      function getAddress(address _adminSigner, bytes calldata _data) public view returns (address) {
                          bytes32 salt = _generateSalt(_adminSigner, _data);
                          return Clones.predictDeterministicAddress(accountImplementation, salt);
                      }
                      /// @notice Returns all accounts that the given address is a signer of.
                      function getAccountsOfSigner(address signer) external view returns (address[] memory accounts) {
                          return accountsOfSigner[signer].values();
                      }
                      /*///////////////////////////////////////////////////////////////
                                              Internal functions
                      //////////////////////////////////////////////////////////////*/
                      /// @dev Returns whether the caller is an account deployed by this factory.
                      function _isAccountOfFactory(address _account, bytes32 _salt) internal view virtual returns (bool) {
                          address predicted = Clones.predictDeterministicAddress(accountImplementation, _salt);
                          return _account == predicted;
                      }
                      function _getImplementation(address cloneAddress) internal view returns (address) {
                          bytes memory code = cloneAddress.code;
                          return BytesLib.toAddress(code, 10);
                      }
                      /// @dev Returns the salt used when deploying an Account.
                      function _generateSalt(address _admin, bytes memory _data) internal view virtual returns (bytes32) {
                          return keccak256(abi.encode(_admin, _data));
                      }
                      /// @dev Called in `createAccount`. Initializes the account contract created in `createAccount`.
                      function _initializeAccount(address _account, address _admin, bytes calldata _data) internal virtual;
                  }
                  // SPDX-License-Identifier: GPL-3.0
                  pragma solidity ^0.8.12;
                  /* solhint-disable no-inline-assembly */
                  /* solhint-disable func-visibility */
                  /**
                   * returned data from validateUserOp.
                   * validateUserOp returns a uint256, with 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
                  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);
                  }
                  // intersect account and paymaster ranges.
                  function _intersectTimeRange(
                      uint256 validationData,
                      uint256 paymasterValidationData
                  ) pure returns (ValidationData memory) {
                      ValidationData memory accountValidationData = _parseValidationData(validationData);
                      ValidationData memory pmValidationData = _parseValidationData(paymasterValidationData);
                      address aggregator = accountValidationData.aggregator;
                      if (aggregator == address(0)) {
                          aggregator = pmValidationData.aggregator;
                      }
                      uint48 validAfter = accountValidationData.validAfter;
                      uint48 validUntil = accountValidationData.validUntil;
                      uint48 pmValidAfter = pmValidationData.validAfter;
                      uint48 pmValidUntil = pmValidationData.validUntil;
                      if (validAfter < pmValidAfter) validAfter = pmValidAfter;
                      if (validUntil > pmValidUntil) validUntil = pmValidUntil;
                      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 {
                          let mem := mload(0x40)
                          let len := data.length
                          calldatacopy(mem, data.offset, len)
                          ret := keccak256(mem, len)
                      }
                  }
                  // SPDX-License-Identifier: GPL-3.0
                  pragma solidity ^0.8.12;
                  /* solhint-disable no-inline-assembly */
                  import { calldataKeccak } from "./Helpers.sol";
                  /**
                   * 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 callGasLimit the gas limit passed to the callData method call.
                   * @param verificationGasLimit gas used for validateUserOp and validatePaymasterUserOp.
                   * @param preVerificationGas gas not calculated by the handleOps method, but added to the gas paid. Covers batch overhead.
                   * @param maxFeePerGas same as EIP-1559 gas parameter.
                   * @param maxPriorityFeePerGas same as EIP-1559 gas parameter.
                   * @param paymasterAndData if set, this field holds the paymaster address and paymaster-specific 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 UserOperation {
                      address sender;
                      uint256 nonce;
                      bytes initCode;
                      bytes callData;
                      uint256 callGasLimit;
                      uint256 verificationGasLimit;
                      uint256 preVerificationGas;
                      uint256 maxFeePerGas;
                      uint256 maxPriorityFeePerGas;
                      bytes paymasterAndData;
                      bytes signature;
                  }
                  /**
                   * Utility functions helpful when working with UserOperation structs.
                   */
                  library UserOperationLib {
                      function getSender(UserOperation 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.
                      function gasPrice(UserOperation calldata userOp) internal view returns (uint256) {
                          unchecked {
                              uint256 maxFeePerGas = userOp.maxFeePerGas;
                              uint256 maxPriorityFeePerGas = userOp.maxPriorityFeePerGas;
                              if (maxFeePerGas == maxPriorityFeePerGas) {
                                  //legacy mode (for networks that don't support basefee opcode)
                                  return maxFeePerGas;
                              }
                              return min(maxFeePerGas, maxPriorityFeePerGas + block.basefee);
                          }
                      }
                      function pack(UserOperation 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);
                          uint256 callGasLimit = userOp.callGasLimit;
                          uint256 verificationGasLimit = userOp.verificationGasLimit;
                          uint256 preVerificationGas = userOp.preVerificationGas;
                          uint256 maxFeePerGas = userOp.maxFeePerGas;
                          uint256 maxPriorityFeePerGas = userOp.maxPriorityFeePerGas;
                          bytes32 hashPaymasterAndData = calldataKeccak(userOp.paymasterAndData);
                          return
                              abi.encode(
                                  sender,
                                  nonce,
                                  hashInitCode,
                                  hashCallData,
                                  callGasLimit,
                                  verificationGasLimit,
                                  preVerificationGas,
                                  maxFeePerGas,
                                  maxPriorityFeePerGas,
                                  hashPaymasterAndData
                              );
                      }
                      function hash(UserOperation calldata userOp) internal pure returns (bytes32) {
                          return keccak256(pack(userOp));
                      }
                      function min(uint256 a, uint256 b) internal pure returns (uint256) {
                          return a < b ? a : b;
                      }
                  }