ETH Price: $3,483.46 (+1.75%)

Transaction Decoder

Block:
22968621 at Jul-21-2025 04:00:35 PM +UTC
Transaction Fee:
0.00066706409825061 ETH $2.32
Gas Used:
112,985 Gas / 5.904005826 Gwei

Emitted Events:

218 KeepToken.Approval( owner=[Sender] 0x1868de3d1446e1a1d580937211de544fb814d1b2, spender=VendingMachine, value=4832024000000000000000 )
219 VendingMachine.Wrapped( recipient=[Sender] 0x1868de3d1446e1a1d580937211de544fb814d1b2, wrappedTokenAmount=4832024000000000000000, tTokenAmount=23112482262751387432384 )
220 KeepToken.Transfer( from=[Sender] 0x1868de3d1446e1a1d580937211de544fb814d1b2, to=VendingMachine, value=4832024000000000000000 )
221 KeepToken.Approval( owner=[Sender] 0x1868de3d1446e1a1d580937211de544fb814d1b2, spender=VendingMachine, value=0 )
222 T.Transfer( from=VendingMachine, to=[Sender] 0x1868de3d1446e1a1d580937211de544fb814d1b2, value=23112482262751387432384 )

Account State Difference:

  Address   Before After State Difference Code
0x1868dE3d...Fb814d1b2
0.004052 Eth
Nonce: 0
0.00338493590174939 Eth
Nonce: 1
0.00066706409825061
(Titan Builder)
19.203180933502219057 Eth19.203293918502219057 Eth0.000112985
0x85Eee30c...c3009aFEC
0xCdF7028c...72994beE5
T
0xE47c80e8...9D5D16bb0
(Threshold Network : Vending Machine 2)

Execution Trace

KeepToken.approveAndCall( _spender=0xE47c80e8c23f6B4A1aE41c34837a0599D5D16bb0, _value=4832024000000000000000, _extraData=0x ) => ( success=True )
  • VendingMachine.receiveApproval( from=0x1868dE3d1446E1A1D580937211DE544Fb814d1b2, amount=4832024000000000000000, token=0x85Eee30c52B0b379b046Fb0F85F4f3Dc3009aFEC, 0x )
    • KeepToken.transferFrom( sender=0x1868dE3d1446E1A1D580937211DE544Fb814d1b2, recipient=0xE47c80e8c23f6B4A1aE41c34837a0599D5D16bb0, amount=4832024000000000000000 ) => ( True )
    • T.transfer( recipient=0x1868dE3d1446E1A1D580937211DE544Fb814d1b2, amount=23112482262751387432384 ) => ( True )
      approveAndCall[KeepToken (ln:42)]
      File 1 of 3: KeepToken
      /**
      ▓▓▌ ▓▓ ▐▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▄
      ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
        ▓▓▓▓▓▓    ▓▓▓▓▓▓▓▀    ▐▓▓▓▓▓▓    ▐▓▓▓▓▓   ▓▓▓▓▓▓     ▓▓▓▓▓   ▐▓▓▓▓▓▌   ▐▓▓▓▓▓▓
        ▓▓▓▓▓▓▄▄▓▓▓▓▓▓▓▀      ▐▓▓▓▓▓▓▄▄▄▄         ▓▓▓▓▓▓▄▄▄▄         ▐▓▓▓▓▓▌   ▐▓▓▓▓▓▓
        ▓▓▓▓▓▓▓▓▓▓▓▓▓▀        ▐▓▓▓▓▓▓▓▓▓▓         ▓▓▓▓▓▓▓▓▓▓▌        ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
        ▓▓▓▓▓▓▀▀▓▓▓▓▓▓▄       ▐▓▓▓▓▓▓▀▀▀▀         ▓▓▓▓▓▓▀▀▀▀         ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▀
        ▓▓▓▓▓▓   ▀▓▓▓▓▓▓▄     ▐▓▓▓▓▓▓     ▓▓▓▓▓   ▓▓▓▓▓▓     ▓▓▓▓▓   ▐▓▓▓▓▓▌
      ▓▓▓▓▓▓▓▓▓▓ █▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓  ▓▓▓▓▓▓▓▓▓▓
      ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓  ▓▓▓▓▓▓▓▓▓▓
                                 Trust math, not hardware.
      */
      pragma solidity 0.5.17;
      import "openzeppelin-solidity/contracts/token/ERC20/ERC20Burnable.sol";
      import "openzeppelin-solidity/contracts/token/ERC20/ERC20Detailed.sol";
      /// @dev Interface of recipient contract for approveAndCall pattern.
      interface tokenRecipient {
          function receiveApproval(
              address _from,
              uint256 _value,
              address _token,
              bytes calldata _extraData
          ) external;
      }
      /// @title KEEP Token
      /// @dev Standard ERC20Burnable token
      contract KeepToken is ERC20Burnable, ERC20Detailed {
          string public constant NAME = "KEEP Token";
          string public constant SYMBOL = "KEEP";
          uint8 public constant DECIMALS = 18; // The number of digits after the decimal place when displaying token values on-screen.
          uint256 public constant INITIAL_SUPPLY = 10**27; // 1 billion tokens, 18 decimal places.
          /// @dev Gives msg.sender all of existing tokens.
          constructor() public ERC20Detailed(NAME, SYMBOL, DECIMALS) {
              _mint(msg.sender, INITIAL_SUPPLY);
          }
          /// @notice Set allowance for other address and notify.
          /// Allows `_spender` to spend no more than `_value` tokens
          /// on your behalf and then ping the contract about it.
          /// @param _spender The address authorized to spend.
          /// @param _value The max amount they can spend.
          /// @param _extraData Extra information to send to the approved contract.
          function approveAndCall(
              address _spender,
              uint256 _value,
              bytes memory _extraData
          ) public returns (bool success) {
              tokenRecipient spender = tokenRecipient(_spender);
              if (approve(_spender, _value)) {
                  spender.receiveApproval(
                      msg.sender,
                      _value,
                      address(this),
                      _extraData
                  );
                  return true;
              }
          }
      }
      pragma solidity ^0.5.0;
      import "../../GSN/Context.sol";
      import "./ERC20.sol";
      /**
       * @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).
       */
      contract ERC20Burnable is Context, ERC20 {
          /**
           * @dev Destroys `amount` tokens from the caller.
           *
           * See {ERC20-_burn}.
           */
          function burn(uint256 amount) public {
              _burn(_msgSender(), amount);
          }
          /**
           * @dev See {ERC20-_burnFrom}.
           */
          function burnFrom(address account, uint256 amount) public {
              _burnFrom(account, amount);
          }
      }
      pragma solidity ^0.5.0;
      import "./IERC20.sol";
      /**
       * @dev Optional functions from the ERC20 standard.
       */
      contract ERC20Detailed is IERC20 {
          string private _name;
          string private _symbol;
          uint8 private _decimals;
          /**
           * @dev Sets the values for `name`, `symbol`, and `decimals`. All three of
           * these values are immutable: they can only be set once during
           * construction.
           */
          constructor (string memory name, string memory symbol, uint8 decimals) public {
              _name = name;
              _symbol = symbol;
              _decimals = decimals;
          }
          /**
           * @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.
           *
           * 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;
          }
      }
      pragma solidity ^0.5.0;
      import "../../GSN/Context.sol";
      import "./IERC20.sol";
      import "../../math/SafeMath.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 {ERC20Mintable}.
       *
       * 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;
          mapping (address => uint256) private _balances;
          mapping (address => mapping (address => uint256)) private _allowances;
          uint256 private _totalSupply;
          /**
           * @dev See {IERC20-totalSupply}.
           */
          function totalSupply() public view returns (uint256) {
              return _totalSupply;
          }
          /**
           * @dev See {IERC20-balanceOf}.
           */
          function balanceOf(address account) public view 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 returns (bool) {
              _transfer(_msgSender(), recipient, amount);
              return true;
          }
          /**
           * @dev See {IERC20-allowance}.
           */
          function allowance(address owner, address spender) public view returns (uint256) {
              return _allowances[owner][spender];
          }
          /**
           * @dev See {IERC20-approve}.
           *
           * Requirements:
           *
           * - `spender` cannot be the zero address.
           */
          function approve(address spender, uint256 amount) public 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 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 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 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 {
              require(sender != address(0), "ERC20: transfer from the zero address");
              require(recipient != address(0), "ERC20: transfer to the zero address");
              _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 {
              require(account != address(0), "ERC20: mint to the zero address");
              _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 {
              require(account != address(0), "ERC20: burn from the zero address");
              _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 {
              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 Destroys `amount` tokens from `account`.`amount` is then deducted
           * from the caller's allowance.
           *
           * See {_burn} and {_approve}.
           */
          function _burnFrom(address account, uint256 amount) internal {
              _burn(account, amount);
              _approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance"));
          }
      }
      pragma solidity ^0.5.0;
      /**
       * @dev Interface of the ERC20 standard as defined in the EIP. Does not include
       * the optional functions; to access them see {ERC20Detailed}.
       */
      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);
      }
      pragma solidity ^0.5.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.
       */
      contract Context {
          // Empty internal constructor, to prevent people from mistakenly deploying
          // an instance of this contract, which should be used via inheritance.
          constructor () internal { }
          // solhint-disable-previous-line no-empty-blocks
          function _msgSender() internal view returns (address payable) {
              return msg.sender;
          }
          function _msgData() internal view returns (bytes memory) {
              this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
              return msg.data;
          }
      }
      pragma solidity ^0.5.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.
           *
           * _Available since v2.4.0._
           */
          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.
           *
           * _Available since v2.4.0._
           */
          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.
           *
           * _Available since v2.4.0._
           */
          function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              require(b != 0, errorMessage);
              return a % b;
          }
      }
      

      File 2 of 3: VendingMachine
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.0 (access/Ownable.sol)
      pragma solidity ^0.8.0;
      import "../utils/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.
       */
      abstract contract Ownable is Context {
          address private _owner;
          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
          /**
           * @dev Initializes the contract setting the deployer as the initial owner.
           */
          constructor() {
              _transferOwnership(_msgSender());
          }
          /**
           * @dev Returns the address of the current owner.
           */
          function owner() public view virtual returns (address) {
              return _owner;
          }
          /**
           * @dev Throws if called by any account other than the owner.
           */
          modifier onlyOwner() {
              require(owner() == _msgSender(), "Ownable: caller is not the owner");
              _;
          }
          /**
           * @dev Leaves the contract without owner. It will not be possible to call
           * `onlyOwner` functions anymore. Can only be called by the current owner.
           *
           * NOTE: Renouncing ownership will leave the contract without an owner,
           * thereby removing any functionality that is only available to the owner.
           */
          function renounceOwnership() public virtual onlyOwner {
              _transferOwnership(address(0));
          }
          /**
           * @dev Transfers ownership of the contract to a new account (`newOwner`).
           * Can only be called by the current owner.
           */
          function transferOwnership(address newOwner) public virtual onlyOwner {
              require(newOwner != address(0), "Ownable: new owner is the zero address");
              _transferOwnership(newOwner);
          }
          /**
           * @dev Transfers ownership of the contract to a new account (`newOwner`).
           * Internal function without access restriction.
           */
          function _transferOwnership(address newOwner) internal virtual {
              address oldOwner = _owner;
              _owner = newOwner;
              emit OwnershipTransferred(oldOwner, newOwner);
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.0 (token/ERC20/IERC20.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Interface of the ERC20 standard as defined in the EIP.
       */
      interface IERC20 {
          /**
           * @dev Returns the amount of tokens in existence.
           */
          function totalSupply() external view returns (uint256);
          /**
           * @dev Returns the amount of tokens owned by `account`.
           */
          function balanceOf(address account) external view returns (uint256);
          /**
           * @dev Moves `amount` tokens from the caller's account to `recipient`.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transfer(address recipient, uint256 amount) external returns (bool);
          /**
           * @dev Returns the remaining number of tokens that `spender` will be
           * allowed to spend on behalf of `owner` through {transferFrom}. This is
           * zero by default.
           *
           * This value changes when {approve} or {transferFrom} are called.
           */
          function allowance(address owner, address spender) external view returns (uint256);
          /**
           * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * IMPORTANT: Beware that changing an allowance with this method brings the risk
           * that someone may use both the old and the new allowance by unfortunate
           * transaction ordering. One possible solution to mitigate this race
           * condition is to first reduce the spender's allowance to 0 and set the
           * desired value afterwards:
           * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
           *
           * Emits an {Approval} event.
           */
          function approve(address spender, uint256 amount) external returns (bool);
          /**
           * @dev Moves `amount` tokens from `sender` to `recipient` using the
           * allowance mechanism. `amount` is then deducted from the caller's
           * allowance.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transferFrom(
              address sender,
              address recipient,
              uint256 amount
          ) external returns (bool);
          /**
           * @dev Emitted when `value` tokens are moved from one account (`from`) to
           * another (`to`).
           *
           * Note that `value` may be zero.
           */
          event Transfer(address indexed from, address indexed to, uint256 value);
          /**
           * @dev Emitted when the allowance of a `spender` for an `owner` is set by
           * a call to {approve}. `value` is the new allowance.
           */
          event Approval(address indexed owner, address indexed spender, uint256 value);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.0 (token/ERC20/extensions/IERC20Metadata.sol)
      pragma solidity ^0.8.0;
      import "../IERC20.sol";
      /**
       * @dev Interface for the optional metadata functions from the ERC20 standard.
       *
       * _Available since v4.1._
       */
      interface IERC20Metadata is IERC20 {
          /**
           * @dev Returns the name of the token.
           */
          function name() external view returns (string memory);
          /**
           * @dev Returns the symbol of the token.
           */
          function symbol() external view returns (string memory);
          /**
           * @dev Returns the decimals places of the token.
           */
          function decimals() external view returns (uint8);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.0 (token/ERC20/utils/SafeERC20.sol)
      pragma solidity ^0.8.0;
      import "../IERC20.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 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'
              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) + value;
              _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
          }
          function safeDecreaseAllowance(
              IERC20 token,
              address spender,
              uint256 value
          ) internal {
              unchecked {
                  uint256 oldAllowance = token.allowance(address(this), spender);
                  require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                  uint256 newAllowance = oldAllowance - value;
                  _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
                  require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.0 (token/ERC721/IERC721.sol)
      pragma solidity ^0.8.0;
      import "../../utils/introspection/IERC165.sol";
      /**
       * @dev Required interface of an ERC721 compliant contract.
       */
      interface IERC721 is IERC165 {
          /**
           * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
           */
          event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
          /**
           * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
           */
          event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
          /**
           * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
           */
          event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
          /**
           * @dev Returns the number of tokens in ``owner``'s account.
           */
          function balanceOf(address owner) external view returns (uint256 balance);
          /**
           * @dev Returns the owner of the `tokenId` token.
           *
           * Requirements:
           *
           * - `tokenId` must exist.
           */
          function ownerOf(uint256 tokenId) external view returns (address owner);
          /**
           * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
           * are aware of the ERC721 protocol to prevent tokens from being forever locked.
           *
           * Requirements:
           *
           * - `from` cannot be the zero address.
           * - `to` cannot be the zero address.
           * - `tokenId` token must exist and be owned by `from`.
           * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
           * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
           *
           * Emits a {Transfer} event.
           */
          function safeTransferFrom(
              address from,
              address to,
              uint256 tokenId
          ) external;
          /**
           * @dev Transfers `tokenId` token from `from` to `to`.
           *
           * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
           *
           * Requirements:
           *
           * - `from` cannot be the zero address.
           * - `to` cannot be the zero address.
           * - `tokenId` token must be owned by `from`.
           * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
           *
           * Emits a {Transfer} event.
           */
          function transferFrom(
              address from,
              address to,
              uint256 tokenId
          ) external;
          /**
           * @dev Gives permission to `to` to transfer `tokenId` token to another account.
           * The approval is cleared when the token is transferred.
           *
           * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
           *
           * Requirements:
           *
           * - The caller must own the token or be an approved operator.
           * - `tokenId` must exist.
           *
           * Emits an {Approval} event.
           */
          function approve(address to, uint256 tokenId) external;
          /**
           * @dev Returns the account approved for `tokenId` token.
           *
           * Requirements:
           *
           * - `tokenId` must exist.
           */
          function getApproved(uint256 tokenId) external view returns (address operator);
          /**
           * @dev Approve or remove `operator` as an operator for the caller.
           * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
           *
           * Requirements:
           *
           * - The `operator` cannot be the caller.
           *
           * Emits an {ApprovalForAll} event.
           */
          function setApprovalForAll(address operator, bool _approved) external;
          /**
           * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
           *
           * See {setApprovalForAll}
           */
          function isApprovedForAll(address owner, address operator) external view returns (bool);
          /**
           * @dev Safely transfers `tokenId` token from `from` to `to`.
           *
           * Requirements:
           *
           * - `from` cannot be the zero address.
           * - `to` cannot be the zero address.
           * - `tokenId` token must exist and be owned by `from`.
           * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
           * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
           *
           * Emits a {Transfer} event.
           */
          function safeTransferFrom(
              address from,
              address to,
              uint256 tokenId,
              bytes calldata data
          ) external;
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.0 (utils/Address.sol)
      pragma solidity ^0.8.0;
      /**
       * @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 on extcodesize, which returns 0 for contracts in
              // construction, since the code is only stored at the end of the
              // constructor execution.
              uint256 size;
              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");
              (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");
              require(isContract(target), "Address: call to non-contract");
              (bool success, bytes memory returndata) = target.call{value: value}(data);
              return verifyCallResult(success, returndata, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
              return functionStaticCall(target, data, "Address: low-level static call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal view returns (bytes memory) {
              require(isContract(target), "Address: static call to non-contract");
              (bool success, bytes memory returndata) = target.staticcall(data);
              return verifyCallResult(success, returndata, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.4._
           */
          function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionDelegateCall(target, data, "Address: low-level delegate call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.4._
           */
          function functionDelegateCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal returns (bytes memory) {
              require(isContract(target), "Address: delegate call to non-contract");
              (bool success, bytes memory returndata) = target.delegatecall(data);
              return verifyCallResult(success, returndata, errorMessage);
          }
          /**
           * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
           * revert reason using the provided one.
           *
           * _Available since v4.3._
           */
          function verifyCallResult(
              bool success,
              bytes memory returndata,
              string memory errorMessage
          ) internal pure returns (bytes memory) {
              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
                      assembly {
                          let returndata_size := mload(returndata)
                          revert(add(32, returndata), returndata_size)
                      }
                  } else {
                      revert(errorMessage);
                  }
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.0 (utils/Context.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Provides information about the current execution context, including the
       * sender of the transaction and its data. While these are generally available
       * via msg.sender and msg.data, they should not be accessed in such a direct
       * manner, since when dealing with meta-transactions the account sending and
       * paying for execution may not be the actual sender (as far as an application
       * is concerned).
       *
       * This contract is only required for intermediate, library-like contracts.
       */
      abstract contract Context {
          function _msgSender() internal view virtual returns (address) {
              return msg.sender;
          }
          function _msgData() internal view virtual returns (bytes calldata) {
              return msg.data;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.0 (utils/Strings.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev String operations.
       */
      library Strings {
          bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
          /**
           * @dev Converts a `uint256` to its ASCII `string` decimal representation.
           */
          function toString(uint256 value) internal pure returns (string memory) {
              // Inspired by OraclizeAPI's implementation - MIT licence
              // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
              if (value == 0) {
                  return "0";
              }
              uint256 temp = value;
              uint256 digits;
              while (temp != 0) {
                  digits++;
                  temp /= 10;
              }
              bytes memory buffer = new bytes(digits);
              while (value != 0) {
                  digits -= 1;
                  buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
                  value /= 10;
              }
              return string(buffer);
          }
          /**
           * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
           */
          function toHexString(uint256 value) internal pure returns (string memory) {
              if (value == 0) {
                  return "0x00";
              }
              uint256 temp = value;
              uint256 length = 0;
              while (temp != 0) {
                  length++;
                  temp >>= 8;
              }
              return toHexString(value, length);
          }
          /**
           * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
           */
          function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
              bytes memory buffer = new bytes(2 * length + 2);
              buffer[0] = "0";
              buffer[1] = "x";
              for (uint256 i = 2 * length + 1; i > 1; --i) {
                  buffer[i] = _HEX_SYMBOLS[value & 0xf];
                  value >>= 4;
              }
              require(value == 0, "Strings: hex length insufficient");
              return string(buffer);
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.0 (utils/cryptography/ECDSA.sol)
      pragma solidity ^0.8.0;
      import "../Strings.sol";
      /**
       * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
       *
       * These functions can be used to verify that a message was signed by the holder
       * of the private keys of a given address.
       */
      library ECDSA {
          enum RecoverError {
              NoError,
              InvalidSignature,
              InvalidSignatureLength,
              InvalidSignatureS,
              InvalidSignatureV
          }
          function _throwError(RecoverError error) private pure {
              if (error == RecoverError.NoError) {
                  return; // no error: do nothing
              } else if (error == RecoverError.InvalidSignature) {
                  revert("ECDSA: invalid signature");
              } else if (error == RecoverError.InvalidSignatureLength) {
                  revert("ECDSA: invalid signature length");
              } else if (error == RecoverError.InvalidSignatureS) {
                  revert("ECDSA: invalid signature 's' value");
              } else if (error == RecoverError.InvalidSignatureV) {
                  revert("ECDSA: invalid signature 'v' value");
              }
          }
          /**
           * @dev Returns the address that signed a hashed message (`hash`) with
           * `signature` or error string. This address can then be used for verification purposes.
           *
           * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
           * this function rejects them by requiring the `s` value to be in the lower
           * half order, and the `v` value to be either 27 or 28.
           *
           * IMPORTANT: `hash` _must_ be the result of a hash operation for the
           * verification to be secure: it is possible to craft signatures that
           * recover to arbitrary addresses for non-hashed data. A safe way to ensure
           * this is by receiving a hash of the original message (which may otherwise
           * be too long), and then calling {toEthSignedMessageHash} on it.
           *
           * Documentation for signature generation:
           * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
           * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
           *
           * _Available since v4.3._
           */
          function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
              // Check the signature length
              // - case 65: r,s,v signature (standard)
              // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
              if (signature.length == 65) {
                  bytes32 r;
                  bytes32 s;
                  uint8 v;
                  // ecrecover takes the signature parameters, and the only way to get them
                  // currently is to use assembly.
                  assembly {
                      r := mload(add(signature, 0x20))
                      s := mload(add(signature, 0x40))
                      v := byte(0, mload(add(signature, 0x60)))
                  }
                  return tryRecover(hash, v, r, s);
              } else if (signature.length == 64) {
                  bytes32 r;
                  bytes32 vs;
                  // ecrecover takes the signature parameters, and the only way to get them
                  // currently is to use assembly.
                  assembly {
                      r := mload(add(signature, 0x20))
                      vs := mload(add(signature, 0x40))
                  }
                  return tryRecover(hash, r, vs);
              } else {
                  return (address(0), RecoverError.InvalidSignatureLength);
              }
          }
          /**
           * @dev Returns the address that signed a hashed message (`hash`) with
           * `signature`. This address can then be used for verification purposes.
           *
           * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
           * this function rejects them by requiring the `s` value to be in the lower
           * half order, and the `v` value to be either 27 or 28.
           *
           * IMPORTANT: `hash` _must_ be the result of a hash operation for the
           * verification to be secure: it is possible to craft signatures that
           * recover to arbitrary addresses for non-hashed data. A safe way to ensure
           * this is by receiving a hash of the original message (which may otherwise
           * be too long), and then calling {toEthSignedMessageHash} on it.
           */
          function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
              (address recovered, RecoverError error) = tryRecover(hash, signature);
              _throwError(error);
              return recovered;
          }
          /**
           * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
           *
           * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
           *
           * _Available since v4.3._
           */
          function tryRecover(
              bytes32 hash,
              bytes32 r,
              bytes32 vs
          ) internal pure returns (address, RecoverError) {
              bytes32 s;
              uint8 v;
              assembly {
                  s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
                  v := add(shr(255, vs), 27)
              }
              return tryRecover(hash, v, r, s);
          }
          /**
           * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
           *
           * _Available since v4.2._
           */
          function recover(
              bytes32 hash,
              bytes32 r,
              bytes32 vs
          ) internal pure returns (address) {
              (address recovered, RecoverError error) = tryRecover(hash, r, vs);
              _throwError(error);
              return recovered;
          }
          /**
           * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
           * `r` and `s` signature fields separately.
           *
           * _Available since v4.3._
           */
          function tryRecover(
              bytes32 hash,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) internal pure returns (address, RecoverError) {
              // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
              // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
              // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
              // signatures from current libraries generate a unique signature with an s-value in the lower half order.
              //
              // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
              // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
              // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
              // these malleable signatures as well.
              if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
                  return (address(0), RecoverError.InvalidSignatureS);
              }
              if (v != 27 && v != 28) {
                  return (address(0), RecoverError.InvalidSignatureV);
              }
              // If the signature is valid (and not malleable), return the signer address
              address signer = ecrecover(hash, v, r, s);
              if (signer == address(0)) {
                  return (address(0), RecoverError.InvalidSignature);
              }
              return (signer, RecoverError.NoError);
          }
          /**
           * @dev Overload of {ECDSA-recover} that receives the `v`,
           * `r` and `s` signature fields separately.
           */
          function recover(
              bytes32 hash,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) internal pure returns (address) {
              (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
              _throwError(error);
              return recovered;
          }
          /**
           * @dev Returns an Ethereum Signed Message, created from a `hash`. This
           * produces hash corresponding to the one signed with the
           * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
           * JSON-RPC method as part of EIP-191.
           *
           * See {recover}.
           */
          function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
              // 32 is the length in bytes of hash,
              // enforced by the type signature above
              return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
      32", hash));
          }
          /**
           * @dev Returns an Ethereum Signed Message, created from `s`. This
           * produces hash corresponding to the one signed with the
           * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
           * JSON-RPC method as part of EIP-191.
           *
           * See {recover}.
           */
          function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
              return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
      ", Strings.toString(s.length), s));
          }
          /**
           * @dev Returns an Ethereum Signed Typed Data, created from a
           * `domainSeparator` and a `structHash`. This produces hash corresponding
           * to the one signed with the
           * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
           * JSON-RPC method as part of EIP-712.
           *
           * See {recover}.
           */
          function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
              return keccak256(abi.encodePacked("\\x19\\x01", domainSeparator, structHash));
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.0 (utils/introspection/IERC165.sol)
      pragma solidity ^0.8.0;
      /**
       * @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);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.0 (utils/math/Math.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Standard math utilities missing in the Solidity language.
       */
      library Math {
          /**
           * @dev Returns the largest of two numbers.
           */
          function max(uint256 a, uint256 b) internal pure returns (uint256) {
              return a >= b ? a : b;
          }
          /**
           * @dev Returns the smallest of two numbers.
           */
          function min(uint256 a, uint256 b) internal pure returns (uint256) {
              return a < b ? a : b;
          }
          /**
           * @dev Returns the average of two numbers. The result is rounded towards
           * zero.
           */
          function average(uint256 a, uint256 b) internal pure returns (uint256) {
              // (a + b) / 2 can overflow.
              return (a & b) + (a ^ b) / 2;
          }
          /**
           * @dev Returns the ceiling of the division of two numbers.
           *
           * This differs from standard division with `/` in that it rounds up instead
           * of rounding down.
           */
          function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
              // (a + b - 1) / b can overflow on addition, so we distribute.
              return a / b + (a % b == 0 ? 0 : 1);
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.0 (utils/math/SafeCast.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
       * checks.
       *
       * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
       * easily result in undesired exploitation or bugs, since developers usually
       * assume that overflows raise errors. `SafeCast` restores this intuition by
       * reverting the transaction when such 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.
       *
       * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
       * all math on `uint256` and `int256` and then downcasting.
       */
      library SafeCast {
          /**
           * @dev Returns the downcasted uint224 from uint256, reverting on
           * overflow (when the input is greater than largest uint224).
           *
           * Counterpart to Solidity's `uint224` operator.
           *
           * Requirements:
           *
           * - input must fit into 224 bits
           */
          function toUint224(uint256 value) internal pure returns (uint224) {
              require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
              return uint224(value);
          }
          /**
           * @dev Returns the downcasted uint128 from uint256, reverting on
           * overflow (when the input is greater than largest uint128).
           *
           * Counterpart to Solidity's `uint128` operator.
           *
           * Requirements:
           *
           * - input must fit into 128 bits
           */
          function toUint128(uint256 value) internal pure returns (uint128) {
              require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
              return uint128(value);
          }
          /**
           * @dev Returns the downcasted uint96 from uint256, reverting on
           * overflow (when the input is greater than largest uint96).
           *
           * Counterpart to Solidity's `uint96` operator.
           *
           * Requirements:
           *
           * - input must fit into 96 bits
           */
          function toUint96(uint256 value) internal pure returns (uint96) {
              require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
              return uint96(value);
          }
          /**
           * @dev Returns the downcasted uint64 from uint256, reverting on
           * overflow (when the input is greater than largest uint64).
           *
           * Counterpart to Solidity's `uint64` operator.
           *
           * Requirements:
           *
           * - input must fit into 64 bits
           */
          function toUint64(uint256 value) internal pure returns (uint64) {
              require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
              return uint64(value);
          }
          /**
           * @dev Returns the downcasted uint32 from uint256, reverting on
           * overflow (when the input is greater than largest uint32).
           *
           * Counterpart to Solidity's `uint32` operator.
           *
           * Requirements:
           *
           * - input must fit into 32 bits
           */
          function toUint32(uint256 value) internal pure returns (uint32) {
              require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
              return uint32(value);
          }
          /**
           * @dev Returns the downcasted uint16 from uint256, reverting on
           * overflow (when the input is greater than largest uint16).
           *
           * Counterpart to Solidity's `uint16` operator.
           *
           * Requirements:
           *
           * - input must fit into 16 bits
           */
          function toUint16(uint256 value) internal pure returns (uint16) {
              require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
              return uint16(value);
          }
          /**
           * @dev Returns the downcasted uint8 from uint256, reverting on
           * overflow (when the input is greater than largest uint8).
           *
           * Counterpart to Solidity's `uint8` operator.
           *
           * Requirements:
           *
           * - input must fit into 8 bits.
           */
          function toUint8(uint256 value) internal pure returns (uint8) {
              require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
              return uint8(value);
          }
          /**
           * @dev Converts a signed int256 into an unsigned uint256.
           *
           * Requirements:
           *
           * - input must be greater than or equal to 0.
           */
          function toUint256(int256 value) internal pure returns (uint256) {
              require(value >= 0, "SafeCast: value must be positive");
              return uint256(value);
          }
          /**
           * @dev Returns the downcasted int128 from int256, reverting on
           * overflow (when the input is less than smallest int128 or
           * greater than largest int128).
           *
           * Counterpart to Solidity's `int128` operator.
           *
           * Requirements:
           *
           * - input must fit into 128 bits
           *
           * _Available since v3.1._
           */
          function toInt128(int256 value) internal pure returns (int128) {
              require(value >= type(int128).min && value <= type(int128).max, "SafeCast: value doesn't fit in 128 bits");
              return int128(value);
          }
          /**
           * @dev Returns the downcasted int64 from int256, reverting on
           * overflow (when the input is less than smallest int64 or
           * greater than largest int64).
           *
           * Counterpart to Solidity's `int64` operator.
           *
           * Requirements:
           *
           * - input must fit into 64 bits
           *
           * _Available since v3.1._
           */
          function toInt64(int256 value) internal pure returns (int64) {
              require(value >= type(int64).min && value <= type(int64).max, "SafeCast: value doesn't fit in 64 bits");
              return int64(value);
          }
          /**
           * @dev Returns the downcasted int32 from int256, reverting on
           * overflow (when the input is less than smallest int32 or
           * greater than largest int32).
           *
           * Counterpart to Solidity's `int32` operator.
           *
           * Requirements:
           *
           * - input must fit into 32 bits
           *
           * _Available since v3.1._
           */
          function toInt32(int256 value) internal pure returns (int32) {
              require(value >= type(int32).min && value <= type(int32).max, "SafeCast: value doesn't fit in 32 bits");
              return int32(value);
          }
          /**
           * @dev Returns the downcasted int16 from int256, reverting on
           * overflow (when the input is less than smallest int16 or
           * greater than largest int16).
           *
           * Counterpart to Solidity's `int16` operator.
           *
           * Requirements:
           *
           * - input must fit into 16 bits
           *
           * _Available since v3.1._
           */
          function toInt16(int256 value) internal pure returns (int16) {
              require(value >= type(int16).min && value <= type(int16).max, "SafeCast: value doesn't fit in 16 bits");
              return int16(value);
          }
          /**
           * @dev Returns the downcasted int8 from int256, reverting on
           * overflow (when the input is less than smallest int8 or
           * greater than largest int8).
           *
           * Counterpart to Solidity's `int8` operator.
           *
           * Requirements:
           *
           * - input must fit into 8 bits.
           *
           * _Available since v3.1._
           */
          function toInt8(int256 value) internal pure returns (int8) {
              require(value >= type(int8).min && value <= type(int8).max, "SafeCast: value doesn't fit in 8 bits");
              return int8(value);
          }
          /**
           * @dev Converts an unsigned uint256 into a signed int256.
           *
           * Requirements:
           *
           * - input must be less than or equal to maxInt256.
           */
          function toInt256(uint256 value) internal pure returns (int256) {
              // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
              require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
              return int256(value);
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.4;
      import "@openzeppelin/contracts/access/Ownable.sol";
      import "./IERC20WithPermit.sol";
      import "./IReceiveApproval.sol";
      /// @title  ERC20WithPermit
      /// @notice Burnable ERC20 token with EIP2612 permit functionality. User can
      ///         authorize a transfer of their token with a signature conforming
      ///         EIP712 standard instead of an on-chain transaction from their
      ///         address. Anyone can submit this signature on the user's behalf by
      ///         calling the permit function, as specified in EIP2612 standard,
      ///         paying gas fees, and possibly performing other actions in the same
      ///         transaction.
      contract ERC20WithPermit is IERC20WithPermit, Ownable {
          /// @notice The amount of tokens owned by the given account.
          mapping(address => uint256) public override balanceOf;
          /// @notice The remaining number of tokens that spender will be
          ///         allowed to spend on behalf of owner through `transferFrom` and
          ///         `burnFrom`. This is zero by default.
          mapping(address => mapping(address => uint256)) public override allowance;
          /// @notice Returns the current nonce for EIP2612 permission for the
          ///         provided token owner for a replay protection. Used to construct
          ///         EIP2612 signature provided to `permit` function.
          mapping(address => uint256) public override nonce;
          uint256 public immutable cachedChainId;
          bytes32 public immutable cachedDomainSeparator;
          /// @notice Returns EIP2612 Permit message hash. Used to construct EIP2612
          ///         signature provided to `permit` function.
          bytes32 public constant override PERMIT_TYPEHASH =
              keccak256(
                  "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
              );
          /// @notice The amount of tokens in existence.
          uint256 public override totalSupply;
          /// @notice The name of the token.
          string public override name;
          /// @notice The symbol of the token.
          string public override symbol;
          /// @notice The decimals places of the token.
          uint8 public constant override decimals = 18;
          constructor(string memory _name, string memory _symbol) {
              name = _name;
              symbol = _symbol;
              cachedChainId = block.chainid;
              cachedDomainSeparator = buildDomainSeparator();
          }
          /// @notice Moves `amount` tokens from the caller's account to `recipient`.
          /// @return True if the operation succeeded, reverts otherwise.
          /// @dev Requirements:
          ///       - `recipient` cannot be the zero address,
          ///       - the caller must have a balance of at least `amount`.
          function transfer(address recipient, uint256 amount)
              external
              override
              returns (bool)
          {
              _transfer(msg.sender, recipient, amount);
              return true;
          }
          /// @notice Moves `amount` tokens from `spender` to `recipient` using the
          ///         allowance mechanism. `amount` is then deducted from the caller's
          ///         allowance unless the allowance was made for `type(uint256).max`.
          /// @return True if the operation succeeded, reverts otherwise.
          /// @dev Requirements:
          ///      - `spender` and `recipient` cannot be the zero address,
          ///      - `spender` must have a balance of at least `amount`,
          ///      - the caller must have allowance for `spender`'s tokens of at least
          ///        `amount`.
          function transferFrom(
              address spender,
              address recipient,
              uint256 amount
          ) external override returns (bool) {
              uint256 currentAllowance = allowance[spender][msg.sender];
              if (currentAllowance != type(uint256).max) {
                  require(
                      currentAllowance >= amount,
                      "Transfer amount exceeds allowance"
                  );
                  _approve(spender, msg.sender, currentAllowance - amount);
              }
              _transfer(spender, recipient, amount);
              return true;
          }
          /// @notice EIP2612 approval made with secp256k1 signature.
          ///         Users can authorize a transfer of their tokens with a signature
          ///         conforming EIP712 standard, rather than an on-chain transaction
          ///         from their address. Anyone can submit this signature on the
          ///         user's behalf by calling the permit function, paying gas fees,
          ///         and possibly performing other actions in the same transaction.
          /// @dev    The deadline argument can be set to `type(uint256).max to create
          ///         permits that effectively never expire.  If the `amount` is set
          ///         to `type(uint256).max` then `transferFrom` and `burnFrom` will
          ///         not reduce an allowance.
          function permit(
              address owner,
              address spender,
              uint256 amount,
              uint256 deadline,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) external override {
              /* solhint-disable-next-line not-rely-on-time */
              require(deadline >= block.timestamp, "Permission expired");
              // Validate `s` and `v` values for a malleability concern described in EIP2.
              // Only signatures with `s` value in the lower half of the secp256k1
              // curve's order and `v` value of 27 or 28 are considered valid.
              require(
                  uint256(s) <=
                      0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0,
                  "Invalid signature 's' value"
              );
              require(v == 27 || v == 28, "Invalid signature 'v' value");
              bytes32 digest = keccak256(
                  abi.encodePacked(
                      "\\x19\\x01",
                      DOMAIN_SEPARATOR(),
                      keccak256(
                          abi.encode(
                              PERMIT_TYPEHASH,
                              owner,
                              spender,
                              amount,
                              nonce[owner]++,
                              deadline
                          )
                      )
                  )
              );
              address recoveredAddress = ecrecover(digest, v, r, s);
              require(
                  recoveredAddress != address(0) && recoveredAddress == owner,
                  "Invalid signature"
              );
              _approve(owner, spender, amount);
          }
          /// @notice Creates `amount` tokens and assigns them to `account`,
          ///         increasing the total supply.
          /// @dev Requirements:
          ///      - `recipient` cannot be the zero address.
          function mint(address recipient, uint256 amount) external onlyOwner {
              require(recipient != address(0), "Mint to the zero address");
              beforeTokenTransfer(address(0), recipient, amount);
              totalSupply += amount;
              balanceOf[recipient] += amount;
              emit Transfer(address(0), recipient, amount);
          }
          /// @notice Destroys `amount` tokens from the caller.
          /// @dev Requirements:
          ///       - the caller must have a balance of at least `amount`.
          function burn(uint256 amount) external override {
              _burn(msg.sender, amount);
          }
          /// @notice Destroys `amount` of tokens from `account` using the allowance
          ///         mechanism. `amount` is then deducted from the caller's allowance
          ///         unless the allowance was made for `type(uint256).max`.
          /// @dev Requirements:
          ///      - `account` must have a balance of at least `amount`,
          ///      - the caller must have allowance for `account`'s tokens of at least
          ///        `amount`.
          function burnFrom(address account, uint256 amount) external override {
              uint256 currentAllowance = allowance[account][msg.sender];
              if (currentAllowance != type(uint256).max) {
                  require(
                      currentAllowance >= amount,
                      "Burn amount exceeds allowance"
                  );
                  _approve(account, msg.sender, currentAllowance - amount);
              }
              _burn(account, amount);
          }
          /// @notice Calls `receiveApproval` function on spender previously approving
          ///         the spender to withdraw from the caller multiple times, up to
          ///         the `amount` amount. If this function is called again, it
          ///         overwrites the current allowance with `amount`. Reverts if the
          ///         approval reverted or if `receiveApproval` call on the spender
          ///         reverted.
          /// @return True if both approval and `receiveApproval` calls succeeded.
          /// @dev If the `amount` is set to `type(uint256).max` then
          ///      `transferFrom` and `burnFrom` will not reduce an allowance.
          function approveAndCall(
              address spender,
              uint256 amount,
              bytes memory extraData
          ) external override returns (bool) {
              if (approve(spender, amount)) {
                  IReceiveApproval(spender).receiveApproval(
                      msg.sender,
                      amount,
                      address(this),
                      extraData
                  );
                  return true;
              }
              return false;
          }
          /// @notice Sets `amount` as the allowance of `spender` over the caller's
          ///         tokens.
          /// @return True if the operation succeeded.
          /// @dev If the `amount` is set to `type(uint256).max` then
          ///      `transferFrom` and `burnFrom` will not reduce an allowance.
          ///      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
          function approve(address spender, uint256 amount)
              public
              override
              returns (bool)
          {
              _approve(msg.sender, spender, amount);
              return true;
          }
          /// @notice Returns hash of EIP712 Domain struct with the token name as
          ///         a signing domain and token contract as a verifying contract.
          ///         Used to construct EIP2612 signature provided to `permit`
          ///         function.
          /* solhint-disable-next-line func-name-mixedcase */
          function DOMAIN_SEPARATOR() public view override returns (bytes32) {
              // As explained in EIP-2612, if the DOMAIN_SEPARATOR contains the
              // chainId and is defined at contract deployment instead of
              // reconstructed for every signature, there is a risk of possible replay
              // attacks between chains in the event of a future chain split.
              // To address this issue, we check the cached chain ID against the
              // current one and in case they are different, we build domain separator
              // from scratch.
              if (block.chainid == cachedChainId) {
                  return cachedDomainSeparator;
              } else {
                  return buildDomainSeparator();
              }
          }
          /// @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.
          // slither-disable-next-line dead-code
          function beforeTokenTransfer(
              address from,
              address to,
              uint256 amount
          ) internal virtual {}
          function _burn(address account, uint256 amount) internal {
              uint256 currentBalance = balanceOf[account];
              require(currentBalance >= amount, "Burn amount exceeds balance");
              beforeTokenTransfer(account, address(0), amount);
              balanceOf[account] = currentBalance - amount;
              totalSupply -= amount;
              emit Transfer(account, address(0), amount);
          }
          function _transfer(
              address spender,
              address recipient,
              uint256 amount
          ) private {
              require(spender != address(0), "Transfer from the zero address");
              require(recipient != address(0), "Transfer to the zero address");
              require(recipient != address(this), "Transfer to the token address");
              beforeTokenTransfer(spender, recipient, amount);
              uint256 spenderBalance = balanceOf[spender];
              require(spenderBalance >= amount, "Transfer amount exceeds balance");
              balanceOf[spender] = spenderBalance - amount;
              balanceOf[recipient] += amount;
              emit Transfer(spender, recipient, amount);
          }
          function _approve(
              address owner,
              address spender,
              uint256 amount
          ) private {
              require(owner != address(0), "Approve from the zero address");
              require(spender != address(0), "Approve to the zero address");
              allowance[owner][spender] = amount;
              emit Approval(owner, spender, amount);
          }
          function buildDomainSeparator() private view returns (bytes32) {
              return
                  keccak256(
                      abi.encode(
                          keccak256(
                              "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
                          ),
                          keccak256(bytes(name)),
                          keccak256(bytes("1")),
                          block.chainid,
                          address(this)
                      )
                  );
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.4;
      /// @notice An interface that should be implemented by tokens supporting
      ///         `approveAndCall`/`receiveApproval` pattern.
      interface IApproveAndCall {
          /// @notice Executes `receiveApproval` function on spender as specified in
          ///         `IReceiveApproval` interface. Approves spender to withdraw from
          ///         the caller multiple times, up to the `amount`. If this
          ///         function is called again, it overwrites the current allowance
          ///         with `amount`. Reverts if the approval reverted or if
          ///         `receiveApproval` call on the spender reverted.
          function approveAndCall(
              address spender,
              uint256 amount,
              bytes memory extraData
          ) external returns (bool);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.4;
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
      import "./IApproveAndCall.sol";
      /// @title  IERC20WithPermit
      /// @notice Burnable ERC20 token with EIP2612 permit functionality. User can
      ///         authorize a transfer of their token with a signature conforming
      ///         EIP712 standard instead of an on-chain transaction from their
      ///         address. Anyone can submit this signature on the user's behalf by
      ///         calling the permit function, as specified in EIP2612 standard,
      ///         paying gas fees, and possibly performing other actions in the same
      ///         transaction.
      interface IERC20WithPermit is IERC20, IERC20Metadata, IApproveAndCall {
          /// @notice EIP2612 approval made with secp256k1 signature.
          ///         Users can authorize a transfer of their tokens with a signature
          ///         conforming EIP712 standard, rather than an on-chain transaction
          ///         from their address. Anyone can submit this signature on the
          ///         user's behalf by calling the permit function, paying gas fees,
          ///         and possibly performing other actions in the same transaction.
          /// @dev    The deadline argument can be set to `type(uint256).max to create
          ///         permits that effectively never expire.
          function permit(
              address owner,
              address spender,
              uint256 amount,
              uint256 deadline,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) external;
          /// @notice Destroys `amount` tokens from the caller.
          function burn(uint256 amount) external;
          /// @notice Destroys `amount` of tokens from `account`, deducting the amount
          ///         from caller's allowance.
          function burnFrom(address account, uint256 amount) external;
          /// @notice Returns hash of EIP712 Domain struct with the token name as
          ///         a signing domain and token contract as a verifying contract.
          ///         Used to construct EIP2612 signature provided to `permit`
          ///         function.
          /* solhint-disable-next-line func-name-mixedcase */
          function DOMAIN_SEPARATOR() external view returns (bytes32);
          /// @notice Returns the current nonce for EIP2612 permission for the
          ///         provided token owner for a replay protection. Used to construct
          ///         EIP2612 signature provided to `permit` function.
          function nonce(address owner) external view returns (uint256);
          /// @notice Returns EIP2612 Permit message hash. Used to construct EIP2612
          ///         signature provided to `permit` function.
          /* solhint-disable-next-line func-name-mixedcase */
          function PERMIT_TYPEHASH() external pure returns (bytes32);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.4;
      /// @notice An interface that should be implemented by contracts supporting
      ///         `approveAndCall`/`receiveApproval` pattern.
      interface IReceiveApproval {
          /// @notice Receives approval to spend tokens. Called as a result of
          ///         `approveAndCall` call on the token.
          function receiveApproval(
              address from,
              uint256 amount,
              address token,
              bytes calldata extraData
          ) external;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.4;
      import "@openzeppelin/contracts/access/Ownable.sol";
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
      import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
      /// @title  MisfundRecovery
      /// @notice Allows the owner of the token contract extending MisfundRecovery
      ///         to recover any ERC20 and ERC721 sent mistakenly to the token
      ///         contract address.
      contract MisfundRecovery is Ownable {
          using SafeERC20 for IERC20;
          function recoverERC20(
              IERC20 token,
              address recipient,
              uint256 amount
          ) external onlyOwner {
              token.safeTransfer(recipient, amount);
          }
          function recoverERC721(
              IERC721 token,
              address recipient,
              uint256 tokenId,
              bytes calldata data
          ) external onlyOwner {
              token.safeTransferFrom(address(this), recipient, tokenId, data);
          }
      }
      // SPDX-License-Identifier: GPL-3.0-or-later
      // ██████████████     ▐████▌     ██████████████
      // ██████████████     ▐████▌     ██████████████
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      // ██████████████     ▐████▌     ██████████████
      // ██████████████     ▐████▌     ██████████████
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      pragma solidity 0.8.9;
      import "./IVotesHistory.sol";
      import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
      import "@openzeppelin/contracts/utils/math/Math.sol";
      import "@openzeppelin/contracts/utils/math/SafeCast.sol";
      /// @title Checkpoints
      /// @dev Abstract contract to support checkpoints for Compound-like voting and
      ///      delegation. This implementation supports token supply up to 2^96 - 1.
      ///      This contract keeps a history (checkpoints) of each account's vote
      ///      power. Vote power can be delegated either by calling the {delegate}
      ///      function directly, or by providing a signature to be used with
      ///      {delegateBySig}. Voting power can be publicly queried through
      ///      {getVotes} and {getPastVotes}.
      ///      NOTE: Extracted from OpenZeppelin ERCVotes.sol.
      abstract contract Checkpoints is IVotesHistory {
          struct Checkpoint {
              uint32 fromBlock;
              uint96 votes;
          }
          // slither-disable-next-line uninitialized-state
          mapping(address => address) internal _delegates;
          mapping(address => uint128[]) internal _checkpoints;
          uint128[] internal _totalSupplyCheckpoints;
          /// @notice Emitted when an account changes their delegate.
          event DelegateChanged(
              address indexed delegator,
              address indexed fromDelegate,
              address indexed toDelegate
          );
          /// @notice Emitted when a balance or delegate change results in changes
          ///         to an account's voting power.
          event DelegateVotesChanged(
              address indexed delegate,
              uint256 previousBalance,
              uint256 newBalance
          );
          function checkpoints(address account, uint32 pos)
              public
              view
              virtual
              returns (Checkpoint memory checkpoint)
          {
              (uint32 fromBlock, uint96 votes) = decodeCheckpoint(
                  _checkpoints[account][pos]
              );
              checkpoint = Checkpoint(fromBlock, votes);
          }
          /// @notice Get number of checkpoints for `account`.
          function numCheckpoints(address account)
              public
              view
              virtual
              returns (uint32)
          {
              return SafeCast.toUint32(_checkpoints[account].length);
          }
          /// @notice Get the address `account` is currently delegating to.
          function delegates(address account) public view virtual returns (address) {
              return _delegates[account];
          }
          /// @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 getVotes(address account) public view returns (uint96) {
              uint256 pos = _checkpoints[account].length;
              return pos == 0 ? 0 : decodeValue(_checkpoints[account][pos - 1]);
          }
          /// @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 getPastVotes(address account, uint256 blockNumber)
              public
              view
              returns (uint96)
          {
              return lookupCheckpoint(_checkpoints[account], blockNumber);
          }
          /// @notice Retrieve the `totalSupply` at the end of `blockNumber`.
          ///         Note, this value is the sum of all balances, but it is NOT the
          ///         sum of all the delegated votes!
          /// @param blockNumber The block number to get the total supply at
          /// @dev `blockNumber` must have been already mined
          function getPastTotalSupply(uint256 blockNumber)
              public
              view
              returns (uint96)
          {
              return lookupCheckpoint(_totalSupplyCheckpoints, blockNumber);
          }
          /// @notice Change delegation for `delegator` to `delegatee`.
          // slither-disable-next-line dead-code
          function delegate(address delegator, address delegatee) internal virtual;
          /// @notice Moves voting power from one delegate to another
          /// @param src Address of old delegate
          /// @param dst Address of new delegate
          /// @param amount Voting power amount to transfer between delegates
          function moveVotingPower(
              address src,
              address dst,
              uint256 amount
          ) internal {
              if (src != dst && amount > 0) {
                  if (src != address(0)) {
                      // https://github.com/crytic/slither/issues/960
                      // slither-disable-next-line variable-scope
                      (uint256 oldWeight, uint256 newWeight) = writeCheckpoint(
                          _checkpoints[src],
                          subtract,
                          amount
                      );
                      emit DelegateVotesChanged(src, oldWeight, newWeight);
                  }
                  if (dst != address(0)) {
                      // https://github.com/crytic/slither/issues/959
                      // slither-disable-next-line uninitialized-local
                      (uint256 oldWeight, uint256 newWeight) = writeCheckpoint(
                          _checkpoints[dst],
                          add,
                          amount
                      );
                      emit DelegateVotesChanged(dst, oldWeight, newWeight);
                  }
              }
          }
          /// @notice Writes a new checkpoint based on operating last stored value
          ///         with a `delta`. Usually, said operation is the `add` or
          ///         `subtract` functions from this contract, but more complex
          ///         functions can be passed as parameters.
          /// @param ckpts The checkpoints array to use
          /// @param op The function to apply over the last value and the `delta`
          /// @param delta Variation with respect to last stored value to be used
          ///              for new checkpoint
          function writeCheckpoint(
              uint128[] storage ckpts,
              function(uint256, uint256) view returns (uint256) op,
              uint256 delta
          ) internal returns (uint256 oldWeight, uint256 newWeight) {
              uint256 pos = ckpts.length;
              oldWeight = pos == 0 ? 0 : decodeValue(ckpts[pos - 1]);
              newWeight = op(oldWeight, delta);
              if (pos > 0) {
                  uint32 fromBlock = decodeBlockNumber(ckpts[pos - 1]);
                  // slither-disable-next-line incorrect-equality
                  if (fromBlock == block.number) {
                      ckpts[pos - 1] = encodeCheckpoint(
                          fromBlock,
                          SafeCast.toUint96(newWeight)
                      );
                      return (oldWeight, newWeight);
                  }
              }
              ckpts.push(
                  encodeCheckpoint(
                      SafeCast.toUint32(block.number),
                      SafeCast.toUint96(newWeight)
                  )
              );
          }
          /// @notice Lookup a value in a list of (sorted) checkpoints.
          /// @param ckpts The checkpoints array to use
          /// @param blockNumber Block number when we want to get the checkpoint at
          function lookupCheckpoint(uint128[] storage ckpts, uint256 blockNumber)
              internal
              view
              returns (uint96)
          {
              // We run a binary search to look for the earliest checkpoint taken
              // after `blockNumber`. During the loop, the index of the wanted
              // checkpoint remains in the range [low-1, high). With each iteration,
              // either `low` or `high` is moved towards the middle of the range to
              // maintain the invariant.
              // - If the middle checkpoint is after `blockNumber`,
              //   we look in [low, mid)
              // - If the middle checkpoint is before or equal to `blockNumber`,
              //   we look in [mid+1, high)
              // Once we reach a single value (when low == high), we've found the
              // right checkpoint at the index high-1, if not out of bounds (in that
              // case we're looking too far in the past and the result is 0).
              // Note that if the latest checkpoint available is exactly for
              // `blockNumber`, we end up with an index that is past the end of the
              // array, so we technically don't find a checkpoint after
              // `blockNumber`, but it works out the same.
              require(blockNumber < block.number, "Block not yet determined");
              uint256 high = ckpts.length;
              uint256 low = 0;
              while (low < high) {
                  uint256 mid = Math.average(low, high);
                  uint32 midBlock = decodeBlockNumber(ckpts[mid]);
                  if (midBlock > blockNumber) {
                      high = mid;
                  } else {
                      low = mid + 1;
                  }
              }
              return high == 0 ? 0 : decodeValue(ckpts[high - 1]);
          }
          /// @notice Maximum token supply. Defaults to `type(uint96).max` (2^96 - 1)
          // slither-disable-next-line dead-code
          function maxSupply() internal view virtual returns (uint96) {
              return type(uint96).max;
          }
          /// @notice Encodes a `blockNumber` and `value` into a single `uint128`
          ///         checkpoint.
          /// @dev `blockNumber` is stored in the first 32 bits, while `value` in the
          ///      remaining 96 bits.
          function encodeCheckpoint(uint32 blockNumber, uint96 value)
              internal
              pure
              returns (uint128)
          {
              return (uint128(blockNumber) << 96) | uint128(value);
          }
          /// @notice Decodes a block number from a `uint128` `checkpoint`.
          function decodeBlockNumber(uint128 checkpoint)
              internal
              pure
              returns (uint32)
          {
              return uint32(bytes4(bytes16(checkpoint)));
          }
          /// @notice Decodes a voting value from a `uint128` `checkpoint`.
          function decodeValue(uint128 checkpoint) internal pure returns (uint96) {
              return uint96(checkpoint);
          }
          /// @notice Decodes a block number and voting value from a `uint128`
          ///         `checkpoint`.
          function decodeCheckpoint(uint128 checkpoint)
              internal
              pure
              returns (uint32 blockNumber, uint96 value)
          {
              blockNumber = decodeBlockNumber(checkpoint);
              value = decodeValue(checkpoint);
          }
          // slither-disable-next-line dead-code
          function add(uint256 a, uint256 b) internal pure returns (uint256) {
              return a + b;
          }
          // slither-disable-next-line dead-code
          function subtract(uint256 a, uint256 b) internal pure returns (uint256) {
              return a - b;
          }
      }
      // SPDX-License-Identifier: GPL-3.0-or-later
      // ██████████████     ▐████▌     ██████████████
      // ██████████████     ▐████▌     ██████████████
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      // ██████████████     ▐████▌     ██████████████
      // ██████████████     ▐████▌     ██████████████
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      pragma solidity 0.8.9;
      interface IVotesHistory {
          function getPastVotes(address account, uint256 blockNumber)
              external
              view
              returns (uint96);
          function getPastTotalSupply(uint256 blockNumber)
              external
              view
              returns (uint96);
      }
      // SPDX-License-Identifier: GPL-3.0-or-later
      // ██████████████     ▐████▌     ██████████████
      // ██████████████     ▐████▌     ██████████████
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      // ██████████████     ▐████▌     ██████████████
      // ██████████████     ▐████▌     ██████████████
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      pragma solidity 0.8.9;
      import "../governance/Checkpoints.sol";
      import "@openzeppelin/contracts/utils/math/SafeCast.sol";
      import "@thesis/solidity-contracts/contracts/token/ERC20WithPermit.sol";
      import "@thesis/solidity-contracts/contracts/token/MisfundRecovery.sol";
      /// @title T token
      /// @notice Threshold Network T token
      /// @dev By default, token balance does not account for voting power.
      ///      This makes transfers cheaper. The downside is that it requires users
      ///      to delegate to themselves to activate checkpoints and have their
      ///      voting power tracked.
      contract T is ERC20WithPermit, MisfundRecovery, Checkpoints {
          /// @notice The EIP-712 typehash for the delegation struct used by
          ///         `delegateBySig`.
          bytes32 public constant DELEGATION_TYPEHASH =
              keccak256(
                  "Delegation(address delegatee,uint256 nonce,uint256 deadline)"
              );
          constructor() ERC20WithPermit("Threshold Network Token", "T") {}
          /// @notice Delegates votes from signatory to `delegatee`
          /// @param delegatee The address to delegate votes to
          /// @param deadline 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 signatory,
              address delegatee,
              uint256 deadline,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) external {
              /* solhint-disable-next-line not-rely-on-time */
              require(deadline >= block.timestamp, "Delegation expired");
              // Validate `s` and `v` values for a malleability concern described in EIP2.
              // Only signatures with `s` value in the lower half of the secp256k1
              // curve's order and `v` value of 27 or 28 are considered valid.
              require(
                  uint256(s) <=
                      0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0,
                  "Invalid signature 's' value"
              );
              require(v == 27 || v == 28, "Invalid signature 'v' value");
              bytes32 digest = keccak256(
                  abi.encodePacked(
                      "\\x19\\x01",
                      DOMAIN_SEPARATOR(),
                      keccak256(
                          abi.encode(
                              DELEGATION_TYPEHASH,
                              delegatee,
                              nonce[signatory]++,
                              deadline
                          )
                      )
                  )
              );
              address recoveredAddress = ecrecover(digest, v, r, s);
              require(
                  recoveredAddress != address(0) && recoveredAddress == signatory,
                  "Invalid signature"
              );
              return delegate(signatory, delegatee);
          }
          /// @notice Delegate votes from `msg.sender` to `delegatee`.
          /// @param delegatee The address to delegate votes to
          function delegate(address delegatee) public virtual {
              return delegate(msg.sender, delegatee);
          }
          // slither-disable-next-line dead-code
          function beforeTokenTransfer(
              address from,
              address to,
              uint256 amount
          ) internal override {
              uint96 safeAmount = SafeCast.toUint96(amount);
              // When minting:
              if (from == address(0)) {
                  // Does not allow to mint more than uint96 can fit. Otherwise, the
                  // Checkpoint might not fit the balance.
                  require(
                      totalSupply + amount <= maxSupply(),
                      "Maximum total supply exceeded"
                  );
                  writeCheckpoint(_totalSupplyCheckpoints, add, safeAmount);
              }
              // When burning:
              if (to == address(0)) {
                  writeCheckpoint(_totalSupplyCheckpoints, subtract, safeAmount);
              }
              moveVotingPower(delegates(from), delegates(to), safeAmount);
          }
          function delegate(address delegator, address delegatee)
              internal
              virtual
              override
          {
              address currentDelegate = delegates(delegator);
              uint96 delegatorBalance = SafeCast.toUint96(balanceOf[delegator]);
              _delegates[delegator] = delegatee;
              emit DelegateChanged(delegator, currentDelegate, delegatee);
              moveVotingPower(currentDelegate, delegatee, delegatorBalance);
          }
      }
      // SPDX-License-Identifier: GPL-3.0-or-later
      // ██████████████     ▐████▌     ██████████████
      // ██████████████     ▐████▌     ██████████████
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      // ██████████████     ▐████▌     ██████████████
      // ██████████████     ▐████▌     ██████████████
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      pragma solidity 0.8.9;
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
      import "@thesis/solidity-contracts/contracts/token/IReceiveApproval.sol";
      import "../token/T.sol";
      /// @title T token vending machine
      /// @notice Contract implements a special update protocol to enable KEEP/NU
      ///         token holders to wrap their tokens and obtain T tokens according
      ///         to a fixed ratio. This will go on indefinitely and enable NU and
      ///         KEEP token holders to join T network without needing to buy or
      ///         sell any assets. Logistically, anyone holding NU or KEEP can wrap
      ///         those assets in order to upgrade to T. They can also unwrap T in
      ///         order to downgrade back to the underlying asset. There is a separate
      ///         instance of this contract deployed for KEEP holders and a separate
      ///         instance of this contract deployed for NU holders.
      contract VendingMachine is IReceiveApproval {
          using SafeERC20 for IERC20;
          using SafeERC20 for T;
          /// @notice Number of decimal places of precision in conversion to/from
          ///         wrapped tokens (assuming typical ERC20 token with 18 decimals).
          ///         This implies that amounts of wrapped tokens below this precision
          ///         won't take part in the conversion. E.g., for a value of 3, then
          ///         for a conversion of 1.123456789 wrapped tokens, only 1.123 is
          ///         convertible (i.e., 3 decimal places), and 0.000456789 is left.
          uint256 public constant WRAPPED_TOKEN_CONVERSION_PRECISION = 3;
          /// @notice Divisor for precision purposes, used to represent fractions.
          uint256 public constant FLOATING_POINT_DIVISOR =
              10**(18 - WRAPPED_TOKEN_CONVERSION_PRECISION);
          /// @notice The token being wrapped to T (KEEP/NU).
          IERC20 public immutable wrappedToken;
          /// @notice T token contract.
          T public immutable tToken;
          /// @notice The ratio with which T token is converted based on the provided
          ///         token being wrapped (KEEP/NU), expressed in 1e18 precision.
          ///
          ///         When wrapping:
          ///           x [T] = amount [KEEP/NU] * ratio / FLOATING_POINT_DIVISOR
          ///
          ///         When unwrapping:
          ///           x [KEEP/NU] = amount [T] * FLOATING_POINT_DIVISOR / ratio
          uint256 public immutable ratio;
          /// @notice The total balance of wrapped tokens for the given holder
          ///         account. Only holders that have previously wrapped KEEP/NU to T
          ///         can unwrap, up to the amount previously wrapped.
          mapping(address => uint256) public wrappedBalance;
          event Wrapped(
              address indexed recipient,
              uint256 wrappedTokenAmount,
              uint256 tTokenAmount
          );
          event Unwrapped(
              address indexed recipient,
              uint256 tTokenAmount,
              uint256 wrappedTokenAmount
          );
          /// @notice Sets the reference to `wrappedToken` and `tToken`. Initializes
          ///         conversion `ratio` between wrapped token and T based on the
          ///         provided `_tTokenAllocation` and `_wrappedTokenAllocation`.
          /// @param _wrappedToken Address to ERC20 token that will be wrapped to T
          /// @param _tToken Address of T token
          /// @param _wrappedTokenAllocation The total supply of the token that will be
          ///       wrapped to T
          /// @param _tTokenAllocation The allocation of T this instance of Vending
          ///        Machine will receive
          /// @dev Multiplications in this contract can't overflow uint256 as we
          ///     restrict `_wrappedTokenAllocation` and `_tTokenAllocation` to
          ///     96 bits and FLOATING_POINT_DIVISOR fits in less than 60 bits.
          constructor(
              IERC20 _wrappedToken,
              T _tToken,
              uint96 _wrappedTokenAllocation,
              uint96 _tTokenAllocation
          ) {
              wrappedToken = _wrappedToken;
              tToken = _tToken;
              ratio =
                  (FLOATING_POINT_DIVISOR * _tTokenAllocation) /
                  _wrappedTokenAllocation;
          }
          /// @notice Wraps up to the the given `amount` of the token (KEEP/NU) and
          ///         releases T token proportionally to the amount being wrapped with
          ///         respect to the wrap ratio. The token holder needs to have at
          ///         least the given amount of the wrapped token (KEEP/NU) approved
          ///         to transfer to the Vending Machine before calling this function.
          /// @param amount The amount of KEEP/NU to be wrapped
          function wrap(uint256 amount) external {
              _wrap(msg.sender, amount);
          }
          /// @notice Wraps up to the given amount of the token (KEEP/NU) and releases
          ///         T token proportionally to the amount being wrapped with respect
          ///         to the wrap ratio. This is a shortcut to `wrap` function that
          ///         avoids a separate approval transaction. Only KEEP/NU token
          ///         is allowed as a caller, so please call this function via
          ///         token's `approveAndCall`.
          /// @param from Caller's address, must be the same as `wrappedToken` field
          /// @param amount The amount of KEEP/NU to be wrapped
          /// @param token Token's address, must be the same as `wrappedToken` field
          function receiveApproval(
              address from,
              uint256 amount,
              address token,
              bytes calldata
          ) external override {
              require(
                  token == address(wrappedToken),
                  "Token is not the wrapped token"
              );
              require(
                  msg.sender == address(wrappedToken),
                  "Only wrapped token caller allowed"
              );
              _wrap(from, amount);
          }
          /// @notice Unwraps up to the given `amount` of T back to the legacy token
          ///         (KEEP/NU) according to the wrap ratio. It can only be called by
          ///         a token holder who previously wrapped their tokens in this
          ///         vending machine contract. The token holder can't unwrap more
          ///         tokens than they originally wrapped. The token holder needs to
          ///         have at least the given amount of T tokens approved to transfer
          ///         to the Vending Machine before calling this function.
          /// @param amount The amount of T to unwrap back to the collateral (KEEP/NU)
          function unwrap(uint256 amount) external {
              _unwrap(msg.sender, amount);
          }
          /// @notice Returns the T token amount that's obtained from `amount` wrapped
          ///         tokens (KEEP/NU), and the remainder that can't be upgraded.
          function conversionToT(uint256 amount)
              public
              view
              returns (uint256 tAmount, uint256 wrappedRemainder)
          {
              wrappedRemainder = amount % FLOATING_POINT_DIVISOR;
              uint256 convertibleAmount = amount - wrappedRemainder;
              tAmount = (convertibleAmount * ratio) / FLOATING_POINT_DIVISOR;
          }
          /// @notice The amount of wrapped tokens (KEEP/NU) that's obtained from
          ///         `amount` T tokens, and the remainder that can't be downgraded.
          function conversionFromT(uint256 amount)
              public
              view
              returns (uint256 wrappedAmount, uint256 tRemainder)
          {
              tRemainder = amount % ratio;
              uint256 convertibleAmount = amount - tRemainder;
              wrappedAmount = (convertibleAmount * FLOATING_POINT_DIVISOR) / ratio;
          }
          function _wrap(address tokenHolder, uint256 wrappedTokenAmount) internal {
              (uint256 tTokenAmount, uint256 remainder) = conversionToT(
                  wrappedTokenAmount
              );
              wrappedTokenAmount -= remainder;
              require(wrappedTokenAmount > 0, "Disallow conversions of zero value");
              emit Wrapped(tokenHolder, wrappedTokenAmount, tTokenAmount);
              wrappedBalance[tokenHolder] += wrappedTokenAmount;
              wrappedToken.safeTransferFrom(
                  tokenHolder,
                  address(this),
                  wrappedTokenAmount
              );
              tToken.safeTransfer(tokenHolder, tTokenAmount);
          }
          function _unwrap(address tokenHolder, uint256 tTokenAmount) internal {
              (uint256 wrappedTokenAmount, uint256 remainder) = conversionFromT(
                  tTokenAmount
              );
              tTokenAmount -= remainder;
              require(tTokenAmount > 0, "Disallow conversions of zero value");
              require(
                  wrappedBalance[tokenHolder] >= wrappedTokenAmount,
                  "Can not unwrap more than previously wrapped"
              );
              emit Unwrapped(tokenHolder, tTokenAmount, wrappedTokenAmount);
              wrappedBalance[tokenHolder] -= wrappedTokenAmount;
              tToken.safeTransferFrom(tokenHolder, address(this), tTokenAmount);
              wrappedToken.safeTransfer(tokenHolder, wrappedTokenAmount);
          }
      }
      

      File 3 of 3: T
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.0 (access/Ownable.sol)
      pragma solidity ^0.8.0;
      import "../utils/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.
       */
      abstract contract Ownable is Context {
          address private _owner;
          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
          /**
           * @dev Initializes the contract setting the deployer as the initial owner.
           */
          constructor() {
              _transferOwnership(_msgSender());
          }
          /**
           * @dev Returns the address of the current owner.
           */
          function owner() public view virtual returns (address) {
              return _owner;
          }
          /**
           * @dev Throws if called by any account other than the owner.
           */
          modifier onlyOwner() {
              require(owner() == _msgSender(), "Ownable: caller is not the owner");
              _;
          }
          /**
           * @dev Leaves the contract without owner. It will not be possible to call
           * `onlyOwner` functions anymore. Can only be called by the current owner.
           *
           * NOTE: Renouncing ownership will leave the contract without an owner,
           * thereby removing any functionality that is only available to the owner.
           */
          function renounceOwnership() public virtual onlyOwner {
              _transferOwnership(address(0));
          }
          /**
           * @dev Transfers ownership of the contract to a new account (`newOwner`).
           * Can only be called by the current owner.
           */
          function transferOwnership(address newOwner) public virtual onlyOwner {
              require(newOwner != address(0), "Ownable: new owner is the zero address");
              _transferOwnership(newOwner);
          }
          /**
           * @dev Transfers ownership of the contract to a new account (`newOwner`).
           * Internal function without access restriction.
           */
          function _transferOwnership(address newOwner) internal virtual {
              address oldOwner = _owner;
              _owner = newOwner;
              emit OwnershipTransferred(oldOwner, newOwner);
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.0 (token/ERC20/IERC20.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Interface of the ERC20 standard as defined in the EIP.
       */
      interface IERC20 {
          /**
           * @dev Returns the amount of tokens in existence.
           */
          function totalSupply() external view returns (uint256);
          /**
           * @dev Returns the amount of tokens owned by `account`.
           */
          function balanceOf(address account) external view returns (uint256);
          /**
           * @dev Moves `amount` tokens from the caller's account to `recipient`.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transfer(address recipient, uint256 amount) external returns (bool);
          /**
           * @dev Returns the remaining number of tokens that `spender` will be
           * allowed to spend on behalf of `owner` through {transferFrom}. This is
           * zero by default.
           *
           * This value changes when {approve} or {transferFrom} are called.
           */
          function allowance(address owner, address spender) external view returns (uint256);
          /**
           * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * IMPORTANT: Beware that changing an allowance with this method brings the risk
           * that someone may use both the old and the new allowance by unfortunate
           * transaction ordering. One possible solution to mitigate this race
           * condition is to first reduce the spender's allowance to 0 and set the
           * desired value afterwards:
           * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
           *
           * Emits an {Approval} event.
           */
          function approve(address spender, uint256 amount) external returns (bool);
          /**
           * @dev Moves `amount` tokens from `sender` to `recipient` using the
           * allowance mechanism. `amount` is then deducted from the caller's
           * allowance.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transferFrom(
              address sender,
              address recipient,
              uint256 amount
          ) external returns (bool);
          /**
           * @dev Emitted when `value` tokens are moved from one account (`from`) to
           * another (`to`).
           *
           * Note that `value` may be zero.
           */
          event Transfer(address indexed from, address indexed to, uint256 value);
          /**
           * @dev Emitted when the allowance of a `spender` for an `owner` is set by
           * a call to {approve}. `value` is the new allowance.
           */
          event Approval(address indexed owner, address indexed spender, uint256 value);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.0 (token/ERC20/extensions/IERC20Metadata.sol)
      pragma solidity ^0.8.0;
      import "../IERC20.sol";
      /**
       * @dev Interface for the optional metadata functions from the ERC20 standard.
       *
       * _Available since v4.1._
       */
      interface IERC20Metadata is IERC20 {
          /**
           * @dev Returns the name of the token.
           */
          function name() external view returns (string memory);
          /**
           * @dev Returns the symbol of the token.
           */
          function symbol() external view returns (string memory);
          /**
           * @dev Returns the decimals places of the token.
           */
          function decimals() external view returns (uint8);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.0 (token/ERC20/utils/SafeERC20.sol)
      pragma solidity ^0.8.0;
      import "../IERC20.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 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'
              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) + value;
              _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
          }
          function safeDecreaseAllowance(
              IERC20 token,
              address spender,
              uint256 value
          ) internal {
              unchecked {
                  uint256 oldAllowance = token.allowance(address(this), spender);
                  require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                  uint256 newAllowance = oldAllowance - value;
                  _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
                  require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.0 (token/ERC721/IERC721.sol)
      pragma solidity ^0.8.0;
      import "../../utils/introspection/IERC165.sol";
      /**
       * @dev Required interface of an ERC721 compliant contract.
       */
      interface IERC721 is IERC165 {
          /**
           * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
           */
          event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
          /**
           * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
           */
          event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
          /**
           * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
           */
          event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
          /**
           * @dev Returns the number of tokens in ``owner``'s account.
           */
          function balanceOf(address owner) external view returns (uint256 balance);
          /**
           * @dev Returns the owner of the `tokenId` token.
           *
           * Requirements:
           *
           * - `tokenId` must exist.
           */
          function ownerOf(uint256 tokenId) external view returns (address owner);
          /**
           * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
           * are aware of the ERC721 protocol to prevent tokens from being forever locked.
           *
           * Requirements:
           *
           * - `from` cannot be the zero address.
           * - `to` cannot be the zero address.
           * - `tokenId` token must exist and be owned by `from`.
           * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
           * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
           *
           * Emits a {Transfer} event.
           */
          function safeTransferFrom(
              address from,
              address to,
              uint256 tokenId
          ) external;
          /**
           * @dev Transfers `tokenId` token from `from` to `to`.
           *
           * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
           *
           * Requirements:
           *
           * - `from` cannot be the zero address.
           * - `to` cannot be the zero address.
           * - `tokenId` token must be owned by `from`.
           * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
           *
           * Emits a {Transfer} event.
           */
          function transferFrom(
              address from,
              address to,
              uint256 tokenId
          ) external;
          /**
           * @dev Gives permission to `to` to transfer `tokenId` token to another account.
           * The approval is cleared when the token is transferred.
           *
           * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
           *
           * Requirements:
           *
           * - The caller must own the token or be an approved operator.
           * - `tokenId` must exist.
           *
           * Emits an {Approval} event.
           */
          function approve(address to, uint256 tokenId) external;
          /**
           * @dev Returns the account approved for `tokenId` token.
           *
           * Requirements:
           *
           * - `tokenId` must exist.
           */
          function getApproved(uint256 tokenId) external view returns (address operator);
          /**
           * @dev Approve or remove `operator` as an operator for the caller.
           * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
           *
           * Requirements:
           *
           * - The `operator` cannot be the caller.
           *
           * Emits an {ApprovalForAll} event.
           */
          function setApprovalForAll(address operator, bool _approved) external;
          /**
           * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
           *
           * See {setApprovalForAll}
           */
          function isApprovedForAll(address owner, address operator) external view returns (bool);
          /**
           * @dev Safely transfers `tokenId` token from `from` to `to`.
           *
           * Requirements:
           *
           * - `from` cannot be the zero address.
           * - `to` cannot be the zero address.
           * - `tokenId` token must exist and be owned by `from`.
           * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
           * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
           *
           * Emits a {Transfer} event.
           */
          function safeTransferFrom(
              address from,
              address to,
              uint256 tokenId,
              bytes calldata data
          ) external;
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.0 (utils/Address.sol)
      pragma solidity ^0.8.0;
      /**
       * @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 on extcodesize, which returns 0 for contracts in
              // construction, since the code is only stored at the end of the
              // constructor execution.
              uint256 size;
              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");
              (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");
              require(isContract(target), "Address: call to non-contract");
              (bool success, bytes memory returndata) = target.call{value: value}(data);
              return verifyCallResult(success, returndata, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
              return functionStaticCall(target, data, "Address: low-level static call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal view returns (bytes memory) {
              require(isContract(target), "Address: static call to non-contract");
              (bool success, bytes memory returndata) = target.staticcall(data);
              return verifyCallResult(success, returndata, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.4._
           */
          function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionDelegateCall(target, data, "Address: low-level delegate call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.4._
           */
          function functionDelegateCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal returns (bytes memory) {
              require(isContract(target), "Address: delegate call to non-contract");
              (bool success, bytes memory returndata) = target.delegatecall(data);
              return verifyCallResult(success, returndata, errorMessage);
          }
          /**
           * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
           * revert reason using the provided one.
           *
           * _Available since v4.3._
           */
          function verifyCallResult(
              bool success,
              bytes memory returndata,
              string memory errorMessage
          ) internal pure returns (bytes memory) {
              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
                      assembly {
                          let returndata_size := mload(returndata)
                          revert(add(32, returndata), returndata_size)
                      }
                  } else {
                      revert(errorMessage);
                  }
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.0 (utils/Context.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Provides information about the current execution context, including the
       * sender of the transaction and its data. While these are generally available
       * via msg.sender and msg.data, they should not be accessed in such a direct
       * manner, since when dealing with meta-transactions the account sending and
       * paying for execution may not be the actual sender (as far as an application
       * is concerned).
       *
       * This contract is only required for intermediate, library-like contracts.
       */
      abstract contract Context {
          function _msgSender() internal view virtual returns (address) {
              return msg.sender;
          }
          function _msgData() internal view virtual returns (bytes calldata) {
              return msg.data;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.0 (utils/Strings.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev String operations.
       */
      library Strings {
          bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
          /**
           * @dev Converts a `uint256` to its ASCII `string` decimal representation.
           */
          function toString(uint256 value) internal pure returns (string memory) {
              // Inspired by OraclizeAPI's implementation - MIT licence
              // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
              if (value == 0) {
                  return "0";
              }
              uint256 temp = value;
              uint256 digits;
              while (temp != 0) {
                  digits++;
                  temp /= 10;
              }
              bytes memory buffer = new bytes(digits);
              while (value != 0) {
                  digits -= 1;
                  buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
                  value /= 10;
              }
              return string(buffer);
          }
          /**
           * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
           */
          function toHexString(uint256 value) internal pure returns (string memory) {
              if (value == 0) {
                  return "0x00";
              }
              uint256 temp = value;
              uint256 length = 0;
              while (temp != 0) {
                  length++;
                  temp >>= 8;
              }
              return toHexString(value, length);
          }
          /**
           * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
           */
          function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
              bytes memory buffer = new bytes(2 * length + 2);
              buffer[0] = "0";
              buffer[1] = "x";
              for (uint256 i = 2 * length + 1; i > 1; --i) {
                  buffer[i] = _HEX_SYMBOLS[value & 0xf];
                  value >>= 4;
              }
              require(value == 0, "Strings: hex length insufficient");
              return string(buffer);
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.0 (utils/cryptography/ECDSA.sol)
      pragma solidity ^0.8.0;
      import "../Strings.sol";
      /**
       * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
       *
       * These functions can be used to verify that a message was signed by the holder
       * of the private keys of a given address.
       */
      library ECDSA {
          enum RecoverError {
              NoError,
              InvalidSignature,
              InvalidSignatureLength,
              InvalidSignatureS,
              InvalidSignatureV
          }
          function _throwError(RecoverError error) private pure {
              if (error == RecoverError.NoError) {
                  return; // no error: do nothing
              } else if (error == RecoverError.InvalidSignature) {
                  revert("ECDSA: invalid signature");
              } else if (error == RecoverError.InvalidSignatureLength) {
                  revert("ECDSA: invalid signature length");
              } else if (error == RecoverError.InvalidSignatureS) {
                  revert("ECDSA: invalid signature 's' value");
              } else if (error == RecoverError.InvalidSignatureV) {
                  revert("ECDSA: invalid signature 'v' value");
              }
          }
          /**
           * @dev Returns the address that signed a hashed message (`hash`) with
           * `signature` or error string. This address can then be used for verification purposes.
           *
           * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
           * this function rejects them by requiring the `s` value to be in the lower
           * half order, and the `v` value to be either 27 or 28.
           *
           * IMPORTANT: `hash` _must_ be the result of a hash operation for the
           * verification to be secure: it is possible to craft signatures that
           * recover to arbitrary addresses for non-hashed data. A safe way to ensure
           * this is by receiving a hash of the original message (which may otherwise
           * be too long), and then calling {toEthSignedMessageHash} on it.
           *
           * Documentation for signature generation:
           * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
           * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
           *
           * _Available since v4.3._
           */
          function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
              // Check the signature length
              // - case 65: r,s,v signature (standard)
              // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
              if (signature.length == 65) {
                  bytes32 r;
                  bytes32 s;
                  uint8 v;
                  // ecrecover takes the signature parameters, and the only way to get them
                  // currently is to use assembly.
                  assembly {
                      r := mload(add(signature, 0x20))
                      s := mload(add(signature, 0x40))
                      v := byte(0, mload(add(signature, 0x60)))
                  }
                  return tryRecover(hash, v, r, s);
              } else if (signature.length == 64) {
                  bytes32 r;
                  bytes32 vs;
                  // ecrecover takes the signature parameters, and the only way to get them
                  // currently is to use assembly.
                  assembly {
                      r := mload(add(signature, 0x20))
                      vs := mload(add(signature, 0x40))
                  }
                  return tryRecover(hash, r, vs);
              } else {
                  return (address(0), RecoverError.InvalidSignatureLength);
              }
          }
          /**
           * @dev Returns the address that signed a hashed message (`hash`) with
           * `signature`. This address can then be used for verification purposes.
           *
           * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
           * this function rejects them by requiring the `s` value to be in the lower
           * half order, and the `v` value to be either 27 or 28.
           *
           * IMPORTANT: `hash` _must_ be the result of a hash operation for the
           * verification to be secure: it is possible to craft signatures that
           * recover to arbitrary addresses for non-hashed data. A safe way to ensure
           * this is by receiving a hash of the original message (which may otherwise
           * be too long), and then calling {toEthSignedMessageHash} on it.
           */
          function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
              (address recovered, RecoverError error) = tryRecover(hash, signature);
              _throwError(error);
              return recovered;
          }
          /**
           * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
           *
           * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
           *
           * _Available since v4.3._
           */
          function tryRecover(
              bytes32 hash,
              bytes32 r,
              bytes32 vs
          ) internal pure returns (address, RecoverError) {
              bytes32 s;
              uint8 v;
              assembly {
                  s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
                  v := add(shr(255, vs), 27)
              }
              return tryRecover(hash, v, r, s);
          }
          /**
           * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
           *
           * _Available since v4.2._
           */
          function recover(
              bytes32 hash,
              bytes32 r,
              bytes32 vs
          ) internal pure returns (address) {
              (address recovered, RecoverError error) = tryRecover(hash, r, vs);
              _throwError(error);
              return recovered;
          }
          /**
           * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
           * `r` and `s` signature fields separately.
           *
           * _Available since v4.3._
           */
          function tryRecover(
              bytes32 hash,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) internal pure returns (address, RecoverError) {
              // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
              // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
              // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
              // signatures from current libraries generate a unique signature with an s-value in the lower half order.
              //
              // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
              // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
              // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
              // these malleable signatures as well.
              if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
                  return (address(0), RecoverError.InvalidSignatureS);
              }
              if (v != 27 && v != 28) {
                  return (address(0), RecoverError.InvalidSignatureV);
              }
              // If the signature is valid (and not malleable), return the signer address
              address signer = ecrecover(hash, v, r, s);
              if (signer == address(0)) {
                  return (address(0), RecoverError.InvalidSignature);
              }
              return (signer, RecoverError.NoError);
          }
          /**
           * @dev Overload of {ECDSA-recover} that receives the `v`,
           * `r` and `s` signature fields separately.
           */
          function recover(
              bytes32 hash,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) internal pure returns (address) {
              (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
              _throwError(error);
              return recovered;
          }
          /**
           * @dev Returns an Ethereum Signed Message, created from a `hash`. This
           * produces hash corresponding to the one signed with the
           * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
           * JSON-RPC method as part of EIP-191.
           *
           * See {recover}.
           */
          function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
              // 32 is the length in bytes of hash,
              // enforced by the type signature above
              return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
      32", hash));
          }
          /**
           * @dev Returns an Ethereum Signed Message, created from `s`. This
           * produces hash corresponding to the one signed with the
           * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
           * JSON-RPC method as part of EIP-191.
           *
           * See {recover}.
           */
          function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
              return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
      ", Strings.toString(s.length), s));
          }
          /**
           * @dev Returns an Ethereum Signed Typed Data, created from a
           * `domainSeparator` and a `structHash`. This produces hash corresponding
           * to the one signed with the
           * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
           * JSON-RPC method as part of EIP-712.
           *
           * See {recover}.
           */
          function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
              return keccak256(abi.encodePacked("\\x19\\x01", domainSeparator, structHash));
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.0 (utils/introspection/IERC165.sol)
      pragma solidity ^0.8.0;
      /**
       * @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);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.0 (utils/math/Math.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Standard math utilities missing in the Solidity language.
       */
      library Math {
          /**
           * @dev Returns the largest of two numbers.
           */
          function max(uint256 a, uint256 b) internal pure returns (uint256) {
              return a >= b ? a : b;
          }
          /**
           * @dev Returns the smallest of two numbers.
           */
          function min(uint256 a, uint256 b) internal pure returns (uint256) {
              return a < b ? a : b;
          }
          /**
           * @dev Returns the average of two numbers. The result is rounded towards
           * zero.
           */
          function average(uint256 a, uint256 b) internal pure returns (uint256) {
              // (a + b) / 2 can overflow.
              return (a & b) + (a ^ b) / 2;
          }
          /**
           * @dev Returns the ceiling of the division of two numbers.
           *
           * This differs from standard division with `/` in that it rounds up instead
           * of rounding down.
           */
          function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
              // (a + b - 1) / b can overflow on addition, so we distribute.
              return a / b + (a % b == 0 ? 0 : 1);
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.0 (utils/math/SafeCast.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
       * checks.
       *
       * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
       * easily result in undesired exploitation or bugs, since developers usually
       * assume that overflows raise errors. `SafeCast` restores this intuition by
       * reverting the transaction when such 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.
       *
       * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
       * all math on `uint256` and `int256` and then downcasting.
       */
      library SafeCast {
          /**
           * @dev Returns the downcasted uint224 from uint256, reverting on
           * overflow (when the input is greater than largest uint224).
           *
           * Counterpart to Solidity's `uint224` operator.
           *
           * Requirements:
           *
           * - input must fit into 224 bits
           */
          function toUint224(uint256 value) internal pure returns (uint224) {
              require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
              return uint224(value);
          }
          /**
           * @dev Returns the downcasted uint128 from uint256, reverting on
           * overflow (when the input is greater than largest uint128).
           *
           * Counterpart to Solidity's `uint128` operator.
           *
           * Requirements:
           *
           * - input must fit into 128 bits
           */
          function toUint128(uint256 value) internal pure returns (uint128) {
              require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
              return uint128(value);
          }
          /**
           * @dev Returns the downcasted uint96 from uint256, reverting on
           * overflow (when the input is greater than largest uint96).
           *
           * Counterpart to Solidity's `uint96` operator.
           *
           * Requirements:
           *
           * - input must fit into 96 bits
           */
          function toUint96(uint256 value) internal pure returns (uint96) {
              require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
              return uint96(value);
          }
          /**
           * @dev Returns the downcasted uint64 from uint256, reverting on
           * overflow (when the input is greater than largest uint64).
           *
           * Counterpart to Solidity's `uint64` operator.
           *
           * Requirements:
           *
           * - input must fit into 64 bits
           */
          function toUint64(uint256 value) internal pure returns (uint64) {
              require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
              return uint64(value);
          }
          /**
           * @dev Returns the downcasted uint32 from uint256, reverting on
           * overflow (when the input is greater than largest uint32).
           *
           * Counterpart to Solidity's `uint32` operator.
           *
           * Requirements:
           *
           * - input must fit into 32 bits
           */
          function toUint32(uint256 value) internal pure returns (uint32) {
              require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
              return uint32(value);
          }
          /**
           * @dev Returns the downcasted uint16 from uint256, reverting on
           * overflow (when the input is greater than largest uint16).
           *
           * Counterpart to Solidity's `uint16` operator.
           *
           * Requirements:
           *
           * - input must fit into 16 bits
           */
          function toUint16(uint256 value) internal pure returns (uint16) {
              require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
              return uint16(value);
          }
          /**
           * @dev Returns the downcasted uint8 from uint256, reverting on
           * overflow (when the input is greater than largest uint8).
           *
           * Counterpart to Solidity's `uint8` operator.
           *
           * Requirements:
           *
           * - input must fit into 8 bits.
           */
          function toUint8(uint256 value) internal pure returns (uint8) {
              require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
              return uint8(value);
          }
          /**
           * @dev Converts a signed int256 into an unsigned uint256.
           *
           * Requirements:
           *
           * - input must be greater than or equal to 0.
           */
          function toUint256(int256 value) internal pure returns (uint256) {
              require(value >= 0, "SafeCast: value must be positive");
              return uint256(value);
          }
          /**
           * @dev Returns the downcasted int128 from int256, reverting on
           * overflow (when the input is less than smallest int128 or
           * greater than largest int128).
           *
           * Counterpart to Solidity's `int128` operator.
           *
           * Requirements:
           *
           * - input must fit into 128 bits
           *
           * _Available since v3.1._
           */
          function toInt128(int256 value) internal pure returns (int128) {
              require(value >= type(int128).min && value <= type(int128).max, "SafeCast: value doesn't fit in 128 bits");
              return int128(value);
          }
          /**
           * @dev Returns the downcasted int64 from int256, reverting on
           * overflow (when the input is less than smallest int64 or
           * greater than largest int64).
           *
           * Counterpart to Solidity's `int64` operator.
           *
           * Requirements:
           *
           * - input must fit into 64 bits
           *
           * _Available since v3.1._
           */
          function toInt64(int256 value) internal pure returns (int64) {
              require(value >= type(int64).min && value <= type(int64).max, "SafeCast: value doesn't fit in 64 bits");
              return int64(value);
          }
          /**
           * @dev Returns the downcasted int32 from int256, reverting on
           * overflow (when the input is less than smallest int32 or
           * greater than largest int32).
           *
           * Counterpart to Solidity's `int32` operator.
           *
           * Requirements:
           *
           * - input must fit into 32 bits
           *
           * _Available since v3.1._
           */
          function toInt32(int256 value) internal pure returns (int32) {
              require(value >= type(int32).min && value <= type(int32).max, "SafeCast: value doesn't fit in 32 bits");
              return int32(value);
          }
          /**
           * @dev Returns the downcasted int16 from int256, reverting on
           * overflow (when the input is less than smallest int16 or
           * greater than largest int16).
           *
           * Counterpart to Solidity's `int16` operator.
           *
           * Requirements:
           *
           * - input must fit into 16 bits
           *
           * _Available since v3.1._
           */
          function toInt16(int256 value) internal pure returns (int16) {
              require(value >= type(int16).min && value <= type(int16).max, "SafeCast: value doesn't fit in 16 bits");
              return int16(value);
          }
          /**
           * @dev Returns the downcasted int8 from int256, reverting on
           * overflow (when the input is less than smallest int8 or
           * greater than largest int8).
           *
           * Counterpart to Solidity's `int8` operator.
           *
           * Requirements:
           *
           * - input must fit into 8 bits.
           *
           * _Available since v3.1._
           */
          function toInt8(int256 value) internal pure returns (int8) {
              require(value >= type(int8).min && value <= type(int8).max, "SafeCast: value doesn't fit in 8 bits");
              return int8(value);
          }
          /**
           * @dev Converts an unsigned uint256 into a signed int256.
           *
           * Requirements:
           *
           * - input must be less than or equal to maxInt256.
           */
          function toInt256(uint256 value) internal pure returns (int256) {
              // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
              require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
              return int256(value);
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.4;
      import "@openzeppelin/contracts/access/Ownable.sol";
      import "./IERC20WithPermit.sol";
      import "./IReceiveApproval.sol";
      /// @title  ERC20WithPermit
      /// @notice Burnable ERC20 token with EIP2612 permit functionality. User can
      ///         authorize a transfer of their token with a signature conforming
      ///         EIP712 standard instead of an on-chain transaction from their
      ///         address. Anyone can submit this signature on the user's behalf by
      ///         calling the permit function, as specified in EIP2612 standard,
      ///         paying gas fees, and possibly performing other actions in the same
      ///         transaction.
      contract ERC20WithPermit is IERC20WithPermit, Ownable {
          /// @notice The amount of tokens owned by the given account.
          mapping(address => uint256) public override balanceOf;
          /// @notice The remaining number of tokens that spender will be
          ///         allowed to spend on behalf of owner through `transferFrom` and
          ///         `burnFrom`. This is zero by default.
          mapping(address => mapping(address => uint256)) public override allowance;
          /// @notice Returns the current nonce for EIP2612 permission for the
          ///         provided token owner for a replay protection. Used to construct
          ///         EIP2612 signature provided to `permit` function.
          mapping(address => uint256) public override nonce;
          uint256 public immutable cachedChainId;
          bytes32 public immutable cachedDomainSeparator;
          /// @notice Returns EIP2612 Permit message hash. Used to construct EIP2612
          ///         signature provided to `permit` function.
          bytes32 public constant override PERMIT_TYPEHASH =
              keccak256(
                  "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
              );
          /// @notice The amount of tokens in existence.
          uint256 public override totalSupply;
          /// @notice The name of the token.
          string public override name;
          /// @notice The symbol of the token.
          string public override symbol;
          /// @notice The decimals places of the token.
          uint8 public constant override decimals = 18;
          constructor(string memory _name, string memory _symbol) {
              name = _name;
              symbol = _symbol;
              cachedChainId = block.chainid;
              cachedDomainSeparator = buildDomainSeparator();
          }
          /// @notice Moves `amount` tokens from the caller's account to `recipient`.
          /// @return True if the operation succeeded, reverts otherwise.
          /// @dev Requirements:
          ///       - `recipient` cannot be the zero address,
          ///       - the caller must have a balance of at least `amount`.
          function transfer(address recipient, uint256 amount)
              external
              override
              returns (bool)
          {
              _transfer(msg.sender, recipient, amount);
              return true;
          }
          /// @notice Moves `amount` tokens from `spender` to `recipient` using the
          ///         allowance mechanism. `amount` is then deducted from the caller's
          ///         allowance unless the allowance was made for `type(uint256).max`.
          /// @return True if the operation succeeded, reverts otherwise.
          /// @dev Requirements:
          ///      - `spender` and `recipient` cannot be the zero address,
          ///      - `spender` must have a balance of at least `amount`,
          ///      - the caller must have allowance for `spender`'s tokens of at least
          ///        `amount`.
          function transferFrom(
              address spender,
              address recipient,
              uint256 amount
          ) external override returns (bool) {
              uint256 currentAllowance = allowance[spender][msg.sender];
              if (currentAllowance != type(uint256).max) {
                  require(
                      currentAllowance >= amount,
                      "Transfer amount exceeds allowance"
                  );
                  _approve(spender, msg.sender, currentAllowance - amount);
              }
              _transfer(spender, recipient, amount);
              return true;
          }
          /// @notice EIP2612 approval made with secp256k1 signature.
          ///         Users can authorize a transfer of their tokens with a signature
          ///         conforming EIP712 standard, rather than an on-chain transaction
          ///         from their address. Anyone can submit this signature on the
          ///         user's behalf by calling the permit function, paying gas fees,
          ///         and possibly performing other actions in the same transaction.
          /// @dev    The deadline argument can be set to `type(uint256).max to create
          ///         permits that effectively never expire.  If the `amount` is set
          ///         to `type(uint256).max` then `transferFrom` and `burnFrom` will
          ///         not reduce an allowance.
          function permit(
              address owner,
              address spender,
              uint256 amount,
              uint256 deadline,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) external override {
              /* solhint-disable-next-line not-rely-on-time */
              require(deadline >= block.timestamp, "Permission expired");
              // Validate `s` and `v` values for a malleability concern described in EIP2.
              // Only signatures with `s` value in the lower half of the secp256k1
              // curve's order and `v` value of 27 or 28 are considered valid.
              require(
                  uint256(s) <=
                      0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0,
                  "Invalid signature 's' value"
              );
              require(v == 27 || v == 28, "Invalid signature 'v' value");
              bytes32 digest = keccak256(
                  abi.encodePacked(
                      "\\x19\\x01",
                      DOMAIN_SEPARATOR(),
                      keccak256(
                          abi.encode(
                              PERMIT_TYPEHASH,
                              owner,
                              spender,
                              amount,
                              nonce[owner]++,
                              deadline
                          )
                      )
                  )
              );
              address recoveredAddress = ecrecover(digest, v, r, s);
              require(
                  recoveredAddress != address(0) && recoveredAddress == owner,
                  "Invalid signature"
              );
              _approve(owner, spender, amount);
          }
          /// @notice Creates `amount` tokens and assigns them to `account`,
          ///         increasing the total supply.
          /// @dev Requirements:
          ///      - `recipient` cannot be the zero address.
          function mint(address recipient, uint256 amount) external onlyOwner {
              require(recipient != address(0), "Mint to the zero address");
              beforeTokenTransfer(address(0), recipient, amount);
              totalSupply += amount;
              balanceOf[recipient] += amount;
              emit Transfer(address(0), recipient, amount);
          }
          /// @notice Destroys `amount` tokens from the caller.
          /// @dev Requirements:
          ///       - the caller must have a balance of at least `amount`.
          function burn(uint256 amount) external override {
              _burn(msg.sender, amount);
          }
          /// @notice Destroys `amount` of tokens from `account` using the allowance
          ///         mechanism. `amount` is then deducted from the caller's allowance
          ///         unless the allowance was made for `type(uint256).max`.
          /// @dev Requirements:
          ///      - `account` must have a balance of at least `amount`,
          ///      - the caller must have allowance for `account`'s tokens of at least
          ///        `amount`.
          function burnFrom(address account, uint256 amount) external override {
              uint256 currentAllowance = allowance[account][msg.sender];
              if (currentAllowance != type(uint256).max) {
                  require(
                      currentAllowance >= amount,
                      "Burn amount exceeds allowance"
                  );
                  _approve(account, msg.sender, currentAllowance - amount);
              }
              _burn(account, amount);
          }
          /// @notice Calls `receiveApproval` function on spender previously approving
          ///         the spender to withdraw from the caller multiple times, up to
          ///         the `amount` amount. If this function is called again, it
          ///         overwrites the current allowance with `amount`. Reverts if the
          ///         approval reverted or if `receiveApproval` call on the spender
          ///         reverted.
          /// @return True if both approval and `receiveApproval` calls succeeded.
          /// @dev If the `amount` is set to `type(uint256).max` then
          ///      `transferFrom` and `burnFrom` will not reduce an allowance.
          function approveAndCall(
              address spender,
              uint256 amount,
              bytes memory extraData
          ) external override returns (bool) {
              if (approve(spender, amount)) {
                  IReceiveApproval(spender).receiveApproval(
                      msg.sender,
                      amount,
                      address(this),
                      extraData
                  );
                  return true;
              }
              return false;
          }
          /// @notice Sets `amount` as the allowance of `spender` over the caller's
          ///         tokens.
          /// @return True if the operation succeeded.
          /// @dev If the `amount` is set to `type(uint256).max` then
          ///      `transferFrom` and `burnFrom` will not reduce an allowance.
          ///      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
          function approve(address spender, uint256 amount)
              public
              override
              returns (bool)
          {
              _approve(msg.sender, spender, amount);
              return true;
          }
          /// @notice Returns hash of EIP712 Domain struct with the token name as
          ///         a signing domain and token contract as a verifying contract.
          ///         Used to construct EIP2612 signature provided to `permit`
          ///         function.
          /* solhint-disable-next-line func-name-mixedcase */
          function DOMAIN_SEPARATOR() public view override returns (bytes32) {
              // As explained in EIP-2612, if the DOMAIN_SEPARATOR contains the
              // chainId and is defined at contract deployment instead of
              // reconstructed for every signature, there is a risk of possible replay
              // attacks between chains in the event of a future chain split.
              // To address this issue, we check the cached chain ID against the
              // current one and in case they are different, we build domain separator
              // from scratch.
              if (block.chainid == cachedChainId) {
                  return cachedDomainSeparator;
              } else {
                  return buildDomainSeparator();
              }
          }
          /// @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.
          // slither-disable-next-line dead-code
          function beforeTokenTransfer(
              address from,
              address to,
              uint256 amount
          ) internal virtual {}
          function _burn(address account, uint256 amount) internal {
              uint256 currentBalance = balanceOf[account];
              require(currentBalance >= amount, "Burn amount exceeds balance");
              beforeTokenTransfer(account, address(0), amount);
              balanceOf[account] = currentBalance - amount;
              totalSupply -= amount;
              emit Transfer(account, address(0), amount);
          }
          function _transfer(
              address spender,
              address recipient,
              uint256 amount
          ) private {
              require(spender != address(0), "Transfer from the zero address");
              require(recipient != address(0), "Transfer to the zero address");
              require(recipient != address(this), "Transfer to the token address");
              beforeTokenTransfer(spender, recipient, amount);
              uint256 spenderBalance = balanceOf[spender];
              require(spenderBalance >= amount, "Transfer amount exceeds balance");
              balanceOf[spender] = spenderBalance - amount;
              balanceOf[recipient] += amount;
              emit Transfer(spender, recipient, amount);
          }
          function _approve(
              address owner,
              address spender,
              uint256 amount
          ) private {
              require(owner != address(0), "Approve from the zero address");
              require(spender != address(0), "Approve to the zero address");
              allowance[owner][spender] = amount;
              emit Approval(owner, spender, amount);
          }
          function buildDomainSeparator() private view returns (bytes32) {
              return
                  keccak256(
                      abi.encode(
                          keccak256(
                              "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
                          ),
                          keccak256(bytes(name)),
                          keccak256(bytes("1")),
                          block.chainid,
                          address(this)
                      )
                  );
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.4;
      /// @notice An interface that should be implemented by tokens supporting
      ///         `approveAndCall`/`receiveApproval` pattern.
      interface IApproveAndCall {
          /// @notice Executes `receiveApproval` function on spender as specified in
          ///         `IReceiveApproval` interface. Approves spender to withdraw from
          ///         the caller multiple times, up to the `amount`. If this
          ///         function is called again, it overwrites the current allowance
          ///         with `amount`. Reverts if the approval reverted or if
          ///         `receiveApproval` call on the spender reverted.
          function approveAndCall(
              address spender,
              uint256 amount,
              bytes memory extraData
          ) external returns (bool);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.4;
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
      import "./IApproveAndCall.sol";
      /// @title  IERC20WithPermit
      /// @notice Burnable ERC20 token with EIP2612 permit functionality. User can
      ///         authorize a transfer of their token with a signature conforming
      ///         EIP712 standard instead of an on-chain transaction from their
      ///         address. Anyone can submit this signature on the user's behalf by
      ///         calling the permit function, as specified in EIP2612 standard,
      ///         paying gas fees, and possibly performing other actions in the same
      ///         transaction.
      interface IERC20WithPermit is IERC20, IERC20Metadata, IApproveAndCall {
          /// @notice EIP2612 approval made with secp256k1 signature.
          ///         Users can authorize a transfer of their tokens with a signature
          ///         conforming EIP712 standard, rather than an on-chain transaction
          ///         from their address. Anyone can submit this signature on the
          ///         user's behalf by calling the permit function, paying gas fees,
          ///         and possibly performing other actions in the same transaction.
          /// @dev    The deadline argument can be set to `type(uint256).max to create
          ///         permits that effectively never expire.
          function permit(
              address owner,
              address spender,
              uint256 amount,
              uint256 deadline,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) external;
          /// @notice Destroys `amount` tokens from the caller.
          function burn(uint256 amount) external;
          /// @notice Destroys `amount` of tokens from `account`, deducting the amount
          ///         from caller's allowance.
          function burnFrom(address account, uint256 amount) external;
          /// @notice Returns hash of EIP712 Domain struct with the token name as
          ///         a signing domain and token contract as a verifying contract.
          ///         Used to construct EIP2612 signature provided to `permit`
          ///         function.
          /* solhint-disable-next-line func-name-mixedcase */
          function DOMAIN_SEPARATOR() external view returns (bytes32);
          /// @notice Returns the current nonce for EIP2612 permission for the
          ///         provided token owner for a replay protection. Used to construct
          ///         EIP2612 signature provided to `permit` function.
          function nonce(address owner) external view returns (uint256);
          /// @notice Returns EIP2612 Permit message hash. Used to construct EIP2612
          ///         signature provided to `permit` function.
          /* solhint-disable-next-line func-name-mixedcase */
          function PERMIT_TYPEHASH() external pure returns (bytes32);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.4;
      /// @notice An interface that should be implemented by contracts supporting
      ///         `approveAndCall`/`receiveApproval` pattern.
      interface IReceiveApproval {
          /// @notice Receives approval to spend tokens. Called as a result of
          ///         `approveAndCall` call on the token.
          function receiveApproval(
              address from,
              uint256 amount,
              address token,
              bytes calldata extraData
          ) external;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.4;
      import "@openzeppelin/contracts/access/Ownable.sol";
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
      import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
      /// @title  MisfundRecovery
      /// @notice Allows the owner of the token contract extending MisfundRecovery
      ///         to recover any ERC20 and ERC721 sent mistakenly to the token
      ///         contract address.
      contract MisfundRecovery is Ownable {
          using SafeERC20 for IERC20;
          function recoverERC20(
              IERC20 token,
              address recipient,
              uint256 amount
          ) external onlyOwner {
              token.safeTransfer(recipient, amount);
          }
          function recoverERC721(
              IERC721 token,
              address recipient,
              uint256 tokenId,
              bytes calldata data
          ) external onlyOwner {
              token.safeTransferFrom(address(this), recipient, tokenId, data);
          }
      }
      // SPDX-License-Identifier: GPL-3.0-or-later
      // ██████████████     ▐████▌     ██████████████
      // ██████████████     ▐████▌     ██████████████
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      // ██████████████     ▐████▌     ██████████████
      // ██████████████     ▐████▌     ██████████████
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      pragma solidity 0.8.9;
      import "./IVotesHistory.sol";
      import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
      import "@openzeppelin/contracts/utils/math/Math.sol";
      import "@openzeppelin/contracts/utils/math/SafeCast.sol";
      /// @title Checkpoints
      /// @dev Abstract contract to support checkpoints for Compound-like voting and
      ///      delegation. This implementation supports token supply up to 2^96 - 1.
      ///      This contract keeps a history (checkpoints) of each account's vote
      ///      power. Vote power can be delegated either by calling the {delegate}
      ///      function directly, or by providing a signature to be used with
      ///      {delegateBySig}. Voting power can be publicly queried through
      ///      {getVotes} and {getPastVotes}.
      ///      NOTE: Extracted from OpenZeppelin ERCVotes.sol.
      abstract contract Checkpoints is IVotesHistory {
          struct Checkpoint {
              uint32 fromBlock;
              uint96 votes;
          }
          // slither-disable-next-line uninitialized-state
          mapping(address => address) internal _delegates;
          mapping(address => uint128[]) internal _checkpoints;
          uint128[] internal _totalSupplyCheckpoints;
          /// @notice Emitted when an account changes their delegate.
          event DelegateChanged(
              address indexed delegator,
              address indexed fromDelegate,
              address indexed toDelegate
          );
          /// @notice Emitted when a balance or delegate change results in changes
          ///         to an account's voting power.
          event DelegateVotesChanged(
              address indexed delegate,
              uint256 previousBalance,
              uint256 newBalance
          );
          function checkpoints(address account, uint32 pos)
              public
              view
              virtual
              returns (Checkpoint memory checkpoint)
          {
              (uint32 fromBlock, uint96 votes) = decodeCheckpoint(
                  _checkpoints[account][pos]
              );
              checkpoint = Checkpoint(fromBlock, votes);
          }
          /// @notice Get number of checkpoints for `account`.
          function numCheckpoints(address account)
              public
              view
              virtual
              returns (uint32)
          {
              return SafeCast.toUint32(_checkpoints[account].length);
          }
          /// @notice Get the address `account` is currently delegating to.
          function delegates(address account) public view virtual returns (address) {
              return _delegates[account];
          }
          /// @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 getVotes(address account) public view returns (uint96) {
              uint256 pos = _checkpoints[account].length;
              return pos == 0 ? 0 : decodeValue(_checkpoints[account][pos - 1]);
          }
          /// @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 getPastVotes(address account, uint256 blockNumber)
              public
              view
              returns (uint96)
          {
              return lookupCheckpoint(_checkpoints[account], blockNumber);
          }
          /// @notice Retrieve the `totalSupply` at the end of `blockNumber`.
          ///         Note, this value is the sum of all balances, but it is NOT the
          ///         sum of all the delegated votes!
          /// @param blockNumber The block number to get the total supply at
          /// @dev `blockNumber` must have been already mined
          function getPastTotalSupply(uint256 blockNumber)
              public
              view
              returns (uint96)
          {
              return lookupCheckpoint(_totalSupplyCheckpoints, blockNumber);
          }
          /// @notice Change delegation for `delegator` to `delegatee`.
          // slither-disable-next-line dead-code
          function delegate(address delegator, address delegatee) internal virtual;
          /// @notice Moves voting power from one delegate to another
          /// @param src Address of old delegate
          /// @param dst Address of new delegate
          /// @param amount Voting power amount to transfer between delegates
          function moveVotingPower(
              address src,
              address dst,
              uint256 amount
          ) internal {
              if (src != dst && amount > 0) {
                  if (src != address(0)) {
                      // https://github.com/crytic/slither/issues/960
                      // slither-disable-next-line variable-scope
                      (uint256 oldWeight, uint256 newWeight) = writeCheckpoint(
                          _checkpoints[src],
                          subtract,
                          amount
                      );
                      emit DelegateVotesChanged(src, oldWeight, newWeight);
                  }
                  if (dst != address(0)) {
                      // https://github.com/crytic/slither/issues/959
                      // slither-disable-next-line uninitialized-local
                      (uint256 oldWeight, uint256 newWeight) = writeCheckpoint(
                          _checkpoints[dst],
                          add,
                          amount
                      );
                      emit DelegateVotesChanged(dst, oldWeight, newWeight);
                  }
              }
          }
          /// @notice Writes a new checkpoint based on operating last stored value
          ///         with a `delta`. Usually, said operation is the `add` or
          ///         `subtract` functions from this contract, but more complex
          ///         functions can be passed as parameters.
          /// @param ckpts The checkpoints array to use
          /// @param op The function to apply over the last value and the `delta`
          /// @param delta Variation with respect to last stored value to be used
          ///              for new checkpoint
          function writeCheckpoint(
              uint128[] storage ckpts,
              function(uint256, uint256) view returns (uint256) op,
              uint256 delta
          ) internal returns (uint256 oldWeight, uint256 newWeight) {
              uint256 pos = ckpts.length;
              oldWeight = pos == 0 ? 0 : decodeValue(ckpts[pos - 1]);
              newWeight = op(oldWeight, delta);
              if (pos > 0) {
                  uint32 fromBlock = decodeBlockNumber(ckpts[pos - 1]);
                  // slither-disable-next-line incorrect-equality
                  if (fromBlock == block.number) {
                      ckpts[pos - 1] = encodeCheckpoint(
                          fromBlock,
                          SafeCast.toUint96(newWeight)
                      );
                      return (oldWeight, newWeight);
                  }
              }
              ckpts.push(
                  encodeCheckpoint(
                      SafeCast.toUint32(block.number),
                      SafeCast.toUint96(newWeight)
                  )
              );
          }
          /// @notice Lookup a value in a list of (sorted) checkpoints.
          /// @param ckpts The checkpoints array to use
          /// @param blockNumber Block number when we want to get the checkpoint at
          function lookupCheckpoint(uint128[] storage ckpts, uint256 blockNumber)
              internal
              view
              returns (uint96)
          {
              // We run a binary search to look for the earliest checkpoint taken
              // after `blockNumber`. During the loop, the index of the wanted
              // checkpoint remains in the range [low-1, high). With each iteration,
              // either `low` or `high` is moved towards the middle of the range to
              // maintain the invariant.
              // - If the middle checkpoint is after `blockNumber`,
              //   we look in [low, mid)
              // - If the middle checkpoint is before or equal to `blockNumber`,
              //   we look in [mid+1, high)
              // Once we reach a single value (when low == high), we've found the
              // right checkpoint at the index high-1, if not out of bounds (in that
              // case we're looking too far in the past and the result is 0).
              // Note that if the latest checkpoint available is exactly for
              // `blockNumber`, we end up with an index that is past the end of the
              // array, so we technically don't find a checkpoint after
              // `blockNumber`, but it works out the same.
              require(blockNumber < block.number, "Block not yet determined");
              uint256 high = ckpts.length;
              uint256 low = 0;
              while (low < high) {
                  uint256 mid = Math.average(low, high);
                  uint32 midBlock = decodeBlockNumber(ckpts[mid]);
                  if (midBlock > blockNumber) {
                      high = mid;
                  } else {
                      low = mid + 1;
                  }
              }
              return high == 0 ? 0 : decodeValue(ckpts[high - 1]);
          }
          /// @notice Maximum token supply. Defaults to `type(uint96).max` (2^96 - 1)
          // slither-disable-next-line dead-code
          function maxSupply() internal view virtual returns (uint96) {
              return type(uint96).max;
          }
          /// @notice Encodes a `blockNumber` and `value` into a single `uint128`
          ///         checkpoint.
          /// @dev `blockNumber` is stored in the first 32 bits, while `value` in the
          ///      remaining 96 bits.
          function encodeCheckpoint(uint32 blockNumber, uint96 value)
              internal
              pure
              returns (uint128)
          {
              return (uint128(blockNumber) << 96) | uint128(value);
          }
          /// @notice Decodes a block number from a `uint128` `checkpoint`.
          function decodeBlockNumber(uint128 checkpoint)
              internal
              pure
              returns (uint32)
          {
              return uint32(bytes4(bytes16(checkpoint)));
          }
          /// @notice Decodes a voting value from a `uint128` `checkpoint`.
          function decodeValue(uint128 checkpoint) internal pure returns (uint96) {
              return uint96(checkpoint);
          }
          /// @notice Decodes a block number and voting value from a `uint128`
          ///         `checkpoint`.
          function decodeCheckpoint(uint128 checkpoint)
              internal
              pure
              returns (uint32 blockNumber, uint96 value)
          {
              blockNumber = decodeBlockNumber(checkpoint);
              value = decodeValue(checkpoint);
          }
          // slither-disable-next-line dead-code
          function add(uint256 a, uint256 b) internal pure returns (uint256) {
              return a + b;
          }
          // slither-disable-next-line dead-code
          function subtract(uint256 a, uint256 b) internal pure returns (uint256) {
              return a - b;
          }
      }
      // SPDX-License-Identifier: GPL-3.0-or-later
      // ██████████████     ▐████▌     ██████████████
      // ██████████████     ▐████▌     ██████████████
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      // ██████████████     ▐████▌     ██████████████
      // ██████████████     ▐████▌     ██████████████
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      pragma solidity 0.8.9;
      interface IVotesHistory {
          function getPastVotes(address account, uint256 blockNumber)
              external
              view
              returns (uint96);
          function getPastTotalSupply(uint256 blockNumber)
              external
              view
              returns (uint96);
      }
      // SPDX-License-Identifier: GPL-3.0-or-later
      // ██████████████     ▐████▌     ██████████████
      // ██████████████     ▐████▌     ██████████████
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      // ██████████████     ▐████▌     ██████████████
      // ██████████████     ▐████▌     ██████████████
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      //               ▐████▌    ▐████▌
      pragma solidity 0.8.9;
      import "../governance/Checkpoints.sol";
      import "@openzeppelin/contracts/utils/math/SafeCast.sol";
      import "@thesis/solidity-contracts/contracts/token/ERC20WithPermit.sol";
      import "@thesis/solidity-contracts/contracts/token/MisfundRecovery.sol";
      /// @title T token
      /// @notice Threshold Network T token
      /// @dev By default, token balance does not account for voting power.
      ///      This makes transfers cheaper. The downside is that it requires users
      ///      to delegate to themselves to activate checkpoints and have their
      ///      voting power tracked.
      contract T is ERC20WithPermit, MisfundRecovery, Checkpoints {
          /// @notice The EIP-712 typehash for the delegation struct used by
          ///         `delegateBySig`.
          bytes32 public constant DELEGATION_TYPEHASH =
              keccak256(
                  "Delegation(address delegatee,uint256 nonce,uint256 deadline)"
              );
          constructor() ERC20WithPermit("Threshold Network Token", "T") {}
          /// @notice Delegates votes from signatory to `delegatee`
          /// @param delegatee The address to delegate votes to
          /// @param deadline 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 signatory,
              address delegatee,
              uint256 deadline,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) external {
              /* solhint-disable-next-line not-rely-on-time */
              require(deadline >= block.timestamp, "Delegation expired");
              // Validate `s` and `v` values for a malleability concern described in EIP2.
              // Only signatures with `s` value in the lower half of the secp256k1
              // curve's order and `v` value of 27 or 28 are considered valid.
              require(
                  uint256(s) <=
                      0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0,
                  "Invalid signature 's' value"
              );
              require(v == 27 || v == 28, "Invalid signature 'v' value");
              bytes32 digest = keccak256(
                  abi.encodePacked(
                      "\\x19\\x01",
                      DOMAIN_SEPARATOR(),
                      keccak256(
                          abi.encode(
                              DELEGATION_TYPEHASH,
                              delegatee,
                              nonce[signatory]++,
                              deadline
                          )
                      )
                  )
              );
              address recoveredAddress = ecrecover(digest, v, r, s);
              require(
                  recoveredAddress != address(0) && recoveredAddress == signatory,
                  "Invalid signature"
              );
              return delegate(signatory, delegatee);
          }
          /// @notice Delegate votes from `msg.sender` to `delegatee`.
          /// @param delegatee The address to delegate votes to
          function delegate(address delegatee) public virtual {
              return delegate(msg.sender, delegatee);
          }
          // slither-disable-next-line dead-code
          function beforeTokenTransfer(
              address from,
              address to,
              uint256 amount
          ) internal override {
              uint96 safeAmount = SafeCast.toUint96(amount);
              // When minting:
              if (from == address(0)) {
                  // Does not allow to mint more than uint96 can fit. Otherwise, the
                  // Checkpoint might not fit the balance.
                  require(
                      totalSupply + amount <= maxSupply(),
                      "Maximum total supply exceeded"
                  );
                  writeCheckpoint(_totalSupplyCheckpoints, add, safeAmount);
              }
              // When burning:
              if (to == address(0)) {
                  writeCheckpoint(_totalSupplyCheckpoints, subtract, safeAmount);
              }
              moveVotingPower(delegates(from), delegates(to), safeAmount);
          }
          function delegate(address delegator, address delegatee)
              internal
              virtual
              override
          {
              address currentDelegate = delegates(delegator);
              uint96 delegatorBalance = SafeCast.toUint96(balanceOf[delegator]);
              _delegates[delegator] = delegatee;
              emit DelegateChanged(delegator, currentDelegate, delegatee);
              moveVotingPower(currentDelegate, delegatee, delegatorBalance);
          }
      }