ETH Price: $2,047.79 (+1.18%)

Transaction Decoder

Block:
24001737 at Dec-13-2025 05:55:47 AM +UTC
Transaction Fee:
0.000005434317113758 ETH $0.01
Gas Used:
148,886 Gas / 0.036499853 Gwei

Account State Difference:

  Address   Before After State Difference Code
0x18677585...03f6Dc8a4 1.582420070284507174 Eth1.582430070284507174 Eth0.00001
(Titan Builder)
13.50922660178519698 Eth13.509226601785494752 Eth0.000000000000297772
0xCFaFC6Bd...712165AA8
0.867717173965665499 Eth
Nonce: 11693
0.867711739648551741 Eth
Nonce: 11694
0.000005434317113758
0xDA49160b...753e8154F 0.007780418806993033 Eth0.007770418806993033 Eth0.00001

Execution Trace

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

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