ETH Price: $3,347.46 (+9.17%)

Transaction Decoder

Block:
22926277 at Jul-15-2025 06:09:11 PM +UTC
Transaction Fee:
0.001839406434091422 ETH $6.16
Gas Used:
225,726 Gas / 8.148846097 Gwei

Emitted Events:

189 TetherToken.Transfer( from=[Sender] 0x01ad603cb9bef2a56cf4b1e321242031c14a1b01, to=Spender, value=2500000000 )
190 TetherToken.Transfer( from=Spender, to=0x51C72848c68a965f66FA7a88855F9f7784502a7F, value=2478125000 )
191 PepeToken.Transfer( from=0x51C72848c68a965f66FA7a88855F9f7784502a7F, to=Spender, value=194460842184451714476670976 )
192 PepeToken.Approval( owner=0x51C72848c68a965f66FA7a88855F9f7784502a7F, spender=SwapERC20, value=115792089237316195423570985008687907853269984664634966173222350899572302591031 )
193 PepeToken.Transfer( from=0x51C72848c68a965f66FA7a88855F9f7784502a7F, to=SplitWallet, value=97230421092225857238335 )
194 PepeToken.Approval( owner=0x51C72848c68a965f66FA7a88855F9f7784502a7F, spender=SwapERC20, value=115792089237316195423570985008687907853269984664634966075991929807346445352696 )
195 SwapERC20.SwapERC20( nonce=1747242086938, signerWallet=0x51C72848c68a965f66FA7a88855F9f7784502a7F )
196 TetherToken.Transfer( from=Spender, to=GnosisSafeProxy, value=21875000 )
197 PepeToken.Transfer( from=Spender, to=[Sender] 0x01ad603cb9bef2a56cf4b1e321242031c14a1b01, value=194460842184451714476670976 )
198 0x881d40237659c251811cec9c364ef91dc08d300c.0xbeee1e6e7fe307ddcf84b0a16137a4430ad5e2480fc4f4a8e250ab56ccd7630d( 0xbeee1e6e7fe307ddcf84b0a16137a4430ad5e2480fc4f4a8e250ab56ccd7630d, 0x74008a9e83921090a0cc3c37a7b39398a692db963164a76e069ab2e8fb053e10, 0x00000000000000000000000001ad603cb9bef2a56cf4b1e321242031c14a1b01 )

Account State Difference:

  Address   Before After State Difference Code
0x01aD603C...1C14a1B01
0.153866956772152712 Eth
Nonce: 126
0.15202755033806129 Eth
Nonce: 127
0.001839406434091422
0x69825081...3d2311933
0xD82E10B9...e6CF2fC46
(AirSwap: V5 DEX SwapERC20)
0xdAC17F95...13D831ec7
(BuilderNet)
111.643663765334465109 Eth111.643889491335368013 Eth0.000225726000902904

Execution Trace

Metamask: Swap Router.5f575529( )
  • TetherToken.transferFrom( _from=0x01aD603Cb9bEf2A56cf4B1E321242031C14a1B01, _to=0x74de5d4FCbf63E00296fd95d33236B9794016631, _value=2500000000 )
  • Spender.swap( adapter=0x60FBaF99832Fb4360351AbC2b55e4B1F2fe98c71, data=0x4495C08800000000000000000000000001AD603CB9BEF2A56CF4B1E321242031C14A1B0100000000000000000000000000000000000000000000000000000196CFBE721A00000000000000000000000000000000000000000000000000000000687699CD00000000000000000000000051C72848C68A965F66FA7A88855F9F7784502A7F0000000000000000000000006982508145454CE325DDBE47A25D4EC3D2311933000000000000000000000000000000000000000000A0DAAF60C3C46000000000000000000000000000000000DAC17F958D2EE523A2206206994597C13D831EC70000000000000000000000000000000000000000000000000000000093B52FC8000000000000000000000000000000000000000000000000000000000000001C4C6267F8334B7C4D04DA54A0C6D3B26C568F8EE0066DB4283AF82BEFBF3488B567D32415FD2EB569A51A84C94839517A2AC336A46B034CD494BF66C1851A155900000000000000000000000000000000000000000000000000000000014DC9380000000000000000000000002ACF35C9A3F4C5C3F4C78EF5FB64C3EE82F07C450000000000000000000000000000000000000000000000000000000000000000 )
    • 0x60fbaf99832fb4360351abc2b55e4b1f2fe98c71.4495c088( )
      • TetherToken.allowance( _owner=0x74de5d4FCbf63E00296fd95d33236B9794016631, _spender=0xD82E10B9A4107939e55fCCa9B53A9ede6CF2fC46 ) => ( remaining=115792089237316195423570985008687907853269984665640564039457584007913129639935 )
      • SwapERC20.swapLight( nonce=1747242086938, expiry=1752603085, signerWallet=0x51C72848c68a965f66FA7a88855F9f7784502a7F, signerToken=0x6982508145454Ce325dDbE47a25d4ec3d2311933, signerAmount=194460842184451714476670976, senderToken=0xdAC17F958D2ee523a2206206994597C13D831ec7, senderAmount=2478125000, v=28, r=4C6267F8334B7C4D04DA54A0C6D3B26C568F8EE0066DB4283AF82BEFBF3488B5, s=67D32415FD2EB569A51A84C94839517A2AC336A46B034CD494BF66C1851A1559 )
        • Null: 0x000...001.383f3b2e( )
        • TetherToken.transferFrom( _from=0x74de5d4FCbf63E00296fd95d33236B9794016631, _to=0x51C72848c68a965f66FA7a88855F9f7784502a7F, _value=2478125000 )
        • PepeToken.transferFrom( sender=0x51C72848c68a965f66FA7a88855F9f7784502a7F, recipient=0x74de5d4FCbf63E00296fd95d33236B9794016631, amount=194460842184451714476670976 ) => ( True )
        • PepeToken.transferFrom( sender=0x51C72848c68a965f66FA7a88855F9f7784502a7F, recipient=0xaD30f7EEBD9Bd5150a256F47DA41d4403033CdF0, amount=97230421092225857238335 ) => ( True )
        • TetherToken.transfer( _to=0x2aCf35C9A3F4c5C3F4c78EF5Fb64c3EE82f07c45, _value=21875000 )
        • TetherToken.balanceOf( who=0x74de5d4FCbf63E00296fd95d33236B9794016631 ) => ( 0 )
        • PepeToken.balanceOf( account=0x74de5d4FCbf63E00296fd95d33236B9794016631 ) => ( 194460842184451714476670976 )
        • PepeToken.transfer( recipient=0x01aD603Cb9bEf2A56cf4B1E321242031C14a1B01, amount=194460842184451714476670976 ) => ( True )
          File 1 of 6: Spender
          {"Constants.84ef19f8.sol":{"content":"// SPDX-License-Identifier: MIT\r\n\r\npragma solidity ^0.6.0;\r\n\r\nlibrary Constants {\r\n    address internal constant ETH = 0x0000000000000000000000000000000000000000;\r\n}\r\n"},"Spender.3372a096.sol":{"content":"// SPDX-License-Identifier: MIT\r\n\r\npragma solidity ^0.6.0;\r\n\r\nimport \"./Constants.84ef19f8.sol\";\r\n\r\ncontract Spender {\r\n    address public immutable metaswap;\r\n\r\n    constructor() public {\r\n        metaswap = msg.sender;\r\n    }\r\n\r\n    /// @dev Receives ether from swaps\r\n    fallback() external payable {}\r\n\r\n    function swap(address adapter, bytes calldata data) external payable {\r\n        require(msg.sender == metaswap, \"FORBIDDEN\");\r\n        require(adapter != address(0), \"ADAPTER_NOT_PROVIDED\");\r\n        _delegate(adapter, data, \"ADAPTER_DELEGATECALL_FAILED\");\r\n    }\r\n\r\n    /**\r\n     * @dev Performs a delegatecall and bubbles up the errors, adapted from\r\n     * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol\r\n     * @param target Address of the contract to delegatecall\r\n     * @param data Data passed in the delegatecall\r\n     * @param errorMessage Fallback revert reason\r\n     */\r\n    function _delegate(\r\n        address target,\r\n        bytes memory data,\r\n        string memory errorMessage\r\n    ) private returns (bytes memory) {\r\n        // solhint-disable-next-line avoid-low-level-calls\r\n        (bool success, bytes memory returndata) = target.delegatecall(data);\r\n        if (success) {\r\n            return returndata;\r\n        } else {\r\n            // Look for revert reason and bubble it up if present\r\n            if (returndata.length \u003e 0) {\r\n                // The easiest way to bubble the revert reason is using memory via assembly\r\n\r\n                // solhint-disable-next-line no-inline-assembly\r\n                assembly {\r\n                    let returndata_size := mload(returndata)\r\n                    revert(add(32, returndata), returndata_size)\r\n                }\r\n            } else {\r\n                revert(errorMessage);\r\n            }\r\n        }\r\n    }\r\n}\r\n"}}

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

          File 3 of 6: PepeToken
          // Sources flattened with hardhat v2.7.0 https://hardhat.org
          
          // File @openzeppelin/contracts/utils/[email protected]
          
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts v4.4.0 (utils/Context.sol)
          
          pragma solidity ^0.8.0;
          
          /**
           * @dev Provides information about the current execution context, including the
           * sender of the transaction and its data. While these are generally available
           * via msg.sender and msg.data, they should not be accessed in such a direct
           * manner, since when dealing with meta-transactions the account sending and
           * paying for execution may not be the actual sender (as far as an application
           * is concerned).
           *
           * This contract is only required for intermediate, library-like contracts.
           */
          abstract contract Context {
              function _msgSender() internal view virtual returns (address) {
                  return msg.sender;
              }
          
              function _msgData() internal view virtual returns (bytes calldata) {
                  return msg.data;
              }
          }
          
          
          // File @openzeppelin/contracts/access/[email protected]
          
          
          // OpenZeppelin Contracts v4.4.0 (access/Ownable.sol)
          
          pragma solidity ^0.8.0;
          
          /**
           * @dev Contract module which provides a basic access control mechanism, where
           * there is an account (an owner) that can be granted exclusive access to
           * specific functions.
           *
           * By default, the owner account will be the one that deploys the contract. This
           * can later be changed with {transferOwnership}.
           *
           * This module is used through inheritance. It will make available the modifier
           * `onlyOwner`, which can be applied to your functions to restrict their use to
           * the owner.
           */
          abstract contract Ownable is Context {
              address private _owner;
          
              event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
          
              /**
               * @dev Initializes the contract setting the deployer as the initial owner.
               */
              constructor() {
                  _transferOwnership(_msgSender());
              }
          
              /**
               * @dev Returns the address of the current owner.
               */
              function owner() public view virtual returns (address) {
                  return _owner;
              }
          
              /**
               * @dev Throws if called by any account other than the owner.
               */
              modifier onlyOwner() {
                  require(owner() == _msgSender(), "Ownable: caller is not the owner");
                  _;
              }
          
              /**
               * @dev Leaves the contract without owner. It will not be possible to call
               * `onlyOwner` functions anymore. Can only be called by the current owner.
               *
               * NOTE: Renouncing ownership will leave the contract without an owner,
               * thereby removing any functionality that is only available to the owner.
               */
              function renounceOwnership() public virtual onlyOwner {
                  _transferOwnership(address(0));
              }
          
              /**
               * @dev Transfers ownership of the contract to a new account (`newOwner`).
               * Can only be called by the current owner.
               */
              function transferOwnership(address newOwner) public virtual onlyOwner {
                  require(newOwner != address(0), "Ownable: new owner is the zero address");
                  _transferOwnership(newOwner);
              }
          
              /**
               * @dev Transfers ownership of the contract to a new account (`newOwner`).
               * Internal function without access restriction.
               */
              function _transferOwnership(address newOwner) internal virtual {
                  address oldOwner = _owner;
                  _owner = newOwner;
                  emit OwnershipTransferred(oldOwner, newOwner);
              }
          }
          
          
          // File @openzeppelin/contracts/token/ERC20/[email protected]
          
          
          // OpenZeppelin Contracts v4.4.0 (token/ERC20/IERC20.sol)
          
          pragma solidity ^0.8.0;
          
          /**
           * @dev Interface of the ERC20 standard as defined in the EIP.
           */
          interface IERC20 {
              /**
               * @dev Returns the amount of tokens in existence.
               */
              function totalSupply() external view returns (uint256);
          
              /**
               * @dev Returns the amount of tokens owned by `account`.
               */
              function balanceOf(address account) external view returns (uint256);
          
              /**
               * @dev Moves `amount` tokens from the caller's account to `recipient`.
               *
               * Returns a boolean value indicating whether the operation succeeded.
               *
               * Emits a {Transfer} event.
               */
              function transfer(address recipient, uint256 amount) external returns (bool);
          
              /**
               * @dev Returns the remaining number of tokens that `spender` will be
               * allowed to spend on behalf of `owner` through {transferFrom}. This is
               * zero by default.
               *
               * This value changes when {approve} or {transferFrom} are called.
               */
              function allowance(address owner, address spender) external view returns (uint256);
          
              /**
               * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
               *
               * Returns a boolean value indicating whether the operation succeeded.
               *
               * IMPORTANT: Beware that changing an allowance with this method brings the risk
               * that someone may use both the old and the new allowance by unfortunate
               * transaction ordering. One possible solution to mitigate this race
               * condition is to first reduce the spender's allowance to 0 and set the
               * desired value afterwards:
               * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
               *
               * Emits an {Approval} event.
               */
              function approve(address spender, uint256 amount) external returns (bool);
          
              /**
               * @dev Moves `amount` tokens from `sender` to `recipient` using the
               * allowance mechanism. `amount` is then deducted from the caller's
               * allowance.
               *
               * Returns a boolean value indicating whether the operation succeeded.
               *
               * Emits a {Transfer} event.
               */
              function transferFrom(
                  address sender,
                  address recipient,
                  uint256 amount
              ) external returns (bool);
          
              /**
               * @dev Emitted when `value` tokens are moved from one account (`from`) to
               * another (`to`).
               *
               * Note that `value` may be zero.
               */
              event Transfer(address indexed from, address indexed to, uint256 value);
          
              /**
               * @dev Emitted when the allowance of a `spender` for an `owner` is set by
               * a call to {approve}. `value` is the new allowance.
               */
              event Approval(address indexed owner, address indexed spender, uint256 value);
          }
          
          
          // File @openzeppelin/contracts/token/ERC20/extensions/[email protected]
          
          
          // OpenZeppelin Contracts v4.4.0 (token/ERC20/extensions/IERC20Metadata.sol)
          
          pragma solidity ^0.8.0;
          
          /**
           * @dev Interface for the optional metadata functions from the ERC20 standard.
           *
           * _Available since v4.1._
           */
          interface IERC20Metadata is IERC20 {
              /**
               * @dev Returns the name of the token.
               */
              function name() external view returns (string memory);
          
              /**
               * @dev Returns the symbol of the token.
               */
              function symbol() external view returns (string memory);
          
              /**
               * @dev Returns the decimals places of the token.
               */
              function decimals() external view returns (uint8);
          }
          
          
          // File @openzeppelin/contracts/token/ERC20/[email protected]
          
          
          // OpenZeppelin Contracts v4.4.0 (token/ERC20/ERC20.sol)
          
          pragma solidity ^0.8.0;
          
          
          
          /**
           * @dev Implementation of the {IERC20} interface.
           *
           * This implementation is agnostic to the way tokens are created. This means
           * that a supply mechanism has to be added in a derived contract using {_mint}.
           * For a generic mechanism see {ERC20PresetMinterPauser}.
           *
           * TIP: For a detailed writeup see our guide
           * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
           * to implement supply mechanisms].
           *
           * We have followed general OpenZeppelin Contracts guidelines: functions revert
           * instead returning `false` on failure. This behavior is nonetheless
           * conventional and does not conflict with the expectations of ERC20
           * applications.
           *
           * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
           * This allows applications to reconstruct the allowance for all accounts just
           * by listening to said events. Other implementations of the EIP may not emit
           * these events, as it isn't required by the specification.
           *
           * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
           * functions have been added to mitigate the well-known issues around setting
           * allowances. See {IERC20-approve}.
           */
          contract ERC20 is Context, IERC20, IERC20Metadata {
              mapping(address => uint256) private _balances;
          
              mapping(address => mapping(address => uint256)) private _allowances;
          
              uint256 private _totalSupply;
          
              string private _name;
              string private _symbol;
          
              /**
               * @dev Sets the values for {name} and {symbol}.
               *
               * The default value of {decimals} is 18. To select a different value for
               * {decimals} you should overload it.
               *
               * All two of these values are immutable: they can only be set once during
               * construction.
               */
              constructor(string memory name_, string memory symbol_) {
                  _name = name_;
                  _symbol = symbol_;
              }
          
              /**
               * @dev Returns the name of the token.
               */
              function name() public view virtual override returns (string memory) {
                  return _name;
              }
          
              /**
               * @dev Returns the symbol of the token, usually a shorter version of the
               * name.
               */
              function symbol() public view virtual override returns (string memory) {
                  return _symbol;
              }
          
              /**
               * @dev Returns the number of decimals used to get its user representation.
               * For example, if `decimals` equals `2`, a balance of `505` tokens should
               * be displayed to a user as `5.05` (`505 / 10 ** 2`).
               *
               * Tokens usually opt for a value of 18, imitating the relationship between
               * Ether and Wei. This is the value {ERC20} uses, unless this function is
               * overridden;
               *
               * NOTE: This information is only used for _display_ purposes: it in
               * no way affects any of the arithmetic of the contract, including
               * {IERC20-balanceOf} and {IERC20-transfer}.
               */
              function decimals() public view virtual override returns (uint8) {
                  return 18;
              }
          
              /**
               * @dev See {IERC20-totalSupply}.
               */
              function totalSupply() public view virtual override returns (uint256) {
                  return _totalSupply;
              }
          
              /**
               * @dev See {IERC20-balanceOf}.
               */
              function balanceOf(address account) public view virtual override returns (uint256) {
                  return _balances[account];
              }
          
              /**
               * @dev See {IERC20-transfer}.
               *
               * Requirements:
               *
               * - `recipient` cannot be the zero address.
               * - the caller must have a balance of at least `amount`.
               */
              function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
                  _transfer(_msgSender(), recipient, amount);
                  return true;
              }
          
              /**
               * @dev See {IERC20-allowance}.
               */
              function allowance(address owner, address spender) public view virtual override returns (uint256) {
                  return _allowances[owner][spender];
              }
          
              /**
               * @dev See {IERC20-approve}.
               *
               * Requirements:
               *
               * - `spender` cannot be the zero address.
               */
              function approve(address spender, uint256 amount) public virtual override returns (bool) {
                  _approve(_msgSender(), spender, amount);
                  return true;
              }
          
              /**
               * @dev See {IERC20-transferFrom}.
               *
               * Emits an {Approval} event indicating the updated allowance. This is not
               * required by the EIP. See the note at the beginning of {ERC20}.
               *
               * Requirements:
               *
               * - `sender` and `recipient` cannot be the zero address.
               * - `sender` must have a balance of at least `amount`.
               * - the caller must have allowance for ``sender``'s tokens of at least
               * `amount`.
               */
              function transferFrom(
                  address sender,
                  address recipient,
                  uint256 amount
              ) public virtual override returns (bool) {
                  _transfer(sender, recipient, amount);
          
                  uint256 currentAllowance = _allowances[sender][_msgSender()];
                  require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
                  unchecked {
                      _approve(sender, _msgSender(), currentAllowance - amount);
                  }
          
                  return true;
              }
          
              /**
               * @dev Atomically increases the allowance granted to `spender` by the caller.
               *
               * This is an alternative to {approve} that can be used as a mitigation for
               * problems described in {IERC20-approve}.
               *
               * Emits an {Approval} event indicating the updated allowance.
               *
               * Requirements:
               *
               * - `spender` cannot be the zero address.
               */
              function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
                  _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
                  return true;
              }
          
              /**
               * @dev Atomically decreases the allowance granted to `spender` by the caller.
               *
               * This is an alternative to {approve} that can be used as a mitigation for
               * problems described in {IERC20-approve}.
               *
               * Emits an {Approval} event indicating the updated allowance.
               *
               * Requirements:
               *
               * - `spender` cannot be the zero address.
               * - `spender` must have allowance for the caller of at least
               * `subtractedValue`.
               */
              function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
                  uint256 currentAllowance = _allowances[_msgSender()][spender];
                  require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
                  unchecked {
                      _approve(_msgSender(), spender, currentAllowance - subtractedValue);
                  }
          
                  return true;
              }
          
              /**
               * @dev Moves `amount` of tokens from `sender` to `recipient`.
               *
               * This internal function is equivalent to {transfer}, and can be used to
               * e.g. implement automatic token fees, slashing mechanisms, etc.
               *
               * Emits a {Transfer} event.
               *
               * Requirements:
               *
               * - `sender` cannot be the zero address.
               * - `recipient` cannot be the zero address.
               * - `sender` must have a balance of at least `amount`.
               */
              function _transfer(
                  address sender,
                  address recipient,
                  uint256 amount
              ) internal virtual {
                  require(sender != address(0), "ERC20: transfer from the zero address");
                  require(recipient != address(0), "ERC20: transfer to the zero address");
          
                  _beforeTokenTransfer(sender, recipient, amount);
          
                  uint256 senderBalance = _balances[sender];
                  require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
                  unchecked {
                      _balances[sender] = senderBalance - amount;
                  }
                  _balances[recipient] += amount;
          
                  emit Transfer(sender, recipient, amount);
          
                  _afterTokenTransfer(sender, recipient, amount);
              }
          
              /** @dev Creates `amount` tokens and assigns them to `account`, increasing
               * the total supply.
               *
               * Emits a {Transfer} event with `from` set to the zero address.
               *
               * Requirements:
               *
               * - `account` cannot be the zero address.
               */
              function _mint(address account, uint256 amount) internal virtual {
                  require(account != address(0), "ERC20: mint to the zero address");
          
                  _beforeTokenTransfer(address(0), account, amount);
          
                  _totalSupply += amount;
                  _balances[account] += amount;
                  emit Transfer(address(0), account, amount);
          
                  _afterTokenTransfer(address(0), account, amount);
              }
          
              /**
               * @dev Destroys `amount` tokens from `account`, reducing the
               * total supply.
               *
               * Emits a {Transfer} event with `to` set to the zero address.
               *
               * Requirements:
               *
               * - `account` cannot be the zero address.
               * - `account` must have at least `amount` tokens.
               */
              function _burn(address account, uint256 amount) internal virtual {
                  require(account != address(0), "ERC20: burn from the zero address");
          
                  _beforeTokenTransfer(account, address(0), amount);
          
                  uint256 accountBalance = _balances[account];
                  require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
                  unchecked {
                      _balances[account] = accountBalance - amount;
                  }
                  _totalSupply -= amount;
          
                  emit Transfer(account, address(0), amount);
          
                  _afterTokenTransfer(account, address(0), amount);
              }
          
              /**
               * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
               *
               * This internal function is equivalent to `approve`, and can be used to
               * e.g. set automatic allowances for certain subsystems, etc.
               *
               * Emits an {Approval} event.
               *
               * Requirements:
               *
               * - `owner` cannot be the zero address.
               * - `spender` cannot be the zero address.
               */
              function _approve(
                  address owner,
                  address spender,
                  uint256 amount
              ) internal virtual {
                  require(owner != address(0), "ERC20: approve from the zero address");
                  require(spender != address(0), "ERC20: approve to the zero address");
          
                  _allowances[owner][spender] = amount;
                  emit Approval(owner, spender, amount);
              }
          
              /**
               * @dev Hook that is called before any transfer of tokens. This includes
               * minting and burning.
               *
               * Calling conditions:
               *
               * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
               * will be transferred to `to`.
               * - when `from` is zero, `amount` tokens will be minted for `to`.
               * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
               * - `from` and `to` are never both zero.
               *
               * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
               */
              function _beforeTokenTransfer(
                  address from,
                  address to,
                  uint256 amount
              ) internal virtual {}
          
              /**
               * @dev Hook that is called after any transfer of tokens. This includes
               * minting and burning.
               *
               * Calling conditions:
               *
               * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
               * has been transferred to `to`.
               * - when `from` is zero, `amount` tokens have been minted for `to`.
               * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
               * - `from` and `to` are never both zero.
               *
               * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
               */
              function _afterTokenTransfer(
                  address from,
                  address to,
                  uint256 amount
              ) internal virtual {}
          }
          
          
          // File contracts/PepeToken.sol
          
          
          
          pragma solidity ^0.8.0;
          
          
          contract PepeToken is Ownable, ERC20 {
              bool public limited;
              uint256 public maxHoldingAmount;
              uint256 public minHoldingAmount;
              address public uniswapV2Pair;
              mapping(address => bool) public blacklists;
          
              constructor(uint256 _totalSupply) ERC20("Pepe", "PEPE") {
                  _mint(msg.sender, _totalSupply);
              }
          
              function blacklist(address _address, bool _isBlacklisting) external onlyOwner {
                  blacklists[_address] = _isBlacklisting;
              }
          
              function setRule(bool _limited, address _uniswapV2Pair, uint256 _maxHoldingAmount, uint256 _minHoldingAmount) external onlyOwner {
                  limited = _limited;
                  uniswapV2Pair = _uniswapV2Pair;
                  maxHoldingAmount = _maxHoldingAmount;
                  minHoldingAmount = _minHoldingAmount;
              }
          
              function _beforeTokenTransfer(
                  address from,
                  address to,
                  uint256 amount
              ) override internal virtual {
                  require(!blacklists[to] && !blacklists[from], "Blacklisted");
          
                  if (uniswapV2Pair == address(0)) {
                      require(from == owner() || to == owner(), "trading is not started");
                      return;
                  }
          
                  if (limited && from == uniswapV2Pair) {
                      require(super.balanceOf(to) + amount <= maxHoldingAmount && super.balanceOf(to) + amount >= minHoldingAmount, "Forbid");
                  }
              }
          
              function burn(uint256 value) external {
                  _burn(msg.sender, value);
              }
          }

          File 4 of 6: SplitWallet
          // SPDX-License-Identifier: GPL-3.0-or-later
          pragma solidity 0.8.4;
          import {ISplitMain} from './interfaces/ISplitMain.sol';
          import {ERC20} from '@rari-capital/solmate/src/tokens/ERC20.sol';
          import {SafeTransferLib} from '@rari-capital/solmate/src/utils/SafeTransferLib.sol';
          /**
           * ERRORS
           */
          /// @notice Unauthorized sender
          error Unauthorized();
          /**
           * @title SplitWallet
           * @author 0xSplits <[email protected]>
           * @notice The implementation logic for `SplitProxy`.
           * @dev `SplitProxy` handles `receive()` itself to avoid the gas cost with `DELEGATECALL`.
           */
          contract SplitWallet {
            using SafeTransferLib for address;
            using SafeTransferLib for ERC20;
            /**
             * EVENTS
             */
            /** @notice emitted after each successful ETH transfer to proxy
             *  @param split Address of the split that received ETH
             *  @param amount Amount of ETH received
             */
            event ReceiveETH(address indexed split, uint256 amount);
            /**
             * STORAGE
             */
            /**
             * STORAGE - CONSTANTS & IMMUTABLES
             */
            /// @notice address of SplitMain for split distributions & EOA/SC withdrawals
            ISplitMain public immutable splitMain;
            /**
             * MODIFIERS
             */
            /// @notice Reverts if the sender isn't SplitMain
            modifier onlySplitMain() {
              if (msg.sender != address(splitMain)) revert Unauthorized();
              _;
            }
            /**
             * CONSTRUCTOR
             */
            constructor() {
              splitMain = ISplitMain(msg.sender);
            }
            /**
             * FUNCTIONS - PUBLIC & EXTERNAL
             */
            /** @notice Sends amount `amount` of ETH in proxy to SplitMain
             *  @dev payable reduces gas cost; no vulnerability to accidentally lock
             *  ETH introduced since fn call is restricted to SplitMain
             *  @param amount Amount to send
             */
            function sendETHToMain(uint256 amount) external payable onlySplitMain() {
              address(splitMain).safeTransferETH(amount);
            }
            /** @notice Sends amount `amount` of ERC20 `token` in proxy to SplitMain
             *  @dev payable reduces gas cost; no vulnerability to accidentally lock
             *  ETH introduced since fn call is restricted to SplitMain
             *  @param token Token to send
             *  @param amount Amount to send
             */
            function sendERC20ToMain(ERC20 token, uint256 amount)
              external
              payable
              onlySplitMain()
            {
              token.safeTransfer(address(splitMain), amount);
            }
          }
          // SPDX-License-Identifier: GPL-3.0-or-later
          pragma solidity 0.8.4;
          import {ERC20} from '@rari-capital/solmate/src/tokens/ERC20.sol';
          /**
           * @title ISplitMain
           * @author 0xSplits <[email protected]>
           */
          interface ISplitMain {
            /**
             * FUNCTIONS
             */
            function walletImplementation() external returns (address);
            function createSplit(
              address[] calldata accounts,
              uint32[] calldata percentAllocations,
              uint32 distributorFee,
              address controller
            ) external returns (address);
            function predictImmutableSplitAddress(
              address[] calldata accounts,
              uint32[] calldata percentAllocations,
              uint32 distributorFee
            ) external view returns (address);
            function updateSplit(
              address split,
              address[] calldata accounts,
              uint32[] calldata percentAllocations,
              uint32 distributorFee
            ) external;
            function transferControl(address split, address newController) external;
            function cancelControlTransfer(address split) external;
            function acceptControl(address split) external;
            function makeSplitImmutable(address split) external;
            function distributeETH(
              address split,
              address[] calldata accounts,
              uint32[] calldata percentAllocations,
              uint32 distributorFee,
              address distributorAddress
            ) external;
            function updateAndDistributeETH(
              address split,
              address[] calldata accounts,
              uint32[] calldata percentAllocations,
              uint32 distributorFee,
              address distributorAddress
            ) external;
            function distributeERC20(
              address split,
              ERC20 token,
              address[] calldata accounts,
              uint32[] calldata percentAllocations,
              uint32 distributorFee,
              address distributorAddress
            ) external;
            function updateAndDistributeERC20(
              address split,
              ERC20 token,
              address[] calldata accounts,
              uint32[] calldata percentAllocations,
              uint32 distributorFee,
              address distributorAddress
            ) external;
            function withdraw(
              address account,
              uint256 withdrawETH,
              ERC20[] calldata tokens
            ) external;
            /**
             * EVENTS
             */
            /** @notice emitted after each successful split creation
             *  @param split Address of the created split
             */
            event CreateSplit(address indexed split);
            /** @notice emitted after each successful split update
             *  @param split Address of the updated split
             */
            event UpdateSplit(address indexed split);
            /** @notice emitted after each initiated split control transfer
             *  @param split Address of the split control transfer was initiated for
             *  @param newPotentialController Address of the split's new potential controller
             */
            event InitiateControlTransfer(
              address indexed split,
              address indexed newPotentialController
            );
            /** @notice emitted after each canceled split control transfer
             *  @param split Address of the split control transfer was canceled for
             */
            event CancelControlTransfer(address indexed split);
            /** @notice emitted after each successful split control transfer
             *  @param split Address of the split control was transferred for
             *  @param previousController Address of the split's previous controller
             *  @param newController Address of the split's new controller
             */
            event ControlTransfer(
              address indexed split,
              address indexed previousController,
              address indexed newController
            );
            /** @notice emitted after each successful ETH balance split
             *  @param split Address of the split that distributed its balance
             *  @param amount Amount of ETH distributed
             *  @param distributorAddress Address to credit distributor fee to
             */
            event DistributeETH(
              address indexed split,
              uint256 amount,
              address indexed distributorAddress
            );
            /** @notice emitted after each successful ERC20 balance split
             *  @param split Address of the split that distributed its balance
             *  @param token Address of ERC20 distributed
             *  @param amount Amount of ERC20 distributed
             *  @param distributorAddress Address to credit distributor fee to
             */
            event DistributeERC20(
              address indexed split,
              ERC20 indexed token,
              uint256 amount,
              address indexed distributorAddress
            );
            /** @notice emitted after each successful withdrawal
             *  @param account Address that funds were withdrawn to
             *  @param ethAmount Amount of ETH withdrawn
             *  @param tokens Addresses of ERC20s withdrawn
             *  @param tokenAmounts Amounts of corresponding ERC20s withdrawn
             */
            event Withdrawal(
              address indexed account,
              uint256 ethAmount,
              ERC20[] tokens,
              uint256[] tokenAmounts
            );
          }
          // SPDX-License-Identifier: AGPL-3.0-only
          pragma solidity >=0.8.0;
          /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
          /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC20.sol)
          /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
          /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
          abstract contract ERC20 {
              /*///////////////////////////////////////////////////////////////
                                            EVENTS
              //////////////////////////////////////////////////////////////*/
              event Transfer(address indexed from, address indexed to, uint256 amount);
              event Approval(address indexed owner, address indexed spender, uint256 amount);
              /*///////////////////////////////////////////////////////////////
                                       METADATA STORAGE
              //////////////////////////////////////////////////////////////*/
              string public name;
              string public symbol;
              uint8 public immutable decimals;
              /*///////////////////////////////////////////////////////////////
                                        ERC20 STORAGE
              //////////////////////////////////////////////////////////////*/
              uint256 public totalSupply;
              mapping(address => uint256) public balanceOf;
              mapping(address => mapping(address => uint256)) public allowance;
              /*///////////////////////////////////////////////////////////////
                                       EIP-2612 STORAGE
              //////////////////////////////////////////////////////////////*/
              bytes32 public constant PERMIT_TYPEHASH =
                  keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
              uint256 internal immutable INITIAL_CHAIN_ID;
              bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
              mapping(address => uint256) public nonces;
              /*///////////////////////////////////////////////////////////////
                                         CONSTRUCTOR
              //////////////////////////////////////////////////////////////*/
              constructor(
                  string memory _name,
                  string memory _symbol,
                  uint8 _decimals
              ) {
                  name = _name;
                  symbol = _symbol;
                  decimals = _decimals;
                  INITIAL_CHAIN_ID = block.chainid;
                  INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
              }
              /*///////////////////////////////////////////////////////////////
                                        ERC20 LOGIC
              //////////////////////////////////////////////////////////////*/
              function approve(address spender, uint256 amount) public virtual returns (bool) {
                  allowance[msg.sender][spender] = amount;
                  emit Approval(msg.sender, spender, amount);
                  return true;
              }
              function transfer(address to, uint256 amount) public virtual returns (bool) {
                  balanceOf[msg.sender] -= amount;
                  // Cannot overflow because the sum of all user
                  // balances can't exceed the max uint256 value.
                  unchecked {
                      balanceOf[to] += amount;
                  }
                  emit Transfer(msg.sender, to, amount);
                  return true;
              }
              function transferFrom(
                  address from,
                  address to,
                  uint256 amount
              ) public virtual returns (bool) {
                  uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
                  if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
                  balanceOf[from] -= amount;
                  // Cannot overflow because the sum of all user
                  // balances can't exceed the max uint256 value.
                  unchecked {
                      balanceOf[to] += amount;
                  }
                  emit Transfer(from, to, amount);
                  return true;
              }
              /*///////////////////////////////////////////////////////////////
                                        EIP-2612 LOGIC
              //////////////////////////////////////////////////////////////*/
              function permit(
                  address owner,
                  address spender,
                  uint256 value,
                  uint256 deadline,
                  uint8 v,
                  bytes32 r,
                  bytes32 s
              ) public virtual {
                  require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
                  // Unchecked because the only math done is incrementing
                  // the owner's nonce which cannot realistically overflow.
                  unchecked {
                      bytes32 digest = keccak256(
                          abi.encodePacked(
                              "\\x19\\x01",
                              DOMAIN_SEPARATOR(),
                              keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
                          )
                      );
                      address recoveredAddress = ecrecover(digest, v, r, s);
                      require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
                      allowance[recoveredAddress][spender] = value;
                  }
                  emit Approval(owner, spender, value);
              }
              function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
                  return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
              }
              function computeDomainSeparator() internal view virtual returns (bytes32) {
                  return
                      keccak256(
                          abi.encode(
                              keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                              keccak256(bytes(name)),
                              keccak256("1"),
                              block.chainid,
                              address(this)
                          )
                      );
              }
              /*///////////////////////////////////////////////////////////////
                                 INTERNAL MINT/BURN LOGIC
              //////////////////////////////////////////////////////////////*/
              function _mint(address to, uint256 amount) internal virtual {
                  totalSupply += amount;
                  // Cannot overflow because the sum of all user
                  // balances can't exceed the max uint256 value.
                  unchecked {
                      balanceOf[to] += amount;
                  }
                  emit Transfer(address(0), to, amount);
              }
              function _burn(address from, uint256 amount) internal virtual {
                  balanceOf[from] -= amount;
                  // Cannot underflow because a user's balance
                  // will never be larger than the total supply.
                  unchecked {
                      totalSupply -= amount;
                  }
                  emit Transfer(from, address(0), amount);
              }
          }
          // SPDX-License-Identifier: AGPL-3.0-only
          pragma solidity >=0.8.0;
          import {ERC20} from "../tokens/ERC20.sol";
          /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
          /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/SafeTransferLib.sol)
          /// @author Modified from Gnosis (https://github.com/gnosis/gp-v2-contracts/blob/main/src/contracts/libraries/GPv2SafeERC20.sol)
          /// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
          library SafeTransferLib {
              /*///////////////////////////////////////////////////////////////
                                      ETH OPERATIONS
              //////////////////////////////////////////////////////////////*/
              function safeTransferETH(address to, uint256 amount) internal {
                  bool callStatus;
                  assembly {
                      // Transfer the ETH and store if it succeeded or not.
                      callStatus := call(gas(), to, amount, 0, 0, 0, 0)
                  }
                  require(callStatus, "ETH_TRANSFER_FAILED");
              }
              /*///////////////////////////////////////////////////////////////
                                     ERC20 OPERATIONS
              //////////////////////////////////////////////////////////////*/
              function safeTransferFrom(
                  ERC20 token,
                  address from,
                  address to,
                  uint256 amount
              ) internal {
                  bool callStatus;
                  assembly {
                      // Get a pointer to some free memory.
                      let freeMemoryPointer := mload(0x40)
                      // Write the abi-encoded calldata to memory piece by piece:
                      mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000) // Begin with the function selector.
                      mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "from" argument.
                      mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "to" argument.
                      mstore(add(freeMemoryPointer, 68), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value.
                      // Call the token and store if it succeeded or not.
                      // We use 100 because the calldata length is 4 + 32 * 3.
                      callStatus := call(gas(), token, 0, freeMemoryPointer, 100, 0, 0)
                  }
                  require(didLastOptionalReturnCallSucceed(callStatus), "TRANSFER_FROM_FAILED");
              }
              function safeTransfer(
                  ERC20 token,
                  address to,
                  uint256 amount
              ) internal {
                  bool callStatus;
                  assembly {
                      // Get a pointer to some free memory.
                      let freeMemoryPointer := mload(0x40)
                      // Write the abi-encoded calldata to memory piece by piece:
                      mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) // Begin with the function selector.
                      mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "to" argument.
                      mstore(add(freeMemoryPointer, 36), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value.
                      // Call the token and store if it succeeded or not.
                      // We use 68 because the calldata length is 4 + 32 * 2.
                      callStatus := call(gas(), token, 0, freeMemoryPointer, 68, 0, 0)
                  }
                  require(didLastOptionalReturnCallSucceed(callStatus), "TRANSFER_FAILED");
              }
              function safeApprove(
                  ERC20 token,
                  address to,
                  uint256 amount
              ) internal {
                  bool callStatus;
                  assembly {
                      // Get a pointer to some free memory.
                      let freeMemoryPointer := mload(0x40)
                      // Write the abi-encoded calldata to memory piece by piece:
                      mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000) // Begin with the function selector.
                      mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "to" argument.
                      mstore(add(freeMemoryPointer, 36), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value.
                      // Call the token and store if it succeeded or not.
                      // We use 68 because the calldata length is 4 + 32 * 2.
                      callStatus := call(gas(), token, 0, freeMemoryPointer, 68, 0, 0)
                  }
                  require(didLastOptionalReturnCallSucceed(callStatus), "APPROVE_FAILED");
              }
              /*///////////////////////////////////////////////////////////////
                                   INTERNAL HELPER LOGIC
              //////////////////////////////////////////////////////////////*/
              function didLastOptionalReturnCallSucceed(bool callStatus) private pure returns (bool success) {
                  assembly {
                      // Get how many bytes the call returned.
                      let returnDataSize := returndatasize()
                      // If the call reverted:
                      if iszero(callStatus) {
                          // Copy the revert message into memory.
                          returndatacopy(0, 0, returnDataSize)
                          // Revert with the same message.
                          revert(0, returnDataSize)
                      }
                      switch returnDataSize
                      case 32 {
                          // Copy the return data into memory.
                          returndatacopy(0, 0, returnDataSize)
                          // Set success to whether it returned true.
                          success := iszero(iszero(mload(0)))
                      }
                      case 0 {
                          // There was no return data.
                          success := 1
                      }
                      default {
                          // It returned some malformed input.
                          success := 0
                      }
                  }
              }
          }
          

          File 5 of 6: SwapERC20
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.23;
          interface ISwapERC20 {
            struct OrderERC20 {
              uint256 nonce; // Unique number per signatory per order
              uint256 expiry; // Expiry time (seconds since unix epoch)
              address signerWallet; // Party to the swap that sets terms
              address signerToken; // ERC20 token address transferred from signer
              uint256 signerAmount; // Amount of tokens transferred from signer
              address senderWallet; // Party to the swap that accepts terms
              address senderToken; // ERC20 token address transferred from sender
              uint256 senderAmount; // Amount of tokens transferred from sender
              uint8 v; // ECDSA
              bytes32 r;
              bytes32 s;
            }
            event SwapERC20(uint256 indexed nonce, address indexed signerWallet);
            event Cancel(uint256 indexed nonce, address indexed signerWallet);
            event Authorize(address indexed signer, address indexed signerWallet);
            event Revoke(address indexed signer, address indexed signerWallet);
            event SetProtocolFee(uint256 protocolFee);
            event SetProtocolFeeLight(uint256 protocolFeeLight);
            event SetProtocolFeeWallet(address indexed feeWallet);
            event SetBonusScale(uint256 bonusScale);
            event SetBonusMax(uint256 bonusMax);
            event SetStaking(address indexed staking);
            error ChainIdChanged();
            error InvalidFee();
            error InvalidFeeLight();
            error InvalidFeeWallet();
            error InvalidStaking();
            error OrderExpired();
            error MaxTooHigh();
            error NonceAlreadyUsed(uint256);
            error ScaleTooHigh();
            error SignatoryInvalid();
            error SignatureInvalid();
            error TransferFromFailed();
            function swap(
              address recipient,
              uint256 nonce,
              uint256 expiry,
              address signerWallet,
              address signerToken,
              uint256 signerAmount,
              address senderToken,
              uint256 senderAmount,
              uint8 v,
              bytes32 r,
              bytes32 s
            ) external;
            function swapAnySender(
              address recipient,
              uint256 nonce,
              uint256 expiry,
              address signerWallet,
              address signerToken,
              uint256 signerAmount,
              address senderToken,
              uint256 senderAmount,
              uint8 v,
              bytes32 r,
              bytes32 s
            ) external;
            function swapLight(
              uint256 nonce,
              uint256 expiry,
              address signerWallet,
              address signerToken,
              uint256 signerAmount,
              address senderToken,
              uint256 senderAmount,
              uint8 v,
              bytes32 r,
              bytes32 s
            ) external;
            function authorize(address sender) external;
            function revoke() external;
            function cancel(uint256[] calldata nonces) external;
            function check(
              address senderWallet,
              uint256 nonce,
              uint256 expiry,
              address signerWallet,
              address signerToken,
              uint256 signerAmount,
              address senderToken,
              uint256 senderAmount,
              uint8 v,
              bytes32 r,
              bytes32 s
            ) external view returns (bytes32[] memory);
            function nonceUsed(address, uint256) external view returns (bool);
            function authorized(address) external view returns (address);
            function calculateProtocolFee(
              address,
              uint256
            ) external view returns (uint256);
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.23;
          import { ECDSA } from "solady/src/utils/ECDSA.sol";
          import { EIP712 } from "solady/src/utils/EIP712.sol";
          import { ERC20 } from "solady/src/tokens/ERC20.sol";
          import { Ownable } from "solady/src/auth/Ownable.sol";
          import { SafeTransferLib } from "solady/src/utils/SafeTransferLib.sol";
          import { SignatureCheckerLib } from "solady/src/utils/SignatureCheckerLib.sol";
          import "./interfaces/ISwapERC20.sol";
          /**
           * @title AirSwap: Atomic ERC20 Token Swap
           * @notice https://www.airswap.io/
           */
          contract SwapERC20 is ISwapERC20, Ownable, EIP712 {
            uint256 public immutable DOMAIN_CHAIN_ID;
            bytes32 public immutable DOMAIN_SEPARATOR;
            bytes32 public constant ORDER_TYPEHASH =
              keccak256(
                abi.encodePacked(
                  "OrderERC20(uint256 nonce,uint256 expiry,address signerWallet,address signerToken,uint256 signerAmount,",
                  "uint256 protocolFee,address senderWallet,address senderToken,uint256 senderAmount)"
                )
              );
            uint256 public constant FEE_DIVISOR = 10000;
            uint256 private constant MAX_ERROR_COUNT = 8;
            uint256 private constant MAX_MAX = 100;
            uint256 private constant MAX_SCALE = 77;
            /**
             * @notice Double mapping of signers to nonce groups to nonce states
             * @dev The nonce group is computed as nonce / 256, so each group of 256 sequential nonces uses the same key
             * @dev The nonce states are encoded as 256 bits, for each nonce in the group 0 means available and 1 means used
             */
            mapping(address => mapping(uint256 => uint256)) private _nonceGroups;
            // Mapping of signer to authorized signatory
            mapping(address => address) public override authorized;
            uint256 public protocolFee;
            uint256 public protocolFeeLight;
            address public protocolFeeWallet;
            uint256 public bonusScale;
            uint256 public bonusMax;
            address public stakingToken;
            /**
             * @notice SwapERC20 constructor
             * @dev Sets domain and version for EIP712 signatures
             * @param _protocolFee uin256 protocol fee to be assessed on swaps
             * @param _protocolFeeWallet address destination for protocol fees
             * @param _bonusScale uin256 scale factor for bonus
             * @param _bonusMax uint256 max bonus percentage
             */
            constructor(
              uint256 _protocolFee,
              uint256 _protocolFeeLight,
              address _protocolFeeWallet,
              uint256 _bonusScale,
              uint256 _bonusMax
            ) {
              if (_protocolFee >= FEE_DIVISOR) revert InvalidFee();
              if (_protocolFeeLight >= FEE_DIVISOR) revert InvalidFeeLight();
              if (_protocolFeeWallet == address(0)) revert InvalidFeeWallet();
              if (_bonusMax > MAX_MAX) revert MaxTooHigh();
              if (_bonusScale > MAX_SCALE) revert ScaleTooHigh();
              _initializeOwner(msg.sender);
              DOMAIN_CHAIN_ID = block.chainid;
              DOMAIN_SEPARATOR = _domainSeparator();
              protocolFee = _protocolFee;
              protocolFeeLight = _protocolFeeLight;
              protocolFeeWallet = _protocolFeeWallet;
              bonusMax = _bonusMax;
              bonusScale = _bonusScale;
            }
            /**
             * @notice Return EIP712 domain values
             * @return name EIP712 domain name
             * @return version EIP712 domain version
             */
            function _domainNameAndVersion()
              internal
              pure
              override
              returns (string memory name, string memory version)
            {
              name = "SWAP_ERC20";
              version = "4.3";
            }
            /**
             * @notice Atomic ERC20 Swap
             * @param recipient address Wallet to receive sender proceeds
             * @param nonce uint256 Unique and should be sequential
             * @param expiry uint256 Expiry in seconds since 1 January 1970
             * @param signerWallet address Wallet of the signer
             * @param signerToken address ERC20 token transferred from the signer
             * @param signerAmount uint256 Amount transferred from the signer
             * @param senderToken address ERC20 token transferred from the sender
             * @param senderAmount uint256 Amount transferred from the sender
             * @param v uint8 "v" value of the ECDSA signature
             * @param r bytes32 "r" value of the ECDSA signature
             * @param s bytes32 "s" value of the ECDSA signature
             */
            function swap(
              address recipient,
              uint256 nonce,
              uint256 expiry,
              address signerWallet,
              address signerToken,
              uint256 signerAmount,
              address senderToken,
              uint256 senderAmount,
              uint8 v,
              bytes32 r,
              bytes32 s
            ) external override {
              // Ensure the order is valid
              _check(
                nonce,
                expiry,
                signerWallet,
                signerToken,
                signerAmount,
                msg.sender,
                senderToken,
                senderAmount,
                v,
                r,
                s
              );
              // Transfer token from sender to signer
              SafeTransferLib.safeTransferFrom(
                senderToken,
                msg.sender,
                signerWallet,
                senderAmount
              );
              // Transfer token from signer to recipient
              SafeTransferLib.safeTransferFrom(
                signerToken,
                signerWallet,
                recipient,
                signerAmount
              );
              // Calculate and transfer protocol fee
              _transferProtocolFee(signerToken, signerWallet, signerAmount);
              // Emit event
              emit SwapERC20(nonce, signerWallet);
            }
            /**
             * @notice Atomic ERC20 Swap for Any Sender
             * @param recipient address Wallet to receive sender proceeds
             * @param nonce uint256 Unique and should be sequential
             * @param expiry uint256 Expiry in seconds since 1 January 1970
             * @param signerWallet address Wallet of the signer
             * @param signerToken address ERC20 token transferred from the signer
             * @param signerAmount uint256 Amount transferred from the signer
             * @param senderToken address ERC20 token transferred from the sender
             * @param senderAmount uint256 Amount transferred from the sender
             * @param v uint8 "v" value of the ECDSA signature
             * @param r bytes32 "r" value of the ECDSA signature
             * @param s bytes32 "s" value of the ECDSA signature
             */
            function swapAnySender(
              address recipient,
              uint256 nonce,
              uint256 expiry,
              address signerWallet,
              address signerToken,
              uint256 signerAmount,
              address senderToken,
              uint256 senderAmount,
              uint8 v,
              bytes32 r,
              bytes32 s
            ) external override {
              // Ensure the order is valid
              _check(
                nonce,
                expiry,
                signerWallet,
                signerToken,
                signerAmount,
                address(0),
                senderToken,
                senderAmount,
                v,
                r,
                s
              );
              // Transfer token from sender to signer
              SafeTransferLib.safeTransferFrom(
                senderToken,
                msg.sender,
                signerWallet,
                senderAmount
              );
              // Transfer token from signer to recipient
              SafeTransferLib.safeTransferFrom(
                signerToken,
                signerWallet,
                recipient,
                signerAmount
              );
              // Calculate and transfer protocol fee
              _transferProtocolFee(signerToken, signerWallet, signerAmount);
              // Emit event
              emit SwapERC20(nonce, signerWallet);
            }
            /**
             * @notice Swap Atomic ERC20 Swap (Minimal Gas)
             * @dev No transfer checks. Only use with known tokens.
             * @param nonce uint256 Unique and should be sequential
             * @param expiry uint256 Expiry in seconds since 1 January 1970
             * @param signerWallet address Wallet of the signer
             * @param signerToken address ERC20 token transferred from the signer
             * @param signerAmount uint256 Amount transferred from the signer
             * @param senderToken address ERC20 token transferred from the sender
             * @param senderAmount uint256 Amount transferred from the sender
             * @param v uint8 "v" value of the ECDSA signature
             * @param r bytes32 "r" value of the ECDSA signature
             * @param s bytes32 "s" value of the ECDSA signature
             */
            function swapLight(
              uint256 nonce,
              uint256 expiry,
              address signerWallet,
              address signerToken,
              uint256 signerAmount,
              address senderToken,
              uint256 senderAmount,
              uint8 v,
              bytes32 r,
              bytes32 s
            ) external override {
              // Ensure the expiry is not passed
              if (expiry <= block.timestamp) revert OrderExpired();
              // Recover the signatory from the hash and signature
              address signatory = ECDSA.tryRecover(
                keccak256(
                  abi.encodePacked(
                    "\\x19\\x01", // EIP191: Indicates EIP712
                    DOMAIN_SEPARATOR,
                    keccak256(
                      abi.encode(
                        ORDER_TYPEHASH,
                        nonce,
                        expiry,
                        signerWallet,
                        signerToken,
                        signerAmount,
                        protocolFeeLight,
                        msg.sender,
                        senderToken,
                        senderAmount
                      )
                    )
                  )
                ),
                v,
                r,
                s
              );
              // Ensure the signatory is not null
              if (signatory == address(0)) revert SignatureInvalid();
              // Ensure the nonce is not yet used and if not mark it used
              if (!_markNonceAsUsed(signatory, nonce)) revert NonceAlreadyUsed(nonce);
              // Ensure signatory is authorized to sign
              if (authorized[signerWallet] != address(0)) {
                // If one is set by signer wallet, signatory must be authorized
                if (signatory != authorized[signerWallet]) revert SignatureInvalid();
              } else {
                // Otherwise, signatory must be signer wallet
                if (signatory != signerWallet) revert SignatureInvalid();
              }
              // Transfer token from sender to signer
              SafeTransferLib.safeTransferFrom(
                senderToken,
                msg.sender,
                signerWallet,
                senderAmount
              );
              // Transfer token from signer to sender
              SafeTransferLib.safeTransferFrom(
                signerToken,
                signerWallet,
                msg.sender,
                signerAmount
              );
              // Transfer protocol fee from signer to fee wallet
              SafeTransferLib.safeTransferFrom(
                signerToken,
                signerWallet,
                protocolFeeWallet,
                (signerAmount * protocolFeeLight) / FEE_DIVISOR
              );
              // Emit event
              emit SwapERC20(nonce, signerWallet);
            }
            /**
             * @notice Set the protocol fee
             * @param _protocolFee uint256 Value of the fee in basis points
             */
            function setProtocolFee(uint256 _protocolFee) external onlyOwner {
              // Ensure the fee is less than divisor
              if (_protocolFee >= FEE_DIVISOR) revert InvalidFee();
              protocolFee = _protocolFee;
              emit SetProtocolFee(_protocolFee);
            }
            /**
             * @notice Set the light protocol fee
             * @param _protocolFeeLight uint256 Value of the fee in basis points
             */
            function setProtocolFeeLight(uint256 _protocolFeeLight) external onlyOwner {
              // Ensure the fee is less than divisor
              if (_protocolFeeLight >= FEE_DIVISOR) revert InvalidFeeLight();
              protocolFeeLight = _protocolFeeLight;
              emit SetProtocolFeeLight(_protocolFeeLight);
            }
            /**
             * @notice Set the protocol fee wallet
             * @param _protocolFeeWallet address Wallet to transfer fee to
             */
            function setProtocolFeeWallet(address _protocolFeeWallet) external onlyOwner {
              // Ensure the new fee wallet is not null
              if (_protocolFeeWallet == address(0)) revert InvalidFeeWallet();
              protocolFeeWallet = _protocolFeeWallet;
              emit SetProtocolFeeWallet(_protocolFeeWallet);
            }
            /**
             * @notice Set staking bonus max
             * @dev Only owner
             * @param _bonusMax uint256
             */
            function setBonusMax(uint256 _bonusMax) external onlyOwner {
              if (_bonusMax > MAX_MAX) revert MaxTooHigh();
              bonusMax = _bonusMax;
              emit SetBonusMax(_bonusMax);
            }
            /**
             * @notice Set staking bonus scale
             * @dev Only owner
             * @param _bonusScale uint256
             */
            function setBonusScale(uint256 _bonusScale) external onlyOwner {
              if (_bonusScale > MAX_SCALE) revert ScaleTooHigh();
              bonusScale = _bonusScale;
              emit SetBonusScale(_bonusScale);
            }
            /**
             * @notice Set staking token
             * @param _stakingToken address Token to check balances on
             */
            function setStaking(address _stakingToken) external onlyOwner {
              // Ensure the new staking token is not null
              if (_stakingToken == address(0)) revert InvalidStaking();
              stakingToken = _stakingToken;
              emit SetStaking(_stakingToken);
            }
            /**
             * @notice Authorize a signatory
             * @param signatory address Wallet of the signatory to authorize
             * @dev Emits an Authorize event
             */
            function authorize(address signatory) external override {
              if (signatory == address(0)) revert SignatoryInvalid();
              authorized[msg.sender] = signatory;
              emit Authorize(signatory, msg.sender);
            }
            /**
             * @notice Revoke the signatory
             * @dev Emits a Revoke event
             */
            function revoke() external override {
              address tmp = authorized[msg.sender];
              delete authorized[msg.sender];
              emit Revoke(tmp, msg.sender);
            }
            /**
             * @notice Cancel one or more nonces
             * @dev Cancelled nonces are marked as used
             * @dev Emits a Cancel event
             * @dev Out of gas may occur in arrays of length > 400
             * @param nonces uint256[] List of nonces to cancel
             */
            function cancel(uint256[] calldata nonces) external override {
              for (uint256 i; i < nonces.length; ) {
                uint256 nonce = nonces[i];
                if (_markNonceAsUsed(msg.sender, nonce)) {
                  emit Cancel(nonce, msg.sender);
                }
                unchecked {
                  ++i;
                }
              }
            }
            /**
             * @notice Checks an order for errors
             * @param senderWallet address Wallet that would send the order
             * @param nonce uint256 Unique and should be sequential
             * @param expiry uint256 Expiry in seconds since 1 January 1970
             * @param signerWallet address Wallet of the signer
             * @param signerToken address ERC20 token transferred from the signer
             * @param signerAmount uint256 Amount transferred from the signer
             * @param senderToken address ERC20 token transferred from the sender
             * @param senderAmount uint256 Amount transferred from the sender
             * @param v uint8 "v" value of the ECDSA signature
             * @param r bytes32 "r" value of the ECDSA signature
             * @param s bytes32 "s" value of the ECDSA signature
             * @return bytes32[] errors
             */
            function check(
              address senderWallet,
              uint256 nonce,
              uint256 expiry,
              address signerWallet,
              address signerToken,
              uint256 signerAmount,
              address senderToken,
              uint256 senderAmount,
              uint8 v,
              bytes32 r,
              bytes32 s
            ) external view returns (bytes32[] memory) {
              bytes32[] memory errors = new bytes32[](MAX_ERROR_COUNT);
              uint256 count;
              OrderERC20 memory order;
              order.nonce = nonce;
              order.expiry = expiry;
              order.signerWallet = signerWallet;
              order.signerToken = signerToken;
              order.signerAmount = signerAmount;
              order.senderToken = senderToken;
              order.senderAmount = senderAmount;
              order.v = v;
              order.r = r;
              order.s = s;
              order.senderWallet = senderWallet;
              if (DOMAIN_CHAIN_ID != block.chainid) {
                errors[count++] = "ChainIdChanged";
              }
              // Validate as the authorized signatory if set
              address signatory = order.signerWallet;
              if (authorized[signatory] != address(0)) {
                signatory = authorized[signatory];
              }
              if (
                !SignatureCheckerLib.isValidSignatureNow(
                  signatory,
                  _getOrderHash(
                    order.nonce,
                    order.expiry,
                    order.signerWallet,
                    order.signerToken,
                    order.signerAmount,
                    order.senderWallet,
                    order.senderToken,
                    order.senderAmount
                  ),
                  abi.encodePacked(r, s, v)
                )
              ) {
                errors[count++] = "SignatureInvalid";
              } else if (nonceUsed(signatory, order.nonce)) {
                errors[count++] = "NonceAlreadyUsed";
              }
              if (order.expiry < block.timestamp) {
                errors[count++] = "OrderExpired";
              }
              if (order.senderWallet != address(0)) {
                uint256 senderBalance = ERC20(order.senderToken).balanceOf(
                  order.senderWallet
                );
                uint256 senderAllowance = ERC20(order.senderToken).allowance(
                  order.senderWallet,
                  address(this)
                );
                if (senderAllowance < order.senderAmount) {
                  errors[count++] = "SenderAllowanceLow";
                }
                if (senderBalance < order.senderAmount) {
                  errors[count++] = "SenderBalanceLow";
                }
              }
              uint256 signerBalance = ERC20(order.signerToken).balanceOf(
                order.signerWallet
              );
              uint256 signerAllowance = ERC20(order.signerToken).allowance(
                order.signerWallet,
                address(this)
              );
              uint256 signerFeeAmount = (order.signerAmount * protocolFee) / FEE_DIVISOR;
              if (signerAllowance < order.signerAmount + signerFeeAmount) {
                errors[count++] = "SignerAllowanceLow";
              }
              if (signerBalance < order.signerAmount + signerFeeAmount) {
                errors[count++] = "SignerBalanceLow";
              }
              // Truncate errors array to actual count
              if (count != errors.length) {
                assembly {
                  mstore(errors, count)
                }
              }
              return errors;
            }
            /**
             * @notice Calculates bonus from staking balance
             * @param stakingBalance uint256
             * @param feeAmount uint256
             */
            function calculateBonus(
              uint256 stakingBalance,
              uint256 feeAmount
            ) public view returns (uint256) {
              uint256 divisor = (uint256(10) ** bonusScale) + stakingBalance;
              return (bonusMax * stakingBalance * feeAmount) / divisor / MAX_MAX;
            }
            /**
             * @notice Calculates protocol fee for an account
             * @param wallet address
             * @param amount uint256
             */
            function calculateProtocolFee(
              address wallet,
              uint256 amount
            ) external view override returns (uint256) {
              // Transfer fee from signer to feeWallet
              uint256 feeAmount = (amount * protocolFee) / FEE_DIVISOR;
              if (stakingToken != address(0) && feeAmount > 0) {
                uint256 bonusAmount = calculateBonus(
                  ERC20(stakingToken).balanceOf(wallet),
                  feeAmount
                );
                return feeAmount - bonusAmount;
              }
              return feeAmount;
            }
            /**
             * @notice Returns true if the nonce has been used
             * @param signer address Address of the signer
             * @param nonce uint256 Nonce being checked
             */
            function nonceUsed(
              address signer,
              uint256 nonce
            ) public view override returns (bool) {
              uint256 groupKey = nonce / 256;
              uint256 indexInGroup = nonce % 256;
              return (_nonceGroups[signer][groupKey] >> indexInGroup) & 1 == 1;
            }
            /**
             * @notice Marks a nonce as used for the given signer
             * @param signer address Address of the signer for which to mark the nonce as used
             * @param nonce uint256 Nonce to be marked as used
             * @return bool True if the nonce was not marked as used already
             */
            function _markNonceAsUsed(
              address signer,
              uint256 nonce
            ) private returns (bool) {
              uint256 groupKey = nonce / 256;
              uint256 indexInGroup = nonce % 256;
              uint256 group = _nonceGroups[signer][groupKey];
              // If it is already used, return false
              if ((group >> indexInGroup) & 1 == 1) {
                return false;
              }
              _nonceGroups[signer][groupKey] = group | (uint256(1) << indexInGroup);
              return true;
            }
            /**
             * @notice Checks order and reverts on error
             * @param nonce uint256 Unique and should be sequential
             * @param expiry uint256 Expiry in seconds since 1 January 1970
             * @param signerWallet address Wallet of the signer
             * @param signerToken address ERC20 token transferred from the signer
             * @param signerAmount uint256 Amount transferred from the signer
             * @param senderToken address ERC20 token transferred from the sender
             * @param senderAmount uint256 Amount transferred from the sender
             * @param v uint8 "v" value of the ECDSA signature
             * @param r bytes32 "r" value of the ECDSA signature
             * @param s bytes32 "s" value of the ECDSA signature
             */
            function _check(
              uint256 nonce,
              uint256 expiry,
              address signerWallet,
              address signerToken,
              uint256 signerAmount,
              address senderWallet,
              address senderToken,
              uint256 senderAmount,
              uint8 v,
              bytes32 r,
              bytes32 s
            ) private {
              // Ensure execution on the intended chain
              if (DOMAIN_CHAIN_ID != block.chainid) revert ChainIdChanged();
              // Ensure the expiry is not passed
              if (expiry <= block.timestamp) revert OrderExpired();
              // Validate as the authorized signatory if set
              address signatory = signerWallet;
              if (authorized[signatory] != address(0)) {
                signatory = authorized[signatory];
              }
              // Ensure the signature is correct for the order
              if (
                !SignatureCheckerLib.isValidSignatureNow(
                  signatory,
                  _getOrderHash(
                    nonce,
                    expiry,
                    signerWallet,
                    signerToken,
                    signerAmount,
                    senderWallet,
                    senderToken,
                    senderAmount
                  ),
                  abi.encodePacked(r, s, v)
                )
              ) revert SignatureInvalid();
              // Ensure the nonce is not yet used and if not mark as used
              if (!_markNonceAsUsed(signatory, nonce)) revert NonceAlreadyUsed(nonce);
            }
            /**
             * @notice Hashes order parameters
             * @param nonce uint256
             * @param expiry uint256
             * @param signerWallet address
             * @param signerToken address
             * @param signerAmount uint256
             * @param senderToken address
             * @param senderAmount uint256
             * @return bytes32
             */
            function _getOrderHash(
              uint256 nonce,
              uint256 expiry,
              address signerWallet,
              address signerToken,
              uint256 signerAmount,
              address senderWallet,
              address senderToken,
              uint256 senderAmount
            ) private view returns (bytes32) {
              return
                keccak256(
                  abi.encodePacked(
                    "\\x19\\x01", // EIP191: Indicates EIP712
                    DOMAIN_SEPARATOR,
                    keccak256(
                      abi.encode(
                        ORDER_TYPEHASH,
                        nonce,
                        expiry,
                        signerWallet,
                        signerToken,
                        signerAmount,
                        protocolFee,
                        senderWallet,
                        senderToken,
                        senderAmount
                      )
                    )
                  )
                );
            }
            /**
             * @notice Calculates and transfers protocol fee and staking bonus
             * @param sourceToken address
             * @param sourceWallet address
             * @param amount uint256
             */
            function _transferProtocolFee(
              address sourceToken,
              address sourceWallet,
              uint256 amount
            ) private {
              // Determine protocol fee from amount
              uint256 feeAmount = (amount * protocolFee) / FEE_DIVISOR;
              if (feeAmount > 0) {
                uint256 bonusAmount;
                if (stakingToken != address(0)) {
                  // Only check staking bonus if staking token set
                  bonusAmount = calculateBonus(
                    ERC20(stakingToken).balanceOf(msg.sender),
                    feeAmount
                  );
                }
                if (bonusAmount > 0) {
                  // Transfer staking bonus from source to msg.sender
                  SafeTransferLib.safeTransferFrom(
                    sourceToken,
                    sourceWallet,
                    msg.sender,
                    bonusAmount
                  );
                  // Transfer remaining protocol fee from source to fee wallet
                  SafeTransferLib.safeTransferFrom(
                    sourceToken,
                    sourceWallet,
                    protocolFeeWallet,
                    feeAmount - bonusAmount
                  );
                } else {
                  // Transfer full protocol fee from source to fee wallet
                  SafeTransferLib.safeTransferFrom(
                    sourceToken,
                    sourceWallet,
                    protocolFeeWallet,
                    feeAmount
                  );
                }
              }
            }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.4;
          /// @notice Simple single owner authorization mixin.
          /// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
          ///
          /// @dev Note:
          /// This implementation does NOT auto-initialize the owner to `msg.sender`.
          /// You MUST call the `_initializeOwner` in the constructor / initializer.
          ///
          /// While the ownable portion follows
          /// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
          /// the nomenclature for the 2-step ownership handover may be unique to this codebase.
          abstract contract Ownable {
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                       CUSTOM ERRORS                        */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              /// @dev The caller is not authorized to call the function.
              error Unauthorized();
              /// @dev The `newOwner` cannot be the zero address.
              error NewOwnerIsZeroAddress();
              /// @dev The `pendingOwner` does not have a valid handover request.
              error NoHandoverRequest();
              /// @dev Cannot double-initialize.
              error AlreadyInitialized();
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                           EVENTS                           */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              /// @dev The ownership is transferred from `oldOwner` to `newOwner`.
              /// This event is intentionally kept the same as OpenZeppelin's Ownable to be
              /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
              /// despite it not being as lightweight as a single argument event.
              event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);
              /// @dev An ownership handover to `pendingOwner` has been requested.
              event OwnershipHandoverRequested(address indexed pendingOwner);
              /// @dev The ownership handover to `pendingOwner` has been canceled.
              event OwnershipHandoverCanceled(address indexed pendingOwner);
              /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
              uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
                  0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;
              /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
              uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
                  0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;
              /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
              uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
                  0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                          STORAGE                           */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              /// @dev The owner slot is given by:
              /// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`.
              /// It is intentionally chosen to be a high value
              /// to avoid collision with lower slots.
              /// The choice of manual storage layout is to enable compatibility
              /// with both regular and upgradeable contracts.
              bytes32 internal constant _OWNER_SLOT =
                  0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;
              /// The ownership handover slot of `newOwner` is given by:
              /// ```
              ///     mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
              ///     let handoverSlot := keccak256(0x00, 0x20)
              /// ```
              /// It stores the expiry timestamp of the two-step ownership handover.
              uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                     INTERNAL FUNCTIONS                     */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              /// @dev Override to return true to make `_initializeOwner` prevent double-initialization.
              function _guardInitializeOwner() internal pure virtual returns (bool guard) {}
              /// @dev Initializes the owner directly without authorization guard.
              /// This function must be called upon initialization,
              /// regardless of whether the contract is upgradeable or not.
              /// This is to enable generalization to both regular and upgradeable contracts,
              /// and to save gas in case the initial owner is not the caller.
              /// For performance reasons, this function will not check if there
              /// is an existing owner.
              function _initializeOwner(address newOwner) internal virtual {
                  if (_guardInitializeOwner()) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          let ownerSlot := _OWNER_SLOT
                          if sload(ownerSlot) {
                              mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.
                              revert(0x1c, 0x04)
                          }
                          // Clean the upper 96 bits.
                          newOwner := shr(96, shl(96, newOwner))
                          // Store the new value.
                          sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
                          // Emit the {OwnershipTransferred} event.
                          log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
                      }
                  } else {
                      /// @solidity memory-safe-assembly
                      assembly {
                          // Clean the upper 96 bits.
                          newOwner := shr(96, shl(96, newOwner))
                          // Store the new value.
                          sstore(_OWNER_SLOT, newOwner)
                          // Emit the {OwnershipTransferred} event.
                          log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
                      }
                  }
              }
              /// @dev Sets the owner directly without authorization guard.
              function _setOwner(address newOwner) internal virtual {
                  if (_guardInitializeOwner()) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          let ownerSlot := _OWNER_SLOT
                          // Clean the upper 96 bits.
                          newOwner := shr(96, shl(96, newOwner))
                          // Emit the {OwnershipTransferred} event.
                          log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                          // Store the new value.
                          sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
                      }
                  } else {
                      /// @solidity memory-safe-assembly
                      assembly {
                          let ownerSlot := _OWNER_SLOT
                          // Clean the upper 96 bits.
                          newOwner := shr(96, shl(96, newOwner))
                          // Emit the {OwnershipTransferred} event.
                          log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                          // Store the new value.
                          sstore(ownerSlot, newOwner)
                      }
                  }
              }
              /// @dev Throws if the sender is not the owner.
              function _checkOwner() internal view virtual {
                  /// @solidity memory-safe-assembly
                  assembly {
                      // If the caller is not the stored owner, revert.
                      if iszero(eq(caller(), sload(_OWNER_SLOT))) {
                          mstore(0x00, 0x82b42900) // `Unauthorized()`.
                          revert(0x1c, 0x04)
                      }
                  }
              }
              /// @dev Returns how long a two-step ownership handover is valid for in seconds.
              /// Override to return a different value if needed.
              /// Made internal to conserve bytecode. Wrap it in a public function if needed.
              function _ownershipHandoverValidFor() internal view virtual returns (uint64) {
                  return 48 * 3600;
              }
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                  PUBLIC UPDATE FUNCTIONS                   */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              /// @dev Allows the owner to transfer the ownership to `newOwner`.
              function transferOwnership(address newOwner) public payable virtual onlyOwner {
                  /// @solidity memory-safe-assembly
                  assembly {
                      if iszero(shl(96, newOwner)) {
                          mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
                          revert(0x1c, 0x04)
                      }
                  }
                  _setOwner(newOwner);
              }
              /// @dev Allows the owner to renounce their ownership.
              function renounceOwnership() public payable virtual onlyOwner {
                  _setOwner(address(0));
              }
              /// @dev Request a two-step ownership handover to the caller.
              /// The request will automatically expire in 48 hours (172800 seconds) by default.
              function requestOwnershipHandover() public payable virtual {
                  unchecked {
                      uint256 expires = block.timestamp + _ownershipHandoverValidFor();
                      /// @solidity memory-safe-assembly
                      assembly {
                          // Compute and set the handover slot to `expires`.
                          mstore(0x0c, _HANDOVER_SLOT_SEED)
                          mstore(0x00, caller())
                          sstore(keccak256(0x0c, 0x20), expires)
                          // Emit the {OwnershipHandoverRequested} event.
                          log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
                      }
                  }
              }
              /// @dev Cancels the two-step ownership handover to the caller, if any.
              function cancelOwnershipHandover() public payable virtual {
                  /// @solidity memory-safe-assembly
                  assembly {
                      // Compute and set the handover slot to 0.
                      mstore(0x0c, _HANDOVER_SLOT_SEED)
                      mstore(0x00, caller())
                      sstore(keccak256(0x0c, 0x20), 0)
                      // Emit the {OwnershipHandoverCanceled} event.
                      log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
                  }
              }
              /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
              /// Reverts if there is no existing ownership handover requested by `pendingOwner`.
              function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
                  /// @solidity memory-safe-assembly
                  assembly {
                      // Compute and set the handover slot to 0.
                      mstore(0x0c, _HANDOVER_SLOT_SEED)
                      mstore(0x00, pendingOwner)
                      let handoverSlot := keccak256(0x0c, 0x20)
                      // If the handover does not exist, or has expired.
                      if gt(timestamp(), sload(handoverSlot)) {
                          mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.
                          revert(0x1c, 0x04)
                      }
                      // Set the handover slot to 0.
                      sstore(handoverSlot, 0)
                  }
                  _setOwner(pendingOwner);
              }
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                   PUBLIC READ FUNCTIONS                    */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              /// @dev Returns the owner of the contract.
              function owner() public view virtual returns (address result) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      result := sload(_OWNER_SLOT)
                  }
              }
              /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
              function ownershipHandoverExpiresAt(address pendingOwner)
                  public
                  view
                  virtual
                  returns (uint256 result)
              {
                  /// @solidity memory-safe-assembly
                  assembly {
                      // Compute the handover slot.
                      mstore(0x0c, _HANDOVER_SLOT_SEED)
                      mstore(0x00, pendingOwner)
                      // Load the handover slot.
                      result := sload(keccak256(0x0c, 0x20))
                  }
              }
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                         MODIFIERS                          */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              /// @dev Marks a function as only callable by the owner.
              modifier onlyOwner() virtual {
                  _checkOwner();
                  _;
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.4;
          /// @notice Simple ERC20 + EIP-2612 implementation.
          /// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC20.sol)
          /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
          /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol)
          ///
          /// @dev Note:
          /// - The ERC20 standard allows minting and transferring to and from the zero address,
          ///   minting and transferring zero tokens, as well as self-approvals.
          ///   For performance, this implementation WILL NOT revert for such actions.
          ///   Please add any checks with overrides if desired.
          /// - The `permit` function uses the ecrecover precompile (0x1).
          ///
          /// If you are overriding:
          /// - NEVER violate the ERC20 invariant:
          ///   the total sum of all balances must be equal to `totalSupply()`.
          /// - Check that the overridden function is actually used in the function you want to
          ///   change the behavior of. Much of the code has been manually inlined for performance.
          abstract contract ERC20 {
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                       CUSTOM ERRORS                        */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              /// @dev The total supply has overflowed.
              error TotalSupplyOverflow();
              /// @dev The allowance has overflowed.
              error AllowanceOverflow();
              /// @dev The allowance has underflowed.
              error AllowanceUnderflow();
              /// @dev Insufficient balance.
              error InsufficientBalance();
              /// @dev Insufficient allowance.
              error InsufficientAllowance();
              /// @dev The permit is invalid.
              error InvalidPermit();
              /// @dev The permit has expired.
              error PermitExpired();
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                           EVENTS                           */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              /// @dev Emitted when `amount` tokens is transferred from `from` to `to`.
              event Transfer(address indexed from, address indexed to, uint256 amount);
              /// @dev Emitted when `amount` tokens is approved by `owner` to be used by `spender`.
              event Approval(address indexed owner, address indexed spender, uint256 amount);
              /// @dev `keccak256(bytes("Transfer(address,address,uint256)"))`.
              uint256 private constant _TRANSFER_EVENT_SIGNATURE =
                  0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;
              /// @dev `keccak256(bytes("Approval(address,address,uint256)"))`.
              uint256 private constant _APPROVAL_EVENT_SIGNATURE =
                  0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925;
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                          STORAGE                           */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              /// @dev The storage slot for the total supply.
              uint256 private constant _TOTAL_SUPPLY_SLOT = 0x05345cdf77eb68f44c;
              /// @dev The balance slot of `owner` is given by:
              /// ```
              ///     mstore(0x0c, _BALANCE_SLOT_SEED)
              ///     mstore(0x00, owner)
              ///     let balanceSlot := keccak256(0x0c, 0x20)
              /// ```
              uint256 private constant _BALANCE_SLOT_SEED = 0x87a211a2;
              /// @dev The allowance slot of (`owner`, `spender`) is given by:
              /// ```
              ///     mstore(0x20, spender)
              ///     mstore(0x0c, _ALLOWANCE_SLOT_SEED)
              ///     mstore(0x00, owner)
              ///     let allowanceSlot := keccak256(0x0c, 0x34)
              /// ```
              uint256 private constant _ALLOWANCE_SLOT_SEED = 0x7f5e9f20;
              /// @dev The nonce slot of `owner` is given by:
              /// ```
              ///     mstore(0x0c, _NONCES_SLOT_SEED)
              ///     mstore(0x00, owner)
              ///     let nonceSlot := keccak256(0x0c, 0x20)
              /// ```
              uint256 private constant _NONCES_SLOT_SEED = 0x38377508;
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                         CONSTANTS                          */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              /// @dev `(_NONCES_SLOT_SEED << 16) | 0x1901`.
              uint256 private constant _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX = 0x383775081901;
              /// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`.
              bytes32 private constant _DOMAIN_TYPEHASH =
                  0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;
              /// @dev `keccak256("1")`.
              bytes32 private constant _VERSION_HASH =
                  0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6;
              /// @dev `keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")`.
              bytes32 private constant _PERMIT_TYPEHASH =
                  0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                       ERC20 METADATA                       */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              /// @dev Returns the name of the token.
              function name() public view virtual returns (string memory);
              /// @dev Returns the symbol of the token.
              function symbol() public view virtual returns (string memory);
              /// @dev Returns the decimals places of the token.
              function decimals() public view virtual returns (uint8) {
                  return 18;
              }
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                           ERC20                            */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              /// @dev Returns the amount of tokens in existence.
              function totalSupply() public view virtual returns (uint256 result) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      result := sload(_TOTAL_SUPPLY_SLOT)
                  }
              }
              /// @dev Returns the amount of tokens owned by `owner`.
              function balanceOf(address owner) public view virtual returns (uint256 result) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      mstore(0x0c, _BALANCE_SLOT_SEED)
                      mstore(0x00, owner)
                      result := sload(keccak256(0x0c, 0x20))
                  }
              }
              /// @dev Returns the amount of tokens that `spender` can spend on behalf of `owner`.
              function allowance(address owner, address spender)
                  public
                  view
                  virtual
                  returns (uint256 result)
              {
                  /// @solidity memory-safe-assembly
                  assembly {
                      mstore(0x20, spender)
                      mstore(0x0c, _ALLOWANCE_SLOT_SEED)
                      mstore(0x00, owner)
                      result := sload(keccak256(0x0c, 0x34))
                  }
              }
              /// @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
              ///
              /// Emits a {Approval} event.
              function approve(address spender, uint256 amount) public virtual returns (bool) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      // Compute the allowance slot and store the amount.
                      mstore(0x20, spender)
                      mstore(0x0c, _ALLOWANCE_SLOT_SEED)
                      mstore(0x00, caller())
                      sstore(keccak256(0x0c, 0x34), amount)
                      // Emit the {Approval} event.
                      mstore(0x00, amount)
                      log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, caller(), shr(96, mload(0x2c)))
                  }
                  return true;
              }
              /// @dev Transfer `amount` tokens from the caller to `to`.
              ///
              /// Requirements:
              /// - `from` must at least have `amount`.
              ///
              /// Emits a {Transfer} event.
              function transfer(address to, uint256 amount) public virtual returns (bool) {
                  _beforeTokenTransfer(msg.sender, to, amount);
                  /// @solidity memory-safe-assembly
                  assembly {
                      // Compute the balance slot and load its value.
                      mstore(0x0c, _BALANCE_SLOT_SEED)
                      mstore(0x00, caller())
                      let fromBalanceSlot := keccak256(0x0c, 0x20)
                      let fromBalance := sload(fromBalanceSlot)
                      // Revert if insufficient balance.
                      if gt(amount, fromBalance) {
                          mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                          revert(0x1c, 0x04)
                      }
                      // Subtract and store the updated balance.
                      sstore(fromBalanceSlot, sub(fromBalance, amount))
                      // Compute the balance slot of `to`.
                      mstore(0x00, to)
                      let toBalanceSlot := keccak256(0x0c, 0x20)
                      // Add and store the updated balance of `to`.
                      // Will not overflow because the sum of all user balances
                      // cannot exceed the maximum uint256 value.
                      sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
                      // Emit the {Transfer} event.
                      mstore(0x20, amount)
                      log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, caller(), shr(96, mload(0x0c)))
                  }
                  _afterTokenTransfer(msg.sender, to, amount);
                  return true;
              }
              /// @dev Transfers `amount` tokens from `from` to `to`.
              ///
              /// Note: Does not update the allowance if it is the maximum uint256 value.
              ///
              /// Requirements:
              /// - `from` must at least have `amount`.
              /// - The caller must have at least `amount` of allowance to transfer the tokens of `from`.
              ///
              /// Emits a {Transfer} event.
              function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) {
                  _beforeTokenTransfer(from, to, amount);
                  /// @solidity memory-safe-assembly
                  assembly {
                      let from_ := shl(96, from)
                      // Compute the allowance slot and load its value.
                      mstore(0x20, caller())
                      mstore(0x0c, or(from_, _ALLOWANCE_SLOT_SEED))
                      let allowanceSlot := keccak256(0x0c, 0x34)
                      let allowance_ := sload(allowanceSlot)
                      // If the allowance is not the maximum uint256 value.
                      if add(allowance_, 1) {
                          // Revert if the amount to be transferred exceeds the allowance.
                          if gt(amount, allowance_) {
                              mstore(0x00, 0x13be252b) // `InsufficientAllowance()`.
                              revert(0x1c, 0x04)
                          }
                          // Subtract and store the updated allowance.
                          sstore(allowanceSlot, sub(allowance_, amount))
                      }
                      // Compute the balance slot and load its value.
                      mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
                      let fromBalanceSlot := keccak256(0x0c, 0x20)
                      let fromBalance := sload(fromBalanceSlot)
                      // Revert if insufficient balance.
                      if gt(amount, fromBalance) {
                          mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                          revert(0x1c, 0x04)
                      }
                      // Subtract and store the updated balance.
                      sstore(fromBalanceSlot, sub(fromBalance, amount))
                      // Compute the balance slot of `to`.
                      mstore(0x00, to)
                      let toBalanceSlot := keccak256(0x0c, 0x20)
                      // Add and store the updated balance of `to`.
                      // Will not overflow because the sum of all user balances
                      // cannot exceed the maximum uint256 value.
                      sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
                      // Emit the {Transfer} event.
                      mstore(0x20, amount)
                      log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
                  }
                  _afterTokenTransfer(from, to, amount);
                  return true;
              }
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                          EIP-2612                          */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              /// @dev For more performance, override to return the constant value
              /// of `keccak256(bytes(name()))` if `name()` will never change.
              function _constantNameHash() internal view virtual returns (bytes32 result) {}
              /// @dev Returns the current nonce for `owner`.
              /// This value is used to compute the signature for EIP-2612 permit.
              function nonces(address owner) public view virtual returns (uint256 result) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      // Compute the nonce slot and load its value.
                      mstore(0x0c, _NONCES_SLOT_SEED)
                      mstore(0x00, owner)
                      result := sload(keccak256(0x0c, 0x20))
                  }
              }
              /// @dev Sets `value` as the allowance of `spender` over the tokens of `owner`,
              /// authorized by a signed approval by `owner`.
              ///
              /// Emits a {Approval} event.
              function permit(
                  address owner,
                  address spender,
                  uint256 value,
                  uint256 deadline,
                  uint8 v,
                  bytes32 r,
                  bytes32 s
              ) public virtual {
                  bytes32 nameHash = _constantNameHash();
                  //  We simply calculate it on-the-fly to allow for cases where the `name` may change.
                  if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name()));
                  /// @solidity memory-safe-assembly
                  assembly {
                      // Revert if the block timestamp is greater than `deadline`.
                      if gt(timestamp(), deadline) {
                          mstore(0x00, 0x1a15a3cc) // `PermitExpired()`.
                          revert(0x1c, 0x04)
                      }
                      let m := mload(0x40) // Grab the free memory pointer.
                      // Clean the upper 96 bits.
                      owner := shr(96, shl(96, owner))
                      spender := shr(96, shl(96, spender))
                      // Compute the nonce slot and load its value.
                      mstore(0x0e, _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX)
                      mstore(0x00, owner)
                      let nonceSlot := keccak256(0x0c, 0x20)
                      let nonceValue := sload(nonceSlot)
                      // Prepare the domain separator.
                      mstore(m, _DOMAIN_TYPEHASH)
                      mstore(add(m, 0x20), nameHash)
                      mstore(add(m, 0x40), _VERSION_HASH)
                      mstore(add(m, 0x60), chainid())
                      mstore(add(m, 0x80), address())
                      mstore(0x2e, keccak256(m, 0xa0))
                      // Prepare the struct hash.
                      mstore(m, _PERMIT_TYPEHASH)
                      mstore(add(m, 0x20), owner)
                      mstore(add(m, 0x40), spender)
                      mstore(add(m, 0x60), value)
                      mstore(add(m, 0x80), nonceValue)
                      mstore(add(m, 0xa0), deadline)
                      mstore(0x4e, keccak256(m, 0xc0))
                      // Prepare the ecrecover calldata.
                      mstore(0x00, keccak256(0x2c, 0x42))
                      mstore(0x20, and(0xff, v))
                      mstore(0x40, r)
                      mstore(0x60, s)
                      let t := staticcall(gas(), 1, 0, 0x80, 0x20, 0x20)
                      // If the ecrecover fails, the returndatasize will be 0x00,
                      // `owner` will be checked if it equals the hash at 0x00,
                      // which evaluates to false (i.e. 0), and we will revert.
                      // If the ecrecover succeeds, the returndatasize will be 0x20,
                      // `owner` will be compared against the returned address at 0x20.
                      if iszero(eq(mload(returndatasize()), owner)) {
                          mstore(0x00, 0xddafbaef) // `InvalidPermit()`.
                          revert(0x1c, 0x04)
                      }
                      // Increment and store the updated nonce.
                      sstore(nonceSlot, add(nonceValue, t)) // `t` is 1 if ecrecover succeeds.
                      // Compute the allowance slot and store the value.
                      // The `owner` is already at slot 0x20.
                      mstore(0x40, or(shl(160, _ALLOWANCE_SLOT_SEED), spender))
                      sstore(keccak256(0x2c, 0x34), value)
                      // Emit the {Approval} event.
                      log3(add(m, 0x60), 0x20, _APPROVAL_EVENT_SIGNATURE, owner, spender)
                      mstore(0x40, m) // Restore the free memory pointer.
                      mstore(0x60, 0) // Restore the zero pointer.
                  }
              }
              /// @dev Returns the EIP-712 domain separator for the EIP-2612 permit.
              function DOMAIN_SEPARATOR() public view virtual returns (bytes32 result) {
                  bytes32 nameHash = _constantNameHash();
                  //  We simply calculate it on-the-fly to allow for cases where the `name` may change.
                  if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name()));
                  /// @solidity memory-safe-assembly
                  assembly {
                      let m := mload(0x40) // Grab the free memory pointer.
                      mstore(m, _DOMAIN_TYPEHASH)
                      mstore(add(m, 0x20), nameHash)
                      mstore(add(m, 0x40), _VERSION_HASH)
                      mstore(add(m, 0x60), chainid())
                      mstore(add(m, 0x80), address())
                      result := keccak256(m, 0xa0)
                  }
              }
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                  INTERNAL MINT FUNCTIONS                   */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              /// @dev Mints `amount` tokens to `to`, increasing the total supply.
              ///
              /// Emits a {Transfer} event.
              function _mint(address to, uint256 amount) internal virtual {
                  _beforeTokenTransfer(address(0), to, amount);
                  /// @solidity memory-safe-assembly
                  assembly {
                      let totalSupplyBefore := sload(_TOTAL_SUPPLY_SLOT)
                      let totalSupplyAfter := add(totalSupplyBefore, amount)
                      // Revert if the total supply overflows.
                      if lt(totalSupplyAfter, totalSupplyBefore) {
                          mstore(0x00, 0xe5cfe957) // `TotalSupplyOverflow()`.
                          revert(0x1c, 0x04)
                      }
                      // Store the updated total supply.
                      sstore(_TOTAL_SUPPLY_SLOT, totalSupplyAfter)
                      // Compute the balance slot and load its value.
                      mstore(0x0c, _BALANCE_SLOT_SEED)
                      mstore(0x00, to)
                      let toBalanceSlot := keccak256(0x0c, 0x20)
                      // Add and store the updated balance.
                      sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
                      // Emit the {Transfer} event.
                      mstore(0x20, amount)
                      log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, mload(0x0c)))
                  }
                  _afterTokenTransfer(address(0), to, amount);
              }
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                  INTERNAL BURN FUNCTIONS                   */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              /// @dev Burns `amount` tokens from `from`, reducing the total supply.
              ///
              /// Emits a {Transfer} event.
              function _burn(address from, uint256 amount) internal virtual {
                  _beforeTokenTransfer(from, address(0), amount);
                  /// @solidity memory-safe-assembly
                  assembly {
                      // Compute the balance slot and load its value.
                      mstore(0x0c, _BALANCE_SLOT_SEED)
                      mstore(0x00, from)
                      let fromBalanceSlot := keccak256(0x0c, 0x20)
                      let fromBalance := sload(fromBalanceSlot)
                      // Revert if insufficient balance.
                      if gt(amount, fromBalance) {
                          mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                          revert(0x1c, 0x04)
                      }
                      // Subtract and store the updated balance.
                      sstore(fromBalanceSlot, sub(fromBalance, amount))
                      // Subtract and store the updated total supply.
                      sstore(_TOTAL_SUPPLY_SLOT, sub(sload(_TOTAL_SUPPLY_SLOT), amount))
                      // Emit the {Transfer} event.
                      mstore(0x00, amount)
                      log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), 0)
                  }
                  _afterTokenTransfer(from, address(0), amount);
              }
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                INTERNAL TRANSFER FUNCTIONS                 */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              /// @dev Moves `amount` of tokens from `from` to `to`.
              function _transfer(address from, address to, uint256 amount) internal virtual {
                  _beforeTokenTransfer(from, to, amount);
                  /// @solidity memory-safe-assembly
                  assembly {
                      let from_ := shl(96, from)
                      // Compute the balance slot and load its value.
                      mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
                      let fromBalanceSlot := keccak256(0x0c, 0x20)
                      let fromBalance := sload(fromBalanceSlot)
                      // Revert if insufficient balance.
                      if gt(amount, fromBalance) {
                          mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                          revert(0x1c, 0x04)
                      }
                      // Subtract and store the updated balance.
                      sstore(fromBalanceSlot, sub(fromBalance, amount))
                      // Compute the balance slot of `to`.
                      mstore(0x00, to)
                      let toBalanceSlot := keccak256(0x0c, 0x20)
                      // Add and store the updated balance of `to`.
                      // Will not overflow because the sum of all user balances
                      // cannot exceed the maximum uint256 value.
                      sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
                      // Emit the {Transfer} event.
                      mstore(0x20, amount)
                      log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
                  }
                  _afterTokenTransfer(from, to, amount);
              }
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                INTERNAL ALLOWANCE FUNCTIONS                */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              /// @dev Updates the allowance of `owner` for `spender` based on spent `amount`.
              function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
                  /// @solidity memory-safe-assembly
                  assembly {
                      // Compute the allowance slot and load its value.
                      mstore(0x20, spender)
                      mstore(0x0c, _ALLOWANCE_SLOT_SEED)
                      mstore(0x00, owner)
                      let allowanceSlot := keccak256(0x0c, 0x34)
                      let allowance_ := sload(allowanceSlot)
                      // If the allowance is not the maximum uint256 value.
                      if add(allowance_, 1) {
                          // Revert if the amount to be transferred exceeds the allowance.
                          if gt(amount, allowance_) {
                              mstore(0x00, 0x13be252b) // `InsufficientAllowance()`.
                              revert(0x1c, 0x04)
                          }
                          // Subtract and store the updated allowance.
                          sstore(allowanceSlot, sub(allowance_, amount))
                      }
                  }
              }
              /// @dev Sets `amount` as the allowance of `spender` over the tokens of `owner`.
              ///
              /// Emits a {Approval} event.
              function _approve(address owner, address spender, uint256 amount) internal virtual {
                  /// @solidity memory-safe-assembly
                  assembly {
                      let owner_ := shl(96, owner)
                      // Compute the allowance slot and store the amount.
                      mstore(0x20, spender)
                      mstore(0x0c, or(owner_, _ALLOWANCE_SLOT_SEED))
                      sstore(keccak256(0x0c, 0x34), amount)
                      // Emit the {Approval} event.
                      mstore(0x00, amount)
                      log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, shr(96, owner_), shr(96, mload(0x2c)))
                  }
              }
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                     HOOKS TO OVERRIDE                      */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              /// @dev Hook that is called before any transfer of tokens.
              /// This includes minting and burning.
              function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
              /// @dev Hook that is called after any transfer of tokens.
              /// This includes minting and burning.
              function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.4;
          /// @notice Gas optimized ECDSA wrapper.
          /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ECDSA.sol)
          /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ECDSA.sol)
          /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol)
          ///
          /// @dev Note:
          /// - The recovery functions use the ecrecover precompile (0x1).
          /// - As of Solady version 0.0.68, the `recover` variants will revert upon recovery failure.
          ///   This is for more safety by default.
          ///   Use the `tryRecover` variants if you need to get the zero address back
          ///   upon recovery failure instead.
          /// - As of Solady version 0.0.134, all `bytes signature` variants accept both
          ///   regular 65-byte `(r, s, v)` and EIP-2098 `(r, vs)` short form signatures.
          ///   See: https://eips.ethereum.org/EIPS/eip-2098
          ///   This is for calldata efficiency on smart accounts prevalent on L2s.
          ///
          /// WARNING! Do NOT use signatures as unique identifiers:
          /// - Use a nonce in the digest to prevent replay attacks on the same contract.
          /// - Use EIP-712 for the digest to prevent replay attacks across different chains and contracts.
          ///   EIP-712 also enables readable signing of typed data for better user safety.
          /// This implementation does NOT check if a signature is non-malleable.
          library ECDSA {
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                        CUSTOM ERRORS                       */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              /// @dev The signature is invalid.
              error InvalidSignature();
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                    RECOVERY OPERATIONS                     */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
              function recover(bytes32 hash, bytes memory signature) internal view returns (address result) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      result := 1
                      let m := mload(0x40) // Cache the free memory pointer.
                      for {} 1 {} {
                          mstore(0x00, hash)
                          mstore(0x40, mload(add(signature, 0x20))) // `r`.
                          if eq(mload(signature), 64) {
                              let vs := mload(add(signature, 0x40))
                              mstore(0x20, add(shr(255, vs), 27)) // `v`.
                              mstore(0x60, shr(1, shl(1, vs))) // `s`.
                              break
                          }
                          if eq(mload(signature), 65) {
                              mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                              mstore(0x60, mload(add(signature, 0x40))) // `s`.
                              break
                          }
                          result := 0
                          break
                      }
                      result :=
                          mload(
                              staticcall(
                                  gas(), // Amount of gas left for the transaction.
                                  result, // Address of `ecrecover`.
                                  0x00, // Start of input.
                                  0x80, // Size of input.
                                  0x01, // Start of output.
                                  0x20 // Size of output.
                              )
                          )
                      // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                      if iszero(returndatasize()) {
                          mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                          revert(0x1c, 0x04)
                      }
                      mstore(0x60, 0) // Restore the zero slot.
                      mstore(0x40, m) // Restore the free memory pointer.
                  }
              }
              /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
              function recoverCalldata(bytes32 hash, bytes calldata signature)
                  internal
                  view
                  returns (address result)
              {
                  /// @solidity memory-safe-assembly
                  assembly {
                      result := 1
                      let m := mload(0x40) // Cache the free memory pointer.
                      mstore(0x00, hash)
                      for {} 1 {} {
                          if eq(signature.length, 64) {
                              let vs := calldataload(add(signature.offset, 0x20))
                              mstore(0x20, add(shr(255, vs), 27)) // `v`.
                              mstore(0x40, calldataload(signature.offset)) // `r`.
                              mstore(0x60, shr(1, shl(1, vs))) // `s`.
                              break
                          }
                          if eq(signature.length, 65) {
                              mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
                              calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
                              break
                          }
                          result := 0
                          break
                      }
                      result :=
                          mload(
                              staticcall(
                                  gas(), // Amount of gas left for the transaction.
                                  result, // Address of `ecrecover`.
                                  0x00, // Start of input.
                                  0x80, // Size of input.
                                  0x01, // Start of output.
                                  0x20 // Size of output.
                              )
                          )
                      // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                      if iszero(returndatasize()) {
                          mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                          revert(0x1c, 0x04)
                      }
                      mstore(0x60, 0) // Restore the zero slot.
                      mstore(0x40, m) // Restore the free memory pointer.
                  }
              }
              /// @dev Recovers the signer's address from a message digest `hash`,
              /// and the EIP-2098 short form signature defined by `r` and `vs`.
              function recover(bytes32 hash, bytes32 r, bytes32 vs) internal view returns (address result) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      let m := mload(0x40) // Cache the free memory pointer.
                      mstore(0x00, hash)
                      mstore(0x20, add(shr(255, vs), 27)) // `v`.
                      mstore(0x40, r)
                      mstore(0x60, shr(1, shl(1, vs))) // `s`.
                      result :=
                          mload(
                              staticcall(
                                  gas(), // Amount of gas left for the transaction.
                                  1, // Address of `ecrecover`.
                                  0x00, // Start of input.
                                  0x80, // Size of input.
                                  0x01, // Start of output.
                                  0x20 // Size of output.
                              )
                          )
                      // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                      if iszero(returndatasize()) {
                          mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                          revert(0x1c, 0x04)
                      }
                      mstore(0x60, 0) // Restore the zero slot.
                      mstore(0x40, m) // Restore the free memory pointer.
                  }
              }
              /// @dev Recovers the signer's address from a message digest `hash`,
              /// and the signature defined by `v`, `r`, `s`.
              function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
                  internal
                  view
                  returns (address result)
              {
                  /// @solidity memory-safe-assembly
                  assembly {
                      let m := mload(0x40) // Cache the free memory pointer.
                      mstore(0x00, hash)
                      mstore(0x20, and(v, 0xff))
                      mstore(0x40, r)
                      mstore(0x60, s)
                      result :=
                          mload(
                              staticcall(
                                  gas(), // Amount of gas left for the transaction.
                                  1, // Address of `ecrecover`.
                                  0x00, // Start of input.
                                  0x80, // Size of input.
                                  0x01, // Start of output.
                                  0x20 // Size of output.
                              )
                          )
                      // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                      if iszero(returndatasize()) {
                          mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                          revert(0x1c, 0x04)
                      }
                      mstore(0x60, 0) // Restore the zero slot.
                      mstore(0x40, m) // Restore the free memory pointer.
                  }
              }
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                   TRY-RECOVER OPERATIONS                   */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              // WARNING!
              // These functions will NOT revert upon recovery failure.
              // Instead, they will return the zero address upon recovery failure.
              // It is critical that the returned address is NEVER compared against
              // a zero address (e.g. an uninitialized address variable).
              /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
              function tryRecover(bytes32 hash, bytes memory signature)
                  internal
                  view
                  returns (address result)
              {
                  /// @solidity memory-safe-assembly
                  assembly {
                      result := 1
                      let m := mload(0x40) // Cache the free memory pointer.
                      for {} 1 {} {
                          mstore(0x00, hash)
                          mstore(0x40, mload(add(signature, 0x20))) // `r`.
                          if eq(mload(signature), 64) {
                              let vs := mload(add(signature, 0x40))
                              mstore(0x20, add(shr(255, vs), 27)) // `v`.
                              mstore(0x60, shr(1, shl(1, vs))) // `s`.
                              break
                          }
                          if eq(mload(signature), 65) {
                              mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                              mstore(0x60, mload(add(signature, 0x40))) // `s`.
                              break
                          }
                          result := 0
                          break
                      }
                      pop(
                          staticcall(
                              gas(), // Amount of gas left for the transaction.
                              result, // Address of `ecrecover`.
                              0x00, // Start of input.
                              0x80, // Size of input.
                              0x40, // Start of output.
                              0x20 // Size of output.
                          )
                      )
                      mstore(0x60, 0) // Restore the zero slot.
                      // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                      result := mload(xor(0x60, returndatasize()))
                      mstore(0x40, m) // Restore the free memory pointer.
                  }
              }
              /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
              function tryRecoverCalldata(bytes32 hash, bytes calldata signature)
                  internal
                  view
                  returns (address result)
              {
                  /// @solidity memory-safe-assembly
                  assembly {
                      result := 1
                      let m := mload(0x40) // Cache the free memory pointer.
                      mstore(0x00, hash)
                      for {} 1 {} {
                          if eq(signature.length, 64) {
                              let vs := calldataload(add(signature.offset, 0x20))
                              mstore(0x20, add(shr(255, vs), 27)) // `v`.
                              mstore(0x40, calldataload(signature.offset)) // `r`.
                              mstore(0x60, shr(1, shl(1, vs))) // `s`.
                              break
                          }
                          if eq(signature.length, 65) {
                              mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
                              calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
                              break
                          }
                          result := 0
                          break
                      }
                      pop(
                          staticcall(
                              gas(), // Amount of gas left for the transaction.
                              result, // Address of `ecrecover`.
                              0x00, // Start of input.
                              0x80, // Size of input.
                              0x40, // Start of output.
                              0x20 // Size of output.
                          )
                      )
                      mstore(0x60, 0) // Restore the zero slot.
                      // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                      result := mload(xor(0x60, returndatasize()))
                      mstore(0x40, m) // Restore the free memory pointer.
                  }
              }
              /// @dev Recovers the signer's address from a message digest `hash`,
              /// and the EIP-2098 short form signature defined by `r` and `vs`.
              function tryRecover(bytes32 hash, bytes32 r, bytes32 vs)
                  internal
                  view
                  returns (address result)
              {
                  /// @solidity memory-safe-assembly
                  assembly {
                      let m := mload(0x40) // Cache the free memory pointer.
                      mstore(0x00, hash)
                      mstore(0x20, add(shr(255, vs), 27)) // `v`.
                      mstore(0x40, r)
                      mstore(0x60, shr(1, shl(1, vs))) // `s`.
                      pop(
                          staticcall(
                              gas(), // Amount of gas left for the transaction.
                              1, // Address of `ecrecover`.
                              0x00, // Start of input.
                              0x80, // Size of input.
                              0x40, // Start of output.
                              0x20 // Size of output.
                          )
                      )
                      mstore(0x60, 0) // Restore the zero slot.
                      // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                      result := mload(xor(0x60, returndatasize()))
                      mstore(0x40, m) // Restore the free memory pointer.
                  }
              }
              /// @dev Recovers the signer's address from a message digest `hash`,
              /// and the signature defined by `v`, `r`, `s`.
              function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
                  internal
                  view
                  returns (address result)
              {
                  /// @solidity memory-safe-assembly
                  assembly {
                      let m := mload(0x40) // Cache the free memory pointer.
                      mstore(0x00, hash)
                      mstore(0x20, and(v, 0xff))
                      mstore(0x40, r)
                      mstore(0x60, s)
                      pop(
                          staticcall(
                              gas(), // Amount of gas left for the transaction.
                              1, // Address of `ecrecover`.
                              0x00, // Start of input.
                              0x80, // Size of input.
                              0x40, // Start of output.
                              0x20 // Size of output.
                          )
                      )
                      mstore(0x60, 0) // Restore the zero slot.
                      // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                      result := mload(xor(0x60, returndatasize()))
                      mstore(0x40, m) // Restore the free memory pointer.
                  }
              }
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                     HASHING OPERATIONS                     */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              /// @dev Returns an Ethereum Signed Message, created from a `hash`.
              /// This produces a hash corresponding to the one signed with the
              /// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign)
              /// JSON-RPC method as part of EIP-191.
              function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      mstore(0x20, hash) // Store into scratch space for keccak256.
                      mstore(0x00, "\\x00\\x00\\x00\\x00\\x19Ethereum Signed Message:\
          32") // 28 bytes.
                      result := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 0x3c`.
                  }
              }
              /// @dev Returns an Ethereum Signed Message, created from `s`.
              /// This produces a hash corresponding to the one signed with the
              /// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign)
              /// JSON-RPC method as part of EIP-191.
              /// Note: Supports lengths of `s` up to 999999 bytes.
              function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      let sLength := mload(s)
                      let o := 0x20
                      mstore(o, "\\x19Ethereum Signed Message:\
          ") // 26 bytes, zero-right-padded.
                      mstore(0x00, 0x00)
                      // Convert the `s.length` to ASCII decimal representation: `base10(s.length)`.
                      for { let temp := sLength } 1 {} {
                          o := sub(o, 1)
                          mstore8(o, add(48, mod(temp, 10)))
                          temp := div(temp, 10)
                          if iszero(temp) { break }
                      }
                      let n := sub(0x3a, o) // Header length: `26 + 32 - o`.
                      // Throw an out-of-offset error (consumes all gas) if the header exceeds 32 bytes.
                      returndatacopy(returndatasize(), returndatasize(), gt(n, 0x20))
                      mstore(s, or(mload(0x00), mload(n))) // Temporarily store the header.
                      result := keccak256(add(s, sub(0x20, n)), add(n, sLength))
                      mstore(s, sLength) // Restore the length.
                  }
              }
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                   EMPTY CALLDATA HELPERS                   */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              /// @dev Returns an empty calldata bytes.
              function emptySignature() internal pure returns (bytes calldata signature) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      signature.length := 0
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.4;
          /// @notice Contract for EIP-712 typed structured data hashing and signing.
          /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/EIP712.sol)
          /// @author Modified from Solbase (https://github.com/Sol-DAO/solbase/blob/main/src/utils/EIP712.sol)
          /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/EIP712.sol)
          ///
          /// @dev Note, this implementation:
          /// - Uses `address(this)` for the `verifyingContract` field.
          /// - Does NOT use the optional EIP-712 salt.
          /// - Does NOT use any EIP-712 extensions.
          /// This is for simplicity and to save gas.
          /// If you need to customize, please fork / modify accordingly.
          abstract contract EIP712 {
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                  CONSTANTS AND IMMUTABLES                  */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              /// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`.
              bytes32 internal constant _DOMAIN_TYPEHASH =
                  0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;
              uint256 private immutable _cachedThis;
              uint256 private immutable _cachedChainId;
              bytes32 private immutable _cachedNameHash;
              bytes32 private immutable _cachedVersionHash;
              bytes32 private immutable _cachedDomainSeparator;
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                        CONSTRUCTOR                         */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              /// @dev Cache the hashes for cheaper runtime gas costs.
              /// In the case of upgradeable contracts (i.e. proxies),
              /// or if the chain id changes due to a hard fork,
              /// the domain separator will be seamlessly calculated on-the-fly.
              constructor() {
                  _cachedThis = uint256(uint160(address(this)));
                  _cachedChainId = block.chainid;
                  string memory name;
                  string memory version;
                  if (!_domainNameAndVersionMayChange()) (name, version) = _domainNameAndVersion();
                  bytes32 nameHash = _domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(name));
                  bytes32 versionHash =
                      _domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(version));
                  _cachedNameHash = nameHash;
                  _cachedVersionHash = versionHash;
                  bytes32 separator;
                  if (!_domainNameAndVersionMayChange()) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          let m := mload(0x40) // Load the free memory pointer.
                          mstore(m, _DOMAIN_TYPEHASH)
                          mstore(add(m, 0x20), nameHash)
                          mstore(add(m, 0x40), versionHash)
                          mstore(add(m, 0x60), chainid())
                          mstore(add(m, 0x80), address())
                          separator := keccak256(m, 0xa0)
                      }
                  }
                  _cachedDomainSeparator = separator;
              }
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                   FUNCTIONS TO OVERRIDE                    */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              /// @dev Please override this function to return the domain name and version.
              /// ```
              ///     function _domainNameAndVersion()
              ///         internal
              ///         pure
              ///         virtual
              ///         returns (string memory name, string memory version)
              ///     {
              ///         name = "Solady";
              ///         version = "1";
              ///     }
              /// ```
              ///
              /// Note: If the returned result may change after the contract has been deployed,
              /// you must override `_domainNameAndVersionMayChange()` to return true.
              function _domainNameAndVersion()
                  internal
                  view
                  virtual
                  returns (string memory name, string memory version);
              /// @dev Returns if `_domainNameAndVersion()` may change
              /// after the contract has been deployed (i.e. after the constructor).
              /// Default: false.
              function _domainNameAndVersionMayChange() internal pure virtual returns (bool result) {}
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                     HASHING OPERATIONS                     */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              /// @dev Returns the EIP-712 domain separator.
              function _domainSeparator() internal view virtual returns (bytes32 separator) {
                  if (_domainNameAndVersionMayChange()) {
                      separator = _buildDomainSeparator();
                  } else {
                      separator = _cachedDomainSeparator;
                      if (_cachedDomainSeparatorInvalidated()) separator = _buildDomainSeparator();
                  }
              }
              /// @dev Returns the hash of the fully encoded EIP-712 message for this domain,
              /// given `structHash`, as defined in
              /// https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct.
              ///
              /// The hash can be used together with {ECDSA-recover} to obtain the signer of a message:
              /// ```
              ///     bytes32 digest = _hashTypedData(keccak256(abi.encode(
              ///         keccak256("Mail(address to,string contents)"),
              ///         mailTo,
              ///         keccak256(bytes(mailContents))
              ///     )));
              ///     address signer = ECDSA.recover(digest, signature);
              /// ```
              function _hashTypedData(bytes32 structHash) internal view virtual returns (bytes32 digest) {
                  // We will use `digest` to store the domain separator to save a bit of gas.
                  if (_domainNameAndVersionMayChange()) {
                      digest = _buildDomainSeparator();
                  } else {
                      digest = _cachedDomainSeparator;
                      if (_cachedDomainSeparatorInvalidated()) digest = _buildDomainSeparator();
                  }
                  /// @solidity memory-safe-assembly
                  assembly {
                      // Compute the digest.
                      mstore(0x00, 0x1901000000000000) // Store "\\x19\\x01".
                      mstore(0x1a, digest) // Store the domain separator.
                      mstore(0x3a, structHash) // Store the struct hash.
                      digest := keccak256(0x18, 0x42)
                      // Restore the part of the free memory slot that was overwritten.
                      mstore(0x3a, 0)
                  }
              }
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                    EIP-5267 OPERATIONS                     */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              /// @dev See: https://eips.ethereum.org/EIPS/eip-5267
              function eip712Domain()
                  public
                  view
                  virtual
                  returns (
                      bytes1 fields,
                      string memory name,
                      string memory version,
                      uint256 chainId,
                      address verifyingContract,
                      bytes32 salt,
                      uint256[] memory extensions
                  )
              {
                  fields = hex"0f"; // `0b01111`.
                  (name, version) = _domainNameAndVersion();
                  chainId = block.chainid;
                  verifyingContract = address(this);
                  salt = salt; // `bytes32(0)`.
                  extensions = extensions; // `new uint256[](0)`.
              }
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                      PRIVATE HELPERS                       */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              /// @dev Returns the EIP-712 domain separator.
              function _buildDomainSeparator() private view returns (bytes32 separator) {
                  // We will use `separator` to store the name hash to save a bit of gas.
                  bytes32 versionHash;
                  if (_domainNameAndVersionMayChange()) {
                      (string memory name, string memory version) = _domainNameAndVersion();
                      separator = keccak256(bytes(name));
                      versionHash = keccak256(bytes(version));
                  } else {
                      separator = _cachedNameHash;
                      versionHash = _cachedVersionHash;
                  }
                  /// @solidity memory-safe-assembly
                  assembly {
                      let m := mload(0x40) // Load the free memory pointer.
                      mstore(m, _DOMAIN_TYPEHASH)
                      mstore(add(m, 0x20), separator) // Name hash.
                      mstore(add(m, 0x40), versionHash)
                      mstore(add(m, 0x60), chainid())
                      mstore(add(m, 0x80), address())
                      separator := keccak256(m, 0xa0)
                  }
              }
              /// @dev Returns if the cached domain separator has been invalidated.
              function _cachedDomainSeparatorInvalidated() private view returns (bool result) {
                  uint256 cachedChainId = _cachedChainId;
                  uint256 cachedThis = _cachedThis;
                  /// @solidity memory-safe-assembly
                  assembly {
                      result := iszero(and(eq(chainid(), cachedChainId), eq(address(), cachedThis)))
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.4;
          /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
          /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
          /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
          ///
          /// @dev Note:
          /// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection.
          /// - For ERC20s, this implementation won't check that a token has code,
          ///   responsibility is delegated to the caller.
          library SafeTransferLib {
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                       CUSTOM ERRORS                        */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              /// @dev The ETH transfer has failed.
              error ETHTransferFailed();
              /// @dev The ERC20 `transferFrom` has failed.
              error TransferFromFailed();
              /// @dev The ERC20 `transfer` has failed.
              error TransferFailed();
              /// @dev The ERC20 `approve` has failed.
              error ApproveFailed();
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                         CONSTANTS                          */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              /// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes.
              uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300;
              /// @dev Suggested gas stipend for contract receiving ETH to perform a few
              /// storage reads and writes, but low enough to prevent griefing.
              uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000;
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                       ETH OPERATIONS                       */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              // If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants.
              //
              // The regular variants:
              // - Forwards all remaining gas to the target.
              // - Reverts if the target reverts.
              // - Reverts if the current contract has insufficient balance.
              //
              // The force variants:
              // - Forwards with an optional gas stipend
              //   (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).
              // - If the target reverts, or if the gas stipend is exhausted,
              //   creates a temporary contract to force send the ETH via `SELFDESTRUCT`.
              //   Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.
              // - Reverts if the current contract has insufficient balance.
              //
              // The try variants:
              // - Forwards with a mandatory gas stipend.
              // - Instead of reverting, returns whether the transfer succeeded.
              /// @dev Sends `amount` (in wei) ETH to `to`.
              function safeTransferETH(address to, uint256 amount) internal {
                  /// @solidity memory-safe-assembly
                  assembly {
                      if iszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) {
                          mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                          revert(0x1c, 0x04)
                      }
                  }
              }
              /// @dev Sends all the ETH in the current contract to `to`.
              function safeTransferAllETH(address to) internal {
                  /// @solidity memory-safe-assembly
                  assembly {
                      // Transfer all the ETH and check if it succeeded or not.
                      if iszero(call(gas(), to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                          mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                          revert(0x1c, 0x04)
                      }
                  }
              }
              /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
              function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
                  /// @solidity memory-safe-assembly
                  assembly {
                      if lt(selfbalance(), amount) {
                          mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                          revert(0x1c, 0x04)
                      }
                      if iszero(call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                          mstore(0x00, to) // Store the address in scratch space.
                          mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                          mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                          if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
                      }
                  }
              }
              /// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.
              function forceSafeTransferAllETH(address to, uint256 gasStipend) internal {
                  /// @solidity memory-safe-assembly
                  assembly {
                      if iszero(call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                          mstore(0x00, to) // Store the address in scratch space.
                          mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                          mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                          if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
                      }
                  }
              }
              /// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
              function forceSafeTransferETH(address to, uint256 amount) internal {
                  /// @solidity memory-safe-assembly
                  assembly {
                      if lt(selfbalance(), amount) {
                          mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                          revert(0x1c, 0x04)
                      }
                      if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                          mstore(0x00, to) // Store the address in scratch space.
                          mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                          mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                          if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
                      }
                  }
              }
              /// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.
              function forceSafeTransferAllETH(address to) internal {
                  /// @solidity memory-safe-assembly
                  assembly {
                      // forgefmt: disable-next-item
                      if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                          mstore(0x00, to) // Store the address in scratch space.
                          mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                          mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                          if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
                      }
                  }
              }
              /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
              function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
                  internal
                  returns (bool success)
              {
                  /// @solidity memory-safe-assembly
                  assembly {
                      success := call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)
                  }
              }
              /// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.
              function trySafeTransferAllETH(address to, uint256 gasStipend)
                  internal
                  returns (bool success)
              {
                  /// @solidity memory-safe-assembly
                  assembly {
                      success := call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)
                  }
              }
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                      ERC20 OPERATIONS                      */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
              /// Reverts upon failure.
              ///
              /// The `from` account must have at least `amount` approved for
              /// the current contract to manage.
              function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
                  /// @solidity memory-safe-assembly
                  assembly {
                      let m := mload(0x40) // Cache the free memory pointer.
                      mstore(0x60, amount) // Store the `amount` argument.
                      mstore(0x40, to) // Store the `to` argument.
                      mstore(0x2c, shl(96, from)) // Store the `from` argument.
                      mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
                      // Perform the transfer, reverting upon failure.
                      if iszero(
                          and( // The arguments of `and` are evaluated from right to left.
                              or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                              call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                          )
                      ) {
                          mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                          revert(0x1c, 0x04)
                      }
                      mstore(0x60, 0) // Restore the zero slot to zero.
                      mstore(0x40, m) // Restore the free memory pointer.
                  }
              }
              /// @dev Sends all of ERC20 `token` from `from` to `to`.
              /// Reverts upon failure.
              ///
              /// The `from` account must have their entire balance approved for
              /// the current contract to manage.
              function safeTransferAllFrom(address token, address from, address to)
                  internal
                  returns (uint256 amount)
              {
                  /// @solidity memory-safe-assembly
                  assembly {
                      let m := mload(0x40) // Cache the free memory pointer.
                      mstore(0x40, to) // Store the `to` argument.
                      mstore(0x2c, shl(96, from)) // Store the `from` argument.
                      mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
                      // Read the balance, reverting upon failure.
                      if iszero(
                          and( // The arguments of `and` are evaluated from right to left.
                              gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                              staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
                          )
                      ) {
                          mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                          revert(0x1c, 0x04)
                      }
                      mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`.
                      amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it.
                      // Perform the transfer, reverting upon failure.
                      if iszero(
                          and( // The arguments of `and` are evaluated from right to left.
                              or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                              call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                          )
                      ) {
                          mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                          revert(0x1c, 0x04)
                      }
                      mstore(0x60, 0) // Restore the zero slot to zero.
                      mstore(0x40, m) // Restore the free memory pointer.
                  }
              }
              /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
              /// Reverts upon failure.
              function safeTransfer(address token, address to, uint256 amount) internal {
                  /// @solidity memory-safe-assembly
                  assembly {
                      mstore(0x14, to) // Store the `to` argument.
                      mstore(0x34, amount) // Store the `amount` argument.
                      mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
                      // Perform the transfer, reverting upon failure.
                      if iszero(
                          and( // The arguments of `and` are evaluated from right to left.
                              or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                              call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                          )
                      ) {
                          mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                          revert(0x1c, 0x04)
                      }
                      mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
                  }
              }
              /// @dev Sends all of ERC20 `token` from the current contract to `to`.
              /// Reverts upon failure.
              function safeTransferAll(address token, address to) internal returns (uint256 amount) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
                      mstore(0x20, address()) // Store the address of the current contract.
                      // Read the balance, reverting upon failure.
                      if iszero(
                          and( // The arguments of `and` are evaluated from right to left.
                              gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                              staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
                          )
                      ) {
                          mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                          revert(0x1c, 0x04)
                      }
                      mstore(0x14, to) // Store the `to` argument.
                      amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it.
                      mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
                      // Perform the transfer, reverting upon failure.
                      if iszero(
                          and( // The arguments of `and` are evaluated from right to left.
                              or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                              call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                          )
                      ) {
                          mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                          revert(0x1c, 0x04)
                      }
                      mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
                  }
              }
              /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
              /// Reverts upon failure.
              function safeApprove(address token, address to, uint256 amount) internal {
                  /// @solidity memory-safe-assembly
                  assembly {
                      mstore(0x14, to) // Store the `to` argument.
                      mstore(0x34, amount) // Store the `amount` argument.
                      mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
                      // Perform the approval, reverting upon failure.
                      if iszero(
                          and( // The arguments of `and` are evaluated from right to left.
                              or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                              call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                          )
                      ) {
                          mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                          revert(0x1c, 0x04)
                      }
                      mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
                  }
              }
              /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
              /// If the initial attempt to approve fails, attempts to reset the approved amount to zero,
              /// then retries the approval again (some tokens, e.g. USDT, requires this).
              /// Reverts upon failure.
              function safeApproveWithRetry(address token, address to, uint256 amount) internal {
                  /// @solidity memory-safe-assembly
                  assembly {
                      mstore(0x14, to) // Store the `to` argument.
                      mstore(0x34, amount) // Store the `amount` argument.
                      mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
                      // Perform the approval, retrying upon failure.
                      if iszero(
                          and( // The arguments of `and` are evaluated from right to left.
                              or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                              call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                          )
                      ) {
                          mstore(0x34, 0) // Store 0 for the `amount`.
                          mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
                          pop(call(gas(), token, 0, 0x10, 0x44, codesize(), 0x00)) // Reset the approval.
                          mstore(0x34, amount) // Store back the original `amount`.
                          // Retry the approval, reverting upon failure.
                          if iszero(
                              and(
                                  or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                                  call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                              )
                          ) {
                              mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                              revert(0x1c, 0x04)
                          }
                      }
                      mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
                  }
              }
              /// @dev Returns the amount of ERC20 `token` owned by `account`.
              /// Returns zero if the `token` does not exist.
              function balanceOf(address token, address account) internal view returns (uint256 amount) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      mstore(0x14, account) // Store the `account` argument.
                      mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
                      amount :=
                          mul(
                              mload(0x20),
                              and( // The arguments of `and` are evaluated from right to left.
                                  gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                                  staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
                              )
                          )
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.4;
          /// @notice Signature verification helper that supports both ECDSA signatures from EOAs
          /// and ERC1271 signatures from smart contract wallets like Argent and Gnosis safe.
          /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SignatureCheckerLib.sol)
          /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/SignatureChecker.sol)
          ///
          /// @dev Note:
          /// - The signature checking functions use the ecrecover precompile (0x1).
          /// - The `bytes memory signature` variants use the identity precompile (0x4)
          ///   to copy memory internally.
          /// - Unlike ECDSA signatures, contract signatures are revocable.
          /// - As of Solady version 0.0.134, all `bytes signature` variants accept both
          ///   regular 65-byte `(r, s, v)` and EIP-2098 `(r, vs)` short form signatures.
          ///   See: https://eips.ethereum.org/EIPS/eip-2098
          ///   This is for calldata efficiency on smart accounts prevalent on L2s.
          ///
          /// WARNING! Do NOT use signatures as unique identifiers:
          /// - Use a nonce in the digest to prevent replay attacks on the same contract.
          /// - Use EIP-712 for the digest to prevent replay attacks across different chains and contracts.
          ///   EIP-712 also enables readable signing of typed data for better user safety.
          /// This implementation does NOT check if a signature is non-malleable.
          library SignatureCheckerLib {
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*               SIGNATURE CHECKING OPERATIONS                */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              /// @dev Returns whether `signature` is valid for `signer` and `hash`.
              /// If `signer` is a smart contract, the signature is validated with ERC1271.
              /// Otherwise, the signature is validated with `ECDSA.recover`.
              function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature)
                  internal
                  view
                  returns (bool isValid)
              {
                  /// @solidity memory-safe-assembly
                  assembly {
                      // Clean the upper 96 bits of `signer` in case they are dirty.
                      for { signer := shr(96, shl(96, signer)) } signer {} {
                          let m := mload(0x40)
                          mstore(0x00, hash)
                          mstore(0x40, mload(add(signature, 0x20))) // `r`.
                          if eq(mload(signature), 64) {
                              let vs := mload(add(signature, 0x40))
                              mstore(0x20, add(shr(255, vs), 27)) // `v`.
                              mstore(0x60, shr(1, shl(1, vs))) // `s`.
                              let t :=
                                  staticcall(
                                      gas(), // Amount of gas left for the transaction.
                                      1, // Address of `ecrecover`.
                                      0x00, // Start of input.
                                      0x80, // Size of input.
                                      0x01, // Start of output.
                                      0x20 // Size of output.
                                  )
                              // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                              if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) {
                                  isValid := 1
                                  mstore(0x60, 0) // Restore the zero slot.
                                  mstore(0x40, m) // Restore the free memory pointer.
                                  break
                              }
                          }
                          if eq(mload(signature), 65) {
                              mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                              mstore(0x60, mload(add(signature, 0x40))) // `s`.
                              let t :=
                                  staticcall(
                                      gas(), // Amount of gas left for the transaction.
                                      1, // Address of `ecrecover`.
                                      0x00, // Start of input.
                                      0x80, // Size of input.
                                      0x01, // Start of output.
                                      0x20 // Size of output.
                                  )
                              // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                              if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) {
                                  isValid := 1
                                  mstore(0x60, 0) // Restore the zero slot.
                                  mstore(0x40, m) // Restore the free memory pointer.
                                  break
                              }
                          }
                          mstore(0x60, 0) // Restore the zero slot.
                          mstore(0x40, m) // Restore the free memory pointer.
                          let f := shl(224, 0x1626ba7e)
                          mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                          mstore(add(m, 0x04), hash)
                          let d := add(m, 0x24)
                          mstore(d, 0x40) // The offset of the `signature` in the calldata.
                          // Copy the `signature` over.
                          let n := add(0x20, mload(signature))
                          pop(staticcall(gas(), 4, signature, n, add(m, 0x44), n))
                          // forgefmt: disable-next-item
                          isValid := and(
                              // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                              eq(mload(d), f),
                              // Whether the staticcall does not revert.
                              // This must be placed at the end of the `and` clause,
                              // as the arguments are evaluated from right to left.
                              staticcall(
                                  gas(), // Remaining gas.
                                  signer, // The `signer` address.
                                  m, // Offset of calldata in memory.
                                  add(returndatasize(), 0x44), // Length of calldata in memory.
                                  d, // Offset of returndata.
                                  0x20 // Length of returndata to write.
                              )
                          )
                          break
                      }
                  }
              }
              /// @dev Returns whether `signature` is valid for `signer` and `hash`.
              /// If `signer` is a smart contract, the signature is validated with ERC1271.
              /// Otherwise, the signature is validated with `ECDSA.recover`.
              function isValidSignatureNowCalldata(address signer, bytes32 hash, bytes calldata signature)
                  internal
                  view
                  returns (bool isValid)
              {
                  /// @solidity memory-safe-assembly
                  assembly {
                      // Clean the upper 96 bits of `signer` in case they are dirty.
                      for { signer := shr(96, shl(96, signer)) } signer {} {
                          let m := mload(0x40)
                          mstore(0x00, hash)
                          if eq(signature.length, 64) {
                              let vs := calldataload(add(signature.offset, 0x20))
                              mstore(0x20, add(shr(255, vs), 27)) // `v`.
                              mstore(0x40, calldataload(signature.offset)) // `r`.
                              mstore(0x60, shr(1, shl(1, vs))) // `s`.
                              let t :=
                                  staticcall(
                                      gas(), // Amount of gas left for the transaction.
                                      1, // Address of `ecrecover`.
                                      0x00, // Start of input.
                                      0x80, // Size of input.
                                      0x01, // Start of output.
                                      0x20 // Size of output.
                                  )
                              // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                              if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) {
                                  isValid := 1
                                  mstore(0x60, 0) // Restore the zero slot.
                                  mstore(0x40, m) // Restore the free memory pointer.
                                  break
                              }
                          }
                          if eq(signature.length, 65) {
                              mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
                              calldatacopy(0x40, signature.offset, 0x40) // `r`, `s`.
                              let t :=
                                  staticcall(
                                      gas(), // Amount of gas left for the transaction.
                                      1, // Address of `ecrecover`.
                                      0x00, // Start of input.
                                      0x80, // Size of input.
                                      0x01, // Start of output.
                                      0x20 // Size of output.
                                  )
                              // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                              if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) {
                                  isValid := 1
                                  mstore(0x60, 0) // Restore the zero slot.
                                  mstore(0x40, m) // Restore the free memory pointer.
                                  break
                              }
                          }
                          mstore(0x60, 0) // Restore the zero slot.
                          mstore(0x40, m) // Restore the free memory pointer.
                          let f := shl(224, 0x1626ba7e)
                          mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                          mstore(add(m, 0x04), hash)
                          let d := add(m, 0x24)
                          mstore(d, 0x40) // The offset of the `signature` in the calldata.
                          mstore(add(m, 0x44), signature.length)
                          // Copy the `signature` over.
                          calldatacopy(add(m, 0x64), signature.offset, signature.length)
                          // forgefmt: disable-next-item
                          isValid := and(
                              // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                              eq(mload(d), f),
                              // Whether the staticcall does not revert.
                              // This must be placed at the end of the `and` clause,
                              // as the arguments are evaluated from right to left.
                              staticcall(
                                  gas(), // Remaining gas.
                                  signer, // The `signer` address.
                                  m, // Offset of calldata in memory.
                                  add(signature.length, 0x64), // Length of calldata in memory.
                                  d, // Offset of returndata.
                                  0x20 // Length of returndata to write.
                              )
                          )
                          break
                      }
                  }
              }
              /// @dev Returns whether the signature (`r`, `vs`) is valid for `signer` and `hash`.
              /// If `signer` is a smart contract, the signature is validated with ERC1271.
              /// Otherwise, the signature is validated with `ECDSA.recover`.
              function isValidSignatureNow(address signer, bytes32 hash, bytes32 r, bytes32 vs)
                  internal
                  view
                  returns (bool isValid)
              {
                  /// @solidity memory-safe-assembly
                  assembly {
                      // Clean the upper 96 bits of `signer` in case they are dirty.
                      for { signer := shr(96, shl(96, signer)) } signer {} {
                          let m := mload(0x40)
                          mstore(0x00, hash)
                          mstore(0x20, add(shr(255, vs), 27)) // `v`.
                          mstore(0x40, r) // `r`.
                          mstore(0x60, shr(1, shl(1, vs))) // `s`.
                          let t :=
                              staticcall(
                                  gas(), // Amount of gas left for the transaction.
                                  1, // Address of `ecrecover`.
                                  0x00, // Start of input.
                                  0x80, // Size of input.
                                  0x01, // Start of output.
                                  0x20 // Size of output.
                              )
                          // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                          if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) {
                              isValid := 1
                              mstore(0x60, 0) // Restore the zero slot.
                              mstore(0x40, m) // Restore the free memory pointer.
                              break
                          }
                          let f := shl(224, 0x1626ba7e)
                          mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                          mstore(add(m, 0x04), hash)
                          let d := add(m, 0x24)
                          mstore(d, 0x40) // The offset of the `signature` in the calldata.
                          mstore(add(m, 0x44), 65) // Length of the signature.
                          mstore(add(m, 0x64), r) // `r`.
                          mstore(add(m, 0x84), mload(0x60)) // `s`.
                          mstore8(add(m, 0xa4), mload(0x20)) // `v`.
                          // forgefmt: disable-next-item
                          isValid := and(
                              // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                              eq(mload(d), f),
                              // Whether the staticcall does not revert.
                              // This must be placed at the end of the `and` clause,
                              // as the arguments are evaluated from right to left.
                              staticcall(
                                  gas(), // Remaining gas.
                                  signer, // The `signer` address.
                                  m, // Offset of calldata in memory.
                                  0xa5, // Length of calldata in memory.
                                  d, // Offset of returndata.
                                  0x20 // Length of returndata to write.
                              )
                          )
                          mstore(0x60, 0) // Restore the zero slot.
                          mstore(0x40, m) // Restore the free memory pointer.
                          break
                      }
                  }
              }
              /// @dev Returns whether the signature (`v`, `r`, `s`) is valid for `signer` and `hash`.
              /// If `signer` is a smart contract, the signature is validated with ERC1271.
              /// Otherwise, the signature is validated with `ECDSA.recover`.
              function isValidSignatureNow(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s)
                  internal
                  view
                  returns (bool isValid)
              {
                  /// @solidity memory-safe-assembly
                  assembly {
                      // Clean the upper 96 bits of `signer` in case they are dirty.
                      for { signer := shr(96, shl(96, signer)) } signer {} {
                          let m := mload(0x40)
                          mstore(0x00, hash)
                          mstore(0x20, and(v, 0xff)) // `v`.
                          mstore(0x40, r) // `r`.
                          mstore(0x60, s) // `s`.
                          let t :=
                              staticcall(
                                  gas(), // Amount of gas left for the transaction.
                                  1, // Address of `ecrecover`.
                                  0x00, // Start of input.
                                  0x80, // Size of input.
                                  0x01, // Start of output.
                                  0x20 // Size of output.
                              )
                          // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                          if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) {
                              isValid := 1
                              mstore(0x60, 0) // Restore the zero slot.
                              mstore(0x40, m) // Restore the free memory pointer.
                              break
                          }
                          let f := shl(224, 0x1626ba7e)
                          mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                          mstore(add(m, 0x04), hash)
                          let d := add(m, 0x24)
                          mstore(d, 0x40) // The offset of the `signature` in the calldata.
                          mstore(add(m, 0x44), 65) // Length of the signature.
                          mstore(add(m, 0x64), r) // `r`.
                          mstore(add(m, 0x84), s) // `s`.
                          mstore8(add(m, 0xa4), v) // `v`.
                          // forgefmt: disable-next-item
                          isValid := and(
                              // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                              eq(mload(d), f),
                              // Whether the staticcall does not revert.
                              // This must be placed at the end of the `and` clause,
                              // as the arguments are evaluated from right to left.
                              staticcall(
                                  gas(), // Remaining gas.
                                  signer, // The `signer` address.
                                  m, // Offset of calldata in memory.
                                  0xa5, // Length of calldata in memory.
                                  d, // Offset of returndata.
                                  0x20 // Length of returndata to write.
                              )
                          )
                          mstore(0x60, 0) // Restore the zero slot.
                          mstore(0x40, m) // Restore the free memory pointer.
                          break
                      }
                  }
              }
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                     ERC1271 OPERATIONS                     */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              /// @dev Returns whether `signature` is valid for `hash` for an ERC1271 `signer` contract.
              function isValidERC1271SignatureNow(address signer, bytes32 hash, bytes memory signature)
                  internal
                  view
                  returns (bool isValid)
              {
                  /// @solidity memory-safe-assembly
                  assembly {
                      let m := mload(0x40)
                      let f := shl(224, 0x1626ba7e)
                      mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                      mstore(add(m, 0x04), hash)
                      let d := add(m, 0x24)
                      mstore(d, 0x40) // The offset of the `signature` in the calldata.
                      // Copy the `signature` over.
                      let n := add(0x20, mload(signature))
                      pop(staticcall(gas(), 4, signature, n, add(m, 0x44), n))
                      // forgefmt: disable-next-item
                      isValid := and(
                          // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                          eq(mload(d), f),
                          // Whether the staticcall does not revert.
                          // This must be placed at the end of the `and` clause,
                          // as the arguments are evaluated from right to left.
                          staticcall(
                              gas(), // Remaining gas.
                              signer, // The `signer` address.
                              m, // Offset of calldata in memory.
                              add(returndatasize(), 0x44), // Length of calldata in memory.
                              d, // Offset of returndata.
                              0x20 // Length of returndata to write.
                          )
                      )
                  }
              }
              /// @dev Returns whether `signature` is valid for `hash` for an ERC1271 `signer` contract.
              function isValidERC1271SignatureNowCalldata(
                  address signer,
                  bytes32 hash,
                  bytes calldata signature
              ) internal view returns (bool isValid) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      let m := mload(0x40)
                      let f := shl(224, 0x1626ba7e)
                      mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                      mstore(add(m, 0x04), hash)
                      let d := add(m, 0x24)
                      mstore(d, 0x40) // The offset of the `signature` in the calldata.
                      mstore(add(m, 0x44), signature.length)
                      // Copy the `signature` over.
                      calldatacopy(add(m, 0x64), signature.offset, signature.length)
                      // forgefmt: disable-next-item
                      isValid := and(
                          // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                          eq(mload(d), f),
                          // Whether the staticcall does not revert.
                          // This must be placed at the end of the `and` clause,
                          // as the arguments are evaluated from right to left.
                          staticcall(
                              gas(), // Remaining gas.
                              signer, // The `signer` address.
                              m, // Offset of calldata in memory.
                              add(signature.length, 0x64), // Length of calldata in memory.
                              d, // Offset of returndata.
                              0x20 // Length of returndata to write.
                          )
                      )
                  }
              }
              /// @dev Returns whether the signature (`r`, `vs`) is valid for `hash`
              /// for an ERC1271 `signer` contract.
              function isValidERC1271SignatureNow(address signer, bytes32 hash, bytes32 r, bytes32 vs)
                  internal
                  view
                  returns (bool isValid)
              {
                  /// @solidity memory-safe-assembly
                  assembly {
                      let m := mload(0x40)
                      let f := shl(224, 0x1626ba7e)
                      mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                      mstore(add(m, 0x04), hash)
                      let d := add(m, 0x24)
                      mstore(d, 0x40) // The offset of the `signature` in the calldata.
                      mstore(add(m, 0x44), 65) // Length of the signature.
                      mstore(add(m, 0x64), r) // `r`.
                      mstore(add(m, 0x84), shr(1, shl(1, vs))) // `s`.
                      mstore8(add(m, 0xa4), add(shr(255, vs), 27)) // `v`.
                      // forgefmt: disable-next-item
                      isValid := and(
                          // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                          eq(mload(d), f),
                          // Whether the staticcall does not revert.
                          // This must be placed at the end of the `and` clause,
                          // as the arguments are evaluated from right to left.
                          staticcall(
                              gas(), // Remaining gas.
                              signer, // The `signer` address.
                              m, // Offset of calldata in memory.
                              0xa5, // Length of calldata in memory.
                              d, // Offset of returndata.
                              0x20 // Length of returndata to write.
                          )
                      )
                  }
              }
              /// @dev Returns whether the signature (`v`, `r`, `s`) is valid for `hash`
              /// for an ERC1271 `signer` contract.
              function isValidERC1271SignatureNow(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s)
                  internal
                  view
                  returns (bool isValid)
              {
                  /// @solidity memory-safe-assembly
                  assembly {
                      let m := mload(0x40)
                      let f := shl(224, 0x1626ba7e)
                      mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                      mstore(add(m, 0x04), hash)
                      let d := add(m, 0x24)
                      mstore(d, 0x40) // The offset of the `signature` in the calldata.
                      mstore(add(m, 0x44), 65) // Length of the signature.
                      mstore(add(m, 0x64), r) // `r`.
                      mstore(add(m, 0x84), s) // `s`.
                      mstore8(add(m, 0xa4), v) // `v`.
                      // forgefmt: disable-next-item
                      isValid := and(
                          // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                          eq(mload(d), f),
                          // Whether the staticcall does not revert.
                          // This must be placed at the end of the `and` clause,
                          // as the arguments are evaluated from right to left.
                          staticcall(
                              gas(), // Remaining gas.
                              signer, // The `signer` address.
                              m, // Offset of calldata in memory.
                              0xa5, // Length of calldata in memory.
                              d, // Offset of returndata.
                              0x20 // Length of returndata to write.
                          )
                      )
                  }
              }
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                     HASHING OPERATIONS                     */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              /// @dev Returns an Ethereum Signed Message, created from a `hash`.
              /// This produces a hash corresponding to the one signed with the
              /// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign)
              /// JSON-RPC method as part of EIP-191.
              function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      mstore(0x20, hash) // Store into scratch space for keccak256.
                      mstore(0x00, "\\x00\\x00\\x00\\x00\\x19Ethereum Signed Message:\
          32") // 28 bytes.
                      result := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 0x3c`.
                  }
              }
              /// @dev Returns an Ethereum Signed Message, created from `s`.
              /// This produces a hash corresponding to the one signed with the
              /// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign)
              /// JSON-RPC method as part of EIP-191.
              /// Note: Supports lengths of `s` up to 999999 bytes.
              function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      let sLength := mload(s)
                      let o := 0x20
                      mstore(o, "\\x19Ethereum Signed Message:\
          ") // 26 bytes, zero-right-padded.
                      mstore(0x00, 0x00)
                      // Convert the `s.length` to ASCII decimal representation: `base10(s.length)`.
                      for { let temp := sLength } 1 {} {
                          o := sub(o, 1)
                          mstore8(o, add(48, mod(temp, 10)))
                          temp := div(temp, 10)
                          if iszero(temp) { break }
                      }
                      let n := sub(0x3a, o) // Header length: `26 + 32 - o`.
                      // Throw an out-of-offset error (consumes all gas) if the header exceeds 32 bytes.
                      returndatacopy(returndatasize(), returndatasize(), gt(n, 0x20))
                      mstore(s, or(mload(0x00), mload(n))) // Temporarily store the header.
                      result := keccak256(add(s, sub(0x20, n)), add(n, sLength))
                      mstore(s, sLength) // Restore the length.
                  }
              }
              /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
              /*                   EMPTY CALLDATA HELPERS                   */
              /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
              /// @dev Returns an empty calldata bytes.
              function emptySignature() internal pure returns (bytes calldata signature) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      signature.length := 0
                  }
              }
          }
          

          File 6 of 6: GnosisSafeProxy
          // SPDX-License-Identifier: LGPL-3.0-only
          pragma solidity >=0.7.0 <0.9.0;
          
          /// @title IProxy - Helper interface to access masterCopy of the Proxy on-chain
          /// @author Richard Meissner - <[email protected]>
          interface IProxy {
              function masterCopy() external view returns (address);
          }
          
          /// @title GnosisSafeProxy - Generic proxy contract allows to execute all transactions applying the code of a master contract.
          /// @author Stefan George - <[email protected]>
          /// @author Richard Meissner - <[email protected]>
          contract GnosisSafeProxy {
              // singleton always needs to be first declared variable, to ensure that it is at the same location in the contracts to which calls are delegated.
              // To reduce deployment costs this variable is internal and needs to be retrieved via `getStorageAt`
              address internal singleton;
          
              /// @dev Constructor function sets address of singleton contract.
              /// @param _singleton Singleton address.
              constructor(address _singleton) {
                  require(_singleton != address(0), "Invalid singleton address provided");
                  singleton = _singleton;
              }
          
              /// @dev Fallback function forwards all transactions and returns all received return data.
              fallback() external payable {
                  // solhint-disable-next-line no-inline-assembly
                  assembly {
                      let _singleton := and(sload(0), 0xffffffffffffffffffffffffffffffffffffffff)
                      // 0xa619486e == keccak("masterCopy()"). The value is right padded to 32-bytes with 0s
                      if eq(calldataload(0), 0xa619486e00000000000000000000000000000000000000000000000000000000) {
                          mstore(0, _singleton)
                          return(0, 0x20)
                      }
                      calldatacopy(0, 0, calldatasize())
                      let success := delegatecall(gas(), _singleton, 0, calldatasize(), 0, 0)
                      returndatacopy(0, 0, returndatasize())
                      if eq(success, 0) {
                          revert(0, returndatasize())
                      }
                      return(0, returndatasize())
                  }
              }
          }
          
          /// @title Proxy Factory - Allows to create new proxy contact and execute a message call to the new proxy within one transaction.
          /// @author Stefan George - <[email protected]>
          contract GnosisSafeProxyFactory {
              event ProxyCreation(GnosisSafeProxy proxy, address singleton);
          
              /// @dev Allows to create new proxy contact and execute a message call to the new proxy within one transaction.
              /// @param singleton Address of singleton contract.
              /// @param data Payload for message call sent to new proxy contract.
              function createProxy(address singleton, bytes memory data) public returns (GnosisSafeProxy proxy) {
                  proxy = new GnosisSafeProxy(singleton);
                  if (data.length > 0)
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          if eq(call(gas(), proxy, 0, add(data, 0x20), mload(data), 0, 0), 0) {
                              revert(0, 0)
                          }
                      }
                  emit ProxyCreation(proxy, singleton);
              }
          
              /// @dev Allows to retrieve the runtime code of a deployed Proxy. This can be used to check that the expected Proxy was deployed.
              function proxyRuntimeCode() public pure returns (bytes memory) {
                  return type(GnosisSafeProxy).runtimeCode;
              }
          
              /// @dev Allows to retrieve the creation code used for the Proxy deployment. With this it is easily possible to calculate predicted address.
              function proxyCreationCode() public pure returns (bytes memory) {
                  return type(GnosisSafeProxy).creationCode;
              }
          
              /// @dev Allows to create new proxy contact using CREATE2 but it doesn't run the initializer.
              ///      This method is only meant as an utility to be called from other methods
              /// @param _singleton Address of singleton contract.
              /// @param initializer Payload for message call sent to new proxy contract.
              /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
              function deployProxyWithNonce(
                  address _singleton,
                  bytes memory initializer,
                  uint256 saltNonce
              ) internal returns (GnosisSafeProxy proxy) {
                  // If the initializer changes the proxy address should change too. Hashing the initializer data is cheaper than just concatinating it
                  bytes32 salt = keccak256(abi.encodePacked(keccak256(initializer), saltNonce));
                  bytes memory deploymentData = abi.encodePacked(type(GnosisSafeProxy).creationCode, uint256(uint160(_singleton)));
                  // solhint-disable-next-line no-inline-assembly
                  assembly {
                      proxy := create2(0x0, add(0x20, deploymentData), mload(deploymentData), salt)
                  }
                  require(address(proxy) != address(0), "Create2 call failed");
              }
          
              /// @dev Allows to create new proxy contact and execute a message call to the new proxy within one transaction.
              /// @param _singleton Address of singleton contract.
              /// @param initializer Payload for message call sent to new proxy contract.
              /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
              function createProxyWithNonce(
                  address _singleton,
                  bytes memory initializer,
                  uint256 saltNonce
              ) public returns (GnosisSafeProxy proxy) {
                  proxy = deployProxyWithNonce(_singleton, initializer, saltNonce);
                  if (initializer.length > 0)
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          if eq(call(gas(), proxy, 0, add(initializer, 0x20), mload(initializer), 0, 0), 0) {
                              revert(0, 0)
                          }
                      }
                  emit ProxyCreation(proxy, _singleton);
              }
          
              /// @dev Allows to create new proxy contact, execute a message call to the new proxy and call a specified callback within one transaction
              /// @param _singleton Address of singleton contract.
              /// @param initializer Payload for message call sent to new proxy contract.
              /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
              /// @param callback Callback that will be invoced after the new proxy contract has been successfully deployed and initialized.
              function createProxyWithCallback(
                  address _singleton,
                  bytes memory initializer,
                  uint256 saltNonce,
                  IProxyCreationCallback callback
              ) public returns (GnosisSafeProxy proxy) {
                  uint256 saltNonceWithCallback = uint256(keccak256(abi.encodePacked(saltNonce, callback)));
                  proxy = createProxyWithNonce(_singleton, initializer, saltNonceWithCallback);
                  if (address(callback) != address(0)) callback.proxyCreated(proxy, _singleton, initializer, saltNonce);
              }
          
              /// @dev Allows to get the address for a new proxy contact created via `createProxyWithNonce`
              ///      This method is only meant for address calculation purpose when you use an initializer that would revert,
              ///      therefore the response is returned with a revert. When calling this method set `from` to the address of the proxy factory.
              /// @param _singleton Address of singleton contract.
              /// @param initializer Payload for message call sent to new proxy contract.
              /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
              function calculateCreateProxyWithNonceAddress(
                  address _singleton,
                  bytes calldata initializer,
                  uint256 saltNonce
              ) external returns (GnosisSafeProxy proxy) {
                  proxy = deployProxyWithNonce(_singleton, initializer, saltNonce);
                  revert(string(abi.encodePacked(proxy)));
              }
          }
          
          interface IProxyCreationCallback {
              function proxyCreated(
                  GnosisSafeProxy proxy,
                  address _singleton,
                  bytes calldata initializer,
                  uint256 saltNonce
              ) external;
          }