ETH Price: $4,630.85 (+8.35%)

Transaction Decoder

Block:
22544838 at May-23-2025 10:01:59 AM +UTC
Transaction Fee:
0.000593101085143105 ETH $2.75
Gas Used:
228,305 Gas / 2.597845361 Gwei

Emitted Events:

258 FetchToken.Transfer( from=[Sender] 0xf707a0cef04f1d40508ed3669e754658f0e102c0, to=Spender, value=500000000000000000000 )
259 FetchToken.Approval( owner=[Sender] 0xf707a0cef04f1d40508ed3669e754658f0e102c0, spender=[Receiver] MetaSwap, value=115792089237316195423570985008687907853269984665640564017455584007913129639935 )
260 FetchToken.Transfer( from=Spender, to=0x51C72848c68a965f66FA7a88855F9f7784502a7F, value=495625000000000000000 )
261 FetchToken.Approval( owner=Spender, spender=SwapERC20, value=115792089237316195423570985008687907853269984665640560415470435364103195070235 )
262 Ondo.Approval( owner=0x51C72848c68a965f66FA7a88855F9f7784502a7F, spender=SwapERC20, amount=110891031803431974916442 )
263 Ondo.Transfer( from=0x51C72848c68a965f66FA7a88855F9f7784502a7F, to=Spender, amount=469179641979926675456 )
264 Ondo.Approval( owner=0x51C72848c68a965f66FA7a88855F9f7784502a7F, spender=SwapERC20, amount=110890797213610984953105 )
265 Ondo.Transfer( from=0x51C72848c68a965f66FA7a88855F9f7784502a7F, to=SplitWallet, amount=234589820989963337 )
266 SwapERC20.SwapERC20( nonce=1747239892071, signerWallet=0x51C72848c68a965f66FA7a88855F9f7784502a7F )
267 FetchToken.Transfer( from=Spender, to=0x4ACb6C4321253548a7d4Bb9C84032Cc4eE04bFD7, value=4375000000000000000 )
268 Ondo.Transfer( from=Spender, to=[Sender] 0xf707a0cef04f1d40508ed3669e754658f0e102c0, amount=469179641979926675456 )
269 MetaSwap.Swap( 0xbeee1e6e7fe307ddcf84b0a16137a4430ad5e2480fc4f4a8e250ab56ccd7630d, 0x74008a9e83921090a0cc3c37a7b39398a692db963164a76e069ab2e8fb053e10, 0x000000000000000000000000f707a0cef04f1d40508ed3669e754658f0e102c0 )

Account State Difference:

  Address   Before After State Difference Code
(Titan Builder)
11.462170221828802383 Eth11.462398526829715603 Eth0.00022830500091322
0xaea46A60...7EF41Ad85
0xD82E10B9...e6CF2fC46
(AirSwap: V5 DEX SwapERC20)
0xf707a0ce...8f0e102C0
2.341772667071245542 Eth
Nonce: 93
2.341179565986102437 Eth
Nonce: 94
0.000593101085143105
0xfAbA6f8e...577269BE3

Execution Trace

MetaSwap.swap( aggregatorId=airSwap4_3FeeDynamic, tokenFrom=0xaea46A60368A7bD060eec7DF8CBa43b7EF41Ad85, amount=500000000000000000000, data=0x00000000000000000000000000000000000000000000000000000196CF9CF467000000000000000000000000000000000000000000000000000000006830481100000000000000000000000051C72848C68A965F66FA7A88855F9F7784502A7F000000000000000000000000FABA6F8E4A5E8AB82F62FE7C39859FA577269BE30000000000000000000000000000000000000000000000196F2CEE8F7A7B0000000000000000000000000000AEA46A60368A7BD060EEC7DF8CBA43B7EF41AD8500000000000000000000000000000000000000000000001ADE2DB790F2FA8000000000000000000000000000000000000000000000000000000000000000001C389C03B1FE4517874F5FA1D557848E3D2C6CBEE27855952C2BF87907D0638A4D5DC87D2B865BB409A51AA3833A97E595FDEE908CB140904F6AC9554C37B9F86C0000000000000000000000000000000000000000000000003CB71F51FC5580000000000000000000000000004ACB6C4321253548A7D4BB9C84032CC4EE04BFD70000000000000000000000000000000000000000000000000000000000000000 )
  • FetchToken.transferFrom( sender=0xf707a0ceF04F1D40508Ed3669e754658f0e102C0, recipient=0x74de5d4FCbf63E00296fd95d33236B9794016631, amount=500000000000000000000 ) => ( True )
  • Spender.swap( adapter=0x60FBaF99832Fb4360351AbC2b55e4B1F2fe98c71, data=0x4495C088000000000000000000000000F707A0CEF04F1D40508ED3669E754658F0E102C000000000000000000000000000000000000000000000000000000196CF9CF467000000000000000000000000000000000000000000000000000000006830481100000000000000000000000051C72848C68A965F66FA7A88855F9F7784502A7F000000000000000000000000FABA6F8E4A5E8AB82F62FE7C39859FA577269BE30000000000000000000000000000000000000000000000196F2CEE8F7A7B0000000000000000000000000000AEA46A60368A7BD060EEC7DF8CBA43B7EF41AD8500000000000000000000000000000000000000000000001ADE2DB790F2FA8000000000000000000000000000000000000000000000000000000000000000001C389C03B1FE4517874F5FA1D557848E3D2C6CBEE27855952C2BF87907D0638A4D5DC87D2B865BB409A51AA3833A97E595FDEE908CB140904F6AC9554C37B9F86C0000000000000000000000000000000000000000000000003CB71F51FC5580000000000000000000000000004ACB6C4321253548A7D4BB9C84032CC4EE04BFD70000000000000000000000000000000000000000000000000000000000000000 )
    • 0x60fbaf99832fb4360351abc2b55e4b1f2fe98c71.4495c088( )
      • FetchToken.allowance( owner=0x74de5d4FCbf63E00296fd95d33236B9794016631, spender=0xD82E10B9A4107939e55fCCa9B53A9ede6CF2fC46 ) => ( 115792089237316195423570985008687907853269984665640560415966060364103195070235 )
      • SwapERC20.swapLight( nonce=1747239892071, expiry=1747994641, signerWallet=0x51C72848c68a965f66FA7a88855F9f7784502a7F, signerToken=0xfAbA6f8e4a5E8Ab82F62fe7C39859FA577269BE3, signerAmount=469179641979926675456, senderToken=0xaea46A60368A7bD060eec7DF8CBa43b7EF41Ad85, senderAmount=495625000000000000000, v=28, r=389C03B1FE4517874F5FA1D557848E3D2C6CBEE27855952C2BF87907D0638A4D, s=5DC87D2B865BB409A51AA3833A97E595FDEE908CB140904F6AC9554C37B9F86C )
        • Null: 0x000...001.4e5ca07f( )
        • FetchToken.transferFrom( sender=0x74de5d4FCbf63E00296fd95d33236B9794016631, recipient=0x51C72848c68a965f66FA7a88855F9f7784502a7F, amount=495625000000000000000 ) => ( True )
        • Ondo.transferFrom( src=0x51C72848c68a965f66FA7a88855F9f7784502a7F, dst=0x74de5d4FCbf63E00296fd95d33236B9794016631, rawAmount=469179641979926675456 ) => ( True )
        • Ondo.transferFrom( src=0x51C72848c68a965f66FA7a88855F9f7784502a7F, dst=0xaD30f7EEBD9Bd5150a256F47DA41d4403033CdF0, rawAmount=234589820989963337 ) => ( True )
        • FetchToken.transfer( recipient=0x4ACb6C4321253548a7d4Bb9C84032Cc4eE04bFD7, amount=4375000000000000000 ) => ( True )
        • FetchToken.balanceOf( account=0x74de5d4FCbf63E00296fd95d33236B9794016631 ) => ( 0 )
        • Ondo.balanceOf( account=0x74de5d4FCbf63E00296fd95d33236B9794016631 ) => ( 469179641979926675456 )
        • Ondo.transfer( dst=0xf707a0ceF04F1D40508Ed3669e754658f0e102C0, rawAmount=469179641979926675456 ) => ( True )
          File 1 of 6: 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 6: Spender
          {"Constants.84ef19f8.sol":{"content":"// SPDX-License-Identifier: MIT\r\n\r\npragma solidity ^0.6.0;\r\n\r\nlibrary Constants {\r\n    address internal constant ETH = 0x0000000000000000000000000000000000000000;\r\n}\r\n"},"Spender.3372a096.sol":{"content":"// SPDX-License-Identifier: MIT\r\n\r\npragma solidity ^0.6.0;\r\n\r\nimport \"./Constants.84ef19f8.sol\";\r\n\r\ncontract Spender {\r\n    address public immutable metaswap;\r\n\r\n    constructor() public {\r\n        metaswap = msg.sender;\r\n    }\r\n\r\n    /// @dev Receives ether from swaps\r\n    fallback() external payable {}\r\n\r\n    function swap(address adapter, bytes calldata data) external payable {\r\n        require(msg.sender == metaswap, \"FORBIDDEN\");\r\n        require(adapter != address(0), \"ADAPTER_NOT_PROVIDED\");\r\n        _delegate(adapter, data, \"ADAPTER_DELEGATECALL_FAILED\");\r\n    }\r\n\r\n    /**\r\n     * @dev Performs a delegatecall and bubbles up the errors, adapted from\r\n     * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol\r\n     * @param target Address of the contract to delegatecall\r\n     * @param data Data passed in the delegatecall\r\n     * @param errorMessage Fallback revert reason\r\n     */\r\n    function _delegate(\r\n        address target,\r\n        bytes memory data,\r\n        string memory errorMessage\r\n    ) private returns (bytes memory) {\r\n        // solhint-disable-next-line avoid-low-level-calls\r\n        (bool success, bytes memory returndata) = target.delegatecall(data);\r\n        if (success) {\r\n            return returndata;\r\n        } else {\r\n            // Look for revert reason and bubble it up if present\r\n            if (returndata.length \u003e 0) {\r\n                // The easiest way to bubble the revert reason is using memory via assembly\r\n\r\n                // solhint-disable-next-line no-inline-assembly\r\n                assembly {\r\n                    let returndata_size := mload(returndata)\r\n                    revert(add(32, returndata), returndata_size)\r\n                }\r\n            } else {\r\n                revert(errorMessage);\r\n            }\r\n        }\r\n    }\r\n}\r\n"}}

          File 3 of 6: FetchToken
          // SPDX-License-Identifier: Apache-2.0
          pragma solidity 0.6.2;
          
          /**
           * @dev Library for managing
           * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
           * types.
           *
           * Sets have the following properties:
           *
           * - Elements are added, removed, and checked for existence in constant time
           * (O(1)).
           * - Elements are enumerated in O(n). No guarantees are made on the ordering.
           *
           * ```
           * contract Example {
           *     // Add the library methods
           *     using EnumerableSet for EnumerableSet.AddressSet;
           *
           *     // Declare a set state variable
           *     EnumerableSet.AddressSet private mySet;
           * }
           * ```
           *
           * As of v3.0.0, only sets of type `address` (`AddressSet`) and `uint256`
           * (`UintSet`) are supported.
           */
          library EnumerableSet {
              // To implement this library for multiple types with as little code
              // repetition as possible, we write it in terms of a generic Set type with
              // bytes32 values.
              // The Set implementation uses private functions, and user-facing
              // implementations (such as AddressSet) are just wrappers around the
              // underlying Set.
              // This means that we can only create new EnumerableSets for types that fit
              // in bytes32.
          
              struct Set {
                  // Storage of set values
                  bytes32[] _values;
          
                  // Position of the value in the `values` array, plus 1 because index 0
                  // means a value is not in the set.
                  mapping (bytes32 => uint256) _indexes;
              }
          
              /**
               * @dev Add a value to a set. O(1).
               *
               * Returns true if the value was added to the set, that is if it was not
               * already present.
               */
              function _add(Set storage set, bytes32 value) private returns (bool) {
                  if (!_contains(set, value)) {
                      set._values.push(value);
                      // The value is stored at length-1, but we add 1 to all indexes
                      // and use 0 as a sentinel value
                      set._indexes[value] = set._values.length;
                      return true;
                  } else {
                      return false;
                  }
              }
          
              /**
               * @dev Removes a value from a set. O(1).
               *
               * Returns true if the value was removed from the set, that is if it was
               * present.
               */
              function _remove(Set storage set, bytes32 value) private returns (bool) {
                  // We read and store the value's index to prevent multiple reads from the same storage slot
                  uint256 valueIndex = set._indexes[value];
          
                  if (valueIndex != 0) { // Equivalent to contains(set, value)
                      // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
                      // the array, and then remove the last element (sometimes called as 'swap and pop').
                      // This modifies the order of the array, as noted in {at}.
          
                      uint256 toDeleteIndex = valueIndex - 1;
                      uint256 lastIndex = set._values.length - 1;
          
                      // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
                      // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.
          
                      bytes32 lastvalue = set._values[lastIndex];
          
                      // Move the last value to the index where the value to delete is
                      set._values[toDeleteIndex] = lastvalue;
                      // Update the index for the moved value
                      set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based
          
                      // Delete the slot where the moved value was stored
                      set._values.pop();
          
                      // Delete the index for the deleted slot
                      delete set._indexes[value];
          
                      return true;
                  } else {
                      return false;
                  }
              }
          
              /**
               * @dev Returns true if the value is in the set. O(1).
               */
              function _contains(Set storage set, bytes32 value) private view returns (bool) {
                  return set._indexes[value] != 0;
              }
          
              /**
               * @dev Returns the number of values on the set. O(1).
               */
              function _length(Set storage set) private view returns (uint256) {
                  return set._values.length;
              }
          
             /**
              * @dev Returns the value stored at position `index` in the set. O(1).
              *
              * Note that there are no guarantees on the ordering of values inside the
              * array, and it may change when more values are added or removed.
              *
              * Requirements:
              *
              * - `index` must be strictly less than {length}.
              */
              function _at(Set storage set, uint256 index) private view returns (bytes32) {
                  require(set._values.length > index, "EnumerableSet: index out of bounds");
                  return set._values[index];
              }
          
              // AddressSet
          
              struct AddressSet {
                  Set _inner;
              }
          
              /**
               * @dev Add a value to a set. O(1).
               *
               * Returns true if the value was added to the set, that is if it was not
               * already present.
               */
              function add(AddressSet storage set, address value) internal returns (bool) {
                  return _add(set._inner, bytes32(uint256(value)));
              }
          
              /**
               * @dev Removes a value from a set. O(1).
               *
               * Returns true if the value was removed from the set, that is if it was
               * present.
               */
              function remove(AddressSet storage set, address value) internal returns (bool) {
                  return _remove(set._inner, bytes32(uint256(value)));
              }
          
              /**
               * @dev Returns true if the value is in the set. O(1).
               */
              function contains(AddressSet storage set, address value) internal view returns (bool) {
                  return _contains(set._inner, bytes32(uint256(value)));
              }
          
              /**
               * @dev Returns the number of values in the set. O(1).
               */
              function length(AddressSet storage set) internal view returns (uint256) {
                  return _length(set._inner);
              }
          
             /**
              * @dev Returns the value stored at position `index` in the set. O(1).
              *
              * Note that there are no guarantees on the ordering of values inside the
              * array, and it may change when more values are added or removed.
              *
              * Requirements:
              *
              * - `index` must be strictly less than {length}.
              */
              function at(AddressSet storage set, uint256 index) internal view returns (address) {
                  return address(uint256(_at(set._inner, index)));
              }
          
          
              // UintSet
          
              struct UintSet {
                  Set _inner;
              }
          
              /**
               * @dev Add a value to a set. O(1).
               *
               * Returns true if the value was added to the set, that is if it was not
               * already present.
               */
              function add(UintSet storage set, uint256 value) internal returns (bool) {
                  return _add(set._inner, bytes32(value));
              }
          
              /**
               * @dev Removes a value from a set. O(1).
               *
               * Returns true if the value was removed from the set, that is if it was
               * present.
               */
              function remove(UintSet storage set, uint256 value) internal returns (bool) {
                  return _remove(set._inner, bytes32(value));
              }
          
              /**
               * @dev Returns true if the value is in the set. O(1).
               */
              function contains(UintSet storage set, uint256 value) internal view returns (bool) {
                  return _contains(set._inner, bytes32(value));
              }
          
              /**
               * @dev Returns the number of values on the set. O(1).
               */
              function length(UintSet storage set) internal view returns (uint256) {
                  return _length(set._inner);
              }
          
             /**
              * @dev Returns the value stored at position `index` in the set. O(1).
              *
              * Note that there are no guarantees on the ordering of values inside the
              * array, and it may change when more values are added or removed.
              *
              * Requirements:
              *
              * - `index` must be strictly less than {length}.
              */
              function at(UintSet storage set, uint256 index) internal view returns (uint256) {
                  return uint256(_at(set._inner, index));
              }
          }
          
          /**
           * @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) {
                  // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
                  // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
                  // for accounts without code, i.e. `keccak256('')`
                  bytes32 codehash;
                  bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
                  // solhint-disable-next-line no-inline-assembly
                  assembly { codehash := extcodehash(account) }
                  return (codehash != accountHash && codehash != 0x0);
              }
          
              /**
               * @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 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.
           */
          contract Context {
              // Empty internal constructor, to prevent people from mistakenly deploying
              // an instance of this contract, which should be used via inheritance.
              constructor () internal { }
          
              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;
              }
          }
          
          /**
           * @dev Contract module that allows children to implement role-based access
           * control mechanisms.
           *
           * 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, _msgSender()));
           *     ...
           * }
           * ```
           *
           * 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}.
           */
          abstract contract AccessControl is Context {
              using EnumerableSet for EnumerableSet.AddressSet;
              using Address for address;
          
              struct RoleData {
                  EnumerableSet.AddressSet members;
                  bytes32 adminRole;
              }
          
              mapping (bytes32 => RoleData) private _roles;
          
              bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
          
              /**
               * @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 Returns `true` if `account` has been granted `role`.
               */
              function hasRole(bytes32 role, address account) public view returns (bool) {
                  return _roles[role].members.contains(account);
              }
          
              /**
               * @dev Returns the number of accounts that have `role`. Can be used
               * together with {getRoleMember} to enumerate all bearers of a role.
               */
              function getRoleMemberCount(bytes32 role) public view returns (uint256) {
                  return _roles[role].members.length();
              }
          
              /**
               * @dev Returns one of the accounts that have `role`. `index` must be a
               * value between 0 and {getRoleMemberCount}, non-inclusive.
               *
               * Role bearers are not sorted in any particular way, and their ordering may
               * change at any point.
               *
               * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
               * you perform all queries on the same block. See the following
               * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
               * for more information.
               */
              function getRoleMember(bytes32 role, uint256 index) public view returns (address) {
                  return _roles[role].members.at(index);
              }
          
              /**
               * @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 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 {
                  require(hasRole(_roles[role].adminRole, _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 {
                  require(hasRole(_roles[role].adminRole, _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 {
                  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.
               */
              function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
                  _roles[role].adminRole = adminRole;
              }
          
              function _grantRole(bytes32 role, address account) private {
                  if (_roles[role].members.add(account)) {
                      emit RoleGranted(role, account, _msgSender());
                  }
              }
          
              function _revokeRole(bytes32 role, address account) private {
                  if (_roles[role].members.remove(account)) {
                      emit RoleRevoked(role, account, _msgSender());
                  }
              }
          }
          
          
          
          /**
           * @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);
          }
          
          /**
           * @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) {
                  // Solidity only automatically asserts when dividing by 0
                  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;
              }
          }
          
          /**
           * @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 {ERC20MinterPauser}.
           *
           * 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 is 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 { }
          }
          
          
          /**
           * @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.
               */
              modifier whenNotPaused() {
                  require(!_paused, "Pausable: paused");
                  _;
              }
          
              /**
               * @dev Modifier to make a function callable only when the contract is paused.
               */
              modifier whenPaused() {
                  require(_paused, "Pausable: not paused");
                  _;
              }
          
              /**
               * @dev Triggers stopped state.
               */
              function _pause() internal virtual whenNotPaused {
                  _paused = true;
                  emit Paused(_msgSender());
              }
          
              /**
               * @dev Returns to normal state.
               */
              function _unpause() internal virtual whenPaused {
                  _paused = false;
                  emit Unpaused(_msgSender());
              }
          }
          
          /**
           * @dev ERC20 token with pausable token transfers, minting and burning.
           *
           * Useful for scenarios such as preventing trades until the end of an evaluation
           * period, or having an emergency switch for freezing all token transfers in the
           * event of a large bug.
           */
          abstract contract ERC20Pausable is ERC20, Pausable {
              /**
               * @dev See {ERC20-_beforeTokenTransfer}.
               *
               * Requirements:
               *
               * - the contract must not be paused.
               */
              function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override {
                  super._beforeTokenTransfer(from, to, amount);
          
                  require(!paused(), "ERC20Pausable: token transfer while paused");
              }
          }
          
          
          /**
           * @dev Extension of {ERC20} that allows token holders to destroy both their own
           * tokens and those that they have an allowance for, in a way that can be
           * recognized off-chain (via event analysis).
           */
          abstract contract ERC20Burnable is Context, ERC20 {
              /**
               * @dev Destroys `amount` tokens from the caller.
               *
               * See {ERC20-_burn}.
               */
              function burn(uint256 amount) external {
                  _burn(_msgSender(), amount);
              }
          
              /**
               * @dev Destroys `amount` tokens from `account`, deducting from the caller's
               * allowance.
               *
               * See {ERC20-_burn} and {ERC20-allowance}.
               *
               * Requirements:
               *
               * - the caller must have allowance for ``accounts``'s tokens of at least
               * `amount`.
               */
              function burnFrom(address account, uint256 amount) external {
                  uint256 decreasedAllowance = allowance(account, _msgSender()).sub(amount, "ERC20: burn amount exceeds allowance");
          
                  _approve(account, _msgSender(), decreasedAllowance);
                  _burn(account, amount);
              }
          }
          
          /**
           * @dev {ERC20} token, including:
           *
           *  - ability for holders to burn (destroy) their tokens
           *  - a minter role that allows for token minting (creation)
           *  - a pauser role that allows to stop all token transfers
           *
           * This contract uses {AccessControl} to lock permissioned functions using the
           * different roles - head to its documentation for details.
           *
           * The account that deploys the contract will be granted the minter and pauser
           * roles, as well as the default admin role, which will let it grant both minter
           * and pauser roles to other accounts.
           */
          contract FetchToken is Context, AccessControl, ERC20Burnable, ERC20Pausable {
              bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
              bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
          
              /**
               * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE` and `PAUSER_ROLE` to the
               * account that deploys the contract.
               *
               * See {ERC20-constructor}.
               */
          
          
              modifier onlyMinter {
                  require(hasRole(MINTER_ROLE, _msgSender()), "signer must have minter role to mint");
                  _; 
                  }
          
              modifier onlyPauser {
                  require(hasRole(PAUSER_ROLE, _msgSender()), "signer must have pauser role to pause/unpause");
                  _; 
                  }
          
          
              constructor(string memory name, 
                          string memory symbol, 
                          uint256 _initialSupply) public ERC20(name, symbol) {
                  _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
          
                  _setupRole(MINTER_ROLE, _msgSender());
                  _setupRole(PAUSER_ROLE, _msgSender());
                  _mint(_msgSender(), _initialSupply); 
              }
          
              /**
               * @dev Creates `amount` new tokens for `to`.
               *
               * See {ERC20-_mint}.
               *
               * Requirements:
               *
               * - the caller must have the `MINTER_ROLE`.
               */
              function mint(address to, uint256 amount) onlyMinter external {
                  _mint(to, amount);
              }
          
              /**
               * @dev Pauses all token transfers.
               *
               * See {ERC20Pausable} and {Pausable-_pause}.
               *
               * Requirements:
               *
               * - the caller must have the `PAUSER_ROLE`.
               */
              function pause() onlyPauser external {
                  _pause();
              }
          
              /**
               * @dev Unpauses all token transfers.
               *
               * See {ERC20Pausable} and {Pausable-_unpause}.
               *
               * Requirements:
               *
               * - the caller must have the `PAUSER_ROLE`.
               */
              function unpause() onlyPauser external {
                  _unpause();
              }
          
              function _beforeTokenTransfer(address from, address to, uint256 amount) internal override(ERC20, ERC20Pausable) {
                  super._beforeTokenTransfer(from, to, amount);
              }
          }

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

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