ETH Price: $1,972.38 (+0.54%)

Transaction Decoder

Block:
24508999 at Feb-22-2026 01:03:35 AM +UTC
Transaction Fee:
0.000007266573159048 ETH $0.01
Gas Used:
185,348 Gas / 0.039205026 Gwei

Emitted Events:

271 0x40642c7f51b7c2a3ecaa8d45f4801cfa28b35fa9.0x6c9d76c1677b0909ce573fabdda9f1bbbc891f981a7efa98415bdcd0ff9f5411( 0x6c9d76c1677b0909ce573fabdda9f1bbbc891f981a7efa98415bdcd0ff9f5411, 000000000000000000000000d6a4503d534f816d55df1addff5d2b9edcfd2b4a )
272 0xd6a4503d534f816d55df1addff5d2b9edcfd2b4a.0x43363c9c190568ae590a153b4a3ed2a627491aebe76dd1d77ab2bf0f9c17824f( 0x43363c9c190568ae590a153b4a3ed2a627491aebe76dd1d77ab2bf0f9c17824f, 0000000000000000000000000000000000000000000000000000000000000000 )
273 TetherToken.Transfer( from=[Receiver] 0x40642c7f51b7c2a3ecaa8d45f4801cfa28b35fa9, to=0x4b5f834Ede79C52bD33d952C719a0B641a0310EA, value=753100000 )
274 TetherToken.Transfer( from=[Receiver] 0x40642c7f51b7c2a3ecaa8d45f4801cfa28b35fa9, to=0x9968f86863cD0b7cE2F965FbA2ED2749Aa36416F, value=25130 )

Account State Difference:

  Address   Before After State Difference Code
0x40642C7F...A28b35FA9
0 Eth
Nonce: 0
0 Eth
Nonce: 2
From: 0 To: 22892027298644800313173275107382871928300858035648303947
0x7d8F6522...1191AF30A
0.444922008736784994 Eth
Nonce: 12736
0.444914742163625946 Eth
Nonce: 12737
0.000007266573159048
0xD6A4503D...edCfD2b4a
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 57617015923227607092762336464232457728451365742661656024643731737598616365684773818412790234367088657674848732635070948708922270558345764650618492335585952254651095230184165472705142697
0xdAC17F95...13D831ec7
(BuilderNet)
132.867745805167040846 Eth132.867746582978810758 Eth0.000000777811769912

Execution Trace

0x40642c7f51b7c2a3ecaa8d45f4801cfa28b35fa9.32fba9bb( )
  • 0x40642c7f51b7c2a3ecaa8d45f4801cfa28b35fa9.CALL( )
    • 0xd6a4503d534f816d55df1addff5d2b9edcfd2b4a.61004d3d( )
    • 0x40642c7f51b7c2a3ecaa8d45f4801cfa28b35fa9.fcfbd33a( )
      • 0xd6a4503d534f816d55df1addff5d2b9edcfd2b4a.1485540f( )
        • Storage.readAndUpdateNonce( validator=0x0000000000000000000000000000000000000001 ) => ( 0 )
        • Null: 0x000...001.a19e37d9( )
        • TetherToken.transfer( _to=0x4b5f834Ede79C52bD33d952C719a0B641a0310EA, _value=753100000 )
        • TetherToken.transfer( _to=0x9968f86863cD0b7cE2F965FbA2ED2749Aa36416F, _value=25130 )
          File 1 of 2: TetherToken
          pragma solidity ^0.4.17;
          
          /**
           * @title SafeMath
           * @dev Math operations with safety checks that throw on error
           */
          library SafeMath {
              function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                  if (a == 0) {
                      return 0;
                  }
                  uint256 c = a * b;
                  assert(c / a == b);
                  return c;
              }
          
              function div(uint256 a, uint256 b) internal pure returns (uint256) {
                  // assert(b > 0); // Solidity automatically throws when dividing by 0
                  uint256 c = a / b;
                  // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                  return c;
              }
          
              function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                  assert(b <= a);
                  return a - b;
              }
          
              function add(uint256 a, uint256 b) internal pure returns (uint256) {
                  uint256 c = a + b;
                  assert(c >= a);
                  return c;
              }
          }
          
          /**
           * @title Ownable
           * @dev The Ownable contract has an owner address, and provides basic authorization control
           * functions, this simplifies the implementation of "user permissions".
           */
          contract Ownable {
              address public owner;
          
              /**
                * @dev The Ownable constructor sets the original `owner` of the contract to the sender
                * account.
                */
              function Ownable() public {
                  owner = msg.sender;
              }
          
              /**
                * @dev Throws if called by any account other than the owner.
                */
              modifier onlyOwner() {
                  require(msg.sender == owner);
                  _;
              }
          
              /**
              * @dev Allows the current owner to transfer control of the contract to a newOwner.
              * @param newOwner The address to transfer ownership to.
              */
              function transferOwnership(address newOwner) public onlyOwner {
                  if (newOwner != address(0)) {
                      owner = newOwner;
                  }
              }
          
          }
          
          /**
           * @title ERC20Basic
           * @dev Simpler version of ERC20 interface
           * @dev see https://github.com/ethereum/EIPs/issues/20
           */
          contract ERC20Basic {
              uint public _totalSupply;
              function totalSupply() public constant returns (uint);
              function balanceOf(address who) public constant returns (uint);
              function transfer(address to, uint value) public;
              event Transfer(address indexed from, address indexed to, uint value);
          }
          
          /**
           * @title ERC20 interface
           * @dev see https://github.com/ethereum/EIPs/issues/20
           */
          contract ERC20 is ERC20Basic {
              function allowance(address owner, address spender) public constant returns (uint);
              function transferFrom(address from, address to, uint value) public;
              function approve(address spender, uint value) public;
              event Approval(address indexed owner, address indexed spender, uint value);
          }
          
          /**
           * @title Basic token
           * @dev Basic version of StandardToken, with no allowances.
           */
          contract BasicToken is Ownable, ERC20Basic {
              using SafeMath for uint;
          
              mapping(address => uint) public balances;
          
              // additional variables for use if transaction fees ever became necessary
              uint public basisPointsRate = 0;
              uint public maximumFee = 0;
          
              /**
              * @dev Fix for the ERC20 short address attack.
              */
              modifier onlyPayloadSize(uint size) {
                  require(!(msg.data.length < size + 4));
                  _;
              }
          
              /**
              * @dev transfer token for a specified address
              * @param _to The address to transfer to.
              * @param _value The amount to be transferred.
              */
              function transfer(address _to, uint _value) public onlyPayloadSize(2 * 32) {
                  uint fee = (_value.mul(basisPointsRate)).div(10000);
                  if (fee > maximumFee) {
                      fee = maximumFee;
                  }
                  uint sendAmount = _value.sub(fee);
                  balances[msg.sender] = balances[msg.sender].sub(_value);
                  balances[_to] = balances[_to].add(sendAmount);
                  if (fee > 0) {
                      balances[owner] = balances[owner].add(fee);
                      Transfer(msg.sender, owner, fee);
                  }
                  Transfer(msg.sender, _to, sendAmount);
              }
          
              /**
              * @dev Gets the balance of the specified address.
              * @param _owner The address to query the the balance of.
              * @return An uint representing the amount owned by the passed address.
              */
              function balanceOf(address _owner) public constant returns (uint balance) {
                  return balances[_owner];
              }
          
          }
          
          /**
           * @title Standard ERC20 token
           *
           * @dev Implementation of the basic standard token.
           * @dev https://github.com/ethereum/EIPs/issues/20
           * @dev Based oncode by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
           */
          contract StandardToken is BasicToken, ERC20 {
          
              mapping (address => mapping (address => uint)) public allowed;
          
              uint public constant MAX_UINT = 2**256 - 1;
          
              /**
              * @dev Transfer tokens from one address to another
              * @param _from address The address which you want to send tokens from
              * @param _to address The address which you want to transfer to
              * @param _value uint the amount of tokens to be transferred
              */
              function transferFrom(address _from, address _to, uint _value) public onlyPayloadSize(3 * 32) {
                  var _allowance = allowed[_from][msg.sender];
          
                  // Check is not needed because sub(_allowance, _value) will already throw if this condition is not met
                  // if (_value > _allowance) throw;
          
                  uint fee = (_value.mul(basisPointsRate)).div(10000);
                  if (fee > maximumFee) {
                      fee = maximumFee;
                  }
                  if (_allowance < MAX_UINT) {
                      allowed[_from][msg.sender] = _allowance.sub(_value);
                  }
                  uint sendAmount = _value.sub(fee);
                  balances[_from] = balances[_from].sub(_value);
                  balances[_to] = balances[_to].add(sendAmount);
                  if (fee > 0) {
                      balances[owner] = balances[owner].add(fee);
                      Transfer(_from, owner, fee);
                  }
                  Transfer(_from, _to, sendAmount);
              }
          
              /**
              * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
              * @param _spender The address which will spend the funds.
              * @param _value The amount of tokens to be spent.
              */
              function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) {
          
                  // To change the approve amount you first have to reduce the addresses`
                  //  allowance to zero by calling `approve(_spender, 0)` if it is not
                  //  already 0 to mitigate the race condition described here:
                  //  https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                  require(!((_value != 0) && (allowed[msg.sender][_spender] != 0)));
          
                  allowed[msg.sender][_spender] = _value;
                  Approval(msg.sender, _spender, _value);
              }
          
              /**
              * @dev Function to check the amount of tokens than an owner allowed to a spender.
              * @param _owner address The address which owns the funds.
              * @param _spender address The address which will spend the funds.
              * @return A uint specifying the amount of tokens still available for the spender.
              */
              function allowance(address _owner, address _spender) public constant returns (uint remaining) {
                  return allowed[_owner][_spender];
              }
          
          }
          
          
          /**
           * @title Pausable
           * @dev Base contract which allows children to implement an emergency stop mechanism.
           */
          contract Pausable is Ownable {
            event Pause();
            event Unpause();
          
            bool public paused = false;
          
          
            /**
             * @dev Modifier to make a function callable only when the contract is not paused.
             */
            modifier whenNotPaused() {
              require(!paused);
              _;
            }
          
            /**
             * @dev Modifier to make a function callable only when the contract is paused.
             */
            modifier whenPaused() {
              require(paused);
              _;
            }
          
            /**
             * @dev called by the owner to pause, triggers stopped state
             */
            function pause() onlyOwner whenNotPaused public {
              paused = true;
              Pause();
            }
          
            /**
             * @dev called by the owner to unpause, returns to normal state
             */
            function unpause() onlyOwner whenPaused public {
              paused = false;
              Unpause();
            }
          }
          
          contract BlackList is Ownable, BasicToken {
          
              /////// Getters to allow the same blacklist to be used also by other contracts (including upgraded Tether) ///////
              function getBlackListStatus(address _maker) external constant returns (bool) {
                  return isBlackListed[_maker];
              }
          
              function getOwner() external constant returns (address) {
                  return owner;
              }
          
              mapping (address => bool) public isBlackListed;
              
              function addBlackList (address _evilUser) public onlyOwner {
                  isBlackListed[_evilUser] = true;
                  AddedBlackList(_evilUser);
              }
          
              function removeBlackList (address _clearedUser) public onlyOwner {
                  isBlackListed[_clearedUser] = false;
                  RemovedBlackList(_clearedUser);
              }
          
              function destroyBlackFunds (address _blackListedUser) public onlyOwner {
                  require(isBlackListed[_blackListedUser]);
                  uint dirtyFunds = balanceOf(_blackListedUser);
                  balances[_blackListedUser] = 0;
                  _totalSupply -= dirtyFunds;
                  DestroyedBlackFunds(_blackListedUser, dirtyFunds);
              }
          
              event DestroyedBlackFunds(address _blackListedUser, uint _balance);
          
              event AddedBlackList(address _user);
          
              event RemovedBlackList(address _user);
          
          }
          
          contract UpgradedStandardToken is StandardToken{
              // those methods are called by the legacy contract
              // and they must ensure msg.sender to be the contract address
              function transferByLegacy(address from, address to, uint value) public;
              function transferFromByLegacy(address sender, address from, address spender, uint value) public;
              function approveByLegacy(address from, address spender, uint value) public;
          }
          
          contract TetherToken is Pausable, StandardToken, BlackList {
          
              string public name;
              string public symbol;
              uint public decimals;
              address public upgradedAddress;
              bool public deprecated;
          
              //  The contract can be initialized with a number of tokens
              //  All the tokens are deposited to the owner address
              //
              // @param _balance Initial supply of the contract
              // @param _name Token Name
              // @param _symbol Token symbol
              // @param _decimals Token decimals
              function TetherToken(uint _initialSupply, string _name, string _symbol, uint _decimals) public {
                  _totalSupply = _initialSupply;
                  name = _name;
                  symbol = _symbol;
                  decimals = _decimals;
                  balances[owner] = _initialSupply;
                  deprecated = false;
              }
          
              // Forward ERC20 methods to upgraded contract if this one is deprecated
              function transfer(address _to, uint _value) public whenNotPaused {
                  require(!isBlackListed[msg.sender]);
                  if (deprecated) {
                      return UpgradedStandardToken(upgradedAddress).transferByLegacy(msg.sender, _to, _value);
                  } else {
                      return super.transfer(_to, _value);
                  }
              }
          
              // Forward ERC20 methods to upgraded contract if this one is deprecated
              function transferFrom(address _from, address _to, uint _value) public whenNotPaused {
                  require(!isBlackListed[_from]);
                  if (deprecated) {
                      return UpgradedStandardToken(upgradedAddress).transferFromByLegacy(msg.sender, _from, _to, _value);
                  } else {
                      return super.transferFrom(_from, _to, _value);
                  }
              }
          
              // Forward ERC20 methods to upgraded contract if this one is deprecated
              function balanceOf(address who) public constant returns (uint) {
                  if (deprecated) {
                      return UpgradedStandardToken(upgradedAddress).balanceOf(who);
                  } else {
                      return super.balanceOf(who);
                  }
              }
          
              // Forward ERC20 methods to upgraded contract if this one is deprecated
              function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) {
                  if (deprecated) {
                      return UpgradedStandardToken(upgradedAddress).approveByLegacy(msg.sender, _spender, _value);
                  } else {
                      return super.approve(_spender, _value);
                  }
              }
          
              // Forward ERC20 methods to upgraded contract if this one is deprecated
              function allowance(address _owner, address _spender) public constant returns (uint remaining) {
                  if (deprecated) {
                      return StandardToken(upgradedAddress).allowance(_owner, _spender);
                  } else {
                      return super.allowance(_owner, _spender);
                  }
              }
          
              // deprecate current contract in favour of a new one
              function deprecate(address _upgradedAddress) public onlyOwner {
                  deprecated = true;
                  upgradedAddress = _upgradedAddress;
                  Deprecate(_upgradedAddress);
              }
          
              // deprecate current contract if favour of a new one
              function totalSupply() public constant returns (uint) {
                  if (deprecated) {
                      return StandardToken(upgradedAddress).totalSupply();
                  } else {
                      return _totalSupply;
                  }
              }
          
              // Issue a new amount of tokens
              // these tokens are deposited into the owner address
              //
              // @param _amount Number of tokens to be issued
              function issue(uint amount) public onlyOwner {
                  require(_totalSupply + amount > _totalSupply);
                  require(balances[owner] + amount > balances[owner]);
          
                  balances[owner] += amount;
                  _totalSupply += amount;
                  Issue(amount);
              }
          
              // Redeem tokens.
              // These tokens are withdrawn from the owner address
              // if the balance must be enough to cover the redeem
              // or the call will fail.
              // @param _amount Number of tokens to be issued
              function redeem(uint amount) public onlyOwner {
                  require(_totalSupply >= amount);
                  require(balances[owner] >= amount);
          
                  _totalSupply -= amount;
                  balances[owner] -= amount;
                  Redeem(amount);
              }
          
              function setParams(uint newBasisPoints, uint newMaxFee) public onlyOwner {
                  // Ensure transparency by hardcoding limit beyond which fees can never be added
                  require(newBasisPoints < 20);
                  require(newMaxFee < 50);
          
                  basisPointsRate = newBasisPoints;
                  maximumFee = newMaxFee.mul(10**decimals);
          
                  Params(basisPointsRate, maximumFee);
              }
          
              // Called when new token are issued
              event Issue(uint amount);
          
              // Called when tokens are redeemed
              event Redeem(uint amount);
          
              // Called when contract is deprecated
              event Deprecate(address newAddress);
          
              // Called if contract ever adds fees
              event Params(uint feeBasisPoints, uint maxFee);
          }

          File 2 of 2: Storage
          // SPDX-License-Identifier: GPL-3.0
          pragma solidity 0.8.23;
          import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
          import {IStorage} from "./interfaces/IStorage.sol";
          import {WalletCoreLib} from "./lib/WalletCoreLib.sol";
          import {Errors} from "./lib/Errors.sol";
          contract Storage is IStorage {
              //mutable storage
              uint256 private _nonce;
              mapping(address => bool) private _validValidator;
              mapping(uint256 => bool) private _invalidSessionId;
              /**
               * @notice Restricts function access to the wallet owner only
               * @dev Reverts with INVALID_OWNER if caller is not the owner
               */
              modifier onlyOwner() {
                  if (msg.sender != getOwner()) {
                      revert Errors.InvalidOwner();
                  }
                  _;
              }
              /**
               * @notice Reads the current nonce and increments it for the next transaction
               * @dev Only callable by wallet owner. Uses unchecked math for gas optimization
               * @param validator The address of the validator contract
               * @return uint256 The current nonce before increment
               */
              function readAndUpdateNonce(
                  address validator
              ) external onlyOwner returns (uint256) {
                  validateValidator(validator);
                  unchecked {
                      uint256 currentNonce = _nonce++;
                      emit NonceConsumed(currentNonce);
                      return currentNonce;
                  }
              }
              /**
               * @notice Sets a validator's whitelist status
               * @dev Only callable by wallet owner
               * @param validator Address of the validator
               * @param isValid True to whitelist, false to remove
               */
              function setValidatorStatus(
                  address validator,
                  bool isValid
              ) external onlyOwner {
                  _validValidator[validator] = isValid;
                  emit ValidatorStatusUpdated(validator, isValid);
              }
              /**
               * @notice Revokes the specified session ID, marking it as invalid.
               * @dev Only callable by wallet owner
               * @param id The session ID to be revoked
               */
              function revokeSession(uint256 id) external onlyOwner {
                  _invalidSessionId[id] = true;
                  emit SessionRevoked(id);
              }
              /**
               * @notice Returns the owner address of the wallet
               * @dev Decodes the owner address from the proxy contract's initialization data
               * @return address The owner address of the wallet
               */
              function getOwner() public view returns (address) {
                  return abi.decode(Clones.fetchCloneArgs(address(this)), (address));
              }
              /**
               * @notice Returns the current nonce value
               * @dev Can be called by anyone
               * @return uint256 The current nonce value
               */
              function getNonce() external view returns (uint256) {
                  return _nonce;
              }
              /**
               * @notice Checks if a validator is whitelisted
               * @dev Reverts if validator is not whitelisted
               * @param validator Address of the validator to check
               */
              function validateValidator(address validator) public view {
                  if (
                      validator != WalletCoreLib.SELF_VALIDATION_ADDRESS &&
                      !_validValidator[validator]
                  ) revert Errors.InvalidValidator(validator);
              }
              /**
               * @notice Validates a session and its associated validator
               * @dev Reverts if:
               *      - Session is invalid (blacklisted)
               *      - Validator is not activated
               * @param id The ID of the session to validate
               * @param validator The validator address (0x0 to skip validator check)
               */
              function validateSession(uint256 id, address validator) external view {
                  if (_invalidSessionId[id]) revert Errors.InvalidSessionId();
                  validateValidator(validator);
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v5.2.0) (proxy/Clones.sol)
          pragma solidity ^0.8.20;
          import {Create2} from "../utils/Create2.sol";
          import {Errors} from "../utils/Errors.sol";
          /**
           * @dev https://eips.ethereum.org/EIPS/eip-1167[ERC-1167] is a standard for
           * deploying minimal proxy contracts, also known as "clones".
           *
           * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
           * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
           *
           * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
           * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
           * deterministic method.
           */
          library Clones {
              error CloneArgumentsTooLong();
              /**
               * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
               *
               * This function uses the create opcode, which should never revert.
               */
              function clone(address implementation) internal returns (address instance) {
                  return clone(implementation, 0);
              }
              /**
               * @dev Same as {xref-Clones-clone-address-}[clone], but with a `value` parameter to send native currency
               * to the new contract.
               *
               * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
               * to always have enough balance for new deployments. Consider exposing this function under a payable method.
               */
              function clone(address implementation, uint256 value) internal returns (address instance) {
                  if (address(this).balance < value) {
                      revert Errors.InsufficientBalance(address(this).balance, value);
                  }
                  assembly ("memory-safe") {
                      // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
                      // of the `implementation` address with the bytecode before the address.
                      mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
                      // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
                      mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
                      instance := create(value, 0x09, 0x37)
                  }
                  if (instance == address(0)) {
                      revert Errors.FailedDeployment();
                  }
              }
              /**
               * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
               *
               * This function uses the create2 opcode and a `salt` to deterministically deploy
               * the clone. Using the same `implementation` and `salt` multiple times will revert, since
               * the clones cannot be deployed twice at the same address.
               */
              function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
                  return cloneDeterministic(implementation, salt, 0);
              }
              /**
               * @dev Same as {xref-Clones-cloneDeterministic-address-bytes32-}[cloneDeterministic], but with
               * a `value` parameter to send native currency to the new contract.
               *
               * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
               * to always have enough balance for new deployments. Consider exposing this function under a payable method.
               */
              function cloneDeterministic(
                  address implementation,
                  bytes32 salt,
                  uint256 value
              ) internal returns (address instance) {
                  if (address(this).balance < value) {
                      revert Errors.InsufficientBalance(address(this).balance, value);
                  }
                  assembly ("memory-safe") {
                      // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
                      // of the `implementation` address with the bytecode before the address.
                      mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
                      // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
                      mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
                      instance := create2(value, 0x09, 0x37, salt)
                  }
                  if (instance == address(0)) {
                      revert Errors.FailedDeployment();
                  }
              }
              /**
               * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
               */
              function predictDeterministicAddress(
                  address implementation,
                  bytes32 salt,
                  address deployer
              ) internal pure returns (address predicted) {
                  assembly ("memory-safe") {
                      let ptr := mload(0x40)
                      mstore(add(ptr, 0x38), deployer)
                      mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
                      mstore(add(ptr, 0x14), implementation)
                      mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
                      mstore(add(ptr, 0x58), salt)
                      mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
                      predicted := and(keccak256(add(ptr, 0x43), 0x55), 0xffffffffffffffffffffffffffffffffffffffff)
                  }
              }
              /**
               * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
               */
              function predictDeterministicAddress(
                  address implementation,
                  bytes32 salt
              ) internal view returns (address predicted) {
                  return predictDeterministicAddress(implementation, salt, address(this));
              }
              /**
               * @dev Deploys and returns the address of a clone that mimics the behavior of `implementation` with custom
               * immutable arguments. These are provided through `args` and cannot be changed after deployment. To
               * access the arguments within the implementation, use {fetchCloneArgs}.
               *
               * This function uses the create opcode, which should never revert.
               */
              function cloneWithImmutableArgs(address implementation, bytes memory args) internal returns (address instance) {
                  return cloneWithImmutableArgs(implementation, args, 0);
              }
              /**
               * @dev Same as {xref-Clones-cloneWithImmutableArgs-address-bytes-}[cloneWithImmutableArgs], but with a `value`
               * parameter to send native currency to the new contract.
               *
               * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
               * to always have enough balance for new deployments. Consider exposing this function under a payable method.
               */
              function cloneWithImmutableArgs(
                  address implementation,
                  bytes memory args,
                  uint256 value
              ) internal returns (address instance) {
                  if (address(this).balance < value) {
                      revert Errors.InsufficientBalance(address(this).balance, value);
                  }
                  bytes memory bytecode = _cloneCodeWithImmutableArgs(implementation, args);
                  assembly ("memory-safe") {
                      instance := create(value, add(bytecode, 0x20), mload(bytecode))
                  }
                  if (instance == address(0)) {
                      revert Errors.FailedDeployment();
                  }
              }
              /**
               * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation` with custom
               * immutable arguments. These are provided through `args` and cannot be changed after deployment. To
               * access the arguments within the implementation, use {fetchCloneArgs}.
               *
               * This function uses the create2 opcode and a `salt` to deterministically deploy the clone. Using the same
               * `implementation`, `args` and `salt` multiple times will revert, since the clones cannot be deployed twice
               * at the same address.
               */
              function cloneDeterministicWithImmutableArgs(
                  address implementation,
                  bytes memory args,
                  bytes32 salt
              ) internal returns (address instance) {
                  return cloneDeterministicWithImmutableArgs(implementation, args, salt, 0);
              }
              /**
               * @dev Same as {xref-Clones-cloneDeterministicWithImmutableArgs-address-bytes-bytes32-}[cloneDeterministicWithImmutableArgs],
               * but with a `value` parameter to send native currency to the new contract.
               *
               * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
               * to always have enough balance for new deployments. Consider exposing this function under a payable method.
               */
              function cloneDeterministicWithImmutableArgs(
                  address implementation,
                  bytes memory args,
                  bytes32 salt,
                  uint256 value
              ) internal returns (address instance) {
                  bytes memory bytecode = _cloneCodeWithImmutableArgs(implementation, args);
                  return Create2.deploy(value, salt, bytecode);
              }
              /**
               * @dev Computes the address of a clone deployed using {Clones-cloneDeterministicWithImmutableArgs}.
               */
              function predictDeterministicAddressWithImmutableArgs(
                  address implementation,
                  bytes memory args,
                  bytes32 salt,
                  address deployer
              ) internal pure returns (address predicted) {
                  bytes memory bytecode = _cloneCodeWithImmutableArgs(implementation, args);
                  return Create2.computeAddress(salt, keccak256(bytecode), deployer);
              }
              /**
               * @dev Computes the address of a clone deployed using {Clones-cloneDeterministicWithImmutableArgs}.
               */
              function predictDeterministicAddressWithImmutableArgs(
                  address implementation,
                  bytes memory args,
                  bytes32 salt
              ) internal view returns (address predicted) {
                  return predictDeterministicAddressWithImmutableArgs(implementation, args, salt, address(this));
              }
              /**
               * @dev Get the immutable args attached to a clone.
               *
               * - If `instance` is a clone that was deployed using `clone` or `cloneDeterministic`, this
               *   function will return an empty array.
               * - If `instance` is a clone that was deployed using `cloneWithImmutableArgs` or
               *   `cloneDeterministicWithImmutableArgs`, this function will return the args array used at
               *   creation.
               * - If `instance` is NOT a clone deployed using this library, the behavior is undefined. This
               *   function should only be used to check addresses that are known to be clones.
               */
              function fetchCloneArgs(address instance) internal view returns (bytes memory) {
                  bytes memory result = new bytes(instance.code.length - 45); // revert if length is too short
                  assembly ("memory-safe") {
                      extcodecopy(instance, add(result, 32), 45, mload(result))
                  }
                  return result;
              }
              /**
               * @dev Helper that prepares the initcode of the proxy with immutable args.
               *
               * An assembly variant of this function requires copying the `args` array, which can be efficiently done using
               * `mcopy`. Unfortunately, that opcode is not available before cancun. A pure solidity implementation using
               * abi.encodePacked is more expensive but also more portable and easier to review.
               *
               * NOTE: https://eips.ethereum.org/EIPS/eip-170[EIP-170] limits the length of the contract code to 24576 bytes.
               * With the proxy code taking 45 bytes, that limits the length of the immutable args to 24531 bytes.
               */
              function _cloneCodeWithImmutableArgs(
                  address implementation,
                  bytes memory args
              ) private pure returns (bytes memory) {
                  if (args.length > 24531) revert CloneArgumentsTooLong();
                  return
                      abi.encodePacked(
                          hex"61",
                          uint16(args.length + 45),
                          hex"3d81600a3d39f3363d3d373d3d3d363d73",
                          implementation,
                          hex"5af43d82803e903d91602b57fd5bf3",
                          args
                      );
              }
          }
          // SPDX-License-Identifier: GPL-3.0
          pragma solidity ^0.8.23;
          interface IStorage {
              // EVENTS
              event NonceConsumed(uint256 utilisedNonce);
              event ValidatorStatusUpdated(address validator, bool status);
              event SessionRevoked(uint256 id);
              // FUNCTIONS
              function readAndUpdateNonce(address validator) external returns (uint256);
              function setValidatorStatus(address validator, bool isValid) external;
              function revokeSession(uint256 id) external;
              function getOwner() external view returns (address);
              function getNonce() external view returns (uint256);
              function validateValidator(address validator) external view;
              function validateSession(uint256 id, address validator) external view;
          }
          // SPDX-License-Identifier: GPL-3.0
          pragma solidity ^0.8.23;
          import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
          import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
          import {IValidator} from "../interfaces/IValidator.sol";
          library WalletCoreLib {
              using ECDSA for bytes32;
              using Clones for address;
              /**
               * @notice new storage should have a different salt
               */
              bytes32 public constant STORAGE_SALT =
                  keccak256(abi.encodePacked("storage"));
              bytes32 public constant VALIDATOR_SALT =
                  keccak256(abi.encodePacked("validator"));
              address public constant SELF_VALIDATION_ADDRESS = address(1);
              /**
               * @notice Computes the deterministic address of the wallet's storage contract
               * @dev Uses OpenZeppelin's Clones library to predict the address before deployment
               * @param storageImpl The implementation address of the storage contract
               * @return address The deterministic address where the storage clone will be deployed
               * @custom:args The immutable arguments encoded are:
               *  - address(this): The wallet address that owns this storage
               * @custom:salt A unique salt derived from STORAGE_SALT
               */
              function _getStorage(address storageImpl) internal view returns (address) {
                  return
                      storageImpl.predictDeterministicAddressWithImmutableArgs(
                          abi.encode(address(this)),
                          STORAGE_SALT,
                          address(this)
                      );
              }
              /**
               * @notice Validates a transaction or operation using either ECDSA signatures or an external validator contract
               * @dev Two validation methods are supported:
               *      1. ECDSA validation (when validator == address(1)): Recovers signer from signature and verifies it matches the wallet address
               *      2. External validator (any other address): Calls the validator contract and checks if it's authorized to validate
               * @param validator Address of the validator to use (address(1) for ECDSA signature validation)
               * @param typedDataHash EIP-712 typed data hash of the data to be validated
               * @param validationData For ECDSA: the 65-byte signature; For external validators: custom validation data
               * @return bool True if validation succeeds, false otherwise
               * @custom:security Ensure validator contracts are properly verified and authorized before use
               */
              function validate(
                  address validator,
                  bytes32 typedDataHash,
                  bytes calldata validationData
              ) internal view returns (bool) {
                  if (validator == SELF_VALIDATION_ADDRESS) {
                      return _validateSelf(typedDataHash, validationData);
                  } else {
                      try IValidator(validator).validate(typedDataHash, validationData) {
                          return true;
                      } catch {
                          return false;
                      }
                  }
              }
              /**
               * @notice Validates that a signature was signed by this contract
               * @param typedDataHash The hash of the data that was signed
               * @param signature The ECDSA signature to verify
               * @return bool True if the validation passes, false otherwise
               * @dev Reverts with INVALID_SIGNATURE if the signer is not account itself
               */
              function _validateSelf(
                  bytes32 typedDataHash,
                  bytes calldata signature
              ) internal view returns (bool) {
                  (address recoveredSigner, , ) = typedDataHash.tryRecover(signature);
                  return recoveredSigner == address(this);
              }
              /**
               * @notice Creates a unique deployment salt by combining validator implementation and init code
               * @param validatorImpl The validator implementation address
               * @param initHash Hash of the validator's initialization code
               * @return bytes32 The computed salt for deterministic deployment
               */
              function _computeCreationSalt(
                  address validatorImpl,
                  bytes32 initHash
              ) internal pure returns (bytes32) {
                  return keccak256(abi.encode(validatorImpl, initHash));
              }
              /**
               * @notice Computes the deterministic address of a validator contract before deployment
               * @param validatorImpl The implementation address of the validator
               * @param immutableArgs The initialization data for the validator
               * @param creationSalt A unique salt for deterministic deployment
               * @param deployer The address that will deploy the validator
               * @return The predicted address where the validator will be deployed
               */
              function _computeValidatorAddress(
                  address validatorImpl,
                  bytes calldata immutableArgs,
                  bytes32 creationSalt,
                  address deployer
              ) internal pure returns (address) {
                  return
                      validatorImpl.predictDeterministicAddressWithImmutableArgs(
                          immutableArgs,
                          creationSalt,
                          deployer
                      );
              }
          }
          // SPDX-License-Identifier: GPL-3.0
          pragma solidity ^0.8.23;
          library Errors {
              // Storage related
              error InvalidExecutor();
              error InvalidSession();
              error InvalidSessionId();
              error InvalidOwner();
              // Account related
              error NotFromSelf();
              // Call related
              error CallFailed(uint256 index, bytes returnData);
              // ValidationLogic related
              error InvalidValidator(address validator);
              error InvalidValidatorImpl(address validatorImpl);
              // ECDSAValidator related
              error InvalidSignature();
              // WalletCoreBase related
              error NameTooLong();
              error VersionTooLong();
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v5.1.0) (utils/Create2.sol)
          pragma solidity ^0.8.20;
          import {Errors} from "./Errors.sol";
          /**
           * @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.
           * `CREATE2` can be used to compute in advance the address where a smart
           * contract will be deployed, which allows for interesting new mechanisms known
           * as 'counterfactual interactions'.
           *
           * See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more
           * information.
           */
          library Create2 {
              /**
               * @dev There's no code to deploy.
               */
              error Create2EmptyBytecode();
              /**
               * @dev Deploys a contract using `CREATE2`. The address where the contract
               * will be deployed can be known in advance via {computeAddress}.
               *
               * The bytecode for a contract can be obtained from Solidity with
               * `type(contractName).creationCode`.
               *
               * Requirements:
               *
               * - `bytecode` must not be empty.
               * - `salt` must have not been used for `bytecode` already.
               * - the factory must have a balance of at least `amount`.
               * - if `amount` is non-zero, `bytecode` must have a `payable` constructor.
               */
              function deploy(uint256 amount, bytes32 salt, bytes memory bytecode) internal returns (address addr) {
                  if (address(this).balance < amount) {
                      revert Errors.InsufficientBalance(address(this).balance, amount);
                  }
                  if (bytecode.length == 0) {
                      revert Create2EmptyBytecode();
                  }
                  assembly ("memory-safe") {
                      addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
                      // if no address was created, and returndata is not empty, bubble revert
                      if and(iszero(addr), not(iszero(returndatasize()))) {
                          let p := mload(0x40)
                          returndatacopy(p, 0, returndatasize())
                          revert(p, returndatasize())
                      }
                  }
                  if (addr == address(0)) {
                      revert Errors.FailedDeployment();
                  }
              }
              /**
               * @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the
               * `bytecodeHash` or `salt` will result in a new destination address.
               */
              function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) {
                  return computeAddress(salt, bytecodeHash, address(this));
              }
              /**
               * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at
               * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.
               */
              function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) internal pure returns (address addr) {
                  assembly ("memory-safe") {
                      let ptr := mload(0x40) // Get free memory pointer
                      // |                   | ↓ ptr ...  ↓ ptr + 0x0B (start) ...  ↓ ptr + 0x20 ...  ↓ ptr + 0x40 ...   |
                      // |-------------------|---------------------------------------------------------------------------|
                      // | bytecodeHash      |                                                        CCCCCCCCCCCCC...CC |
                      // | salt              |                                      BBBBBBBBBBBBB...BB                   |
                      // | deployer          | 000000...0000AAAAAAAAAAAAAAAAAAA...AA                                     |
                      // | 0xFF              |            FF                                                             |
                      // |-------------------|---------------------------------------------------------------------------|
                      // | memory            | 000000...00FFAAAAAAAAAAAAAAAAAAA...AABBBBBBBBBBBBB...BBCCCCCCCCCCCCC...CC |
                      // | keccak(start, 85) |            ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ |
                      mstore(add(ptr, 0x40), bytecodeHash)
                      mstore(add(ptr, 0x20), salt)
                      mstore(ptr, deployer) // Right-aligned with 12 preceding garbage bytes
                      let start := add(ptr, 0x0b) // The hashed data starts at the final garbage byte which we will set to 0xff
                      mstore8(start, 0xff)
                      addr := and(keccak256(start, 85), 0xffffffffffffffffffffffffffffffffffffffff)
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol)
          pragma solidity ^0.8.20;
          /**
           * @dev Collection of common custom errors used in multiple contracts
           *
           * IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
           * It is recommended to avoid relying on the error API for critical functionality.
           *
           * _Available since v5.1._
           */
          library Errors {
              /**
               * @dev The ETH balance of the account is not enough to perform the operation.
               */
              error InsufficientBalance(uint256 balance, uint256 needed);
              /**
               * @dev A call to an address target failed. The target may have reverted.
               */
              error FailedCall();
              /**
               * @dev The deployment failed.
               */
              error FailedDeployment();
              /**
               * @dev A necessary precompile is missing.
               */
              error MissingPrecompile(address);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v5.1.0) (utils/cryptography/ECDSA.sol)
          pragma solidity ^0.8.20;
          /**
           * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
           *
           * These functions can be used to verify that a message was signed by the holder
           * of the private keys of a given address.
           */
          library ECDSA {
              enum RecoverError {
                  NoError,
                  InvalidSignature,
                  InvalidSignatureLength,
                  InvalidSignatureS
              }
              /**
               * @dev The signature derives the `address(0)`.
               */
              error ECDSAInvalidSignature();
              /**
               * @dev The signature has an invalid length.
               */
              error ECDSAInvalidSignatureLength(uint256 length);
              /**
               * @dev The signature has an S value that is in the upper half order.
               */
              error ECDSAInvalidSignatureS(bytes32 s);
              /**
               * @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
               * return address(0) without also returning an error description. Errors are documented using an enum (error type)
               * and a bytes32 providing additional information about the error.
               *
               * If no error is returned, then the address can be used for verification purposes.
               *
               * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
               * this function rejects them by requiring the `s` value to be in the lower
               * half order, and the `v` value to be either 27 or 28.
               *
               * IMPORTANT: `hash` _must_ be the result of a hash operation for the
               * verification to be secure: it is possible to craft signatures that
               * recover to arbitrary addresses for non-hashed data. A safe way to ensure
               * this is by receiving a hash of the original message (which may otherwise
               * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
               *
               * Documentation for signature generation:
               * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
               * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
               */
              function tryRecover(
                  bytes32 hash,
                  bytes memory signature
              ) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
                  if (signature.length == 65) {
                      bytes32 r;
                      bytes32 s;
                      uint8 v;
                      // ecrecover takes the signature parameters, and the only way to get them
                      // currently is to use assembly.
                      assembly ("memory-safe") {
                          r := mload(add(signature, 0x20))
                          s := mload(add(signature, 0x40))
                          v := byte(0, mload(add(signature, 0x60)))
                      }
                      return tryRecover(hash, v, r, s);
                  } else {
                      return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
                  }
              }
              /**
               * @dev Returns the address that signed a hashed message (`hash`) with
               * `signature`. This address can then be used for verification purposes.
               *
               * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
               * this function rejects them by requiring the `s` value to be in the lower
               * half order, and the `v` value to be either 27 or 28.
               *
               * IMPORTANT: `hash` _must_ be the result of a hash operation for the
               * verification to be secure: it is possible to craft signatures that
               * recover to arbitrary addresses for non-hashed data. A safe way to ensure
               * this is by receiving a hash of the original message (which may otherwise
               * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
               */
              function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
                  (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
                  _throwError(error, errorArg);
                  return recovered;
              }
              /**
               * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
               *
               * See https://eips.ethereum.org/EIPS/eip-2098[ERC-2098 short signatures]
               */
              function tryRecover(
                  bytes32 hash,
                  bytes32 r,
                  bytes32 vs
              ) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
                  unchecked {
                      bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
                      // We do not check for an overflow here since the shift operation results in 0 or 1.
                      uint8 v = uint8((uint256(vs) >> 255) + 27);
                      return tryRecover(hash, v, r, s);
                  }
              }
              /**
               * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
               */
              function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
                  (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
                  _throwError(error, errorArg);
                  return recovered;
              }
              /**
               * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
               * `r` and `s` signature fields separately.
               */
              function tryRecover(
                  bytes32 hash,
                  uint8 v,
                  bytes32 r,
                  bytes32 s
              ) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
                  // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
                  // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
                  // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
                  // signatures from current libraries generate a unique signature with an s-value in the lower half order.
                  //
                  // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
                  // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
                  // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
                  // these malleable signatures as well.
                  if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
                      return (address(0), RecoverError.InvalidSignatureS, s);
                  }
                  // If the signature is valid (and not malleable), return the signer address
                  address signer = ecrecover(hash, v, r, s);
                  if (signer == address(0)) {
                      return (address(0), RecoverError.InvalidSignature, bytes32(0));
                  }
                  return (signer, RecoverError.NoError, bytes32(0));
              }
              /**
               * @dev Overload of {ECDSA-recover} that receives the `v`,
               * `r` and `s` signature fields separately.
               */
              function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
                  (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
                  _throwError(error, errorArg);
                  return recovered;
              }
              /**
               * @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
               */
              function _throwError(RecoverError error, bytes32 errorArg) private pure {
                  if (error == RecoverError.NoError) {
                      return; // no error: do nothing
                  } else if (error == RecoverError.InvalidSignature) {
                      revert ECDSAInvalidSignature();
                  } else if (error == RecoverError.InvalidSignatureLength) {
                      revert ECDSAInvalidSignatureLength(uint256(errorArg));
                  } else if (error == RecoverError.InvalidSignatureS) {
                      revert ECDSAInvalidSignatureS(errorArg);
                  }
              }
          }
          // SPDX-License-Identifier: GPL-3.0
          pragma solidity ^0.8.23;
          interface IValidator {
              function validate(
                  bytes32 msgHash,
                  bytes calldata validationData
              ) external view;
          }