ETH Price: $2,484.38 (+1.56%)

Transaction Decoder

Block:
16486108 at Jan-25-2023 08:05:47 PM +UTC
Transaction Fee:
0.00525034403595752 ETH $13.04
Gas Used:
131,507 Gas / 39.92444536 Gwei

Emitted Events:

58 PSP.Approval( owner=[Sender] 0xfa814e2322d5bf9b57d42325e174138d8afbb369, spender=[Receiver] sePSP, amount=4296126401411295552869 )
59 PSP.Approval( owner=[Sender] 0xfa814e2322d5bf9b57d42325e174138d8afbb369, spender=[Receiver] sePSP, amount=0 )
60 PSP.Transfer( from=[Sender] 0xfa814e2322d5bf9b57d42325e174138d8afbb369, to=[Receiver] sePSP, amount=4296126401411295552869 )
61 sePSP.Transfer( from=0x0000000000000000000000000000000000000000, to=[Sender] 0xfa814e2322d5bf9b57d42325e174138d8afbb369, value=4296126401411295552869 )
62 sePSP.Deposited( user=[Sender] 0xfa814e2322d5bf9b57d42325e174138d8afbb369, amount=4296126401411295552869 )

Account State Difference:

  Address   Before After State Difference Code
0x716fBC68...94CC5FfAB
(beaverbuild)
138.459587800786437164 Eth138.460219034386437164 Eth0.0006312336
0xcAfE0010...D277f3dE5
0xfa814e23...D8AfbB369
0.046040327080505047 Eth
Nonce: 13
0.040789983044547527 Eth
Nonce: 14
0.00525034403595752

Execution Trace

sePSP.depositWithPermit( _assetAmount=4296126401411295552869, permit=0x000000000000000000000000FA814E2322D5BF9B57D42325E174138D8AFBB369000000000000000000000000716FBC68E0C761684D9280484243FF094CC5FFAB0000000000000000000000000000000000000000000000E8E4BB083AF98125650000000000000000000000000000000000000000000000000000000063D57FE6000000000000000000000000000000000000000000000000000000000000001B87CA4534B09329CADD0906F77DF5160E1D64B489D1B17772E0782E15BE806013436D14461C7D95424E7DDF93CC7E96EBDE64A602DA5A55AFBC629BD9A1275EAF )
  • PSP.permit( owner=0xfa814e2322d5bF9B57D42325e174138D8AfbB369, spender=0x716fBC68E0c761684D9280484243FF094CC5FfAB, rawAmount=4296126401411295552869, deadline=1674936294, v=27, r=87CA4534B09329CADD0906F77DF5160E1D64B489D1B17772E0782E15BE806013, s=436D14461C7D95424E7DDF93CC7E96EBDE64A602DA5A55AFBC629BD9A1275EAF )
    • Null: 0x000...001.edd88507( )
    • PSP.transferFrom( src=0xfa814e2322d5bF9B57D42325e174138D8AfbB369, dst=0x716fBC68E0c761684D9280484243FF094CC5FfAB, rawAmount=4296126401411295552869 ) => ( True )
      File 1 of 2: sePSP
      pragma solidity 0.8.6;
      import "./utils/TimeLockedERC20.sol";
      import "openzeppelin-solidity/contracts/token/ERC20/extensions/ERC20Votes.sol";
      contract sePSP is ERC20Votes, TimeLockedERC20 {
          string constant NAME = "Social Escrowed PSP";
          string constant SYMBOL = "sePSP1";
          constructor(
              IERC20 _asset,
              uint256 _timeLockBlocks,
              uint256 _minTimeLockBlocks,
              uint256 _maxTimeLockBlocks
          ) TimeLockedERC20(NAME, SYMBOL, _asset, _timeLockBlocks, _minTimeLockBlocks, _maxTimeLockBlocks) {}
          function _afterTokenTransfer(
              address from,
              address to,
              uint256 amount
          ) internal override(ERC20, ERC20Votes) {
              super._afterTokenTransfer(from, to, amount);
          }
          function _mint(address to, uint256 amount) internal override(ERC20, ERC20Votes) {
              super._mint(to, amount);
          }
          function _burn(address account, uint256 amount) internal override(ERC20, ERC20Votes) {
              super._burn(account, amount);
          }
      }
      pragma solidity 0.8.6;
      import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol";
      import "openzeppelin-solidity/contracts/token/ERC20/extensions/draft-ERC20Permit.sol";
      import "openzeppelin-solidity/contracts/access/Ownable.sol";
      import "openzeppelin-solidity/contracts/security/Pausable.sol";
      import "./Utils.sol";
      error TimelockOutOfRange(uint256 attemptedTimelockDuration);
      error CannotCancelWithdrawalRequest(int256 reqId);
      error CannotWithdraw(int256 reqId);
      error CannotWithdrawYet(int256 reqId);
      error InvalidWithdrawalRequestId(int256 reqId);
      contract TimeLockedERC20 is ERC20Permit, Ownable, Pausable {
          enum WITHDRAW_STATUS {
              UNUSED,
              UNLOCKING,
              RELEASED,
              CANCELLED
          }
          IERC20 public immutable asset;
          uint256 public timeLockDuration;
          uint256 public immutable minTimeLockDuration;
          uint256 public immutable maxTimeLockDuration;
          uint256 public unlockingAssets;
          struct WithdrawalRequest {
              uint256 amount;
              uint256 releaseTime;
              WITHDRAW_STATUS status;
          }
          mapping(address => mapping(int256 => WithdrawalRequest)) public userVsWithdrawals;
          mapping(address => int256) public userVsNextID;
          event TimeLockChanged(uint256 oldTimeLock, uint256 newTimeLock);
          event RequestedUnlocking(int256 indexed id, address indexed user, uint256 amount);
          event Withdraw(int256 indexed id, address indexed user, uint256 amount);
          event Deposited(address indexed user, uint256 amount);
          event CancelledWithdrawalRequest(int256 indexed id, address indexed user, uint256 amount);
          constructor(
              string memory name,
              string memory symbol,
              IERC20 _asset,
              uint256 _timeLockDuration,
              uint256 _minTimeLockDuration,
              uint256 _maxTimeLockDuration
          ) public ERC20Permit(name) ERC20(name, symbol) {
              if (_timeLockDuration < _minTimeLockDuration || _timeLockDuration > _maxTimeLockDuration) {
                  revert TimelockOutOfRange(_timeLockDuration);
              }
              asset = _asset;
              timeLockDuration = _timeLockDuration;
              minTimeLockDuration = _minTimeLockDuration;
              maxTimeLockDuration = _maxTimeLockDuration;
          }
          function pause() external onlyOwner {
              _pause();
          }
          function unpause() external onlyOwner {
              _unpause();
          }
          function changeTimeLock(uint256 newTimeLockDuration) external onlyOwner {
              if (newTimeLockDuration < minTimeLockDuration || newTimeLockDuration > maxTimeLockDuration) {
                  revert TimelockOutOfRange(newTimeLockDuration);
              }
              emit TimeLockChanged(timeLockDuration, newTimeLockDuration);
              timeLockDuration = newTimeLockDuration;
          }
          function depositWithPermit(uint256 _assetAmount, bytes calldata permit) external {
              Utils.permit(asset, permit);
              deposit(_assetAmount);
          }
          function requestWithdraw(uint256 _unlockingAmount) external {
              int256 id = userVsNextID[msg.sender]++;
              _burn(msg.sender, _unlockingAmount);
              WithdrawalRequest storage request = userVsWithdrawals[msg.sender][id];
              request.amount = _unlockingAmount;
              request.releaseTime = block.timestamp + timeLockDuration;
              request.status = WITHDRAW_STATUS.UNLOCKING;
              unlockingAssets += _unlockingAmount;
              emit RequestedUnlocking(id, msg.sender, _unlockingAmount);
          }
          function withdrawMultiple(int256[] calldata ids) external {
              for (uint256 i = 0; i < ids.length; i++) {
                  withdraw(ids[i]);
              }
          }
          function cancelMultipleWithdrawalRequests(int256[] calldata ids) external {
              for (uint256 i = 0; i < ids.length; i++) {
                  cancelWithdrawalRequest(ids[i]);
              }
          }
          function deposit(uint256 _assetAmount) public {
              asset.transferFrom(msg.sender, address(this), _assetAmount);
              _deposit(_assetAmount);
          }
          function _deposit(uint256 _assetAmount) internal whenNotPaused {
              _mint(msg.sender, _assetAmount);
              emit Deposited(msg.sender, _assetAmount);
          }
          function withdraw(int256 id) public {
              uint256 _assetAmount = _withdraw(id);
              asset.transfer(msg.sender, _assetAmount);
          }
          function _withdraw(int256 id) internal returns (uint256) {
              if (id < 0) {
                  revert InvalidWithdrawalRequestId(id);
              }
              WithdrawalRequest storage request = userVsWithdrawals[msg.sender][id];
              if (request.status != WITHDRAW_STATUS.UNLOCKING) {
                  revert CannotWithdraw(id);
              }
              if (request.releaseTime > block.timestamp) {
                  revert CannotWithdrawYet(id);
              }
              request.status = WITHDRAW_STATUS.RELEASED;
              uint256 _assetAmount = request.amount;
              unlockingAssets -= _assetAmount;
              emit Withdraw(id, msg.sender, _assetAmount);
              return _assetAmount;
          }
          function cancelWithdrawalRequest(int256 id) public whenNotPaused {
              if (id < 0) {
                  revert InvalidWithdrawalRequestId(id);
              }
              WithdrawalRequest storage request = userVsWithdrawals[msg.sender][id];
              if (request.status != WITHDRAW_STATUS.UNLOCKING) {
                  revert CannotCancelWithdrawalRequest(id);
              }
              request.status = WITHDRAW_STATUS.CANCELLED;
              uint256 _assetAmount = request.amount;
              _mint(msg.sender, _assetAmount);
              unlockingAssets -= _assetAmount;
              emit CancelledWithdrawalRequest(id, msg.sender, _assetAmount);
          }
          // This is for use off chain, it finds any locked IDs in the specified range
          // If start is negative, starts looking that many entries back from the end
          function findUnlockingIDs(
              address user,
              int256 start,
              uint16 countToCheck
          ) external view returns (int256[] memory ids) {
              int256 nextID = userVsNextID[user];
              if (start >= nextID) return ids;
              if (start < 0) start += nextID;
              int256 end = start + int256(uint256(countToCheck));
              if (end <= 0) return ids;
              if (end > nextID) end = nextID;
              if (start < 0) start = 0;
              mapping(int256 => WithdrawalRequest) storage withdrawals = userVsWithdrawals[user];
              ids = new int256[](uint256(end - start));
              uint256 length = 0;
              // Nothing in here can overflow so disable the checks for the loop
              unchecked {
                  for (int256 id = start; id < end; ++id) {
                      if (withdrawals[id].status == WITHDRAW_STATUS.UNLOCKING) {
                          ids[length++] = id;
                      }
                  }
              }
              // Need to force the array length to the correct value using assembly
              assembly {
                  mstore(ids, length)
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20Votes.sol)
      pragma solidity ^0.8.0;
      import "./draft-ERC20Permit.sol";
      import "../../../utils/math/Math.sol";
      import "../../../governance/utils/IVotes.sol";
      import "../../../utils/math/SafeCast.sol";
      import "../../../utils/cryptography/ECDSA.sol";
      /**
       * @dev Extension of ERC20 to support Compound-like voting and delegation. This version is more generic than Compound's,
       * and supports token supply up to 2^224^ - 1, while COMP is limited to 2^96^ - 1.
       *
       * NOTE: If exact COMP compatibility is required, use the {ERC20VotesComp} variant of this module.
       *
       * This extension 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 queried through the public accessors {getVotes} and {getPastVotes}.
       *
       * 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 in order to activate checkpoints and have their voting power tracked.
       *
       * _Available since v4.2._
       */
      abstract contract ERC20Votes is IVotes, ERC20Permit {
          struct Checkpoint {
              uint32 fromBlock;
              uint224 votes;
          }
          bytes32 private constant _DELEGATION_TYPEHASH =
              keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");
          mapping(address => address) private _delegates;
          mapping(address => Checkpoint[]) private _checkpoints;
          Checkpoint[] private _totalSupplyCheckpoints;
          /**
           * @dev Get the `pos`-th checkpoint for `account`.
           */
          function checkpoints(address account, uint32 pos) public view virtual returns (Checkpoint memory) {
              return _checkpoints[account][pos];
          }
          /**
           * @dev Get number of checkpoints for `account`.
           */
          function numCheckpoints(address account) public view virtual returns (uint32) {
              return SafeCast.toUint32(_checkpoints[account].length);
          }
          /**
           * @dev Get the address `account` is currently delegating to.
           */
          function delegates(address account) public view virtual override returns (address) {
              return _delegates[account];
          }
          /**
           * @dev Gets the current votes balance for `account`
           */
          function getVotes(address account) public view virtual override returns (uint256) {
              uint256 pos = _checkpoints[account].length;
              return pos == 0 ? 0 : _checkpoints[account][pos - 1].votes;
          }
          /**
           * @dev Retrieve the number of votes for `account` at the end of `blockNumber`.
           *
           * Requirements:
           *
           * - `blockNumber` must have been already mined
           */
          function getPastVotes(address account, uint256 blockNumber) public view virtual override returns (uint256) {
              require(blockNumber < block.number, "ERC20Votes: block not yet mined");
              return _checkpointsLookup(_checkpoints[account], blockNumber);
          }
          /**
           * @dev Retrieve the `totalSupply` at the end of `blockNumber`. Note, this value is the sum of all balances.
           * It is but NOT the sum of all the delegated votes!
           *
           * Requirements:
           *
           * - `blockNumber` must have been already mined
           */
          function getPastTotalSupply(uint256 blockNumber) public view virtual override returns (uint256) {
              require(blockNumber < block.number, "ERC20Votes: block not yet mined");
              return _checkpointsLookup(_totalSupplyCheckpoints, blockNumber);
          }
          /**
           * @dev Lookup a value in a list of (sorted) checkpoints.
           */
          function _checkpointsLookup(Checkpoint[] storage ckpts, uint256 blockNumber) private view returns (uint256) {
              // 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 which 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.
              uint256 high = ckpts.length;
              uint256 low = 0;
              while (low < high) {
                  uint256 mid = Math.average(low, high);
                  if (ckpts[mid].fromBlock > blockNumber) {
                      high = mid;
                  } else {
                      low = mid + 1;
                  }
              }
              return high == 0 ? 0 : ckpts[high - 1].votes;
          }
          /**
           * @dev Delegate votes from the sender to `delegatee`.
           */
          function delegate(address delegatee) public virtual override {
              _delegate(_msgSender(), delegatee);
          }
          /**
           * @dev Delegates votes from signer to `delegatee`
           */
          function delegateBySig(
              address delegatee,
              uint256 nonce,
              uint256 expiry,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) public virtual override {
              require(block.timestamp <= expiry, "ERC20Votes: signature expired");
              address signer = ECDSA.recover(
                  _hashTypedDataV4(keccak256(abi.encode(_DELEGATION_TYPEHASH, delegatee, nonce, expiry))),
                  v,
                  r,
                  s
              );
              require(nonce == _useNonce(signer), "ERC20Votes: invalid nonce");
              _delegate(signer, delegatee);
          }
          /**
           * @dev Maximum token supply. Defaults to `type(uint224).max` (2^224^ - 1).
           */
          function _maxSupply() internal view virtual returns (uint224) {
              return type(uint224).max;
          }
          /**
           * @dev Snapshots the totalSupply after it has been increased.
           */
          function _mint(address account, uint256 amount) internal virtual override {
              super._mint(account, amount);
              require(totalSupply() <= _maxSupply(), "ERC20Votes: total supply risks overflowing votes");
              _writeCheckpoint(_totalSupplyCheckpoints, _add, amount);
          }
          /**
           * @dev Snapshots the totalSupply after it has been decreased.
           */
          function _burn(address account, uint256 amount) internal virtual override {
              super._burn(account, amount);
              _writeCheckpoint(_totalSupplyCheckpoints, _subtract, amount);
          }
          /**
           * @dev Move voting power when tokens are transferred.
           *
           * Emits a {DelegateVotesChanged} event.
           */
          function _afterTokenTransfer(
              address from,
              address to,
              uint256 amount
          ) internal virtual override {
              super._afterTokenTransfer(from, to, amount);
              _moveVotingPower(delegates(from), delegates(to), amount);
          }
          /**
           * @dev Change delegation for `delegator` to `delegatee`.
           *
           * Emits events {DelegateChanged} and {DelegateVotesChanged}.
           */
          function _delegate(address delegator, address delegatee) internal virtual {
              address currentDelegate = delegates(delegator);
              uint256 delegatorBalance = balanceOf(delegator);
              _delegates[delegator] = delegatee;
              emit DelegateChanged(delegator, currentDelegate, delegatee);
              _moveVotingPower(currentDelegate, delegatee, delegatorBalance);
          }
          function _moveVotingPower(
              address src,
              address dst,
              uint256 amount
          ) private {
              if (src != dst && amount > 0) {
                  if (src != address(0)) {
                      (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[src], _subtract, amount);
                      emit DelegateVotesChanged(src, oldWeight, newWeight);
                  }
                  if (dst != address(0)) {
                      (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[dst], _add, amount);
                      emit DelegateVotesChanged(dst, oldWeight, newWeight);
                  }
              }
          }
          function _writeCheckpoint(
              Checkpoint[] storage ckpts,
              function(uint256, uint256) view returns (uint256) op,
              uint256 delta
          ) private returns (uint256 oldWeight, uint256 newWeight) {
              uint256 pos = ckpts.length;
              oldWeight = pos == 0 ? 0 : ckpts[pos - 1].votes;
              newWeight = op(oldWeight, delta);
              if (pos > 0 && ckpts[pos - 1].fromBlock == block.number) {
                  ckpts[pos - 1].votes = SafeCast.toUint224(newWeight);
              } else {
                  ckpts.push(Checkpoint({fromBlock: SafeCast.toUint32(block.number), votes: SafeCast.toUint224(newWeight)}));
              }
          }
          function _add(uint256 a, uint256 b) private pure returns (uint256) {
              return a + b;
          }
          function _subtract(uint256 a, uint256 b) private pure returns (uint256) {
              return a - b;
          }
      }
      pragma solidity 0.8.6;
      import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol";
      import "openzeppelin-solidity/contracts/token/ERC20/extensions/draft-IERC20Permit.sol";
      error PermitFailed();
      error TransferEthFailed();
      library Utils {
          function permit(IERC20 token, bytes memory permit) internal {
              if (permit.length == 32 * 7) {
                  (bool success, ) = address(token).call(abi.encodePacked(IERC20Permit.permit.selector, permit));
                  if (!success) {
                      revert PermitFailed();
                  }
              }
          }
          function transferETH(address payable destination, uint256 amount) internal {
              if (amount > 0) {
                  (bool result, ) = destination.call{ value: amount }("");
                  if (!result) {
                      revert TransferEthFailed();
                  }
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)
      pragma solidity ^0.8.0;
      import "../utils/Context.sol";
      /**
       * @dev Contract module which allows children to implement an emergency stop
       * mechanism that can be triggered by an authorized account.
       *
       * This module is used through inheritance. It will make available the
       * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
       * the functions of your contract. Note that they will not be pausable by
       * simply including this module, only once the modifiers are put in place.
       */
      abstract contract Pausable is Context {
          /**
           * @dev Emitted when the pause is triggered by `account`.
           */
          event Paused(address account);
          /**
           * @dev Emitted when the pause is lifted by `account`.
           */
          event Unpaused(address account);
          bool private _paused;
          /**
           * @dev Initializes the contract in unpaused state.
           */
          constructor() {
              _paused = false;
          }
          /**
           * @dev Returns true if the contract is paused, and false otherwise.
           */
          function paused() public view virtual returns (bool) {
              return _paused;
          }
          /**
           * @dev Modifier to make a function callable only when the contract is not paused.
           *
           * Requirements:
           *
           * - The contract must not be paused.
           */
          modifier whenNotPaused() {
              require(!paused(), "Pausable: paused");
              _;
          }
          /**
           * @dev Modifier to make a function callable only when the contract is paused.
           *
           * Requirements:
           *
           * - The contract must be paused.
           */
          modifier whenPaused() {
              require(paused(), "Pausable: not paused");
              _;
          }
          /**
           * @dev Triggers stopped state.
           *
           * Requirements:
           *
           * - The contract must not be paused.
           */
          function _pause() internal virtual whenNotPaused {
              _paused = true;
              emit Paused(_msgSender());
          }
          /**
           * @dev Returns to normal state.
           *
           * Requirements:
           *
           * - The contract must be paused.
           */
          function _unpause() internal virtual whenPaused {
              _paused = false;
              emit Unpaused(_msgSender());
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (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 (last updated v4.6.0) (token/ERC20/IERC20.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Interface of the ERC20 standard as defined in the EIP.
       */
      interface IERC20 {
          /**
           * @dev Emitted when `value` tokens are moved from one account (`from`) to
           * another (`to`).
           *
           * Note that `value` may be zero.
           */
          event Transfer(address indexed from, address indexed to, uint256 value);
          /**
           * @dev Emitted when the allowance of a `spender` for an `owner` is set by
           * a call to {approve}. `value` is the new allowance.
           */
          event Approval(address indexed owner, address indexed spender, uint256 value);
          /**
           * @dev 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 `to`.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transfer(address to, 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 `from` to `to` 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 from,
              address to,
              uint256 amount
          ) external returns (bool);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/extensions/draft-ERC20Permit.sol)
      pragma solidity ^0.8.0;
      import "./draft-IERC20Permit.sol";
      import "../ERC20.sol";
      import "../../../utils/cryptography/draft-EIP712.sol";
      import "../../../utils/cryptography/ECDSA.sol";
      import "../../../utils/Counters.sol";
      /**
       * @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
       * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
       *
       * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
       * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't
       * need to send a transaction, and thus is not required to hold Ether at all.
       *
       * _Available since v3.4._
       */
      abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {
          using Counters for Counters.Counter;
          mapping(address => Counters.Counter) private _nonces;
          // solhint-disable-next-line var-name-mixedcase
          bytes32 private constant _PERMIT_TYPEHASH =
              keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
          /**
           * @dev In previous versions `_PERMIT_TYPEHASH` was declared as `immutable`.
           * However, to ensure consistency with the upgradeable transpiler, we will continue
           * to reserve a slot.
           * @custom:oz-renamed-from _PERMIT_TYPEHASH
           */
          // solhint-disable-next-line var-name-mixedcase
          bytes32 private _PERMIT_TYPEHASH_DEPRECATED_SLOT;
          /**
           * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`.
           *
           * It's a good idea to use the same `name` that is defined as the ERC20 token name.
           */
          constructor(string memory name) EIP712(name, "1") {}
          /**
           * @dev See {IERC20Permit-permit}.
           */
          function permit(
              address owner,
              address spender,
              uint256 value,
              uint256 deadline,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) public virtual override {
              require(block.timestamp <= deadline, "ERC20Permit: expired deadline");
              bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));
              bytes32 hash = _hashTypedDataV4(structHash);
              address signer = ECDSA.recover(hash, v, r, s);
              require(signer == owner, "ERC20Permit: invalid signature");
              _approve(owner, spender, value);
          }
          /**
           * @dev See {IERC20Permit-nonces}.
           */
          function nonces(address owner) public view virtual override returns (uint256) {
              return _nonces[owner].current();
          }
          /**
           * @dev See {IERC20Permit-DOMAIN_SEPARATOR}.
           */
          // solhint-disable-next-line func-name-mixedcase
          function DOMAIN_SEPARATOR() external view override returns (bytes32) {
              return _domainSeparatorV4();
          }
          /**
           * @dev "Consume a nonce": return the current value and increment.
           *
           * _Available since v4.1._
           */
          function _useNonce(address owner) internal virtual returns (uint256 current) {
              Counters.Counter storage nonce = _nonces[owner];
              current = nonce.current();
              nonce.increment();
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
       * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
       *
       * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
       * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
       * need to send a transaction, and thus is not required to hold Ether at all.
       */
      interface IERC20Permit {
          /**
           * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
           * given ``owner``'s signed approval.
           *
           * IMPORTANT: The same issues {IERC20-approve} has related to transaction
           * ordering also apply here.
           *
           * Emits an {Approval} event.
           *
           * Requirements:
           *
           * - `spender` cannot be the zero address.
           * - `deadline` must be a timestamp in the future.
           * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
           * over the EIP712-formatted function arguments.
           * - the signature must use ``owner``'s current nonce (see {nonces}).
           *
           * For more information on the signature format, see the
           * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
           * section].
           */
          function permit(
              address owner,
              address spender,
              uint256 value,
              uint256 deadline,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) external;
          /**
           * @dev Returns the current nonce for `owner`. This value must be
           * included whenever a signature is generated for {permit}.
           *
           * Every successful call to {permit} increases ``owner``'s nonce by one. This
           * prevents a signature from being used multiple times.
           */
          function nonces(address owner) external view returns (uint256);
          /**
           * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
           */
          // solhint-disable-next-line func-name-mixedcase
          function DOMAIN_SEPARATOR() external view returns (bytes32);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (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 (last updated v4.6.0) (token/ERC20/ERC20.sol)
      pragma solidity ^0.8.0;
      import "./IERC20.sol";
      import "./extensions/IERC20Metadata.sol";
      import "../../utils/Context.sol";
      /**
       * @dev Implementation of the {IERC20} interface.
       *
       * This implementation is agnostic to the way tokens are created. This means
       * that a supply mechanism has to be added in a derived contract using {_mint}.
       * For a generic mechanism see {ERC20PresetMinterPauser}.
       *
       * TIP: For a detailed writeup see our guide
       * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
       * to implement supply mechanisms].
       *
       * We have followed general OpenZeppelin Contracts guidelines: functions revert
       * instead returning `false` on failure. This behavior is nonetheless
       * conventional and does not conflict with the expectations of ERC20
       * applications.
       *
       * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
       * This allows applications to reconstruct the allowance for all accounts just
       * by listening to said events. Other implementations of the EIP may not emit
       * these events, as it isn't required by the specification.
       *
       * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
       * functions have been added to mitigate the well-known issues around setting
       * allowances. See {IERC20-approve}.
       */
      contract ERC20 is Context, IERC20, IERC20Metadata {
          mapping(address => uint256) private _balances;
          mapping(address => mapping(address => uint256)) private _allowances;
          uint256 private _totalSupply;
          string private _name;
          string private _symbol;
          /**
           * @dev Sets the values for {name} and {symbol}.
           *
           * The default value of {decimals} is 18. To select a different value for
           * {decimals} you should overload it.
           *
           * All two of these values are immutable: they can only be set once during
           * construction.
           */
          constructor(string memory name_, string memory symbol_) {
              _name = name_;
              _symbol = symbol_;
          }
          /**
           * @dev Returns the name of the token.
           */
          function name() public view virtual override returns (string memory) {
              return _name;
          }
          /**
           * @dev Returns the symbol of the token, usually a shorter version of the
           * name.
           */
          function symbol() public view virtual override returns (string memory) {
              return _symbol;
          }
          /**
           * @dev Returns the number of decimals used to get its user representation.
           * For example, if `decimals` equals `2`, a balance of `505` tokens should
           * be displayed to a user as `5.05` (`505 / 10 ** 2`).
           *
           * Tokens usually opt for a value of 18, imitating the relationship between
           * Ether and Wei. This is the value {ERC20} uses, unless this function is
           * overridden;
           *
           * NOTE: This information is only used for _display_ purposes: it in
           * no way affects any of the arithmetic of the contract, including
           * {IERC20-balanceOf} and {IERC20-transfer}.
           */
          function decimals() public view virtual override returns (uint8) {
              return 18;
          }
          /**
           * @dev See {IERC20-totalSupply}.
           */
          function totalSupply() public view virtual override returns (uint256) {
              return _totalSupply;
          }
          /**
           * @dev See {IERC20-balanceOf}.
           */
          function balanceOf(address account) public view virtual override returns (uint256) {
              return _balances[account];
          }
          /**
           * @dev See {IERC20-transfer}.
           *
           * Requirements:
           *
           * - `to` cannot be the zero address.
           * - the caller must have a balance of at least `amount`.
           */
          function transfer(address to, uint256 amount) public virtual override returns (bool) {
              address owner = _msgSender();
              _transfer(owner, to, amount);
              return true;
          }
          /**
           * @dev See {IERC20-allowance}.
           */
          function allowance(address owner, address spender) public view virtual override returns (uint256) {
              return _allowances[owner][spender];
          }
          /**
           * @dev See {IERC20-approve}.
           *
           * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
           * `transferFrom`. This is semantically equivalent to an infinite approval.
           *
           * Requirements:
           *
           * - `spender` cannot be the zero address.
           */
          function approve(address spender, uint256 amount) public virtual override returns (bool) {
              address owner = _msgSender();
              _approve(owner, 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}.
           *
           * NOTE: Does not update the allowance if the current allowance
           * is the maximum `uint256`.
           *
           * Requirements:
           *
           * - `from` and `to` cannot be the zero address.
           * - `from` must have a balance of at least `amount`.
           * - the caller must have allowance for ``from``'s tokens of at least
           * `amount`.
           */
          function transferFrom(
              address from,
              address to,
              uint256 amount
          ) public virtual override returns (bool) {
              address spender = _msgSender();
              _spendAllowance(from, spender, amount);
              _transfer(from, to, amount);
              return true;
          }
          /**
           * @dev Atomically increases the allowance granted to `spender` by the caller.
           *
           * This is an alternative to {approve} that can be used as a mitigation for
           * problems described in {IERC20-approve}.
           *
           * Emits an {Approval} event indicating the updated allowance.
           *
           * Requirements:
           *
           * - `spender` cannot be the zero address.
           */
          function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
              address owner = _msgSender();
              _approve(owner, spender, allowance(owner, spender) + addedValue);
              return true;
          }
          /**
           * @dev Atomically decreases the allowance granted to `spender` by the caller.
           *
           * This is an alternative to {approve} that can be used as a mitigation for
           * problems described in {IERC20-approve}.
           *
           * Emits an {Approval} event indicating the updated allowance.
           *
           * Requirements:
           *
           * - `spender` cannot be the zero address.
           * - `spender` must have allowance for the caller of at least
           * `subtractedValue`.
           */
          function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
              address owner = _msgSender();
              uint256 currentAllowance = allowance(owner, spender);
              require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
              unchecked {
                  _approve(owner, spender, currentAllowance - subtractedValue);
              }
              return true;
          }
          /**
           * @dev Moves `amount` of tokens from `sender` to `recipient`.
           *
           * This internal function is equivalent to {transfer}, and can be used to
           * e.g. implement automatic token fees, slashing mechanisms, etc.
           *
           * Emits a {Transfer} event.
           *
           * Requirements:
           *
           * - `from` cannot be the zero address.
           * - `to` cannot be the zero address.
           * - `from` must have a balance of at least `amount`.
           */
          function _transfer(
              address from,
              address to,
              uint256 amount
          ) internal virtual {
              require(from != address(0), "ERC20: transfer from the zero address");
              require(to != address(0), "ERC20: transfer to the zero address");
              _beforeTokenTransfer(from, to, amount);
              uint256 fromBalance = _balances[from];
              require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
              unchecked {
                  _balances[from] = fromBalance - amount;
              }
              _balances[to] += amount;
              emit Transfer(from, to, amount);
              _afterTokenTransfer(from, to, amount);
          }
          /** @dev Creates `amount` tokens and assigns them to `account`, increasing
           * the total supply.
           *
           * Emits a {Transfer} event with `from` set to the zero address.
           *
           * Requirements:
           *
           * - `account` cannot be the zero address.
           */
          function _mint(address account, uint256 amount) internal virtual {
              require(account != address(0), "ERC20: mint to the zero address");
              _beforeTokenTransfer(address(0), account, amount);
              _totalSupply += amount;
              _balances[account] += amount;
              emit Transfer(address(0), account, amount);
              _afterTokenTransfer(address(0), account, amount);
          }
          /**
           * @dev Destroys `amount` tokens from `account`, reducing the
           * total supply.
           *
           * Emits a {Transfer} event with `to` set to the zero address.
           *
           * Requirements:
           *
           * - `account` cannot be the zero address.
           * - `account` must have at least `amount` tokens.
           */
          function _burn(address account, uint256 amount) internal virtual {
              require(account != address(0), "ERC20: burn from the zero address");
              _beforeTokenTransfer(account, address(0), amount);
              uint256 accountBalance = _balances[account];
              require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
              unchecked {
                  _balances[account] = accountBalance - amount;
              }
              _totalSupply -= amount;
              emit Transfer(account, address(0), amount);
              _afterTokenTransfer(account, address(0), amount);
          }
          /**
           * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
           *
           * This internal function is equivalent to `approve`, and can be used to
           * e.g. set automatic allowances for certain subsystems, etc.
           *
           * Emits an {Approval} event.
           *
           * Requirements:
           *
           * - `owner` cannot be the zero address.
           * - `spender` cannot be the zero address.
           */
          function _approve(
              address owner,
              address spender,
              uint256 amount
          ) internal virtual {
              require(owner != address(0), "ERC20: approve from the zero address");
              require(spender != address(0), "ERC20: approve to the zero address");
              _allowances[owner][spender] = amount;
              emit Approval(owner, spender, amount);
          }
          /**
           * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
           *
           * Does not update the allowance amount in case of infinite allowance.
           * Revert if not enough allowance is available.
           *
           * Might emit an {Approval} event.
           */
          function _spendAllowance(
              address owner,
              address spender,
              uint256 amount
          ) internal virtual {
              uint256 currentAllowance = allowance(owner, spender);
              if (currentAllowance != type(uint256).max) {
                  require(currentAllowance >= amount, "ERC20: insufficient allowance");
                  unchecked {
                      _approve(owner, spender, currentAllowance - amount);
                  }
              }
          }
          /**
           * @dev Hook that is called before any transfer of tokens. This includes
           * minting and burning.
           *
           * Calling conditions:
           *
           * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
           * will be transferred to `to`.
           * - when `from` is zero, `amount` tokens will be minted for `to`.
           * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
           * - `from` and `to` are never both zero.
           *
           * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
           */
          function _beforeTokenTransfer(
              address from,
              address to,
              uint256 amount
          ) internal virtual {}
          /**
           * @dev Hook that is called after any transfer of tokens. This includes
           * minting and burning.
           *
           * Calling conditions:
           *
           * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
           * has been transferred to `to`.
           * - when `from` is zero, `amount` tokens have been minted for `to`.
           * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
           * - `from` and `to` are never both zero.
           *
           * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
           */
          function _afterTokenTransfer(
              address from,
              address to,
              uint256 amount
          ) internal virtual {}
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.5.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 = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
              uint8 v = uint8((uint256(vs) >> 255) + 27);
              return tryRecover(hash, v, r, s);
          }
          /**
           * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
           *
           * _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.1 (utils/Counters.sol)
      pragma solidity ^0.8.0;
      /**
       * @title Counters
       * @author Matt Condon (@shrugs)
       * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
       * of elements in a mapping, issuing ERC721 ids, or counting request ids.
       *
       * Include with `using Counters for Counters.Counter;`
       */
      library Counters {
          struct Counter {
              // This variable should never be directly accessed by users of the library: interactions must be restricted to
              // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
              // this feature: see https://github.com/ethereum/solidity/issues/4637
              uint256 _value; // default: 0
          }
          function current(Counter storage counter) internal view returns (uint256) {
              return counter._value;
          }
          function increment(Counter storage counter) internal {
              unchecked {
                  counter._value += 1;
              }
          }
          function decrement(Counter storage counter) internal {
              uint256 value = counter._value;
              require(value > 0, "Counter: decrement overflow");
              unchecked {
                  counter._value = value - 1;
              }
          }
          function reset(Counter storage counter) internal {
              counter._value = 0;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (utils/cryptography/draft-EIP712.sol)
      pragma solidity ^0.8.0;
      import "./ECDSA.sol";
      /**
       * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
       *
       * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
       * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
       * they need in their contracts using a combination of `abi.encode` and `keccak256`.
       *
       * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
       * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
       * ({_hashTypedDataV4}).
       *
       * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
       * the chain id to protect against replay attacks on an eventual fork of the chain.
       *
       * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
       * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
       *
       * _Available since v3.4._
       */
      abstract contract EIP712 {
          /* solhint-disable var-name-mixedcase */
          // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
          // invalidate the cached domain separator if the chain id changes.
          bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
          uint256 private immutable _CACHED_CHAIN_ID;
          address private immutable _CACHED_THIS;
          bytes32 private immutable _HASHED_NAME;
          bytes32 private immutable _HASHED_VERSION;
          bytes32 private immutable _TYPE_HASH;
          /* solhint-enable var-name-mixedcase */
          /**
           * @dev Initializes the domain separator and parameter caches.
           *
           * The meaning of `name` and `version` is specified in
           * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
           *
           * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
           * - `version`: the current major version of the signing domain.
           *
           * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
           * contract upgrade].
           */
          constructor(string memory name, string memory version) {
              bytes32 hashedName = keccak256(bytes(name));
              bytes32 hashedVersion = keccak256(bytes(version));
              bytes32 typeHash = keccak256(
                  "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
              );
              _HASHED_NAME = hashedName;
              _HASHED_VERSION = hashedVersion;
              _CACHED_CHAIN_ID = block.chainid;
              _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);
              _CACHED_THIS = address(this);
              _TYPE_HASH = typeHash;
          }
          /**
           * @dev Returns the domain separator for the current chain.
           */
          function _domainSeparatorV4() internal view returns (bytes32) {
              if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) {
                  return _CACHED_DOMAIN_SEPARATOR;
              } else {
                  return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
              }
          }
          function _buildDomainSeparator(
              bytes32 typeHash,
              bytes32 nameHash,
              bytes32 versionHash
          ) private view returns (bytes32) {
              return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
          }
          /**
           * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
           * function returns the hash of the fully encoded EIP712 message for this domain.
           *
           * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
           *
           * ```solidity
           * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
           *     keccak256("Mail(address to,string contents)"),
           *     mailTo,
           *     keccak256(bytes(mailContents))
           * )));
           * address signer = ECDSA.recover(digest, signature);
           * ```
           */
          function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
              return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (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.1 (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 (last updated v4.5.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.1 (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
      // OpenZeppelin Contracts (last updated v4.5.0) (governance/utils/IVotes.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Common interface for {ERC20Votes}, {ERC721Votes}, and other {Votes}-enabled contracts.
       *
       * _Available since v4.5._
       */
      interface IVotes {
          /**
           * @dev Emitted when an account changes their delegate.
           */
          event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);
          /**
           * @dev Emitted when a token transfer or delegate change results in changes to a delegate's number of votes.
           */
          event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance);
          /**
           * @dev Returns the current amount of votes that `account` has.
           */
          function getVotes(address account) external view returns (uint256);
          /**
           * @dev Returns the amount of votes that `account` had at the end of a past block (`blockNumber`).
           */
          function getPastVotes(address account, uint256 blockNumber) external view returns (uint256);
          /**
           * @dev Returns the total supply of votes available at the end of a past block (`blockNumber`).
           *
           * NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes.
           * Votes that have not been delegated are still part of total supply, even though they would not participate in a
           * vote.
           */
          function getPastTotalSupply(uint256 blockNumber) external view returns (uint256);
          /**
           * @dev Returns the delegate that `account` has chosen.
           */
          function delegates(address account) external view returns (address);
          /**
           * @dev Delegates votes from the sender to `delegatee`.
           */
          function delegate(address delegatee) external;
          /**
           * @dev Delegates votes from signer to `delegatee`.
           */
          function delegateBySig(
              address delegatee,
              uint256 nonce,
              uint256 expiry,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) external;
      }
      

      File 2 of 2: PSP
      pragma solidity ^0.5.16;
      pragma experimental ABIEncoderV2;
      
      // From https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/math/Math.sol
      // Subject to the MIT license.
      
      /**
       * @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 addition of two unsigned integers, reverting with custom message on overflow.
           *
           * Counterpart to Solidity's `+` operator.
           *
           * Requirements:
           * - Addition cannot overflow.
           */
          function add(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              uint256 c = a + b;
              require(c >= a, errorMessage);
      
              return c;
          }
      
          /**
           * @dev Returns the subtraction of two unsigned integers, reverting on underflow (when the result is negative).
           *
           * Counterpart to Solidity's `-` operator.
           *
           * Requirements:
           * - Subtraction cannot underflow.
           */
          function sub(uint256 a, uint256 b) internal pure returns (uint256) {
              return sub(a, b, "SafeMath: subtraction underflow");
          }
      
          /**
           * @dev Returns the subtraction of two unsigned integers, reverting with custom message on underflow (when the result is negative).
           *
           * Counterpart to Solidity's `-` operator.
           *
           * Requirements:
           * - Subtraction cannot underflow.
           */
          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 multiplication of two unsigned integers, reverting on overflow.
           *
           * Counterpart to Solidity's `*` operator.
           *
           * Requirements:
           * - Multiplication cannot overflow.
           */
          function mul(uint256 a, uint256 b, string memory errorMessage) 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, errorMessage);
      
              return c;
          }
      
          /**
           * @dev Returns the integer division of two unsigned integers.
           * Reverts on division by zero. The result is rounded towards zero.
           *
           * Counterpart to Solidity's `/` operator. Note: this function uses a
           * `revert` opcode (which leaves remaining gas untouched) while Solidity
           * uses an invalid opcode to revert (consuming all remaining gas).
           *
           * Requirements:
           * - The divisor cannot be zero.
           */
          function div(uint256 a, uint256 b) internal pure returns (uint256) {
              return div(a, b, "SafeMath: division by zero");
          }
      
          /**
           * @dev Returns the integer division of two unsigned integers.
           * Reverts with custom message on division by zero. The result is rounded towards zero.
           *
           * Counterpart to Solidity's `/` operator. Note: this function uses a
           * `revert` opcode (which leaves remaining gas untouched) while Solidity
           * uses an invalid opcode to revert (consuming all remaining gas).
           *
           * Requirements:
           * - The divisor cannot be zero.
           */
          function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              // Solidity only automatically asserts when dividing by 0
              require(b > 0, errorMessage);
              uint256 c = a / b;
              // assert(a == b * c + a % b); // There is no case in which this doesn't hold
      
              return c;
          }
      
          /**
           * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
           * Reverts when dividing by zero.
           *
           * Counterpart to Solidity's `%` operator. This function uses a `revert`
           * opcode (which leaves remaining gas untouched) while Solidity uses an
           * invalid opcode to revert (consuming all remaining gas).
           *
           * Requirements:
           * - The divisor cannot be zero.
           */
          function mod(uint256 a, uint256 b) internal pure returns (uint256) {
              return mod(a, b, "SafeMath: modulo by zero");
          }
      
          /**
           * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
           * Reverts with custom message when dividing by zero.
           *
           * Counterpart to Solidity's `%` operator. This function uses a `revert`
           * opcode (which leaves remaining gas untouched) while Solidity uses an
           * invalid opcode to revert (consuming all remaining gas).
           *
           * Requirements:
           * - The divisor cannot be zero.
           */
          function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              require(b != 0, errorMessage);
              return a % b;
          }
      }
      
      contract PSP {
          /// @notice EIP-20 token name for this token
          string public constant name = "ParaSwap";
      
          /// @notice EIP-20 token symbol for this token
          string public constant symbol = "PSP";
      
          /// @notice EIP-20 token decimals for this token
          uint8 public constant decimals = 18;
      
          /// @notice Total number of tokens in circulation
          uint public totalSupply = 2_000_000_000e18; // 2 billion PSP
      
          /// @notice Address which may mint new tokens
          address public minter;
      
          /// @notice The timestamp after which minting may occur
          uint public mintingAllowedAfter;
      
          /// @notice Minimum time between mints
          uint32 public constant minimumTimeBetweenMints = 1 days * 365;
      
          /// @notice Cap on the percentage of totalSupply that can be minted at each mint
          uint8 public constant mintCap = 2;
      
          /// @notice Allowance amounts on behalf of others
          mapping (address => mapping (address => uint96)) internal allowances;
      
          /// @notice Official record of token balances for each account
          mapping (address => uint96) internal balances;
      
          /// @notice A record of each accounts delegate
          mapping (address => address) public delegates;
      
          /// @notice A checkpoint for marking number of votes from a given block
          struct Checkpoint {
              uint32 fromBlock;
              uint96 votes;
          }
      
          /// @notice A record of votes checkpoints for each account, by index
          mapping (address => mapping (uint32 => Checkpoint)) public checkpoints;
      
          /// @notice The number of checkpoints for each account
          mapping (address => uint32) public numCheckpoints;
      
          /// @notice The EIP-712 typehash for the contract's domain
          bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)");
      
          /// @notice The EIP-712 typehash for the delegation struct used by the contract
          bytes32 public constant DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");
      
          /// @notice The EIP-712 typehash for the permit struct used by the contract
          bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
      
          /// @notice A record of states for signing / validating signatures
          mapping (address => uint) public nonces;
      
          /// @notice An event thats emitted when the minter address is changed
          event MinterChanged(address minter, address newMinter);
      
          /// @notice An event thats emitted when an account changes its delegate
          event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);
      
          /// @notice An event thats emitted when a delegate account's vote balance changes
          event DelegateVotesChanged(address indexed delegate, uint previousBalance, uint newBalance);
      
          /// @notice The standard EIP-20 transfer event
          event Transfer(address indexed from, address indexed to, uint256 amount);
      
          /// @notice The standard EIP-20 approval event
          event Approval(address indexed owner, address indexed spender, uint256 amount);
      
          /**
           * @notice Construct a new PSP token
           * @param account The initial account to grant all the tokens
           * @param minter_ The account with minting ability
           * @param mintingAllowedAfter_ The timestamp after which minting may occur
           */
          constructor(address account, address minter_, uint mintingAllowedAfter_) public {
              require(mintingAllowedAfter_ >= block.timestamp, "PSP::constructor: minting can only begin after deployment");
      
              balances[account] = uint96(totalSupply);
              emit Transfer(address(0), account, totalSupply);
              minter = minter_;
              emit MinterChanged(address(0), minter);
              mintingAllowedAfter = mintingAllowedAfter_;
          }
      
          /**
           * @notice Change the minter address
           * @param minter_ The address of the new minter
           */
          function setMinter(address minter_) external {
              require(msg.sender == minter, "PSP::setMinter: only the minter can change the minter address");
              emit MinterChanged(minter, minter_);
              minter = minter_;
          }
      
          /**
           * @notice Mint new tokens
           * @param dst The address of the destination account
           * @param rawAmount The number of tokens to be minted
           */
          function mint(address dst, uint rawAmount) external {
              require(msg.sender == minter, "PSP::mint: only the minter can mint");
              require(block.timestamp >= mintingAllowedAfter, "PSP::mint: minting not allowed yet");
              require(dst != address(0), "PSP::mint: cannot transfer to the zero address");
      
              // record the mint
              mintingAllowedAfter = SafeMath.add(block.timestamp, minimumTimeBetweenMints);
      
              // mint the amount
              uint96 amount = safe96(rawAmount, "PSP::mint: amount exceeds 96 bits");
              require(amount <= SafeMath.div(SafeMath.mul(totalSupply, mintCap), 100), "PSP::mint: exceeded mint cap");
              totalSupply = safe96(SafeMath.add(totalSupply, amount), "PSP::mint: totalSupply exceeds 96 bits");
      
              // transfer the amount to the recipient
              balances[dst] = add96(balances[dst], amount, "PSP::mint: transfer amount overflows");
              emit Transfer(address(0), dst, amount);
      
              // move delegates
              _moveDelegates(address(0), delegates[dst], amount);
          }
      
          /**
           * @notice Get the number of tokens `spender` is approved to spend on behalf of `account`
           * @param account The address of the account holding the funds
           * @param spender The address of the account spending the funds
           * @return The number of tokens approved
           */
          function allowance(address account, address spender) external view returns (uint) {
              return allowances[account][spender];
          }
      
          /**
           * @notice Approve `spender` to transfer up to `amount` from `src`
           * @dev This will overwrite the approval amount for `spender`
           *  and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
           * @param spender The address of the account which may transfer tokens
           * @param rawAmount The number of tokens that are approved (2^256-1 means infinite)
           * @return Whether or not the approval succeeded
           */
          function approve(address spender, uint rawAmount) external returns (bool) {
              uint96 amount;
              if (rawAmount == uint(-1)) {
                  amount = uint96(-1);
              } else {
                  amount = safe96(rawAmount, "PSP::approve: amount exceeds 96 bits");
              }
      
              allowances[msg.sender][spender] = amount;
      
              emit Approval(msg.sender, spender, amount);
              return true;
          }
      
          /**
           * @notice Triggers an approval from owner to spends
           * @param owner The address to approve from
           * @param spender The address to be approved
           * @param rawAmount The number of tokens that are approved (2^256-1 means infinite)
           * @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 permit(address owner, address spender, uint rawAmount, uint deadline, uint8 v, bytes32 r, bytes32 s) external {
              uint96 amount;
              if (rawAmount == uint(-1)) {
                  amount = uint96(-1);
              } else {
                  amount = safe96(rawAmount, "PSP::permit: amount exceeds 96 bits");
              }
      
              bytes32 domainSeparator = keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainId(), address(this)));
              bytes32 structHash = keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, rawAmount, nonces[owner]++, deadline));
              bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
              address signatory = ecrecover(digest, v, r, s);
              require(signatory != address(0), "PSP::permit: invalid signature");
              require(signatory == owner, "PSP::permit: unauthorized");
              require(now <= deadline, "PSP::permit: signature expired");
      
              allowances[owner][spender] = amount;
      
              emit Approval(owner, spender, amount);
          }
      
          /**
           * @notice Get the number of tokens held by the `account`
           * @param account The address of the account to get the balance of
           * @return The number of tokens held
           */
          function balanceOf(address account) external view returns (uint) {
              return balances[account];
          }
      
          /**
           * @notice Transfer `amount` tokens from `msg.sender` to `dst`
           * @param dst The address of the destination account
           * @param rawAmount The number of tokens to transfer
           * @return Whether or not the transfer succeeded
           */
          function transfer(address dst, uint rawAmount) external returns (bool) {
              uint96 amount = safe96(rawAmount, "PSP::transfer: amount exceeds 96 bits");
              _transferTokens(msg.sender, dst, amount);
              return true;
          }
      
          /**
           * @notice Transfer `amount` tokens from `src` to `dst`
           * @param src The address of the source account
           * @param dst The address of the destination account
           * @param rawAmount The number of tokens to transfer
           * @return Whether or not the transfer succeeded
           */
          function transferFrom(address src, address dst, uint rawAmount) external returns (bool) {
              address spender = msg.sender;
              uint96 spenderAllowance = allowances[src][spender];
              uint96 amount = safe96(rawAmount, "PSP::approve: amount exceeds 96 bits");
      
              if (spender != src && spenderAllowance != uint96(-1)) {
                  uint96 newAllowance = sub96(spenderAllowance, amount, "PSP::transferFrom: transfer amount exceeds spender allowance");
                  allowances[src][spender] = newAllowance;
      
                  emit Approval(src, spender, newAllowance);
              }
      
              _transferTokens(src, dst, amount);
              return true;
          }
      
          /**
           * @notice Delegate votes from `msg.sender` to `delegatee`
           * @param delegatee The address to delegate votes to
           */
          function delegate(address delegatee) public {
              return _delegate(msg.sender, delegatee);
          }
      
          /**
           * @notice Delegates votes from signatory to `delegatee`
           * @param delegatee The address to delegate votes to
           * @param nonce The contract state required to match the signature
           * @param expiry The time at which to expire the signature
           * @param v The recovery byte of the signature
           * @param r Half of the ECDSA signature pair
           * @param s Half of the ECDSA signature pair
           */
          function delegateBySig(address delegatee, uint nonce, uint expiry, uint8 v, bytes32 r, bytes32 s) public {
              bytes32 domainSeparator = keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainId(), address(this)));
              bytes32 structHash = keccak256(abi.encode(DELEGATION_TYPEHASH, delegatee, nonce, expiry));
              bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
              address signatory = ecrecover(digest, v, r, s);
              require(signatory != address(0), "PSP::delegateBySig: invalid signature");
              require(nonce == nonces[signatory]++, "PSP::delegateBySig: invalid nonce");
              require(now <= expiry, "PSP::delegateBySig: signature expired");
              return _delegate(signatory, delegatee);
          }
      
          /**
           * @notice Gets the current votes balance for `account`
           * @param account The address to get votes balance
           * @return The number of current votes for `account`
           */
          function getCurrentVotes(address account) external view returns (uint96) {
              uint32 nCheckpoints = numCheckpoints[account];
              return nCheckpoints > 0 ? checkpoints[account][nCheckpoints - 1].votes : 0;
          }
      
          /**
           * @notice Determine the prior number of votes for an account as of a block number
           * @dev Block number must be a finalized block or else this function will revert to prevent misinformation.
           * @param account The address of the account to check
           * @param blockNumber The block number to get the vote balance at
           * @return The number of votes the account had as of the given block
           */
          function getPriorVotes(address account, uint blockNumber) public view returns (uint96) {
              require(blockNumber < block.number, "PSP::getPriorVotes: not yet determined");
      
              uint32 nCheckpoints = numCheckpoints[account];
              if (nCheckpoints == 0) {
                  return 0;
              }
      
              // First check most recent balance
              if (checkpoints[account][nCheckpoints - 1].fromBlock <= blockNumber) {
                  return checkpoints[account][nCheckpoints - 1].votes;
              }
      
              // Next check implicit zero balance
              if (checkpoints[account][0].fromBlock > blockNumber) {
                  return 0;
              }
      
              uint32 lower = 0;
              uint32 upper = nCheckpoints - 1;
              while (upper > lower) {
                  uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow
                  Checkpoint memory cp = checkpoints[account][center];
                  if (cp.fromBlock == blockNumber) {
                      return cp.votes;
                  } else if (cp.fromBlock < blockNumber) {
                      lower = center;
                  } else {
                      upper = center - 1;
                  }
              }
              return checkpoints[account][lower].votes;
          }
      
          function _delegate(address delegator, address delegatee) internal {
              address currentDelegate = delegates[delegator];
              uint96 delegatorBalance = balances[delegator];
              delegates[delegator] = delegatee;
      
              emit DelegateChanged(delegator, currentDelegate, delegatee);
      
              _moveDelegates(currentDelegate, delegatee, delegatorBalance);
          }
      
          function _transferTokens(address src, address dst, uint96 amount) internal {
              require(src != address(0), "PSP::_transferTokens: cannot transfer from the zero address");
              require(dst != address(0), "PSP::_transferTokens: cannot transfer to the zero address");
      
              balances[src] = sub96(balances[src], amount, "PSP::_transferTokens: transfer amount exceeds balance");
              balances[dst] = add96(balances[dst], amount, "PSP::_transferTokens: transfer amount overflows");
              emit Transfer(src, dst, amount);
      
              _moveDelegates(delegates[src], delegates[dst], amount);
          }
      
          function _moveDelegates(address srcRep, address dstRep, uint96 amount) internal {
              if (srcRep != dstRep && amount > 0) {
                  if (srcRep != address(0)) {
                      uint32 srcRepNum = numCheckpoints[srcRep];
                      uint96 srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].votes : 0;
                      uint96 srcRepNew = sub96(srcRepOld, amount, "PSP::_moveVotes: vote amount underflows");
                      _writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew);
                  }
      
                  if (dstRep != address(0)) {
                      uint32 dstRepNum = numCheckpoints[dstRep];
                      uint96 dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].votes : 0;
                      uint96 dstRepNew = add96(dstRepOld, amount, "PSP::_moveVotes: vote amount overflows");
                      _writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew);
                  }
              }
          }
      
          function _writeCheckpoint(address delegatee, uint32 nCheckpoints, uint96 oldVotes, uint96 newVotes) internal {
            uint32 blockNumber = safe32(block.number, "PSP::_writeCheckpoint: block number exceeds 32 bits");
      
            if (nCheckpoints > 0 && checkpoints[delegatee][nCheckpoints - 1].fromBlock == blockNumber) {
                checkpoints[delegatee][nCheckpoints - 1].votes = newVotes;
            } else {
                checkpoints[delegatee][nCheckpoints] = Checkpoint(blockNumber, newVotes);
                numCheckpoints[delegatee] = nCheckpoints + 1;
            }
      
            emit DelegateVotesChanged(delegatee, oldVotes, newVotes);
          }
      
          function safe32(uint n, string memory errorMessage) internal pure returns (uint32) {
              require(n < 2**32, errorMessage);
              return uint32(n);
          }
      
          function safe96(uint n, string memory errorMessage) internal pure returns (uint96) {
              require(n < 2**96, errorMessage);
              return uint96(n);
          }
      
          function add96(uint96 a, uint96 b, string memory errorMessage) internal pure returns (uint96) {
              uint96 c = a + b;
              require(c >= a, errorMessage);
              return c;
          }
      
          function sub96(uint96 a, uint96 b, string memory errorMessage) internal pure returns (uint96) {
              require(b <= a, errorMessage);
              return a - b;
          }
      
          function getChainId() internal pure returns (uint) {
              uint256 chainId;
              assembly { chainId := chainid() }
              return chainId;
          }
      }