ETH Price: $1,871.78 (-0.34%)

Transaction Decoder

Block:
21895630 at Feb-21-2025 03:32:35 PM +UTC
Transaction Fee:
0.001702522359440048 ETH $3.19
Gas Used:
214,873 Gas / 7.923388976 Gwei

Emitted Events:

730 TetherToken.Transfer( from=[Sender] 0x546c4d44cb61419ff0cb7cb34de1e82c591a26e8, to=Spender, value=1800000000 )
731 TetherToken.Transfer( from=Spender, to=0x51C72848c68a965f66FA7a88855F9f7784502a7F, value=1784250000 )
732 Ondo.Approval( owner=0x51C72848c68a965f66FA7a88855F9f7784502a7F, spender=SwapERC20, amount=1896147554280882927594074 )
733 Ondo.Transfer( from=0x51C72848c68a965f66FA7a88855F9f7784502a7F, to=Spender, amount=1479725801827552985088 )
734 Ondo.Approval( owner=0x51C72848c68a965f66FA7a88855F9f7784502a7F, spender=SwapERC20, amount=1896146814417982013817582 )
735 Ondo.Transfer( from=0x51C72848c68a965f66FA7a88855F9f7784502a7F, to=SplitWallet, amount=739862900913776492 )
736 SwapERC20.SwapERC20( nonce=1739964182662, signerWallet=0x51C72848c68a965f66FA7a88855F9f7784502a7F )
737 TetherToken.Transfer( from=Spender, to=GnosisSafeProxy, value=15750000 )
738 Ondo.Transfer( from=Spender, to=[Sender] 0x546c4d44cb61419ff0cb7cb34de1e82c591a26e8, amount=1479725801827552985088 )
739 MetaSwap.Swap( 0xbeee1e6e7fe307ddcf84b0a16137a4430ad5e2480fc4f4a8e250ab56ccd7630d, 0x74008a9e83921090a0cc3c37a7b39398a692db963164a76e069ab2e8fb053e10, 0x000000000000000000000000546c4d44cb61419ff0cb7cb34de1e82c591a26e8 )

Account State Difference:

  Address   Before After State Difference Code
(Titan Builder)
11.70014492392783429 Eth11.700692134882078456 Eth0.000547210954244166
0x546C4d44...C591A26E8
0.017742676867900188 Eth
Nonce: 18
0.01604015450846014 Eth
Nonce: 19
0.001702522359440048
0xD82E10B9...e6CF2fC46
(AirSwap: V5 DEX SwapERC20)
0xdAC17F95...13D831ec7
0xfAbA6f8e...577269BE3

Execution Trace

MetaSwap.swap( aggregatorId=airSwap4_3FeeDynamic, tokenFrom=0xdAC17F958D2ee523a2206206994597C13D831ec7, amount=1800000000, data=0x000000000000000000000000000000000000000000000000000001951DF270860000000000000000000000000000000000000000000000000000000067B89D0900000000000000000000000051C72848C68A965F66FA7A88855F9F7784502A7F000000000000000000000000FABA6F8E4A5E8AB82F62FE7C39859FA577269BE3000000000000000000000000000000000000000000000050375218D6BF5C0000000000000000000000000000DAC17F958D2EE523A2206206994597C13D831EC7000000000000000000000000000000000000000000000000000000006A597E90000000000000000000000000000000000000000000000000000000000000001BFE164B607208E95A86FE882A572EC9B0EF69D3A91E063C629A27DFC338F0AB651B76C3216075B7B3E42E1DC89180A19915D7BBCDE3FDFFFC4ACD4025CD24032D0000000000000000000000000000000000000000000000000000000000F053700000000000000000000000002ACF35C9A3F4C5C3F4C78EF5FB64C3EE82F07C450000000000000000000000000000000000000000000000000000000000000000 )
  • TetherToken.transferFrom( _from=0x546C4d44cb61419ff0cb7CB34De1E82C591A26E8, _to=0x74de5d4FCbf63E00296fd95d33236B9794016631, _value=1800000000 )
  • Spender.swap( adapter=0x60FBaF99832Fb4360351AbC2b55e4B1F2fe98c71, data=0x4495C088000000000000000000000000546C4D44CB61419FF0CB7CB34DE1E82C591A26E8000000000000000000000000000000000000000000000000000001951DF270860000000000000000000000000000000000000000000000000000000067B89D0900000000000000000000000051C72848C68A965F66FA7A88855F9F7784502A7F000000000000000000000000FABA6F8E4A5E8AB82F62FE7C39859FA577269BE3000000000000000000000000000000000000000000000050375218D6BF5C0000000000000000000000000000DAC17F958D2EE523A2206206994597C13D831EC7000000000000000000000000000000000000000000000000000000006A597E90000000000000000000000000000000000000000000000000000000000000001BFE164B607208E95A86FE882A572EC9B0EF69D3A91E063C629A27DFC338F0AB651B76C3216075B7B3E42E1DC89180A19915D7BBCDE3FDFFFC4ACD4025CD24032D0000000000000000000000000000000000000000000000000000000000F053700000000000000000000000002ACF35C9A3F4C5C3F4C78EF5FB64C3EE82F07C450000000000000000000000000000000000000000000000000000000000000000 )
    • 0x60fbaf99832fb4360351abc2b55e4b1f2fe98c71.4495c088( )
      • TetherToken.allowance( _owner=0x74de5d4FCbf63E00296fd95d33236B9794016631, _spender=0xD82E10B9A4107939e55fCCa9B53A9ede6CF2fC46 ) => ( remaining=115792089237316195423570985008687907853269984665640564039457584007913129639935 )
      • SwapERC20.swapLight( nonce=1739964182662, expiry=1740152073, signerWallet=0x51C72848c68a965f66FA7a88855F9f7784502a7F, signerToken=0xfAbA6f8e4a5E8Ab82F62fe7C39859FA577269BE3, signerAmount=1479725801827552985088, senderToken=0xdAC17F958D2ee523a2206206994597C13D831ec7, senderAmount=1784250000, v=27, r=FE164B607208E95A86FE882A572EC9B0EF69D3A91E063C629A27DFC338F0AB65, s=1B76C3216075B7B3E42E1DC89180A19915D7BBCDE3FDFFFC4ACD4025CD24032D )
        • Null: 0x000...001.3bd3e67e( )
        • TetherToken.transferFrom( _from=0x74de5d4FCbf63E00296fd95d33236B9794016631, _to=0x51C72848c68a965f66FA7a88855F9f7784502a7F, _value=1784250000 )
        • Ondo.transferFrom( src=0x51C72848c68a965f66FA7a88855F9f7784502a7F, dst=0x74de5d4FCbf63E00296fd95d33236B9794016631, rawAmount=1479725801827552985088 ) => ( True )
        • Ondo.transferFrom( src=0x51C72848c68a965f66FA7a88855F9f7784502a7F, dst=0xaD30f7EEBD9Bd5150a256F47DA41d4403033CdF0, rawAmount=739862900913776492 ) => ( True )
        • TetherToken.transfer( _to=0x2aCf35C9A3F4c5C3F4c78EF5Fb64c3EE82f07c45, _value=15750000 )
        • TetherToken.balanceOf( who=0x74de5d4FCbf63E00296fd95d33236B9794016631 ) => ( 0 )
        • Ondo.balanceOf( account=0x74de5d4FCbf63E00296fd95d33236B9794016631 ) => ( 1479725801827552985088 )
        • Ondo.transfer( dst=0x546C4d44cb61419ff0cb7CB34De1E82C591A26E8, rawAmount=1479725801827552985088 ) => ( True )
          File 1 of 7: MetaSwap
          pragma solidity ^0.6.0;
          import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
          import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
          import "@openzeppelin/contracts/utils/Address.sol";
          import "../Constants.sol";
          contract CommonAdapter {
              using SafeERC20 for IERC20;
              using Address for address;
              using Address for address payable;
              /**
               * @dev Performs a swap
               * @param recipient The original msg.sender performing the swap
               * @param aggregator Address of the aggregator's contract
               * @param spender Address to which tokens will be approved
               * @param method Selector of the function to be called in the aggregator's contract
               * @param tokenFrom Token to be swapped
               * @param tokenTo Token to be received
               * @param amountFrom Amount of tokenFrom to swap
               * @param amountTo Minimum amount of tokenTo to receive
               * @param data Data used for the call made to the aggregator's contract
               */
              function swap(
                  address payable recipient,
                  address aggregator,
                  address spender,
                  bytes4 method,
                  IERC20 tokenFrom,
                  IERC20 tokenTo,
                  uint256 amountFrom,
                  uint256 amountTo,
                  bytes calldata data
              ) external payable {
                  require(tokenFrom != tokenTo, "TOKEN_PAIR_INVALID");
                  if (address(tokenFrom) != Constants.ETH) {
                      _approveSpender(tokenFrom, spender, amountFrom);
                  }
                  // We always forward msg.value as it may be necessary to pay fees
                  bytes memory encodedData = abi.encodePacked(method, data);
                  aggregator.functionCallWithValue(encodedData, msg.value);
                  // Transfer remaining balance of tokenFrom to sender
                  if (address(tokenFrom) != Constants.ETH) {
                      uint256 balance = tokenFrom.balanceOf(address(this));
                      _transfer(tokenFrom, balance, recipient);
                  }
                  uint256 weiBalance = address(this).balance;
                  // Transfer remaining balance of tokenTo to sender
                  if (address(tokenTo) != Constants.ETH) {
                      uint256 balance = tokenTo.balanceOf(address(this));
                      require(balance >= amountTo, "INSUFFICIENT_AMOUNT");
                      _transfer(tokenTo, balance, recipient);
                  } else {
                      // If tokenTo == ETH, then check that the remaining ETH balance >= amountTo
                      require(weiBalance >= amountTo, "INSUFFICIENT_AMOUNT");
                  }
                  // If there are unused fees or if tokenTo is ETH, transfer to sender
                  if (weiBalance > 0) {
                      recipient.sendValue(weiBalance);
                  }
              }
              /**
               * @dev Transfers token to sender if amount > 0
               * @param token IERC20 token to transfer to sender
               * @param amount Amount of token to transfer
               * @param recipient Address that will receive the tokens
               */
              function _transfer(
                  IERC20 token,
                  uint256 amount,
                  address recipient
              ) internal {
                  if (amount > 0) {
                      token.safeTransfer(recipient, amount);
                  }
              }
              // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/SafeERC20.sol
              /**
               * @dev Approves max amount of token to the spender if the allowance is lower than amount
               * @param token The ERC20 token to approve
               * @param spender Address to which funds will be approved
               * @param amount Amount used to compare current allowance
               */
              function _approveSpender(
                  IERC20 token,
                  address spender,
                  uint256 amount
              ) internal {
                  // If allowance is not enough, approve max possible amount
                  uint256 allowance = token.allowance(address(this), spender);
                  if (allowance < amount) {
                      bytes memory returndata = address(token).functionCall(
                          abi.encodeWithSelector(
                              token.approve.selector,
                              spender,
                              type(uint256).max
                          )
                      );
                      if (returndata.length > 0) {
                          // Return data is optional
                          require(abi.decode(returndata, (bool)), "APPROVAL_FAILED");
                      }
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.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);
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.0;
          import "./IERC20.sol";
          import "../../math/SafeMath.sol";
          import "../../utils/Address.sol";
          /**
           * @title SafeERC20
           * @dev Wrappers around ERC20 operations that throw on failure (when the token
           * contract returns false). Tokens that return no value (and instead revert or
           * throw on failure) are also supported, non-reverting calls are assumed to be
           * successful.
           * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
           * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
           */
          library SafeERC20 {
              using SafeMath for uint256;
              using Address for address;
              function safeTransfer(IERC20 token, address to, uint256 value) internal {
                  _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
              }
              function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                  _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
              }
              /**
               * @dev Deprecated. This function has issues similar to the ones found in
               * {IERC20-approve}, and its usage is discouraged.
               *
               * Whenever possible, use {safeIncreaseAllowance} and
               * {safeDecreaseAllowance} instead.
               */
              function safeApprove(IERC20 token, address spender, uint256 value) internal {
                  // safeApprove should only be called when setting an initial allowance,
                  // or when resetting it to zero. To increase and decrease it, use
                  // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                  // solhint-disable-next-line max-line-length
                  require((value == 0) || (token.allowance(address(this), spender) == 0),
                      "SafeERC20: approve from non-zero to non-zero allowance"
                  );
                  _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
              }
              function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                  uint256 newAllowance = token.allowance(address(this), spender).add(value);
                  _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
              }
              function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                  uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
                  _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
              }
              /**
               * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
               * on the return value: the return value is optional (but if data is returned, it must not be false).
               * @param token The token targeted by the call.
               * @param data The call data (encoded using abi.encode or one of its variants).
               */
              function _callOptionalReturn(IERC20 token, bytes memory data) private {
                  // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                  // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
                  // the target address contains contract code and also asserts for success in the low-level call.
                  bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                  if (returndata.length > 0) { // Return data is optional
                      // solhint-disable-next-line max-line-length
                      require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.0;
          /**
           * @dev Wrappers over Solidity's arithmetic operations with added overflow
           * checks.
           *
           * Arithmetic operations in Solidity wrap on overflow. This can easily result
           * in bugs, because programmers usually assume that an overflow raises an
           * error, which is the standard behavior in high level programming languages.
           * `SafeMath` restores this intuition by reverting the transaction when an
           * operation overflows.
           *
           * Using this library instead of the unchecked operations eliminates an entire
           * class of bugs, so it's recommended to use it always.
           */
          library SafeMath {
              /**
               * @dev Returns the addition of two unsigned integers, reverting on
               * overflow.
               *
               * Counterpart to Solidity's `+` operator.
               *
               * Requirements:
               *
               * - Addition cannot overflow.
               */
              function add(uint256 a, uint256 b) internal pure returns (uint256) {
                  uint256 c = a + b;
                  require(c >= a, "SafeMath: addition overflow");
                  return c;
              }
              /**
               * @dev Returns the subtraction of two unsigned integers, reverting on
               * overflow (when the result is negative).
               *
               * Counterpart to Solidity's `-` operator.
               *
               * Requirements:
               *
               * - Subtraction cannot overflow.
               */
              function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                  return sub(a, b, "SafeMath: subtraction overflow");
              }
              /**
               * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
               * overflow (when the result is negative).
               *
               * Counterpart to Solidity's `-` operator.
               *
               * Requirements:
               *
               * - Subtraction cannot overflow.
               */
              function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                  require(b <= a, errorMessage);
                  uint256 c = a - b;
                  return c;
              }
              /**
               * @dev Returns the multiplication of two unsigned integers, reverting on
               * overflow.
               *
               * Counterpart to Solidity's `*` operator.
               *
               * Requirements:
               *
               * - Multiplication cannot overflow.
               */
              function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                  // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                  // benefit is lost if 'b' is also tested.
                  // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                  if (a == 0) {
                      return 0;
                  }
                  uint256 c = a * b;
                  require(c / a == b, "SafeMath: multiplication overflow");
                  return c;
              }
              /**
               * @dev Returns the integer division of two unsigned integers. Reverts on
               * division by zero. The result is rounded towards zero.
               *
               * Counterpart to Solidity's `/` operator. Note: this function uses a
               * `revert` opcode (which leaves remaining gas untouched) while Solidity
               * uses an invalid opcode to revert (consuming all remaining gas).
               *
               * Requirements:
               *
               * - The divisor cannot be zero.
               */
              function div(uint256 a, uint256 b) internal pure returns (uint256) {
                  return div(a, b, "SafeMath: division by zero");
              }
              /**
               * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
               * division by zero. The result is rounded towards zero.
               *
               * Counterpart to Solidity's `/` operator. Note: this function uses a
               * `revert` opcode (which leaves remaining gas untouched) while Solidity
               * uses an invalid opcode to revert (consuming all remaining gas).
               *
               * Requirements:
               *
               * - The divisor cannot be zero.
               */
              function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                  require(b > 0, errorMessage);
                  uint256 c = a / b;
                  // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                  return c;
              }
              /**
               * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
               * Reverts when dividing by zero.
               *
               * Counterpart to Solidity's `%` operator. This function uses a `revert`
               * opcode (which leaves remaining gas untouched) while Solidity uses an
               * invalid opcode to revert (consuming all remaining gas).
               *
               * Requirements:
               *
               * - The divisor cannot be zero.
               */
              function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                  return mod(a, b, "SafeMath: modulo by zero");
              }
              /**
               * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
               * Reverts with custom message when dividing by zero.
               *
               * Counterpart to Solidity's `%` operator. This function uses a `revert`
               * opcode (which leaves remaining gas untouched) while Solidity uses an
               * invalid opcode to revert (consuming all remaining gas).
               *
               * Requirements:
               *
               * - The divisor cannot be zero.
               */
              function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                  require(b != 0, errorMessage);
                  return a % b;
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.2;
          /**
           * @dev Collection of functions related to the address type
           */
          library Address {
              /**
               * @dev Returns true if `account` is a contract.
               *
               * [IMPORTANT]
               * ====
               * It is unsafe to assume that an address for which this function returns
               * false is an externally-owned account (EOA) and not a contract.
               *
               * Among others, `isContract` will return false for the following
               * types of addresses:
               *
               *  - an externally-owned account
               *  - a contract in construction
               *  - an address where a contract will be created
               *  - an address where a contract lived, but was destroyed
               * ====
               */
              function isContract(address account) internal view returns (bool) {
                  // This method relies in extcodesize, which returns 0 for contracts in
                  // construction, since the code is only stored at the end of the
                  // constructor execution.
                  uint256 size;
                  // solhint-disable-next-line no-inline-assembly
                  assembly { size := extcodesize(account) }
                  return size > 0;
              }
              /**
               * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
               * `recipient`, forwarding all available gas and reverting on errors.
               *
               * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
               * of certain opcodes, possibly making contracts go over the 2300 gas limit
               * imposed by `transfer`, making them unable to receive funds via
               * `transfer`. {sendValue} removes this limitation.
               *
               * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
               *
               * IMPORTANT: because control is transferred to `recipient`, care must be
               * taken to not create reentrancy vulnerabilities. Consider using
               * {ReentrancyGuard} or the
               * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
               */
              function sendValue(address payable recipient, uint256 amount) internal {
                  require(address(this).balance >= amount, "Address: insufficient balance");
                  // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                  (bool success, ) = recipient.call{ value: amount }("");
                  require(success, "Address: unable to send value, recipient may have reverted");
              }
              /**
               * @dev Performs a Solidity function call using a low level `call`. A
               * plain`call` is an unsafe replacement for a function call: use this
               * function instead.
               *
               * If `target` reverts with a revert reason, it is bubbled up by this
               * function (like regular Solidity function calls).
               *
               * Returns the raw returned data. To convert to the expected return value,
               * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
               *
               * Requirements:
               *
               * - `target` must be a contract.
               * - calling `target` with `data` must not revert.
               *
               * _Available since v3.1._
               */
              function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                return functionCall(target, data, "Address: low-level call failed");
              }
              /**
               * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
               * `errorMessage` as a fallback revert reason when `target` reverts.
               *
               * _Available since v3.1._
               */
              function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                  return _functionCallWithValue(target, data, 0, errorMessage);
              }
              /**
               * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
               * but also transferring `value` wei to `target`.
               *
               * Requirements:
               *
               * - the calling contract must have an ETH balance of at least `value`.
               * - the called Solidity function must be `payable`.
               *
               * _Available since v3.1._
               */
              function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                  return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
              }
              /**
               * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
               * with `errorMessage` as a fallback revert reason when `target` reverts.
               *
               * _Available since v3.1._
               */
              function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
                  require(address(this).balance >= value, "Address: insufficient balance for call");
                  return _functionCallWithValue(target, data, value, errorMessage);
              }
              function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
                  require(isContract(target), "Address: call to non-contract");
                  // solhint-disable-next-line avoid-low-level-calls
                  (bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
                  if (success) {
                      return returndata;
                  } else {
                      // Look for revert reason and bubble it up if present
                      if (returndata.length > 0) {
                          // The easiest way to bubble the revert reason is using memory via assembly
                          // solhint-disable-next-line no-inline-assembly
                          assembly {
                              let returndata_size := mload(returndata)
                              revert(add(32, returndata), returndata_size)
                          }
                      } else {
                          revert(errorMessage);
                      }
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.0;
          library Constants {
              address internal constant ETH = 0x0000000000000000000000000000000000000000;
          }
          pragma solidity ^0.6.0;
          import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
          import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
          import "@openzeppelin/contracts/math/SafeMath.sol";
          import "@openzeppelin/contracts/utils/Address.sol";
          import "../Constants.sol";
          contract FeeCommonAdapter {
              using SafeERC20 for IERC20;
              using Address for address;
              using Address for address payable;
              using SafeMath for uint256;
              // solhint-disable-next-line var-name-mixedcase
              address payable public immutable FEE_WALLET;
              constructor(address payable feeWallet) public {
                  FEE_WALLET = feeWallet;
              }
              /**
               * @dev Performs a swap
               * @param recipient The original msg.sender performing the swap
               * @param aggregator Address of the aggregator's contract
               * @param spender Address to which tokens will be approved
               * @param method Selector of the function to be called in the aggregator's contract
               * @param tokenFrom Token to be swapped
               * @param tokenTo Token to be received
               * @param amountFrom Amount of tokenFrom to swap
               * @param amountTo Minimum amount of tokenTo to receive
               * @param data Data used for the call made to the aggregator's contract
               * @param fee Amount of tokenFrom sent to the fee wallet
               */
              function swap(
                  address payable recipient,
                  address aggregator,
                  address spender,
                  bytes4 method,
                  IERC20 tokenFrom,
                  IERC20 tokenTo,
                  uint256 amountFrom,
                  uint256 amountTo,
                  bytes calldata data,
                  uint256 fee
              ) external payable {
                  require(tokenFrom != tokenTo, "TOKEN_PAIR_INVALID");
                  if (address(tokenFrom) == Constants.ETH) {
                      FEE_WALLET.sendValue(fee);
                  } else {
                      _transfer(tokenFrom, fee, FEE_WALLET);
                      _approveSpender(tokenFrom, spender, amountFrom);
                  }
                  // We always forward msg.value as it may be necessary to pay fees
                  aggregator.functionCallWithValue(
                      abi.encodePacked(method, data),
                      address(this).balance
                  );
                  // Transfer remaining balance of tokenFrom to sender
                  if (address(tokenFrom) != Constants.ETH) {
                      _transfer(tokenFrom, tokenFrom.balanceOf(address(this)), recipient);
                  }
                  uint256 weiBalance = address(this).balance;
                  // Transfer remaining balance of tokenTo to sender
                  if (address(tokenTo) != Constants.ETH) {
                      uint256 balance = tokenTo.balanceOf(address(this));
                      require(balance >= amountTo, "INSUFFICIENT_AMOUNT");
                      _transfer(tokenTo, balance, recipient);
                  } else {
                      // If tokenTo == ETH, then check that the remaining ETH balance >= amountTo
                      require(weiBalance >= amountTo, "INSUFFICIENT_AMOUNT");
                  }
                  // If there are unused fees or if tokenTo is ETH, transfer to sender
                  if (weiBalance > 0) {
                      recipient.sendValue(weiBalance);
                  }
              }
              /**
               * @dev Transfers token to sender if amount > 0
               * @param token IERC20 token to transfer to sender
               * @param amount Amount of token to transfer
               * @param recipient Address that will receive the tokens
               */
              function _transfer(
                  IERC20 token,
                  uint256 amount,
                  address recipient
              ) internal {
                  if (amount > 0) {
                      token.safeTransfer(recipient, amount);
                  }
              }
              // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/SafeERC20.sol
              /**
               * @dev Approves max amount of token to the spender if the allowance is lower than amount
               * @param token The ERC20 token to approve
               * @param spender Address to which funds will be approved
               * @param amount Amount used to compare current allowance
               */
              function _approveSpender(
                  IERC20 token,
                  address spender,
                  uint256 amount
              ) internal {
                  // If allowance is not enough, approve max possible amount
                  uint256 allowance = token.allowance(address(this), spender);
                  if (allowance < amount) {
                      bytes memory returndata = address(token).functionCall(
                          abi.encodeWithSelector(
                              token.approve.selector,
                              spender,
                              type(uint256).max
                          )
                      );
                      if (returndata.length > 0) {
                          // Return data is optional
                          require(abi.decode(returndata, (bool)), "APPROVAL_FAILED");
                      }
                  }
              }
          }
          pragma solidity ^0.6.0;
          import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
          import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
          import "@openzeppelin/contracts/math/SafeMath.sol";
          import "@openzeppelin/contracts/utils/Address.sol";
          import "../Constants.sol";
          import "../IWETH.sol";
          contract FeeWethAdapter {
              using SafeERC20 for IERC20;
              using Address for address;
              using Address for address payable;
              using SafeMath for uint256;
              IWETH public immutable weth;
              // solhint-disable-next-line var-name-mixedcase
              address payable public immutable FEE_WALLET;
              constructor(IWETH _weth, address payable feeWallet) public {
                  weth = _weth;
                  FEE_WALLET = feeWallet;
              }
              /**
               * @dev Performs a swap
               * @param recipient The original msg.sender performing the swap
               * @param aggregator Address of the aggregator's contract
               * @param spender Address to which tokens will be approved
               * @param method Selector of the function to be called in the aggregator's contract
               * @param tokenFrom Token to be swapped
               * @param tokenTo Token to be received
               * @param amountFrom Amount of tokenFrom to swap
               * @param amountTo Minimum amount of tokenTo to receive
               * @param data Data used for the call made to the aggregator's contract
               * @param fee Amount of tokenFrom sent to the fee wallet
               */
              function swap(
                  address payable recipient,
                  address aggregator,
                  address spender,
                  bytes4 method,
                  IERC20 tokenFrom,
                  IERC20 tokenTo,
                  uint256 amountFrom,
                  uint256 amountTo,
                  bytes calldata data,
                  uint256 fee
              ) external payable {
                  require(tokenFrom != tokenTo, "TOKEN_PAIR_INVALID");
                  if (address(tokenFrom) == Constants.ETH) {
                      FEE_WALLET.sendValue(fee);
                      // If tokenFrom is ETH, msg.value = fee + amountFrom (total fee could be 0)
                      // Can't deal with ETH, convert to WETH, the remaining balance will be the fee
                      weth.deposit{value: amountFrom}();
                      _approveSpender(weth, spender, amountFrom);
                  } else {
                      _transfer(tokenFrom, fee, FEE_WALLET);
                      // Otherwise capture tokens from sender
                      _approveSpender(tokenFrom, spender, amountFrom);
                  }
                  // Perform the swap
                  aggregator.functionCallWithValue(
                      abi.encodePacked(method, data),
                      address(this).balance
                  );
                  // Transfer remaining balance of tokenFrom to sender
                  if (address(tokenFrom) != Constants.ETH) {
                      _transfer(tokenFrom, tokenFrom.balanceOf(address(this)), recipient);
                  } else {
                      // If using ETH, just unwrap any remaining WETH
                      // At the end of this function all ETH will be transferred to the sender
                      _unwrapWETH();
                  }
                  uint256 weiBalance = address(this).balance;
                  // Transfer remaining balance of tokenTo to sender
                  if (address(tokenTo) != Constants.ETH) {
                      uint256 balance = tokenTo.balanceOf(address(this));
                      require(balance >= amountTo, "INSUFFICIENT_AMOUNT");
                      _transfer(tokenTo, balance, recipient);
                  } else {
                      // If tokenTo == ETH, unwrap received WETH and add it to the wei balance,
                      // then check that the remaining ETH balance >= amountTo
                      // It is safe to not use safeMath as no one can have enough Ether to overflow
                      weiBalance += _unwrapWETH();
                      require(weiBalance >= amountTo, "INSUFFICIENT_AMOUNT");
                  }
                  // If there are unused fees or if tokenTo is ETH, transfer to sender
                  if (weiBalance > 0) {
                      recipient.sendValue(weiBalance);
                  }
              }
              /**
               * @dev Unwraps all available WETH into ETH
               */
              function _unwrapWETH() internal returns (uint256) {
                  uint256 balance = weth.balanceOf(address(this));
                  weth.withdraw(balance);
                  return balance;
              }
              /**
               * @dev Transfers token to sender if amount > 0
               * @param token IERC20 token to transfer to sender
               * @param amount Amount of token to transfer
               * @param recipient Address that will receive the tokens
               */
              function _transfer(
                  IERC20 token,
                  uint256 amount,
                  address recipient
              ) internal {
                  if (amount > 0) {
                      token.safeTransfer(recipient, amount);
                  }
              }
              // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/SafeERC20.sol
              /**
               * @dev Approves max amount of token to the spender if the allowance is lower than amount
               * @param token The ERC20 token to approve
               * @param spender Address to which funds will be approved
               * @param amount Amount used to compare current allowance
               */
              function _approveSpender(
                  IERC20 token,
                  address spender,
                  uint256 amount
              ) internal {
                  // If allowance is not enough, approve max possible amount
                  uint256 allowance = token.allowance(address(this), spender);
                  if (allowance < amount) {
                      bytes memory returndata = address(token).functionCall(
                          abi.encodeWithSelector(
                              token.approve.selector,
                              spender,
                              type(uint256).max
                          )
                      );
                      if (returndata.length > 0) {
                          // Return data is optional
                          require(abi.decode(returndata, (bool)), "APPROVAL_FAILED");
                      }
                  }
              }
          }
          pragma solidity ^0.6.0;
          import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
          interface IWETH is IERC20 {
              function deposit() external payable;
              function withdraw(uint256) external;
          }
          pragma solidity ^0.6.0;
          import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
          import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
          import "@openzeppelin/contracts/math/SafeMath.sol";
          import "@openzeppelin/contracts/utils/Address.sol";
          import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
          import "../Constants.sol";
          contract UniswapAdapter {
              using SafeERC20 for IERC20;
              using Address for address;
              using Address for address payable;
              using SafeMath for uint256;
              // solhint-disable-next-line var-name-mixedcase
              IUniswapV2Router02 public immutable UNISWAP;
              // solhint-disable-next-line var-name-mixedcase
              address payable public immutable FEE_WALLET;
              constructor(address payable feeWallet, IUniswapV2Router02 uniswap) public {
                  FEE_WALLET = feeWallet;
                  UNISWAP = uniswap;
              }
              /**
               * @dev Performs a swap
               * @param recipient The original msg.sender performing the swap
               * @param tokenFrom Token to be swapped
               * @param tokenTo Token to be received
               * @param amountFrom Amount of tokenFrom to swap
               * @param amountTo Minimum amount of tokenTo to receive
               * @param path Used by Uniswap
               * @param deadline Timestamp at which the swap becomes invalid. Used by Uniswap
               * @param feeOnTransfer Use `supportingFeeOnTransfer` Uniswap methods
               * @param fee Amount of tokenFrom sent to the fee wallet
               */
              function swap(
                  address payable recipient,
                  IERC20 tokenFrom,
                  IERC20 tokenTo,
                  uint256 amountFrom,
                  uint256 amountTo,
                  address[] calldata path,
                  uint256 deadline,
                  bool feeOnTransfer,
                  uint256 fee
              ) external payable {
                  require(tokenFrom != tokenTo, "TOKEN_PAIR_INVALID");
                  if (address(tokenFrom) == Constants.ETH) {
                      FEE_WALLET.sendValue(fee);
                  } else {
                      _transfer(tokenFrom, fee, FEE_WALLET);
                  }
                  if (address(tokenFrom) == Constants.ETH) {
                      if (feeOnTransfer) {
                          UNISWAP.swapExactETHForTokensSupportingFeeOnTransferTokens{
                              value: address(this).balance
                          }(amountTo, path, address(this), deadline);
                      } else {
                          UNISWAP.swapExactETHForTokens{value: address(this).balance}(
                              amountTo,
                              path,
                              address(this),
                              deadline
                          );
                      }
                  } else {
                      _approveSpender(tokenFrom, address(UNISWAP), amountFrom);
                      if (address(tokenTo) == Constants.ETH) {
                          if (feeOnTransfer) {
                              UNISWAP.swapExactTokensForETHSupportingFeeOnTransferTokens(
                                  amountFrom,
                                  amountTo,
                                  path,
                                  address(this),
                                  deadline
                              );
                          } else {
                              UNISWAP.swapExactTokensForETH(
                                  amountFrom,
                                  amountTo,
                                  path,
                                  address(this),
                                  deadline
                              );
                          }
                      } else {
                          if (feeOnTransfer) {
                              UNISWAP
                                  .swapExactTokensForTokensSupportingFeeOnTransferTokens(
                                  amountFrom,
                                  amountTo,
                                  path,
                                  address(this),
                                  deadline
                              );
                          } else {
                              UNISWAP.swapExactTokensForTokens(
                                  amountFrom,
                                  amountTo,
                                  path,
                                  address(this),
                                  deadline
                              );
                          }
                      }
                  }
                  // Transfer remaining balance of tokenFrom to sender
                  if (address(tokenFrom) != Constants.ETH) {
                      _transfer(tokenFrom, tokenFrom.balanceOf(address(this)), recipient);
                  }
                  uint256 weiBalance = address(this).balance;
                  // Transfer remaining balance of tokenTo to sender
                  if (address(tokenTo) != Constants.ETH) {
                      uint256 balance = tokenTo.balanceOf(address(this));
                      require(balance >= amountTo, "INSUFFICIENT_AMOUNT");
                      _transfer(tokenTo, balance, recipient);
                  } else {
                      // If tokenTo == ETH, then check that the remaining ETH balance >= amountTo
                      require(weiBalance >= amountTo, "INSUFFICIENT_AMOUNT");
                  }
                  // If there are unused fees or if tokenTo is ETH, transfer to sender
                  if (weiBalance > 0) {
                      recipient.sendValue(weiBalance);
                  }
              }
              /**
               * @dev Transfers token to sender if amount > 0
               * @param token IERC20 token to transfer to sender
               * @param amount Amount of token to transfer
               * @param recipient Address that will receive the tokens
               */
              function _transfer(
                  IERC20 token,
                  uint256 amount,
                  address recipient
              ) internal {
                  if (amount > 0) {
                      token.safeTransfer(recipient, amount);
                  }
              }
              // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/SafeERC20.sol
              /**
               * @dev Approves max amount of token to the spender if the allowance is lower than amount
               * @param token The ERC20 token to approve
               * @param spender Address to which funds will be approved
               * @param amount Amount used to compare current allowance
               */
              function _approveSpender(
                  IERC20 token,
                  address spender,
                  uint256 amount
              ) internal {
                  // If allowance is not enough, approve max possible amount
                  uint256 allowance = token.allowance(address(this), spender);
                  if (allowance < amount) {
                      bytes memory returndata = address(token).functionCall(
                          abi.encodeWithSelector(
                              token.approve.selector,
                              spender,
                              type(uint256).max
                          )
                      );
                      if (returndata.length > 0) {
                          // Return data is optional
                          require(abi.decode(returndata, (bool)), "APPROVAL_FAILED");
                      }
                  }
              }
          }
          pragma solidity >=0.6.2;
          import './IUniswapV2Router01.sol';
          interface IUniswapV2Router02 is IUniswapV2Router01 {
              function removeLiquidityETHSupportingFeeOnTransferTokens(
                  address token,
                  uint liquidity,
                  uint amountTokenMin,
                  uint amountETHMin,
                  address to,
                  uint deadline
              ) external returns (uint amountETH);
              function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
                  address token,
                  uint liquidity,
                  uint amountTokenMin,
                  uint amountETHMin,
                  address to,
                  uint deadline,
                  bool approveMax, uint8 v, bytes32 r, bytes32 s
              ) external returns (uint amountETH);
              function swapExactTokensForTokensSupportingFeeOnTransferTokens(
                  uint amountIn,
                  uint amountOutMin,
                  address[] calldata path,
                  address to,
                  uint deadline
              ) external;
              function swapExactETHForTokensSupportingFeeOnTransferTokens(
                  uint amountOutMin,
                  address[] calldata path,
                  address to,
                  uint deadline
              ) external payable;
              function swapExactTokensForETHSupportingFeeOnTransferTokens(
                  uint amountIn,
                  uint amountOutMin,
                  address[] calldata path,
                  address to,
                  uint deadline
              ) external;
          }
          pragma solidity >=0.6.2;
          interface IUniswapV2Router01 {
              function factory() external pure returns (address);
              function WETH() external pure returns (address);
              function addLiquidity(
                  address tokenA,
                  address tokenB,
                  uint amountADesired,
                  uint amountBDesired,
                  uint amountAMin,
                  uint amountBMin,
                  address to,
                  uint deadline
              ) external returns (uint amountA, uint amountB, uint liquidity);
              function addLiquidityETH(
                  address token,
                  uint amountTokenDesired,
                  uint amountTokenMin,
                  uint amountETHMin,
                  address to,
                  uint deadline
              ) external payable returns (uint amountToken, uint amountETH, uint liquidity);
              function removeLiquidity(
                  address tokenA,
                  address tokenB,
                  uint liquidity,
                  uint amountAMin,
                  uint amountBMin,
                  address to,
                  uint deadline
              ) external returns (uint amountA, uint amountB);
              function removeLiquidityETH(
                  address token,
                  uint liquidity,
                  uint amountTokenMin,
                  uint amountETHMin,
                  address to,
                  uint deadline
              ) external returns (uint amountToken, uint amountETH);
              function removeLiquidityWithPermit(
                  address tokenA,
                  address tokenB,
                  uint liquidity,
                  uint amountAMin,
                  uint amountBMin,
                  address to,
                  uint deadline,
                  bool approveMax, uint8 v, bytes32 r, bytes32 s
              ) external returns (uint amountA, uint amountB);
              function removeLiquidityETHWithPermit(
                  address token,
                  uint liquidity,
                  uint amountTokenMin,
                  uint amountETHMin,
                  address to,
                  uint deadline,
                  bool approveMax, uint8 v, bytes32 r, bytes32 s
              ) external returns (uint amountToken, uint amountETH);
              function swapExactTokensForTokens(
                  uint amountIn,
                  uint amountOutMin,
                  address[] calldata path,
                  address to,
                  uint deadline
              ) external returns (uint[] memory amounts);
              function swapTokensForExactTokens(
                  uint amountOut,
                  uint amountInMax,
                  address[] calldata path,
                  address to,
                  uint deadline
              ) external returns (uint[] memory amounts);
              function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
                  external
                  payable
                  returns (uint[] memory amounts);
              function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
                  external
                  returns (uint[] memory amounts);
              function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
                  external
                  returns (uint[] memory amounts);
              function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
                  external
                  payable
                  returns (uint[] memory amounts);
              function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
              function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
              function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
              function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
              function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
          }
          pragma solidity ^0.6.0;
          import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
          import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
          import "@openzeppelin/contracts/utils/Address.sol";
          import "../Constants.sol";
          import "../IWETH.sol";
          contract WethAdapter {
              using SafeERC20 for IERC20;
              using Address for address;
              using Address for address payable;
              IWETH public immutable weth;
              constructor(IWETH _weth) public {
                  weth = _weth;
              }
              /**
               * @dev Performs a swap
               * @param recipient The original msg.sender performing the swap
               * @param aggregator Address of the aggregator's contract
               * @param spender Address to which tokens will be approved
               * @param method Selector of the function to be called in the aggregator's contract
               * @param tokenFrom Token to be swapped
               * @param tokenTo Token to be received
               * @param amountFrom Amount of tokenFrom to swap
               * @param amountTo Minimum amount of tokenTo to receive
               * @param data Data used for the call made to the aggregator's contract
               */
              function swap(
                  address payable recipient,
                  address aggregator,
                  address spender,
                  bytes4 method,
                  IERC20 tokenFrom,
                  IERC20 tokenTo,
                  uint256 amountFrom,
                  uint256 amountTo,
                  bytes calldata data
              ) external payable {
                  require(tokenFrom != tokenTo, "TOKEN_PAIR_INVALID");
                  if (address(tokenFrom) == Constants.ETH) {
                      // If tokenFrom is ETH, msg.value = fee + amountFrom (total fee could be 0)
                      // Can't deal with ETH, convert to WETH, the remaining balance will be the fee
                      weth.deposit{value: amountFrom}();
                      _approveSpender(weth, spender, amountFrom);
                  } else {
                      // Otherwise capture tokens from sender
                      _approveSpender(tokenFrom, spender, amountFrom);
                  }
                  // Perform the swap
                  aggregator.functionCallWithValue(
                      abi.encodePacked(method, data),
                      address(this).balance
                  );
                  // Transfer remaining balance of tokenFrom to sender
                  if (address(tokenFrom) != Constants.ETH) {
                      _transfer(tokenFrom, tokenFrom.balanceOf(address(this)), recipient);
                  } else {
                      // If using ETH, just unwrap any remaining WETH
                      // At the end of this function all ETH will be transferred to the sender
                      _unwrapWETH();
                  }
                  uint256 weiBalance = address(this).balance;
                  // Transfer remaining balance of tokenTo to sender
                  if (address(tokenTo) != Constants.ETH) {
                      uint256 balance = tokenTo.balanceOf(address(this));
                      require(balance >= amountTo, "INSUFFICIENT_AMOUNT");
                      _transfer(tokenTo, balance, recipient);
                  } else {
                      // If tokenTo == ETH, unwrap received WETH and add it to the wei balance,
                      // then check that the remaining ETH balance >= amountTo
                      // It is safe to not use safeMath as no one can have enough Ether to overflow
                      weiBalance += _unwrapWETH();
                      require(weiBalance >= amountTo, "INSUFFICIENT_AMOUNT");
                  }
                  // If there are unused fees or if tokenTo is ETH, transfer to sender
                  if (weiBalance > 0) {
                      recipient.sendValue(weiBalance);
                  }
              }
              /**
               * @dev Unwraps all available WETH into ETH
               */
              function _unwrapWETH() internal returns (uint256) {
                  uint256 balance = weth.balanceOf(address(this));
                  weth.withdraw(balance);
                  return balance;
              }
              /**
               * @dev Transfers token to sender if amount > 0
               * @param token IERC20 token to transfer to sender
               * @param amount Amount of token to transfer
               * @param recipient Address that will receive the tokens
               */
              function _transfer(
                  IERC20 token,
                  uint256 amount,
                  address recipient
              ) internal {
                  if (amount > 0) {
                      token.safeTransfer(recipient, amount);
                  }
              }
              // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/SafeERC20.sol
              /**
               * @dev Approves max amount of token to the spender if the allowance is lower than amount
               * @param token The ERC20 token to approve
               * @param spender Address to which funds will be approved
               * @param amount Amount used to compare current allowance
               */
              function _approveSpender(
                  IERC20 token,
                  address spender,
                  uint256 amount
              ) internal {
                  // If allowance is not enough, approve max possible amount
                  uint256 allowance = token.allowance(address(this), spender);
                  if (allowance < amount) {
                      bytes memory returndata = address(token).functionCall(
                          abi.encodeWithSelector(
                              token.approve.selector,
                              spender,
                              type(uint256).max
                          )
                      );
                      if (returndata.length > 0) {
                          // Return data is optional
                          require(abi.decode(returndata, (bool)), "APPROVAL_FAILED");
                      }
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.0;
          import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
          interface ICHI is IERC20 {
              function freeUpTo(uint256 value) external returns (uint256);
              function freeFromUpTo(
                  address from,
                  uint256 value
              ) external returns (uint256);
              function mint(uint256 value) external;
          }
            
          // SPDX-License-Identifier: UNLICENSED
          pragma solidity ^0.6.0;
          // We import the contract so truffle compiles it, and we have the ABI
          // available when working from truffle console.
          import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; //helpers// SPDX-License-Identifier: MIT
          pragma solidity ^0.6.0;
          import "../../GSN/Context.sol";
          import "./IERC20.sol";
          import "../../math/SafeMath.sol";
          import "../../utils/Address.sol";
          /**
           * @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 guidelines: functions revert instead
           * of 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 {
              using SafeMath for uint256;
              using Address for address;
              mapping (address => uint256) private _balances;
              mapping (address => mapping (address => uint256)) private _allowances;
              uint256 private _totalSupply;
              string private _name;
              string private _symbol;
              uint8 private _decimals;
              /**
               * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
               * a default value of 18.
               *
               * To select a different value for {decimals}, use {_setupDecimals}.
               *
               * All three of these values are immutable: they can only be set once during
               * construction.
               */
              constructor (string memory name, string memory symbol) public {
                  _name = name;
                  _symbol = symbol;
                  _decimals = 18;
              }
              /**
               * @dev Returns the name of the token.
               */
              function name() public view returns (string memory) {
                  return _name;
              }
              /**
               * @dev Returns the symbol of the token, usually a shorter version of the
               * name.
               */
              function symbol() public view 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 {_setupDecimals} is
               * called.
               *
               * 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 returns (uint8) {
                  return _decimals;
              }
              /**
               * @dev See {IERC20-totalSupply}.
               */
              function totalSupply() public view override returns (uint256) {
                  return _totalSupply;
              }
              /**
               * @dev See {IERC20-balanceOf}.
               */
              function balanceOf(address account) public view 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);
                  _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
                  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].add(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) {
                  _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
                  return true;
              }
              /**
               * @dev Moves tokens `amount` from `sender` to `recipient`.
               *
               * This is 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);
                  _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
                  _balances[recipient] = _balances[recipient].add(amount);
                  emit Transfer(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
               *
               * - `to` 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 = _totalSupply.add(amount);
                  _balances[account] = _balances[account].add(amount);
                  emit Transfer(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);
                  _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
                  _totalSupply = _totalSupply.sub(amount);
                  emit Transfer(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 Sets {decimals} to a value other than the default one of 18.
               *
               * WARNING: This function should only be called from the constructor. Most
               * applications that interact with token contracts will not expect
               * {decimals} to ever change, and may work incorrectly if it does.
               */
              function _setupDecimals(uint8 decimals_) internal {
                  _decimals = decimals_;
              }
              /**
               * @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 to 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 { }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.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 GSN 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 payable) {
                  return msg.sender;
              }
              function _msgData() internal view virtual returns (bytes memory) {
                  this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                  return msg.data;
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.0;
          import "@openzeppelin/contracts/access/Ownable.sol";
          import "@openzeppelin/contracts/utils/Pausable.sol";
          import "@openzeppelin/contracts/utils/Address.sol";
          import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
          import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
          import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
          import "./ICHI.sol";
          import "./Spender.sol";
          /**
           * @title MetaSwap
           */
          contract MetaSwap is Ownable, Pausable, ReentrancyGuard {
              using SafeERC20 for IERC20;
              using Address for address;
              using Address for address payable;
              struct Adapter {
                  address addr; // adapter's address
                  bytes4 selector;
                  bytes data; // adapter's fixed data
              }
              ICHI public immutable chi;
              Spender public immutable spender;
              // Mapping of aggregatorId to aggregator
              mapping(string => Adapter) public adapters;
              mapping(string => bool) public adapterRemoved;
              event AdapterSet(
                  string indexed aggregatorId,
                  address indexed addr,
                  bytes4 selector,
                  bytes data
              );
              event AdapterRemoved(string indexed aggregatorId);
              event Swap(string indexed aggregatorId, address indexed sender);
              constructor(ICHI _chi) public {
                  chi = _chi;
                  spender = new Spender();
              }
              /**
               * @dev Sets the adapter for an aggregator. It can't be changed later.
               * @param aggregatorId Aggregator's identifier
               * @param addr Address of the contract that contains the logic for this aggregator
               * @param selector The function selector of the swap function in the adapter
               * @param data Fixed abi encoded data the will be passed in each delegatecall made to the adapter
               */
              function setAdapter(
                  string calldata aggregatorId,
                  address addr,
                  bytes4 selector,
                  bytes calldata data
              ) external onlyOwner {
                  require(addr.isContract(), "ADAPTER_IS_NOT_A_CONTRACT");
                  require(!adapterRemoved[aggregatorId], "ADAPTER_REMOVED");
                  Adapter storage adapter = adapters[aggregatorId];
                  require(adapter.addr == address(0), "ADAPTER_EXISTS");
                  adapter.addr = addr;
                  adapter.selector = selector;
                  adapter.data = data;
                  emit AdapterSet(aggregatorId, addr, selector, data);
              }
              /**
               * @dev Removes the adapter for an existing aggregator. This can't be undone.
               * @param aggregatorId Aggregator's identifier
               */
              function removeAdapter(string calldata aggregatorId) external onlyOwner {
                  require(
                      adapters[aggregatorId].addr != address(0),
                      "ADAPTER_DOES_NOT_EXIST"
                  );
                  delete adapters[aggregatorId];
                  adapterRemoved[aggregatorId] = true;
                  emit AdapterRemoved(aggregatorId);
              }
              /**
               * @dev Performs a swap
               * @param aggregatorId Identifier of the aggregator to be used for the swap
               * @param data Dynamic data which is concatenated with the fixed aggregator's
               * data in the delecatecall made to the adapter
               */
              function swap(
                  string calldata aggregatorId,
                  IERC20 tokenFrom,
                  uint256 amount,
                  bytes calldata data
              ) external payable whenNotPaused nonReentrant {
                  _swap(aggregatorId, tokenFrom, amount, data);
              }
              /**
               * @dev Performs a swap
               * @param aggregatorId Identifier of the aggregator to be used for the swap
               * @param data Dynamic data which is concatenated with the fixed aggregator's
               * data in the delecatecall made to the adapter
               */
              function swapUsingGasToken(
                  string calldata aggregatorId,
                  IERC20 tokenFrom,
                  uint256 amount,
                  bytes calldata data
              ) external payable whenNotPaused nonReentrant {
                  uint256 gas = gasleft();
                  _swap(aggregatorId, tokenFrom, amount, data);
                  uint256 gasSpent = 21000 + gas - gasleft() + 16 * msg.data.length;
                  chi.freeFromUpTo(msg.sender, (gasSpent + 14154) / 41947);
              }
              function pauseSwaps() external onlyOwner {
                  _pause();
              }
              function unpauseSwaps() external onlyOwner {
                  _unpause();
              }
              function _swap(
                  string calldata aggregatorId,
                  IERC20 tokenFrom,
                  uint256 amount,
                  bytes calldata data
              ) internal {
                  Adapter storage adapter = adapters[aggregatorId];
                  if (address(tokenFrom) != Constants.ETH) {
                      tokenFrom.safeTransferFrom(msg.sender, address(spender), amount);
                  }
                  spender.swap{value: msg.value}(
                      adapter.addr,
                      abi.encodePacked(
                          adapter.selector,
                          abi.encode(msg.sender),
                          adapter.data,
                          data
                      )
                  );
                  emit Swap(aggregatorId, msg.sender);
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.0;
          import "../GSN/Context.sol";
          /**
           * @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.
           */
          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 () internal {
                  address msgSender = _msgSender();
                  _owner = msgSender;
                  emit OwnershipTransferred(address(0), msgSender);
              }
              /**
               * @dev Returns the address of the current owner.
               */
              function owner() public view 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 {
                  emit OwnershipTransferred(_owner, address(0));
                  _owner = 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");
                  emit OwnershipTransferred(_owner, newOwner);
                  _owner = newOwner;
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.0;
          import "../GSN/Context.sol";
          /**
           * @dev Contract module which allows children to implement an emergency stop
           * mechanism that can be triggered by an authorized account.
           *
           * This module is used through inheritance. It will make available the
           * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
           * the functions of your contract. Note that they will not be pausable by
           * simply including this module, only once the modifiers are put in place.
           */
          contract Pausable is Context {
              /**
               * @dev Emitted when the pause is triggered by `account`.
               */
              event Paused(address account);
              /**
               * @dev Emitted when the pause is lifted by `account`.
               */
              event Unpaused(address account);
              bool private _paused;
              /**
               * @dev Initializes the contract in unpaused state.
               */
              constructor () internal {
                  _paused = false;
              }
              /**
               * @dev Returns true if the contract is paused, and false otherwise.
               */
              function paused() public view returns (bool) {
                  return _paused;
              }
              /**
               * @dev Modifier to make a function callable only when the contract is not paused.
               *
               * Requirements:
               *
               * - The contract must not be paused.
               */
              modifier whenNotPaused() {
                  require(!_paused, "Pausable: paused");
                  _;
              }
              /**
               * @dev Modifier to make a function callable only when the contract is paused.
               *
               * Requirements:
               *
               * - The contract must be paused.
               */
              modifier whenPaused() {
                  require(_paused, "Pausable: not paused");
                  _;
              }
              /**
               * @dev Triggers stopped state.
               *
               * Requirements:
               *
               * - The contract must not be paused.
               */
              function _pause() internal virtual whenNotPaused {
                  _paused = true;
                  emit Paused(_msgSender());
              }
              /**
               * @dev Returns to normal state.
               *
               * Requirements:
               *
               * - The contract must be paused.
               */
              function _unpause() internal virtual whenPaused {
                  _paused = false;
                  emit Unpaused(_msgSender());
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.0;
          /**
           * @dev Contract module that helps prevent reentrant calls to a function.
           *
           * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
           * available, which can be applied to functions to make sure there are no nested
           * (reentrant) calls to them.
           *
           * Note that because there is a single `nonReentrant` guard, functions marked as
           * `nonReentrant` may not call one another. This can be worked around by making
           * those functions `private`, and then adding `external` `nonReentrant` entry
           * points to them.
           *
           * TIP: If you would like to learn more about reentrancy and alternative ways
           * to protect against it, check out our blog post
           * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
           */
          contract ReentrancyGuard {
              // Booleans are more expensive than uint256 or any type that takes up a full
              // word because each write operation emits an extra SLOAD to first read the
              // slot's contents, replace the bits taken up by the boolean, and then write
              // back. This is the compiler's defense against contract upgrades and
              // pointer aliasing, and it cannot be disabled.
              // The values being non-zero value makes deployment a bit more expensive,
              // but in exchange the refund on every call to nonReentrant will be lower in
              // amount. Since refunds are capped to a percentage of the total
              // transaction's gas, it is best to keep them low in cases like this one, to
              // increase the likelihood of the full refund coming into effect.
              uint256 private constant _NOT_ENTERED = 1;
              uint256 private constant _ENTERED = 2;
              uint256 private _status;
              constructor () internal {
                  _status = _NOT_ENTERED;
              }
              /**
               * @dev Prevents a contract from calling itself, directly or indirectly.
               * Calling a `nonReentrant` function from another `nonReentrant`
               * function is not supported. It is possible to prevent this from happening
               * by making the `nonReentrant` function external, and make it call a
               * `private` function that does the actual work.
               */
              modifier nonReentrant() {
                  // On the first call to nonReentrant, _notEntered will be true
                  require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
                  // Any calls to nonReentrant after this point will fail
                  _status = _ENTERED;
                  _;
                  // By storing the original value once again, a refund is triggered (see
                  // https://eips.ethereum.org/EIPS/eip-2200)
                  _status = _NOT_ENTERED;
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.0;
          import "./Constants.sol";
          contract Spender {
              address public immutable metaswap;
              constructor() public {
                  metaswap = msg.sender;
              }
              /// @dev Receives ether from swaps
              fallback() external payable {}
              function swap(address adapter, bytes calldata data) external payable {
                  require(msg.sender == metaswap, "FORBIDDEN");
                  require(adapter != address(0), "ADAPTER_NOT_PROVIDED");
                  _delegate(adapter, data, "ADAPTER_DELEGATECALL_FAILED");
              }
              /**
               * @dev Performs a delegatecall and bubbles up the errors, adapted from
               * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol
               * @param target Address of the contract to delegatecall
               * @param data Data passed in the delegatecall
               * @param errorMessage Fallback revert reason
               */
              function _delegate(
                  address target,
                  bytes memory data,
                  string memory errorMessage
              ) private returns (bytes memory) {
                  // solhint-disable-next-line avoid-low-level-calls
                  (bool success, bytes memory returndata) = target.delegatecall(data);
                  if (success) {
                      return returndata;
                  } else {
                      // Look for revert reason and bubble it up if present
                      if (returndata.length > 0) {
                          // The easiest way to bubble the revert reason is using memory via assembly
                          // solhint-disable-next-line no-inline-assembly
                          assembly {
                              let returndata_size := mload(returndata)
                              revert(add(32, returndata), returndata_size)
                          }
                      } else {
                          revert(errorMessage);
                      }
                  }
              }
          }
          pragma solidity ^0.6.0;
          import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
          import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
          import "@openzeppelin/contracts/utils/Address.sol";
          contract MockAdapter {
              using SafeERC20 for IERC20;
              using Address for address;
              using Address for address payable;
              event MockAdapterEvent(
                  address sender,
                  uint256 valueFixed,
                  uint256 valueDynamic
              );
              function test(
                  address sender,
                  uint256 valueFixed,
                  uint256 valueDynamic
              ) external payable {
                  emit MockAdapterEvent(sender, valueFixed, valueDynamic);
              }
              function testRevert(
                  address,
                  uint256,
                  uint256
              ) external payable {
                  revert("SWAP_FAILED");
              }
              function testRevertNoReturnData(
                  address,
                  uint256,
                  uint256
              ) external payable {
                  revert();
              }
          }
          pragma solidity ^0.6.0;
          // TAKEN FROM https://github.com/gnosis/mock-contract
          // TODO: use their npm package once it is published for solidity 0.6
          interface MockInterface {
              /**
               * @dev After calling this method, the mock will return `response` when it is called
               * with any calldata that is not mocked more specifically below
               * (e.g. using givenMethodReturn).
               * @param response ABI encoded response that will be returned if method is invoked
               */
              function givenAnyReturn(bytes calldata response) external;
              function givenAnyReturnBool(bool response) external;
              function givenAnyReturnUint(uint256 response) external;
              function givenAnyReturnAddress(address response) external;
              function givenAnyRevert() external;
              function givenAnyRevertWithMessage(string calldata message) external;
              function givenAnyRunOutOfGas() external;
              /**
               * @dev After calling this method, the mock will return `response` when the given
               * methodId is called regardless of arguments. If the methodId and arguments
               * are mocked more specifically (using `givenMethodAndArguments`) the latter
               * will take precedence.
               * @param method ABI encoded methodId. It is valid to pass full calldata (including arguments). The mock will extract the methodId from it
               * @param response ABI encoded response that will be returned if method is invoked
               */
              function givenMethodReturn(bytes calldata method, bytes calldata response)
                  external;
              function givenMethodReturnBool(bytes calldata method, bool response)
                  external;
              function givenMethodReturnUint(bytes calldata method, uint256 response)
                  external;
              function givenMethodReturnAddress(bytes calldata method, address response)
                  external;
              function givenMethodRevert(bytes calldata method) external;
              function givenMethodRevertWithMessage(
                  bytes calldata method,
                  string calldata message
              ) external;
              function givenMethodRunOutOfGas(bytes calldata method) external;
              /**
               * @dev After calling this method, the mock will return `response` when the given
               * methodId is called with matching arguments. These exact calldataMocks will take
               * precedence over all other calldataMocks.
               * @param call ABI encoded calldata (methodId and arguments)
               * @param response ABI encoded response that will be returned if contract is invoked with calldata
               */
              function givenCalldataReturn(bytes calldata call, bytes calldata response)
                  external;
              function givenCalldataReturnBool(bytes calldata call, bool response)
                  external;
              function givenCalldataReturnUint(bytes calldata call, uint256 response)
                  external;
              function givenCalldataReturnAddress(bytes calldata call, address response)
                  external;
              function givenCalldataRevert(bytes calldata call) external;
              function givenCalldataRevertWithMessage(
                  bytes calldata call,
                  string calldata message
              ) external;
              function givenCalldataRunOutOfGas(bytes calldata call) external;
              /**
               * @dev Returns the number of times anything has been called on this mock since last reset
               */
              function invocationCount() external returns (uint256);
              /**
               * @dev Returns the number of times the given method has been called on this mock since last reset
               * @param method ABI encoded methodId. It is valid to pass full calldata (including arguments). The mock will extract the methodId from it
               */
              function invocationCountForMethod(bytes calldata method)
                  external
                  returns (uint256);
              /**
               * @dev Returns the number of times this mock has been called with the exact calldata since last reset.
               * @param call ABI encoded calldata (methodId and arguments)
               */
              function invocationCountForCalldata(bytes calldata call)
                  external
                  returns (uint256);
              /**
               * @dev Resets all mocked methods and invocation counts.
               */
              function reset() external;
          }
          /**
           * Implementation of the MockInterface.
           */
          contract MockContract is MockInterface {
              enum MockType {Return, Revert, OutOfGas}
              bytes32 public constant MOCKS_LIST_START = hex"01";
              bytes public constant MOCKS_LIST_END = "0xff";
              bytes32 public constant MOCKS_LIST_END_HASH = keccak256(MOCKS_LIST_END);
              bytes4 public constant SENTINEL_ANY_MOCKS = hex"01";
              bytes public constant DEFAULT_FALLBACK_VALUE = abi.encode(false);
              // A linked list allows easy iteration and inclusion checks
              mapping(bytes32 => bytes) calldataMocks;
              mapping(bytes => MockType) calldataMockTypes;
              mapping(bytes => bytes) calldataExpectations;
              mapping(bytes => string) calldataRevertMessage;
              mapping(bytes32 => uint256) calldataInvocations;
              mapping(bytes4 => bytes4) methodIdMocks;
              mapping(bytes4 => MockType) methodIdMockTypes;
              mapping(bytes4 => bytes) methodIdExpectations;
              mapping(bytes4 => string) methodIdRevertMessages;
              mapping(bytes32 => uint256) methodIdInvocations;
              MockType fallbackMockType;
              bytes fallbackExpectation = DEFAULT_FALLBACK_VALUE;
              string fallbackRevertMessage;
              uint256 invocations;
              uint256 resetCount;
              constructor() public {
                  calldataMocks[MOCKS_LIST_START] = MOCKS_LIST_END;
                  methodIdMocks[SENTINEL_ANY_MOCKS] = SENTINEL_ANY_MOCKS;
              }
              function trackCalldataMock(bytes memory call) private {
                  bytes32 callHash = keccak256(call);
                  if (calldataMocks[callHash].length == 0) {
                      calldataMocks[callHash] = calldataMocks[MOCKS_LIST_START];
                      calldataMocks[MOCKS_LIST_START] = call;
                  }
              }
              function trackMethodIdMock(bytes4 methodId) private {
                  if (methodIdMocks[methodId] == 0x0) {
                      methodIdMocks[methodId] = methodIdMocks[SENTINEL_ANY_MOCKS];
                      methodIdMocks[SENTINEL_ANY_MOCKS] = methodId;
                  }
              }
              function _givenAnyReturn(bytes memory response) internal {
                  fallbackMockType = MockType.Return;
                  fallbackExpectation = response;
              }
              function givenAnyReturn(bytes calldata response) external override {
                  _givenAnyReturn(response);
              }
              function givenAnyReturnBool(bool response) external override {
                  uint256 flag = response ? 1 : 0;
                  _givenAnyReturn(uintToBytes(flag));
              }
              function givenAnyReturnUint(uint256 response) external override {
                  _givenAnyReturn(uintToBytes(response));
              }
              function givenAnyReturnAddress(address response) external override {
                  _givenAnyReturn(uintToBytes(uint256(response)));
              }
              function givenAnyRevert() external override {
                  fallbackMockType = MockType.Revert;
                  fallbackRevertMessage = "";
              }
              function givenAnyRevertWithMessage(string calldata message)
                  external
                  override
              {
                  fallbackMockType = MockType.Revert;
                  fallbackRevertMessage = message;
              }
              function givenAnyRunOutOfGas() external override {
                  fallbackMockType = MockType.OutOfGas;
              }
              function _givenCalldataReturn(bytes memory call, bytes memory response)
                  private
              {
                  calldataMockTypes[call] = MockType.Return;
                  calldataExpectations[call] = response;
                  trackCalldataMock(call);
              }
              function givenCalldataReturn(bytes calldata call, bytes calldata response)
                  external
                  override
              {
                  _givenCalldataReturn(call, response);
              }
              function givenCalldataReturnBool(bytes calldata call, bool response)
                  external
                  override
              {
                  uint256 flag = response ? 1 : 0;
                  _givenCalldataReturn(call, uintToBytes(flag));
              }
              function givenCalldataReturnUint(bytes calldata call, uint256 response)
                  external
                  override
              {
                  _givenCalldataReturn(call, uintToBytes(response));
              }
              function givenCalldataReturnAddress(bytes calldata call, address response)
                  external
                  override
              {
                  _givenCalldataReturn(call, uintToBytes(uint256(response)));
              }
              function _givenMethodReturn(bytes memory call, bytes memory response)
                  private
              {
                  bytes4 method = bytesToBytes4(call);
                  methodIdMockTypes[method] = MockType.Return;
                  methodIdExpectations[method] = response;
                  trackMethodIdMock(method);
              }
              function givenMethodReturn(bytes calldata call, bytes calldata response)
                  external
                  override
              {
                  _givenMethodReturn(call, response);
              }
              function givenMethodReturnBool(bytes calldata call, bool response)
                  external
                  override
              {
                  uint256 flag = response ? 1 : 0;
                  _givenMethodReturn(call, uintToBytes(flag));
              }
              function givenMethodReturnUint(bytes calldata call, uint256 response)
                  external
                  override
              {
                  _givenMethodReturn(call, uintToBytes(response));
              }
              function givenMethodReturnAddress(bytes calldata call, address response)
                  external
                  override
              {
                  _givenMethodReturn(call, uintToBytes(uint256(response)));
              }
              function givenCalldataRevert(bytes calldata call) external override {
                  calldataMockTypes[call] = MockType.Revert;
                  calldataRevertMessage[call] = "";
                  trackCalldataMock(call);
              }
              function givenMethodRevert(bytes calldata call) external override {
                  bytes4 method = bytesToBytes4(call);
                  methodIdMockTypes[method] = MockType.Revert;
                  trackMethodIdMock(method);
              }
              function givenCalldataRevertWithMessage(
                  bytes calldata call,
                  string calldata message
              ) external override {
                  calldataMockTypes[call] = MockType.Revert;
                  calldataRevertMessage[call] = message;
                  trackCalldataMock(call);
              }
              function givenMethodRevertWithMessage(
                  bytes calldata call,
                  string calldata message
              ) external override {
                  bytes4 method = bytesToBytes4(call);
                  methodIdMockTypes[method] = MockType.Revert;
                  methodIdRevertMessages[method] = message;
                  trackMethodIdMock(method);
              }
              function givenCalldataRunOutOfGas(bytes calldata call) external override {
                  calldataMockTypes[call] = MockType.OutOfGas;
                  trackCalldataMock(call);
              }
              function givenMethodRunOutOfGas(bytes calldata call) external override {
                  bytes4 method = bytesToBytes4(call);
                  methodIdMockTypes[method] = MockType.OutOfGas;
                  trackMethodIdMock(method);
              }
              function invocationCount() external override returns (uint256) {
                  return invocations;
              }
              function invocationCountForMethod(bytes calldata call)
                  external
                  override
                  returns (uint256)
              {
                  bytes4 method = bytesToBytes4(call);
                  return
                      methodIdInvocations[keccak256(
                          abi.encodePacked(resetCount, method)
                      )];
              }
              function invocationCountForCalldata(bytes calldata call)
                  external
                  override
                  returns (uint256)
              {
                  return
                      calldataInvocations[keccak256(abi.encodePacked(resetCount, call))];
              }
              function reset() external override {
                  // Reset all exact calldataMocks
                  bytes memory nextMock = calldataMocks[MOCKS_LIST_START];
                  bytes32 mockHash = keccak256(nextMock);
                  // We cannot compary bytes
                  while (mockHash != MOCKS_LIST_END_HASH) {
                      // Reset all mock maps
                      calldataMockTypes[nextMock] = MockType.Return;
                      calldataExpectations[nextMock] = hex"";
                      calldataRevertMessage[nextMock] = "";
                      // Set next mock to remove
                      nextMock = calldataMocks[mockHash];
                      // Remove from linked list
                      calldataMocks[mockHash] = "";
                      // Update mock hash
                      mockHash = keccak256(nextMock);
                  }
                  // Clear list
                  calldataMocks[MOCKS_LIST_START] = MOCKS_LIST_END;
                  // Reset all any calldataMocks
                  bytes4 nextAnyMock = methodIdMocks[SENTINEL_ANY_MOCKS];
                  while (nextAnyMock != SENTINEL_ANY_MOCKS) {
                      bytes4 currentAnyMock = nextAnyMock;
                      methodIdMockTypes[currentAnyMock] = MockType.Return;
                      methodIdExpectations[currentAnyMock] = hex"";
                      methodIdRevertMessages[currentAnyMock] = "";
                      nextAnyMock = methodIdMocks[currentAnyMock];
                      // Remove from linked list
                      methodIdMocks[currentAnyMock] = 0x0;
                  }
                  // Clear list
                  methodIdMocks[SENTINEL_ANY_MOCKS] = SENTINEL_ANY_MOCKS;
                  fallbackExpectation = DEFAULT_FALLBACK_VALUE;
                  fallbackMockType = MockType.Return;
                  invocations = 0;
                  resetCount += 1;
              }
              function useAllGas() private {
                  while (true) {
                      bool s;
                      assembly {
                          //expensive call to EC multiply contract
                          s := call(sub(gas(), 2000), 6, 0, 0x0, 0xc0, 0x0, 0x60)
                      }
                  }
              }
              function bytesToBytes4(bytes memory b) private pure returns (bytes4) {
                  bytes4 out;
                  for (uint256 i = 0; i < 4; i++) {
                      out |= bytes4(b[i] & 0xFF) >> (i * 8);
                  }
                  return out;
              }
              function uintToBytes(uint256 x) private pure returns (bytes memory b) {
                  b = new bytes(32);
                  assembly {
                      mstore(add(b, 32), x)
                  }
              }
              function updateInvocationCount(
                  bytes4 methodId,
                  bytes memory originalMsgData
              ) public {
                  require(
                      msg.sender == address(this),
                      "Can only be called from the contract itself"
                  );
                  invocations += 1;
                  methodIdInvocations[keccak256(
                      abi.encodePacked(resetCount, methodId)
                  )] += 1;
                  calldataInvocations[keccak256(
                      abi.encodePacked(resetCount, originalMsgData)
                  )] += 1;
              }
              fallback() external payable {
                  bytes4 methodId;
                  assembly {
                      methodId := calldataload(0)
                  }
                  // First, check exact matching overrides
                  if (calldataMockTypes[msg.data] == MockType.Revert) {
                      revert(calldataRevertMessage[msg.data]);
                  }
                  if (calldataMockTypes[msg.data] == MockType.OutOfGas) {
                      useAllGas();
                  }
                  bytes memory result = calldataExpectations[msg.data];
                  // Then check method Id overrides
                  if (result.length == 0) {
                      if (methodIdMockTypes[methodId] == MockType.Revert) {
                          revert(methodIdRevertMessages[methodId]);
                      }
                      if (methodIdMockTypes[methodId] == MockType.OutOfGas) {
                          useAllGas();
                      }
                      result = methodIdExpectations[methodId];
                  }
                  // Last, use the fallback override
                  if (result.length == 0) {
                      if (fallbackMockType == MockType.Revert) {
                          revert(fallbackRevertMessage);
                      }
                      if (fallbackMockType == MockType.OutOfGas) {
                          useAllGas();
                      }
                      result = fallbackExpectation;
                  }
                  // Record invocation as separate call so we don't rollback in case we are called with STATICCALL
                  (, bytes memory r) = address(this).call{gas: 100000}(
                      abi.encodeWithSignature(
                          "updateInvocationCount(bytes4,bytes)",
                          methodId,
                          msg.data
                      )
                  );
                  assert(r.length == 0);
                  assembly {
                      return(add(0x20, result), mload(result))
                  }
              }
          }
          pragma solidity ^0.6.0;
          contract MockSelfDestruct {
              constructor() public payable {}
              fallback() external payable {
                  selfdestruct(msg.sender);
              }
              function kill(address payable target) external payable {
                  selfdestruct(target);
              }
          }
          

          File 2 of 7: 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 3 of 7: 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 4 of 7: Ondo
          // SPDX-License-Identifier: AGPL-3.0
          pragma solidity 0.8.3;
          /*
           * @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) {
              this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
              return msg.data;
            }
          }
          /**
           * @dev Interface of the ERC165 standard, as defined in the
           * https://eips.ethereum.org/EIPS/eip-165[EIP].
           *
           * Implementers can declare support of contract interfaces, which can then be
           * queried by others ({ERC165Checker}).
           *
           * For an implementation, see {ERC165}.
           */
          interface IERC165 {
            /**
             * @dev Returns true if this contract implements the interface defined by
             * `interfaceId`. See the corresponding
             * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
             * to learn more about how these ids are created.
             *
             * This function call must use less than 30 000 gas.
             */
            function supportsInterface(bytes4 interfaceId) external view returns (bool);
          }
          /**
           * @dev Implementation of the {IERC165} interface.
           *
           * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
           * for the additional interface id that will be supported. For example:
           *
           * ```solidity
           * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
           *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
           * }
           * ```
           *
           * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
           */
          abstract contract ERC165 is IERC165 {
            /**
             * @dev See {IERC165-supportsInterface}.
             */
            function supportsInterface(bytes4 interfaceId)
              public
              view
              virtual
              override
              returns (bool)
            {
              return interfaceId == type(IERC165).interfaceId;
            }
          }
          /**
           * @dev External interface of AccessControl declared to support ERC165 detection.
           */
          interface IAccessControl {
            function hasRole(bytes32 role, address account) external view returns (bool);
            function getRoleAdmin(bytes32 role) external view returns (bytes32);
            function grantRole(bytes32 role, address account) external;
            function revokeRole(bytes32 role, address account) external;
            function renounceRole(bytes32 role, address account) external;
          }
          /**
           * @dev Contract module that allows children to implement role-based access
           * control mechanisms. This is a lightweight version that doesn't allow enumerating role
           * members except through off-chain means by accessing the contract event logs. Some
           * applications may benefit from on-chain enumerability, for those cases see
           * {AccessControlEnumerable}.
           *
           * Roles are referred to by their `bytes32` identifier. These should be exposed
           * in the external API and be unique. The best way to achieve this is by
           * using `public constant` hash digests:
           *
           * ```
           * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
           * ```
           *
           * Roles can be used to represent a set of permissions. To restrict access to a
           * function call, use {hasRole}:
           *
           * ```
           * function foo() public {
           *     require(hasRole(MY_ROLE, msg.sender));
           *     ...
           * }
           * ```
           *
           * Roles can be granted and revoked dynamically via the {grantRole} and
           * {revokeRole} functions. Each role has an associated admin role, and only
           * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
           *
           * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
           * that only accounts with this role will be able to grant or revoke other
           * roles. More complex role relationships can be created by using
           * {_setRoleAdmin}.
           *
           * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
           * grant and revoke this role. Extra precautions should be taken to secure
           * accounts that have been granted it.
           */
          abstract contract AccessControl is Context, IAccessControl, ERC165 {
            struct RoleData {
              mapping(address => bool) members;
              bytes32 adminRole;
            }
            mapping(bytes32 => RoleData) private _roles;
            bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
            /**
             * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
             *
             * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
             * {RoleAdminChanged} not being emitted signaling this.
             *
             * _Available since v3.1._
             */
            event RoleAdminChanged(
              bytes32 indexed role,
              bytes32 indexed previousAdminRole,
              bytes32 indexed newAdminRole
            );
            /**
             * @dev Emitted when `account` is granted `role`.
             *
             * `sender` is the account that originated the contract call, an admin role
             * bearer except when using {_setupRole}.
             */
            event RoleGranted(
              bytes32 indexed role,
              address indexed account,
              address indexed sender
            );
            /**
             * @dev Emitted when `account` is revoked `role`.
             *
             * `sender` is the account that originated the contract call:
             *   - if using `revokeRole`, it is the admin role bearer
             *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
             */
            event RoleRevoked(
              bytes32 indexed role,
              address indexed account,
              address indexed sender
            );
            /**
             * @dev See {IERC165-supportsInterface}.
             */
            function supportsInterface(bytes4 interfaceId)
              public
              view
              virtual
              override
              returns (bool)
            {
              return
                interfaceId == type(IAccessControl).interfaceId ||
                super.supportsInterface(interfaceId);
            }
            /**
             * @dev Returns `true` if `account` has been granted `role`.
             */
            function hasRole(bytes32 role, address account)
              public
              view
              override
              returns (bool)
            {
              return _roles[role].members[account];
            }
            /**
             * @dev Returns the admin role that controls `role`. See {grantRole} and
             * {revokeRole}.
             *
             * To change a role's admin, use {_setRoleAdmin}.
             */
            function getRoleAdmin(bytes32 role) public view override returns (bytes32) {
              return _roles[role].adminRole;
            }
            /**
             * @dev Grants `role` to `account`.
             *
             * If `account` had not been already granted `role`, emits a {RoleGranted}
             * event.
             *
             * Requirements:
             *
             * - the caller must have ``role``'s admin role.
             */
            function grantRole(bytes32 role, address account) public virtual override {
              require(
                hasRole(getRoleAdmin(role), _msgSender()),
                "AccessControl: sender must be an admin to grant"
              );
              _grantRole(role, account);
            }
            /**
             * @dev Revokes `role` from `account`.
             *
             * If `account` had been granted `role`, emits a {RoleRevoked} event.
             *
             * Requirements:
             *
             * - the caller must have ``role``'s admin role.
             */
            function revokeRole(bytes32 role, address account) public virtual override {
              require(
                hasRole(getRoleAdmin(role), _msgSender()),
                "AccessControl: sender must be an admin to revoke"
              );
              _revokeRole(role, account);
            }
            /**
             * @dev Revokes `role` from the calling account.
             *
             * Roles are often managed via {grantRole} and {revokeRole}: this function's
             * purpose is to provide a mechanism for accounts to lose their privileges
             * if they are compromised (such as when a trusted device is misplaced).
             *
             * If the calling account had been granted `role`, emits a {RoleRevoked}
             * event.
             *
             * Requirements:
             *
             * - the caller must be `account`.
             */
            function renounceRole(bytes32 role, address account) public virtual override {
              require(
                account == _msgSender(),
                "AccessControl: can only renounce roles for self"
              );
              _revokeRole(role, account);
            }
            /**
             * @dev Grants `role` to `account`.
             *
             * If `account` had not been already granted `role`, emits a {RoleGranted}
             * event. Note that unlike {grantRole}, this function doesn't perform any
             * checks on the calling account.
             *
             * [WARNING]
             * ====
             * This function should only be called from the constructor when setting
             * up the initial roles for the system.
             *
             * Using this function in any other way is effectively circumventing the admin
             * system imposed by {AccessControl}.
             * ====
             */
            function _setupRole(bytes32 role, address account) internal virtual {
              _grantRole(role, account);
            }
            /**
             * @dev Sets `adminRole` as ``role``'s admin role.
             *
             * Emits a {RoleAdminChanged} event.
             */
            function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
              emit RoleAdminChanged(role, getRoleAdmin(role), adminRole);
              _roles[role].adminRole = adminRole;
            }
            function _grantRole(bytes32 role, address account) private {
              if (!hasRole(role, account)) {
                _roles[role].members[account] = true;
                emit RoleGranted(role, account, _msgSender());
              }
            }
            function _revokeRole(bytes32 role, address account) private {
              if (hasRole(role, account)) {
                _roles[role].members[account] = false;
                emit RoleRevoked(role, account, _msgSender());
              }
            }
          }
          interface IOndo {
            enum InvestorType {CoinlistTranche1, CoinlistTranche2, SeedTranche}
            // ----------- State changing api -----------
            /// @notice Called by timelock contract to initialize locked balance of coinlist/seed investor
            function updateTrancheBalance(
              address beneficiary,
              uint256 rawAmount,
              InvestorType tranche
            ) external;
            // ----------- Getters -----------
            /// @notice Gets the TOTAL amount of Ondo available for an address
            function getFreedBalance(address account) external view returns (uint96);
            /// @notice Gets the initial locked balance and unlocked Ondo for an address
            function getVestedBalance(address account)
              external
              view
              returns (uint96, uint96);
          }
          abstract contract LinearTimelock {
            struct InvestorParam {
              IOndo.InvestorType investorType;
              uint96 initialBalance;
            }
            /// @notice the timestamp at which releasing is allowed
            uint256 public cliffTimestamp;
            /// @notice the linear vesting period for the first tranche
            uint256 public immutable tranche1VestingPeriod;
            /// @notice the linear vesting period for the second tranche
            uint256 public immutable tranche2VestingPeriod;
            /// @notice the linear vesting period for the Seed/Series A Tranche
            uint256 public immutable seedVestingPeriod;
            /// @dev mapping of balances for each investor
            mapping(address => InvestorParam) internal investorBalances;
            /// @notice role that allows updating of tranche balances - granted to Merkle airdrop contract
            bytes32 public constant TIMELOCK_UPDATE_ROLE =
              keccak256("TIMELOCK_UPDATE_ROLE");
            constructor(
              uint256 _cliffTimestamp,
              uint256 _tranche1VestingPeriod,
              uint256 _tranche2VestingPeriod,
              uint256 _seedVestingPeriod
            ) {
              cliffTimestamp = _cliffTimestamp;
              tranche1VestingPeriod = _tranche1VestingPeriod;
              tranche2VestingPeriod = _tranche2VestingPeriod;
              seedVestingPeriod = _seedVestingPeriod;
            }
            function passedCliff() public view returns (bool) {
              return block.timestamp > cliffTimestamp;
            }
            /// @dev the seedVestingPeriod is the longest vesting period
            function passedAllVestingPeriods() public view returns (bool) {
              return block.timestamp > cliffTimestamp + seedVestingPeriod;
            }
            /**
              @notice View function to get the user's initial balance and current amount of freed balance
             */
            function getVestedBalance(address account)
              external
              view
              returns (uint256, uint256)
            {
              if (investorBalances[account].initialBalance == 0) {
                return (0, 0);
              }
              InvestorParam memory investorParam = investorBalances[account];
              uint96 amountAvailable;
              if (passedAllVestingPeriods()) {
                amountAvailable = investorParam.initialBalance;
              } else if (passedCliff()) {
                (uint256 vestingPeriod, uint256 elapsed) =
                  _getTrancheInfo(investorParam.investorType);
                amountAvailable = _proportionAvailable(
                  elapsed,
                  vestingPeriod,
                  investorParam
                );
              } else {
                amountAvailable = 0;
              }
              return (investorParam.initialBalance, amountAvailable);
            }
            function _getTrancheInfo(IOndo.InvestorType investorType)
              internal
              view
              returns (uint256 vestingPeriod, uint256 elapsed)
            {
              elapsed = block.timestamp - cliffTimestamp;
              if (investorType == IOndo.InvestorType.CoinlistTranche1) {
                elapsed = elapsed > tranche1VestingPeriod
                  ? tranche1VestingPeriod
                  : elapsed;
                vestingPeriod = tranche1VestingPeriod;
              } else if (investorType == IOndo.InvestorType.CoinlistTranche2) {
                elapsed = elapsed > tranche2VestingPeriod
                  ? tranche2VestingPeriod
                  : elapsed;
                vestingPeriod = tranche2VestingPeriod;
              } else if (investorType == IOndo.InvestorType.SeedTranche) {
                elapsed = elapsed > seedVestingPeriod ? seedVestingPeriod : elapsed;
                vestingPeriod = seedVestingPeriod;
              }
            }
            function _proportionAvailable(
              uint256 elapsed,
              uint256 vestingPeriod,
              InvestorParam memory investorParam
            ) internal pure returns (uint96) {
              if (investorParam.investorType == IOndo.InvestorType.SeedTranche) {
                // Seed/Series A Tranche Balance = proportionAvail*2/3 + x/3, where x = Balance. This allows 1/3 of the series A balance to be unlocked at cliff
                uint96 vestedAmount =
                  safe96(
                    (((investorParam.initialBalance * elapsed) / vestingPeriod) * 2) / 3,
                    "Ondo::_proportionAvailable: amount exceeds 96 bits"
                  );
                return
                  add96(
                    vestedAmount,
                    investorParam.initialBalance / 3,
                    "Ondo::_proportionAvailable: overflow"
                  );
              } else {
                return
                  safe96(
                    (investorParam.initialBalance * elapsed) / vestingPeriod,
                    "Ondo::_proportionAvailable: amount exceeds 96 bits"
                  );
              }
            }
            function safe32(uint256 n, string memory errorMessage)
              internal
              pure
              returns (uint32)
            {
              require(n < 2**32, errorMessage);
              return uint32(n);
            }
            function safe96(uint256 n, string memory errorMessage)
              internal
              pure
              returns (uint96)
            {
              require(n < 2**96, errorMessage);
              return uint96(n);
            }
            function add96(
              uint96 a,
              uint96 b,
              string memory errorMessage
            ) internal pure returns (uint96) {
              uint96 c = a + b;
              require(c >= a, errorMessage);
              return c;
            }
            function sub96(
              uint96 a,
              uint96 b,
              string memory errorMessage
            ) internal pure returns (uint96) {
              require(b <= a, errorMessage);
              return a - b;
            }
          }
          contract Ondo is AccessControl, LinearTimelock {
            /// @notice EIP-20 token name for this token
            string public constant name = "Ondo";
            /// @notice EIP-20 token symbol for this token
            string public constant symbol = "ONDO";
            /// @notice EIP-20 token decimals for this token
            uint8 public constant decimals = 18;
            // whether token transfers are allowed
            bool public transferAllowed; // false by default
            /// @notice Total number of tokens in circulation
            uint256 public totalSupply = 10_000_000_000e18; // 10 billion Ondo
            // Allowance amounts on behalf of others
            mapping(address => mapping(address => uint96)) internal allowances;
            // Official record of token balances for each account
            mapping(address => uint96) internal balances;
            /// @notice A record of each accounts delegate
            mapping(address => address) public delegates;
            /// @notice A checkpoint for marking number of votes from a given block
            struct Checkpoint {
              uint32 fromBlock;
              uint96 votes;
            }
            /// @notice A record of votes checkpoints for each account, by index
            mapping(address => mapping(uint32 => Checkpoint)) public checkpoints;
            /// @notice The number of checkpoints for each account
            mapping(address => uint32) public numCheckpoints;
            /// @notice The EIP-712 typehash for the contract's domain
            bytes32 public constant DOMAIN_TYPEHASH =
              keccak256(
                "EIP712Domain(string name,uint256 chainId,address verifyingContract)"
              );
            /// @notice The EIP-712 typehash for the delegation struct used by the contract
            bytes32 public constant DELEGATION_TYPEHASH =
              keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");
            /// @notice The identifier of the role which allows special transfer privileges.
            bytes32 public constant TRANSFER_ROLE = keccak256("TRANSFER_ROLE");
            bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
            /// @notice A record of states for signing / validating signatures
            mapping(address => uint256) public nonces;
            /// @notice An event thats emitted when an account changes its delegate
            event DelegateChanged(
              address indexed delegator,
              address indexed fromDelegate,
              address indexed toDelegate
            );
            /// @notice An event thats emitted when a delegate account's vote balance changes
            event DelegateVotesChanged(
              address indexed delegate,
              uint256 previousBalance,
              uint256 newBalance
            );
            /// @notice The standard EIP-20 transfer event
            event Transfer(address indexed from, address indexed to, uint256 amount);
            /// @notice The standard EIP-20 approval event
            event Approval(
              address indexed owner,
              address indexed spender,
              uint256 amount
            );
            event CliffTimestampUpdate(uint256 newTimestamp);
            /**
             * @dev Emitted when the transfer is enabled triggered by `account`.
             */
            event TransferEnabled(address account);
            /// @notice a modifier which checks if transfers are allowed
            modifier whenTransferAllowed() {
              require(
                transferAllowed || hasRole(TRANSFER_ROLE, msg.sender),
                "OndoToken: Transfers not allowed or not right privillege"
              );
              _;
            }
            /**
             * @notice Construct a new Ondo token
             * @param _governance The initial account to grant owner permission and all the tokens
             */
            constructor(
              address _governance,
              uint256 _cliffTimestamp,
              uint256 _tranche1VestingPeriod,
              uint256 _tranche2VestingPeriod,
              uint256 _seedVestingPeriod
            )
              LinearTimelock(
                _cliffTimestamp,
                _tranche1VestingPeriod,
                _tranche2VestingPeriod,
                _seedVestingPeriod
              )
            {
              balances[_governance] = uint96(totalSupply);
              _setupRole(DEFAULT_ADMIN_ROLE, _governance);
              _setupRole(TRANSFER_ROLE, _governance);
              _setupRole(MINTER_ROLE, _governance);
              emit Transfer(address(0), _governance, totalSupply);
            }
            /**
             * @notice Get the number of tokens `spender` is approved to spend on behalf of `account`
             * @param account The address of the account holding the funds
             * @param spender The address of the account spending the funds
             * @return The number of tokens approved
             */
            function allowance(address account, address spender)
              external
              view
              returns (uint256)
            {
              return allowances[account][spender];
            }
            /**
             * @notice Approve `spender` to transfer up to `amount` from `src`
             * @dev This will overwrite the approval amount for `spender`
             *  and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
             * @param spender The address of the account which may transfer tokens
             * @param rawAmount The number of tokens that are approved (2^256-1 means infinite)
             * @return Whether or not the approval succeeded
             */
            function approve(address spender, uint256 rawAmount) external returns (bool) {
              uint96 amount;
              if (rawAmount == type(uint256).max) {
                amount = type(uint96).max;
              } else {
                amount = safe96(rawAmount, "Ondo::approve: amount exceeds 96 bits");
              }
              allowances[msg.sender][spender] = amount;
              emit Approval(msg.sender, spender, amount);
              return true;
            }
            /**
             * @notice Get the number of tokens held by the `account`
             * @param account The address of the account to get the balance of
             * @return The number of tokens held
             */
            function balanceOf(address account) external view returns (uint256) {
              return balances[account];
            }
            /**
             * @notice Get the total number of UNLOCKED tokens held by the `account`
             * @param account The address of the account to get the unlocked balance of
             * @return The number of unlocked tokens held.
             */
            function getFreedBalance(address account) external view returns (uint256) {
              if (investorBalances[account].initialBalance > 0) {
                return _getFreedBalance(account);
              } else {
                return balances[account];
              }
            }
            /**
             * @notice Transfer `amount` tokens from `msg.sender` to `dst`
             * @param dst The address of the destination account
             * @param rawAmount The number of tokens to transfer
             * @return Whether or not the transfer succeeded
             */
            function transfer(address dst, uint256 rawAmount) external returns (bool) {
              uint96 amount = safe96(rawAmount, "Ondo::transfer: amount exceeds 96 bits");
              _transferTokens(msg.sender, dst, amount);
              return true;
            }
            /**
             * @notice Transfer `amount` tokens from `src` to `dst`
             * @param src The address of the source account
             * @param dst The address of the destination account
             * @param rawAmount The number of tokens to transfer
             * @return Whether or not the transfer succeeded
             */
            function transferFrom(
              address src,
              address dst,
              uint256 rawAmount
            ) external returns (bool) {
              address spender = msg.sender;
              uint96 spenderAllowance = allowances[src][spender];
              uint96 amount = safe96(rawAmount, "Ondo::approve: amount exceeds 96 bits");
              if (spender != src && spenderAllowance != type(uint96).max) {
                uint96 newAllowance =
                  sub96(
                    spenderAllowance,
                    amount,
                    "Ondo::transferFrom: transfer amount exceeds spender allowance"
                  );
                allowances[src][spender] = newAllowance;
                emit Approval(src, spender, newAllowance);
              }
              _transferTokens(src, dst, amount);
              return true;
            }
            /**
             * @notice Delegate votes from `msg.sender` to `delegatee`
             * @param delegatee The address to delegate votes to
             */
            function delegate(address delegatee) public {
              return _delegate(msg.sender, delegatee);
            }
            /**
             * @notice Delegates votes from signatory to `delegatee`
             * @param delegatee The address to delegate votes to
             * @param nonce The contract state required to match the signature
             * @param expiry The time at which to expire the signature
             * @param v The recovery byte of the signature
             * @param r Half of the ECDSA signature pair
             * @param s Half of the ECDSA signature pair
             */
            function delegateBySig(
              address delegatee,
              uint256 nonce,
              uint256 expiry,
              uint8 v,
              bytes32 r,
              bytes32 s
            ) public {
              bytes32 domainSeparator =
                keccak256(
                  abi.encode(
                    DOMAIN_TYPEHASH,
                    keccak256(bytes(name)),
                    getChainId(),
                    address(this)
                  )
                );
              bytes32 structHash =
                keccak256(abi.encode(DELEGATION_TYPEHASH, delegatee, nonce, expiry));
              bytes32 digest =
                keccak256(abi.encodePacked("\\x19\\x01", domainSeparator, structHash));
              address signatory = ecrecover(digest, v, r, s);
              require(signatory != address(0), "Ondo::delegateBySig: invalid signature");
              require(nonce == nonces[signatory]++, "Ondo::delegateBySig: invalid nonce");
              require(
                block.timestamp <= expiry,
                "Ondo::delegateBySig: signature expired"
              );
              return _delegate(signatory, delegatee);
            }
            /**
             * @notice Gets the current votes balance for `account`
             * @param account The address to get votes balance
             * @return The number of current votes for `account`
             */
            function getCurrentVotes(address account) external view returns (uint96) {
              uint32 nCheckpoints = numCheckpoints[account];
              return nCheckpoints > 0 ? checkpoints[account][nCheckpoints - 1].votes : 0;
            }
            /**
             * @notice Determine the prior number of votes for an account as of a block number
             * @dev Block number must be a finalized block or else this function will revert to prevent misinformation.
             * @param account The address of the account to check
             * @param blockNumber The block number to get the vote balance at
             * @return The number of votes the account had as of the given block
             */
            function getPriorVotes(address account, uint256 blockNumber)
              public
              view
              returns (uint96)
            {
              require(
                blockNumber < block.number,
                "Ondo::getPriorVotes: not yet determined"
              );
              uint32 nCheckpoints = numCheckpoints[account];
              if (nCheckpoints == 0) {
                return 0;
              }
              // First check most recent balance
              if (checkpoints[account][nCheckpoints - 1].fromBlock <= blockNumber) {
                return checkpoints[account][nCheckpoints - 1].votes;
              }
              // Next check implicit zero balance
              if (checkpoints[account][0].fromBlock > blockNumber) {
                return 0;
              }
              uint32 lower = 0;
              uint32 upper = nCheckpoints - 1;
              while (upper > lower) {
                uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow
                Checkpoint memory cp = checkpoints[account][center];
                if (cp.fromBlock == blockNumber) {
                  return cp.votes;
                } else if (cp.fromBlock < blockNumber) {
                  lower = center;
                } else {
                  upper = center - 1;
                }
              }
              return checkpoints[account][lower].votes;
            }
            /**
             * @notice Create `rawAmount` new tokens and assign them to `account`.
             * @param account The address to give newly minted tokens to
             * @param rawAmount Number of new tokens to mint.
             * @dev Even though total token supply is uint96, we use uint256 for the amount for consistency with other external interfaces.
             */
            function mint(address account, uint256 rawAmount) external {
              require(hasRole(MINTER_ROLE, msg.sender), "Ondo::mint: not authorized");
              require(account != address(0), "cannot mint to the zero address");
              uint96 amount = safe96(rawAmount, "Ondo::mint: amount exceeds 96 bits");
              uint96 supply =
                safe96(totalSupply, "Ondo::mint: totalSupply exceeds 96 bits");
              totalSupply = add96(supply, amount, "Ondo::mint: token supply overflow");
              balances[account] = add96(
                balances[account],
                amount,
                "Ondo::mint: balance overflow"
              );
              emit Transfer(address(0), account, amount);
            }
            function _delegate(address delegator, address delegatee) internal {
              address currentDelegate = delegates[delegator];
              uint96 delegatorBalance = balances[delegator];
              delegates[delegator] = delegatee;
              emit DelegateChanged(delegator, currentDelegate, delegatee);
              _moveDelegates(currentDelegate, delegatee, delegatorBalance);
            }
            function _transferTokens(
              address src,
              address dst,
              uint96 amount
            ) internal whenTransferAllowed {
              require(
                src != address(0),
                "Ondo::_transferTokens: cannot transfer from the zero address"
              );
              require(
                dst != address(0),
                "Ondo::_transferTokens: cannot transfer to the zero address"
              );
              if (investorBalances[src].initialBalance > 0) {
                require(
                  amount <= _getFreedBalance(src),
                  "Ondo::_transferTokens: not enough unlocked balance"
                );
              }
              balances[src] = sub96(
                balances[src],
                amount,
                "Ondo::_transferTokens: transfer amount exceeds balance"
              );
              balances[dst] = add96(
                balances[dst],
                amount,
                "Ondo::_transferTokens: transfer amount overflows"
              );
              emit Transfer(src, dst, amount);
              _moveDelegates(delegates[src], delegates[dst], amount);
            }
            function _moveDelegates(
              address srcRep,
              address dstRep,
              uint96 amount
            ) internal {
              if (srcRep != dstRep && amount > 0) {
                if (srcRep != address(0)) {
                  uint32 srcRepNum = numCheckpoints[srcRep];
                  uint96 srcRepOld =
                    srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].votes : 0;
                  uint96 srcRepNew =
                    sub96(srcRepOld, amount, "Ondo::_moveVotes: vote amount underflows");
                  _writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew);
                }
                if (dstRep != address(0)) {
                  uint32 dstRepNum = numCheckpoints[dstRep];
                  uint96 dstRepOld =
                    dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].votes : 0;
                  uint96 dstRepNew =
                    add96(dstRepOld, amount, "Ondo::_moveVotes: vote amount overflows");
                  _writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew);
                }
              }
            }
            function _writeCheckpoint(
              address delegatee,
              uint32 nCheckpoints,
              uint96 oldVotes,
              uint96 newVotes
            ) internal {
              uint32 blockNumber =
                safe32(
                  block.number,
                  "Ondo::_writeCheckpoint: block number exceeds 32 bits"
                );
              if (
                nCheckpoints > 0 &&
                checkpoints[delegatee][nCheckpoints - 1].fromBlock == blockNumber
              ) {
                checkpoints[delegatee][nCheckpoints - 1].votes = newVotes;
              } else {
                checkpoints[delegatee][nCheckpoints] = Checkpoint(blockNumber, newVotes);
                numCheckpoints[delegatee] = nCheckpoints + 1;
              }
              emit DelegateVotesChanged(delegatee, oldVotes, newVotes);
            }
            function getChainId() internal view returns (uint256) {
              uint256 chainId;
              assembly {
                chainId := chainid()
              }
              return chainId;
            }
            /**
             * @notice Turn on _transferAllowed variable. Transfers are enabled
             */
            function enableTransfer() external {
              require(
                hasRole(DEFAULT_ADMIN_ROLE, msg.sender),
                "Ondo::enableTransfer: not authorized"
              );
              transferAllowed = true;
              emit TransferEnabled(msg.sender);
            }
            /**
             * @notice Called by merkle airdrop contract to initialize locked balances
             */
            function updateTrancheBalance(
              address beneficiary,
              uint256 rawAmount,
              IOndo.InvestorType investorType
            ) external {
              require(hasRole(TIMELOCK_UPDATE_ROLE, msg.sender));
              require(rawAmount > 0, "Ondo::updateTrancheBalance: amount must be > 0");
              require(
                investorBalances[beneficiary].initialBalance == 0,
                "Ondo::updateTrancheBalance: already has timelocked Ondo"
              ); //Prevents users from being in more than 1 tranche
              uint96 amount =
                safe96(rawAmount, "Ondo::updateTrancheBalance: amount exceeds 96 bits");
              investorBalances[beneficiary] = InvestorParam(investorType, amount);
            }
            /**
             * @notice Internal function the amount of unlocked Ondo for an account that participated in Coinlist/Seed Investments
             */
            function _getFreedBalance(address account) internal view returns (uint96) {
              if (passedAllVestingPeriods()) {
                //all vesting periods are over, just return the total balance
                return balances[account];
              } else {
                InvestorParam memory investorParam = investorBalances[account];
                if (passedCliff()) {
                  //we are in between the cliff timestamp and last vesting period
                  (uint256 vestingPeriod, uint256 elapsed) =
                    _getTrancheInfo(investorParam.investorType);
                  uint96 lockedBalance =
                    sub96(
                      investorParam.initialBalance,
                      _proportionAvailable(elapsed, vestingPeriod, investorParam),
                      "Ondo::getFreedBalance: locked balance underflow"
                    );
                  return
                    sub96(
                      balances[account],
                      lockedBalance,
                      "Ondo::getFreedBalance: total freed balance underflow"
                    );
                } else {
                  //we have not hit the cliff yet, all investor balance is locked
                  return
                    sub96(
                      balances[account],
                      investorParam.initialBalance,
                      "Ondo::getFreedBalance: balance underflow"
                    );
                }
              }
            }
            function updateCliffTimestamp(uint256 newTimestamp) external {
              require(
                hasRole(DEFAULT_ADMIN_ROLE, msg.sender),
                "Ondo::updateCliffTimestamp: not authorized"
              );
              cliffTimestamp = newTimestamp;
              emit CliffTimestampUpdate(newTimestamp);
            }
          }
          

          File 5 of 7: 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 6 of 7: 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 7 of 7: 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;
          }