ETH Price: $2,577.66 (+1.28%)

Transaction Decoder

Block:
22077807 at Mar-19-2025 02:02:23 AM +UTC
Transaction Fee:
0.0000297066 ETH $0.08
Gas Used:
49,511 Gas / 0.6 Gwei

Emitted Events:

916 DxToken.Transfer( from=[Sender] 0xd3f45e20cb8ec2d65aac5797ceabf989798f58fa, to=AmbireAccount, value=850000000000000000000 )

Account State Difference:

  Address   Before After State Difference Code
(Titan Builder)
17.314677874022959724 Eth17.314682018881330223 Eth0.000004144858370499
0x973e5269...8d27041A9
0xd3f45e20...9798F58Fa
0.007980861497173124 Eth
Nonce: 115
0.007951154897173124 Eth
Nonce: 116
0.0000297066

Execution Trace

DxToken.transfer( _to=0xe6A37f5C75eDCC162f397be7C649a50FC351D597, _value=850000000000000000000 ) => ( True )
File 1 of 2: DxToken
pragma solidity ^0.4.23;


/**
 * @title SafeMath
 * @dev Math operations with safety checks that throw on error
 */
library SafeMath {

  /**
  * @dev Multiplies two numbers, throws on overflow.
  */
  function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
    if (a == 0) {
      return 0;
    }
    c = a * b;
    assert(c / a == b);
    return c;
  }

  /**
  * @dev Integer division of two numbers, truncating the quotient.
  */
  function div(uint256 a, uint256 b) internal pure returns (uint256) {
    // assert(b > 0); // Solidity automatically throws when dividing by 0
    // uint256 c = a / b;
    // assert(a == b * c + a % b); // There is no case in which this doesn't hold
    return a / b;
  }

  /**
  * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
  */
  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
    assert(b <= a);
    return a - b;
  }

  /**
  * @dev Adds two numbers, throws on overflow.
  */
  function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
    c = a + b;
    assert(c >= a);
    return c;
  }
}

/**
 * @title Ownable
 * @dev The Ownable contract has an owner address, and provides basic authorization control
 * functions, this simplifies the implementation of "user permissions".
 */
contract Ownable {
  address public owner;


  event OwnershipRenounced(address indexed previousOwner);
  event OwnershipTransferred(
    address indexed previousOwner,
    address indexed newOwner
  );


  /**
   * @dev The Ownable constructor sets the original `owner` of the contract to the sender
   * account.
   */
  constructor() public {
    owner = msg.sender;
  }

  /**
   * @dev Throws if called by any account other than the owner.
   */
  modifier onlyOwner() {
    require(msg.sender == owner);
    _;
  }

  /**
   * @dev Allows the current owner to transfer control of the contract to a newOwner.
   * @param newOwner The address to transfer ownership to.
   */
  function transferOwnership(address newOwner) public onlyOwner {
    require(newOwner != address(0));
    emit OwnershipTransferred(owner, newOwner);
    owner = newOwner;
  }

  /**
   * @dev Allows the current owner to relinquish control of the contract.
   */
  function renounceOwnership() public onlyOwner {
    emit OwnershipRenounced(owner);
    owner = address(0);
  }
}



/**
 * @title Pausable
 * @dev Base contract which allows children to implement an emergency stop mechanism.
 */
contract Pausable is Ownable {
  event Pause();
  event Unpause();

  bool public paused = false;


  /**
   * @dev Modifier to make a function callable only when the contract is not paused.
   */
  modifier whenNotPaused() {
    require(!paused);
    _;
  }

  /**
   * @dev Modifier to make a function callable only when the contract is paused.
   */
  modifier whenPaused() {
    require(paused);
    _;
  }

  /**
   * @dev called by the owner to pause, triggers stopped state
   */
  function pause() onlyOwner whenNotPaused public {
    paused = true;
    emit Pause();
  }

  /**
   * @dev called by the owner to unpause, returns to normal state
   */
  function unpause() onlyOwner whenPaused public {
    paused = false;
    emit Unpause();
  }
}


/**
 * @title ERC20Basic
 * @dev Simpler version of ERC20 interface
 * @dev see https://github.com/ethereum/EIPs/issues/179
 */
contract ERC20Basic {
  function totalSupply() public view returns (uint256);
  function balanceOf(address who) public view returns (uint256);
  function transfer(address to, uint256 value) public returns (bool);
  event Transfer(address indexed from, address indexed to, uint256 value);
}


/**
 * @title ERC20 interface
 * @dev see https://github.com/ethereum/EIPs/issues/20
 */
contract ERC20 is ERC20Basic {
  function allowance(address owner, address spender)
    public view returns (uint256);

  function transferFrom(address from, address to, uint256 value)
    public returns (bool);

  function approve(address spender, uint256 value) public returns (bool);
  event Approval(
    address indexed owner,
    address indexed spender,
    uint256 value
  );
}

/**
 * @title Basic token
 * @dev Basic version of StandardToken, with no allowances.
 */
contract BasicToken is ERC20Basic {
  using SafeMath for uint256;

  mapping(address => uint256) balances;

  uint256 totalSupply_;

  /**
  * @dev total number of tokens in existence
  */
  function totalSupply() public view returns (uint256) {
    return totalSupply_;
  }

  /**
  * @dev transfer token for a specified address
  * @param _to The address to transfer to.
  * @param _value The amount to be transferred.
  */
  function transfer(address _to, uint256 _value) public returns (bool) {
    require(_to != address(0));
    require(_value <= balances[msg.sender]);

    balances[msg.sender] = balances[msg.sender].sub(_value);
    balances[_to] = balances[_to].add(_value);
    emit Transfer(msg.sender, _to, _value);
    return true;
  }

  /**
  * @dev Gets the balance of the specified address.
  * @param _owner The address to query the the balance of.
  * @return An uint256 representing the amount owned by the passed address.
  */
  function balanceOf(address _owner) public view returns (uint256) {
    return balances[_owner];
  }

}


/**
 * @title Standard ERC20 token
 *
 * @dev Implementation of the basic standard token.
 * @dev https://github.com/ethereum/EIPs/issues/20
 * @dev Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
 */
contract StandardToken is ERC20, BasicToken {

  mapping (address => mapping (address => uint256)) internal allowed;


  /**
   * @dev Transfer tokens from one address to another
   * @param _from address The address which you want to send tokens from
   * @param _to address The address which you want to transfer to
   * @param _value uint256 the amount of tokens to be transferred
   */
  function transferFrom(
    address _from,
    address _to,
    uint256 _value
  )
    public
    returns (bool)
  {
    require(_to != address(0));
    require(_value <= balances[_from]);
    require(_value <= allowed[_from][msg.sender]);

    balances[_from] = balances[_from].sub(_value);
    balances[_to] = balances[_to].add(_value);
    allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
    emit Transfer(_from, _to, _value);
    return true;
  }

  /**
   * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
   *
   * Beware that changing an allowance with this method brings the risk that someone may use both the old
   * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
   * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
   * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
   * @param _spender The address which will spend the funds.
   * @param _value The amount of tokens to be spent.
   */
  function approve(address _spender, uint256 _value) public returns (bool) {
    allowed[msg.sender][_spender] = _value;
    emit Approval(msg.sender, _spender, _value);
    return true;
  }

  /**
   * @dev Function to check the amount of tokens that an owner allowed to a spender.
   * @param _owner address The address which owns the funds.
   * @param _spender address The address which will spend the funds.
   * @return A uint256 specifying the amount of tokens still available for the spender.
   */
  function allowance(
    address _owner,
    address _spender
   )
    public
    view
    returns (uint256)
  {
    return allowed[_owner][_spender];
  }

  /**
   * @dev Increase the amount of tokens that an owner allowed to a spender.
   *
   * approve should be called when allowed[_spender] == 0. To increment
   * allowed value is better to use this function to avoid 2 calls (and wait until
   * the first transaction is mined)
   * From MonolithDAO Token.sol
   * @param _spender The address which will spend the funds.
   * @param _addedValue The amount of tokens to increase the allowance by.
   */
  function increaseApproval(
    address _spender,
    uint _addedValue
  )
    public
    returns (bool)
  {
    allowed[msg.sender][_spender] = (
      allowed[msg.sender][_spender].add(_addedValue));
    emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
    return true;
  }

  /**
   * @dev Decrease the amount of tokens that an owner allowed to a spender.
   *
   * approve should be called when allowed[_spender] == 0. To decrement
   * allowed value is better to use this function to avoid 2 calls (and wait until
   * the first transaction is mined)
   * From MonolithDAO Token.sol
   * @param _spender The address which will spend the funds.
   * @param _subtractedValue The amount of tokens to decrease the allowance by.
   */
  function decreaseApproval(
    address _spender,
    uint _subtractedValue
  )
    public
    returns (bool)
  {
    uint oldValue = allowed[msg.sender][_spender];
    if (_subtractedValue > oldValue) {
      allowed[msg.sender][_spender] = 0;
    } else {
      allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
    }
    emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
    return true;
  }

}


/**
 * @title Pausable token
 * @dev StandardToken modified with pausable transfers.
 **/
contract PausableToken is StandardToken, Pausable {

  function transfer(
    address _to,
    uint256 _value
  )
    public
    whenNotPaused
    returns (bool)
  {
    return super.transfer(_to, _value);
  }

  function transferFrom(
    address _from,
    address _to,
    uint256 _value
  )
    public
    whenNotPaused
    returns (bool)
  {
    return super.transferFrom(_from, _to, _value);
  }

  function approve(
    address _spender,
    uint256 _value
  )
    public
    whenNotPaused
    returns (bool)
  {
    return super.approve(_spender, _value);
  }

  function increaseApproval(
    address _spender,
    uint _addedValue
  )
    public
    whenNotPaused
    returns (bool success)
  {
    return super.increaseApproval(_spender, _addedValue);
  }

  function decreaseApproval(
    address _spender,
    uint _subtractedValue
  )
    public
    whenNotPaused
    returns (bool success)
  {
    return super.decreaseApproval(_spender, _subtractedValue);
  }
}

contract DxToken is PausableToken {
    string public name = "DxChain Token";
    string public symbol = "DX";
    uint public decimals = 18;
    uint public INITIAL_SUPPLY = 10**29;

    constructor() public {
        totalSupply_ = INITIAL_SUPPLY;
        balances[msg.sender] = INITIAL_SUPPLY;
    }
}

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;
}