ETH Price: $1,930.03 (+0.47%)

Transaction Decoder

Block:
16269690 at Dec-26-2022 03:01:59 PM +UTC
Transaction Fee:
0.00256256344758799 ETH $4.95
Gas Used:
163,193 Gas / 15.70265543 Gwei

Emitted Events:

155 FiatTokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x000000000000000000000000dec5ce265e39179ed6e06207d0cd4a1d953712b2, 0x00000000000000000000000007ce82f414a42d9a73b0bd9ec23c249d446a0109, 000000000000000000000000000000000000000000000000000000000ee6b280 )
156 ArcadePass.Transfer( from=0x00000000...000000000, to=[Sender] 0xdec5ce265e39179ed6e06207d0cd4a1d953712b2, tokenId=311 )

Account State Difference:

  Address   Before After State Difference Code
0x07Ce82f4...d446A0109
0xA0b86991...E3606eB48
(MEV Builder: 0xBaF...e19)
5.621015351547369699 Eth5.621260141047369699 Eth0.0002447895
0xdec5ce26...d953712B2
0.055589003710188058 Eth
Nonce: 75
0.053026440262600068 Eth
Nonce: 76
0.00256256344758799

Execution Trace

ArcadePass.mint( )
  • FiatTokenProxy.23b872dd( )
    • FiatTokenV2_1.transferFrom( from=0xdec5ce265E39179Ed6E06207d0Cd4a1d953712B2, to=0x07Ce82f414A42D9A73B0bD9EC23c249d446A0109, value=250000000 ) => ( True )
      File 1 of 3: ArcadePass
      //SPDX-License-Identifier: MIT
      pragma solidity ^0.8.13;
      import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
      import "@openzeppelin/contracts/token/common/ERC2981.sol";
      import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol";
      import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
      import "@openzeppelin/contracts/access/Ownable.sol";
      import "@openzeppelin/contracts/utils/Counters.sol";
      import "@openzeppelin/contracts/utils/Strings.sol";
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
      import "@openzeppelin/contracts/security/Pausable.sol";
      /**
       * >>> Join the Resistance <<<
       * >>>   https://nfa.gg/   <<<
       * @title   NonFungibleArcade Arcade Pass
       * @author  BowTiedPickle
       */
      contract ArcadePass is ERC721, ERC721Burnable, ERC2981, Ownable, Pausable {
          using Counters for Counters.Counter;
          using SafeERC20 for IERC20;
          using Strings for uint256;
          string public baseURI;
          mapping(address => uint256) public claimed;
          Counters.Counter internal nextId;
          uint256 public mintPrice = 250e6;
          uint256 public maxPerWallet = 1;
          uint256 public maxSupply = 1000;
          bool public supplyLocked;
          uint128 public startTime_heroes;
          uint128 public startTime_arcade;
          uint128 public startTime_rebels;
          bytes32 public merkleRoot_heroes;
          bytes32 public merkleRoot_arcade;
          bytes32 public merkleRoot_rebels;
          IERC20 internal immutable USDC;
          /// @notice Total number of tokens in existence
          uint256 public totalSupply;
          /**
           * @param   _owner              Owner address
           * @param   _royaltyBPS         Royalty in basis points, max is 10% (1000 BPS)
           * @param   _merkleRoot_heroes  Merkle root for Heroes phase
           * @param   _merkleRoot_arcade  Merkle root for Arcade phase
           * @param   _merkleRoot_rebels  Merkle root for Rebels phase
           * @param   _USDC               Address of the USDC token proxy
           * @param   _startTime_heroes   Start time for Heroes phase
           * @param   _startTime_arcade   Start time for Arcade phase
           * @param   _startTime_rebels   Start time for Rebels phase
           */
          constructor(
              address _owner,
              uint96 _royaltyBPS,
              bytes32 _merkleRoot_heroes,
              bytes32 _merkleRoot_arcade,
              bytes32 _merkleRoot_rebels,
              address _USDC,
              uint128 _startTime_heroes,
              uint128 _startTime_arcade,
              uint128 _startTime_rebels
          ) ERC721("NonFungibleArcade Arcade Pass", "NFA-PASS") {
              require(_owner != address(0), "!addr");
              require(_royaltyBPS <= 1000, "!bps");
              _transferOwnership(_owner);
              _setDefaultRoyalty(owner(), _royaltyBPS);
              // Start at 1, not at 0
              nextId.increment();
              // Set the merkle roots
              merkleRoot_heroes = _merkleRoot_heroes;
              merkleRoot_arcade = _merkleRoot_arcade;
              merkleRoot_rebels = _merkleRoot_rebels;
              // Set timestamps
              startTime_heroes = _startTime_heroes;
              startTime_arcade = _startTime_arcade;
              startTime_rebels = _startTime_rebels;
              // Set the USDC deployment
              USDC = IERC20(_USDC);
              // Deploy in paused state
              _pause();
          }
          /**
           * @notice  Mint an arcade pass
           * @dev     User must have approved this contract for the required value already
           * @param   _qty    Quantity to mint
           * @param   _proof  Merkle whitelist proof
           */
          function mint(uint256 _qty, bytes32[] calldata _proof) external whenNotPaused {
              require(_qty > 0, "!zero");
              require(claimed[msg.sender] + _qty <= maxPerWallet, "!qty");
              require(nextId.current() + _qty <= maxSupply + 1, "Max supply");
              USDC.safeTransferFrom(msg.sender, address(this), mintPrice * _qty);
              // Verify
              bytes32 leaf = keccak256(abi.encodePacked(msg.sender));
              bytes32 root = getActiveRoot();
              require(root != 0, "!phase");
              require(MerkleProof.verify(_proof, root, leaf), "!proof");
              claimed[msg.sender] += _qty;
              // Mint
              uint256 tokenId;
              for (uint256 i; i < _qty; i++) {
                  tokenId = nextId.current();
                  nextId.increment();
                  _mint(msg.sender, tokenId);
              }
          }
          // ----- Admin Functions -----
          /**
           * @notice  Permissioned mint function
           * @dev     Respects max supply
           * @param   _to     Recipient address
           * @param   _qty    Quantity to mint
           */
          function adminMint(address _to, uint256 _qty) external onlyOwner {
              require(_qty > 0, "!zero");
              require(nextId.current() + _qty <= maxSupply + 1, "Max supply");
              uint256 tokenId;
              for (uint256 i; i < _qty; i++) {
                  tokenId = nextId.current();
                  nextId.increment();
                  _mint(_to, tokenId);
              }
          }
          /**
           * @notice  Withdraw profits from the contract
           */
          function withdraw() external onlyOwner {
              uint256 balance = USDC.balanceOf(address(this));
              USDC.safeTransfer(owner(), balance);
              emit Withdrawal(balance);
          }
          /**
           * @notice  Sets a new royalty numerator
           * @dev     Cannot exceed 10%
           * @param   _royaltyBPS   New royalty, denominated in BPS (10000 = 100%)
           */
          function setRoyalty(uint96 _royaltyBPS) external onlyOwner {
              require(_royaltyBPS <= 1000, "!bps");
              _setDefaultRoyalty(owner(), _royaltyBPS);
              emit NewRoyalty(_royaltyBPS);
          }
          /**
           * @notice  Set a new base URI
           * @param   _newURI     New URI string
           */
          function setURI(string memory _newURI) external onlyOwner {
              emit NewURI(baseURI, _newURI);
              baseURI = _newURI;
          }
          /**
           * @notice  Set a new merkle root
           * @param   _newRoot    New whitelist merkle root
           */
          function setMerkleRoot(bytes32 _newRoot, uint256 _phase) external onlyOwner {
              require(_phase > 0 && _phase <= 3, "!param");
              if (_phase == 1) {
                  emit NewRoot(1, merkleRoot_heroes, _newRoot);
                  merkleRoot_heroes = _newRoot;
              } else if (_phase == 2) {
                  emit NewRoot(2, merkleRoot_arcade, _newRoot);
                  merkleRoot_arcade = _newRoot;
              } else if (_phase == 3) {
                  emit NewRoot(3, merkleRoot_rebels, _newRoot);
                  merkleRoot_rebels = _newRoot;
              }
          }
          /**
           * @notice  Set a new maximum supply
           * @dev     Cannot be set once supply is locked.
           * @dev     Cannot be set below existing total token supply.
           * @param   _newMax     New max supply
           */
          function setMaxSupply(uint256 _newMax) external onlyOwner {
              require(!supplyLocked, "Locked");
              require(_newMax >= nextId.current() - 1, "!supply");
              emit NewMaxSupply(maxSupply, _newMax);
              maxSupply = _newMax;
          }
          /**
           * @notice  Lock the maximum supply, preventing any further changes
           */
          function lockMaxSupply() external onlyOwner {
              supplyLocked = true;
              emit SupplyLocked(maxSupply);
          }
          /**
           * @notice  Set a new maximum per wallet
           * @param   _newMax     New max per wallet
           */
          function setMaxPerWallet(uint256 _newMax) external onlyOwner {
              emit NewMaxPerWallet(maxPerWallet, _newMax);
              maxPerWallet = _newMax;
          }
          /**
           * @notice  Set a new mint price
           * @param   _newPrice   New mint price in units of USDC
           */
          function setMintPrice(uint256 _newPrice) external onlyOwner {
              emit NewMintPrice(mintPrice, _newPrice);
              mintPrice = _newPrice;
          }
          /**
           * @notice  Set the minting pause status
           * @param   _status     True to pause, false to unpause
           */
          function setPaused(bool _status) external onlyOwner {
              if (_status) {
                  _pause();
              } else {
                  _unpause();
              }
          }
          /**
           * @notice  Set start time of each phase
           * @dev     Each phase start must be >= previous phase, all must be >= block.timestamp
           * @param   _startTime_heroes   Start time of Heroes phase in Unix epoch seconds
           * @param   _startTime_arcade   Start time of Arcadelist phase in Unix epoch seconds
           * @param   _startTime_rebels   Start time of Rebels phase in Unix epoch seconds
           */
          function setStartTimes(
              uint128 _startTime_heroes,
              uint128 _startTime_arcade,
              uint128 _startTime_rebels
          ) external onlyOwner {
              require(
                  _startTime_rebels >= _startTime_arcade && _startTime_arcade >= _startTime_heroes && _startTime_heroes >= block.timestamp,
                  "!timing"
              );
              // Set timestamps
              startTime_heroes = _startTime_heroes;
              startTime_arcade = _startTime_arcade;
              startTime_rebels = _startTime_rebels;
              emit NewStartTimes(_startTime_heroes, _startTime_arcade, _startTime_rebels);
          }
          // ----- View Functions -----
          /**
           * @notice  Get the Merkle root of the active phase
           * @return  Merkle root of the active phase, 0 if none active
           */
          function getActiveRoot() public view returns (bytes32) {
              uint256 phase = getActivePhase();
              if (phase == 1) {
                  return merkleRoot_heroes;
              } else if (phase == 2) {
                  return merkleRoot_arcade;
              } else if (phase == 3) {
                  return merkleRoot_rebels;
              } else {
                  return 0;
              }
          }
          /**
           * @notice  Get the ID the active phase
           * @return  1 for Heroes, 2 for Arcade, 3 for Rebels, 0 for no active phase
           */
          function getActivePhase() public view returns (uint256) {
              if (block.timestamp >= startTime_heroes && block.timestamp < startTime_arcade) {
                  return 1;
              } else if (block.timestamp >= startTime_arcade && block.timestamp < startTime_rebels) {
                  return 2;
              } else if (block.timestamp >= startTime_rebels) {
                  return 3;
              } else {
                  return 0;
              }
          }
          // ----- Overrides -----
          function _burn(uint256 tokenId) internal virtual override {
              super._burn(tokenId);
              unchecked {
                  --totalSupply;
              }
          }
          function _mint(address to, uint256 tokenId) internal virtual override {
              super._mint(to, tokenId);
              unchecked {
                  ++totalSupply;
              }
          }
          /// @inheritdoc ERC721
          function _baseURI() internal view virtual override returns (string memory) {
              return baseURI;
          }
          /// @inheritdoc ERC721
          function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
              _requireMinted(tokenId);
              return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString(), ".json")) : "";
          }
          /**
           * @dev See {IERC165-supportsInterface}.
           */
          function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721, ERC2981) returns (bool) {
              return interfaceId == type(ERC2981).interfaceId || super.supportsInterface(interfaceId);
          }
          // ----- Events -----
          event Withdrawal(uint256 balance);
          event SupplyLocked(uint256 finalMaxSupply);
          event NewRoyalty(uint96 newRoyalty);
          event NewURI(string oldURI, string newURI);
          event NewRoot(uint256 indexed phase, bytes32 oldRoot, bytes32 newRoot);
          event NewMaxSupply(uint256 oldMax, uint256 newMax);
          event NewMaxPerWallet(uint256 oldMax, uint256 newMax);
          event NewMintPrice(uint256 oldPrice, uint256 newPrice);
          event NewStartTimes(uint128 startTime_heroes, uint128 startTime_arcade, uint128 startTime_rebels);
      }// SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.7.0) (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 Modifier to make a function callable only when the contract is not paused.
           *
           * Requirements:
           *
           * - The contract must not be paused.
           */
          modifier whenNotPaused() {
              _requireNotPaused();
              _;
          }
          /**
           * @dev Modifier to make a function callable only when the contract is paused.
           *
           * Requirements:
           *
           * - The contract must be paused.
           */
          modifier whenPaused() {
              _requirePaused();
              _;
          }
          /**
           * @dev Returns true if the contract is paused, and false otherwise.
           */
          function paused() public view virtual returns (bool) {
              return _paused;
          }
          /**
           * @dev Throws if the contract is paused.
           */
          function _requireNotPaused() internal view virtual {
              require(!paused(), "Pausable: paused");
          }
          /**
           * @dev Throws if the contract is not paused.
           */
          function _requirePaused() internal view virtual {
              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 (last updated v4.7.0) (token/ERC20/utils/SafeERC20.sol)
      pragma solidity ^0.8.0;
      import "../IERC20.sol";
      import "../extensions/draft-IERC20Permit.sol";
      import "../../../utils/Address.sol";
      /**
       * @title SafeERC20
       * @dev Wrappers around ERC20 operations that throw on failure (when the token
       * contract returns false). Tokens that return no value (and instead revert or
       * throw on failure) are also supported, non-reverting calls are assumed to be
       * successful.
       * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
       * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
       */
      library SafeERC20 {
          using Address for address;
          function safeTransfer(
              IERC20 token,
              address to,
              uint256 value
          ) internal {
              _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
          }
          function safeTransferFrom(
              IERC20 token,
              address from,
              address to,
              uint256 value
          ) internal {
              _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
          }
          /**
           * @dev Deprecated. This function has issues similar to the ones found in
           * {IERC20-approve}, and its usage is discouraged.
           *
           * Whenever possible, use {safeIncreaseAllowance} and
           * {safeDecreaseAllowance} instead.
           */
          function safeApprove(
              IERC20 token,
              address spender,
              uint256 value
          ) internal {
              // safeApprove should only be called when setting an initial allowance,
              // or when resetting it to zero. To increase and decrease it, use
              // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
              require(
                  (value == 0) || (token.allowance(address(this), spender) == 0),
                  "SafeERC20: approve from non-zero to non-zero allowance"
              );
              _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
          }
          function safeIncreaseAllowance(
              IERC20 token,
              address spender,
              uint256 value
          ) internal {
              uint256 newAllowance = token.allowance(address(this), spender) + value;
              _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
          }
          function safeDecreaseAllowance(
              IERC20 token,
              address spender,
              uint256 value
          ) internal {
              unchecked {
                  uint256 oldAllowance = token.allowance(address(this), spender);
                  require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                  uint256 newAllowance = oldAllowance - value;
                  _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
              }
          }
          function safePermit(
              IERC20Permit token,
              address owner,
              address spender,
              uint256 value,
              uint256 deadline,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) internal {
              uint256 nonceBefore = token.nonces(owner);
              token.permit(owner, spender, value, deadline, v, r, s);
              uint256 nonceAfter = token.nonces(owner);
              require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
          }
          /**
           * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
           * on the return value: the return value is optional (but if data is returned, it must not be false).
           * @param token The token targeted by the call.
           * @param data The call data (encoded using abi.encode or one of its variants).
           */
          function _callOptionalReturn(IERC20 token, bytes memory data) private {
              // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
              // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
              // the target address contains contract code and also asserts for success in the low-level call.
              bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
              if (returndata.length > 0) {
                  // Return data is optional
                  require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (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.7.0) (utils/Strings.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev String operations.
       */
      library Strings {
          bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
          uint8 private constant _ADDRESS_LENGTH = 20;
          /**
           * @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);
          }
          /**
           * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
           */
          function toHexString(address addr) internal pure returns (string memory) {
              return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
          }
      }
      // 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 (last updated v4.7.0) (access/Ownable.sol)
      pragma solidity ^0.8.0;
      import "../utils/Context.sol";
      /**
       * @dev Contract module which provides a basic access control mechanism, where
       * there is an account (an owner) that can be granted exclusive access to
       * specific functions.
       *
       * By default, the owner account will be the one that deploys the contract. This
       * can later be changed with {transferOwnership}.
       *
       * This module is used through inheritance. It will make available the modifier
       * `onlyOwner`, which can be applied to your functions to restrict their use to
       * the owner.
       */
      abstract contract Ownable is Context {
          address private _owner;
          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
          /**
           * @dev Initializes the contract setting the deployer as the initial owner.
           */
          constructor() {
              _transferOwnership(_msgSender());
          }
          /**
           * @dev Throws if called by any account other than the owner.
           */
          modifier onlyOwner() {
              _checkOwner();
              _;
          }
          /**
           * @dev Returns the address of the current owner.
           */
          function owner() public view virtual returns (address) {
              return _owner;
          }
          /**
           * @dev Throws if the sender is not the owner.
           */
          function _checkOwner() internal view virtual {
              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.7.0) (utils/cryptography/MerkleProof.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev These functions deal with verification of Merkle Tree proofs.
       *
       * The proofs can be generated using the JavaScript library
       * https://github.com/miguelmota/merkletreejs[merkletreejs].
       * Note: the hashing algorithm should be keccak256 and pair sorting should be enabled.
       *
       * See `test/utils/cryptography/MerkleProof.test.js` for some examples.
       *
       * WARNING: You should avoid using leaf values that are 64 bytes long prior to
       * hashing, or use a hash function other than keccak256 for hashing leaves.
       * This is because the concatenation of a sorted pair of internal nodes in
       * the merkle tree could be reinterpreted as a leaf value.
       */
      library MerkleProof {
          /**
           * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
           * defined by `root`. For this, a `proof` must be provided, containing
           * sibling hashes on the branch from the leaf to the root of the tree. Each
           * pair of leaves and each pair of pre-images are assumed to be sorted.
           */
          function verify(
              bytes32[] memory proof,
              bytes32 root,
              bytes32 leaf
          ) internal pure returns (bool) {
              return processProof(proof, leaf) == root;
          }
          /**
           * @dev Calldata version of {verify}
           *
           * _Available since v4.7._
           */
          function verifyCalldata(
              bytes32[] calldata proof,
              bytes32 root,
              bytes32 leaf
          ) internal pure returns (bool) {
              return processProofCalldata(proof, leaf) == root;
          }
          /**
           * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
           * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
           * hash matches the root of the tree. When processing the proof, the pairs
           * of leafs & pre-images are assumed to be sorted.
           *
           * _Available since v4.4._
           */
          function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
              bytes32 computedHash = leaf;
              for (uint256 i = 0; i < proof.length; i++) {
                  computedHash = _hashPair(computedHash, proof[i]);
              }
              return computedHash;
          }
          /**
           * @dev Calldata version of {processProof}
           *
           * _Available since v4.7._
           */
          function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
              bytes32 computedHash = leaf;
              for (uint256 i = 0; i < proof.length; i++) {
                  computedHash = _hashPair(computedHash, proof[i]);
              }
              return computedHash;
          }
          /**
           * @dev Returns true if the `leaves` can be proved to be a part of a Merkle tree defined by
           * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
           *
           * _Available since v4.7._
           */
          function multiProofVerify(
              bytes32[] memory proof,
              bool[] memory proofFlags,
              bytes32 root,
              bytes32[] memory leaves
          ) internal pure returns (bool) {
              return processMultiProof(proof, proofFlags, leaves) == root;
          }
          /**
           * @dev Calldata version of {multiProofVerify}
           *
           * _Available since v4.7._
           */
          function multiProofVerifyCalldata(
              bytes32[] calldata proof,
              bool[] calldata proofFlags,
              bytes32 root,
              bytes32[] memory leaves
          ) internal pure returns (bool) {
              return processMultiProofCalldata(proof, proofFlags, leaves) == root;
          }
          /**
           * @dev Returns the root of a tree reconstructed from `leaves` and the sibling nodes in `proof`,
           * consuming from one or the other at each step according to the instructions given by
           * `proofFlags`.
           *
           * _Available since v4.7._
           */
          function processMultiProof(
              bytes32[] memory proof,
              bool[] memory proofFlags,
              bytes32[] memory leaves
          ) internal pure returns (bytes32 merkleRoot) {
              // This function rebuild the root hash by traversing the tree up from the leaves. The root is rebuilt by
              // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
              // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
              // the merkle tree.
              uint256 leavesLen = leaves.length;
              uint256 totalHashes = proofFlags.length;
              // Check proof validity.
              require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");
              // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
              // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
              bytes32[] memory hashes = new bytes32[](totalHashes);
              uint256 leafPos = 0;
              uint256 hashPos = 0;
              uint256 proofPos = 0;
              // At each step, we compute the next hash using two values:
              // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
              //   get the next hash.
              // - depending on the flag, either another value for the "main queue" (merging branches) or an element from the
              //   `proof` array.
              for (uint256 i = 0; i < totalHashes; i++) {
                  bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
                  bytes32 b = proofFlags[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proof[proofPos++];
                  hashes[i] = _hashPair(a, b);
              }
              if (totalHashes > 0) {
                  return hashes[totalHashes - 1];
              } else if (leavesLen > 0) {
                  return leaves[0];
              } else {
                  return proof[0];
              }
          }
          /**
           * @dev Calldata version of {processMultiProof}
           *
           * _Available since v4.7._
           */
          function processMultiProofCalldata(
              bytes32[] calldata proof,
              bool[] calldata proofFlags,
              bytes32[] memory leaves
          ) internal pure returns (bytes32 merkleRoot) {
              // This function rebuild the root hash by traversing the tree up from the leaves. The root is rebuilt by
              // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
              // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
              // the merkle tree.
              uint256 leavesLen = leaves.length;
              uint256 totalHashes = proofFlags.length;
              // Check proof validity.
              require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");
              // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
              // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
              bytes32[] memory hashes = new bytes32[](totalHashes);
              uint256 leafPos = 0;
              uint256 hashPos = 0;
              uint256 proofPos = 0;
              // At each step, we compute the next hash using two values:
              // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
              //   get the next hash.
              // - depending on the flag, either another value for the "main queue" (merging branches) or an element from the
              //   `proof` array.
              for (uint256 i = 0; i < totalHashes; i++) {
                  bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
                  bytes32 b = proofFlags[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proof[proofPos++];
                  hashes[i] = _hashPair(a, b);
              }
              if (totalHashes > 0) {
                  return hashes[totalHashes - 1];
              } else if (leavesLen > 0) {
                  return leaves[0];
              } else {
                  return proof[0];
              }
          }
          function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
              return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
          }
          function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
              /// @solidity memory-safe-assembly
              assembly {
                  mstore(0x00, a)
                  mstore(0x20, b)
                  value := keccak256(0x00, 0x40)
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/extensions/ERC721Burnable.sol)
      pragma solidity ^0.8.0;
      import "../ERC721.sol";
      import "../../../utils/Context.sol";
      /**
       * @title ERC721 Burnable Token
       * @dev ERC721 Token that can be burned (destroyed).
       */
      abstract contract ERC721Burnable is Context, ERC721 {
          /**
           * @dev Burns `tokenId`. See {ERC721-_burn}.
           *
           * Requirements:
           *
           * - The caller must own `tokenId` or be an approved operator.
           */
          function burn(uint256 tokenId) public virtual {
              //solhint-disable-next-line max-line-length
              require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
              _burn(tokenId);
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.7.0) (token/common/ERC2981.sol)
      pragma solidity ^0.8.0;
      import "../../interfaces/IERC2981.sol";
      import "../../utils/introspection/ERC165.sol";
      /**
       * @dev Implementation of the NFT Royalty Standard, a standardized way to retrieve royalty payment information.
       *
       * Royalty information can be specified globally for all token ids via {_setDefaultRoyalty}, and/or individually for
       * specific token ids via {_setTokenRoyalty}. The latter takes precedence over the first.
       *
       * Royalty is specified as a fraction of sale price. {_feeDenominator} is overridable but defaults to 10000, meaning the
       * fee is specified in basis points by default.
       *
       * IMPORTANT: ERC-2981 only specifies a way to signal royalty information and does not enforce its payment. See
       * https://eips.ethereum.org/EIPS/eip-2981#optional-royalty-payments[Rationale] in the EIP. Marketplaces are expected to
       * voluntarily pay royalties together with sales, but note that this standard is not yet widely supported.
       *
       * _Available since v4.5._
       */
      abstract contract ERC2981 is IERC2981, ERC165 {
          struct RoyaltyInfo {
              address receiver;
              uint96 royaltyFraction;
          }
          RoyaltyInfo private _defaultRoyaltyInfo;
          mapping(uint256 => RoyaltyInfo) private _tokenRoyaltyInfo;
          /**
           * @dev See {IERC165-supportsInterface}.
           */
          function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) {
              return interfaceId == type(IERC2981).interfaceId || super.supportsInterface(interfaceId);
          }
          /**
           * @inheritdoc IERC2981
           */
          function royaltyInfo(uint256 _tokenId, uint256 _salePrice) public view virtual override returns (address, uint256) {
              RoyaltyInfo memory royalty = _tokenRoyaltyInfo[_tokenId];
              if (royalty.receiver == address(0)) {
                  royalty = _defaultRoyaltyInfo;
              }
              uint256 royaltyAmount = (_salePrice * royalty.royaltyFraction) / _feeDenominator();
              return (royalty.receiver, royaltyAmount);
          }
          /**
           * @dev The denominator with which to interpret the fee set in {_setTokenRoyalty} and {_setDefaultRoyalty} as a
           * fraction of the sale price. Defaults to 10000 so fees are expressed in basis points, but may be customized by an
           * override.
           */
          function _feeDenominator() internal pure virtual returns (uint96) {
              return 10000;
          }
          /**
           * @dev Sets the royalty information that all ids in this contract will default to.
           *
           * Requirements:
           *
           * - `receiver` cannot be the zero address.
           * - `feeNumerator` cannot be greater than the fee denominator.
           */
          function _setDefaultRoyalty(address receiver, uint96 feeNumerator) internal virtual {
              require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice");
              require(receiver != address(0), "ERC2981: invalid receiver");
              _defaultRoyaltyInfo = RoyaltyInfo(receiver, feeNumerator);
          }
          /**
           * @dev Removes default royalty information.
           */
          function _deleteDefaultRoyalty() internal virtual {
              delete _defaultRoyaltyInfo;
          }
          /**
           * @dev Sets the royalty information for a specific token id, overriding the global default.
           *
           * Requirements:
           *
           * - `receiver` cannot be the zero address.
           * - `feeNumerator` cannot be greater than the fee denominator.
           */
          function _setTokenRoyalty(
              uint256 tokenId,
              address receiver,
              uint96 feeNumerator
          ) internal virtual {
              require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice");
              require(receiver != address(0), "ERC2981: Invalid parameters");
              _tokenRoyaltyInfo[tokenId] = RoyaltyInfo(receiver, feeNumerator);
          }
          /**
           * @dev Resets royalty information for the token id back to the global default.
           */
          function _resetTokenRoyalty(uint256 tokenId) internal virtual {
              delete _tokenRoyaltyInfo[tokenId];
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/ERC721.sol)
      pragma solidity ^0.8.0;
      import "./IERC721.sol";
      import "./IERC721Receiver.sol";
      import "./extensions/IERC721Metadata.sol";
      import "../../utils/Address.sol";
      import "../../utils/Context.sol";
      import "../../utils/Strings.sol";
      import "../../utils/introspection/ERC165.sol";
      /**
       * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
       * the Metadata extension, but not including the Enumerable extension, which is available separately as
       * {ERC721Enumerable}.
       */
      contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
          using Address for address;
          using Strings for uint256;
          // Token name
          string private _name;
          // Token symbol
          string private _symbol;
          // Mapping from token ID to owner address
          mapping(uint256 => address) private _owners;
          // Mapping owner address to token count
          mapping(address => uint256) private _balances;
          // Mapping from token ID to approved address
          mapping(uint256 => address) private _tokenApprovals;
          // Mapping from owner to operator approvals
          mapping(address => mapping(address => bool)) private _operatorApprovals;
          /**
           * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
           */
          constructor(string memory name_, string memory symbol_) {
              _name = name_;
              _symbol = symbol_;
          }
          /**
           * @dev See {IERC165-supportsInterface}.
           */
          function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
              return
                  interfaceId == type(IERC721).interfaceId ||
                  interfaceId == type(IERC721Metadata).interfaceId ||
                  super.supportsInterface(interfaceId);
          }
          /**
           * @dev See {IERC721-balanceOf}.
           */
          function balanceOf(address owner) public view virtual override returns (uint256) {
              require(owner != address(0), "ERC721: address zero is not a valid owner");
              return _balances[owner];
          }
          /**
           * @dev See {IERC721-ownerOf}.
           */
          function ownerOf(uint256 tokenId) public view virtual override returns (address) {
              address owner = _owners[tokenId];
              require(owner != address(0), "ERC721: invalid token ID");
              return owner;
          }
          /**
           * @dev See {IERC721Metadata-name}.
           */
          function name() public view virtual override returns (string memory) {
              return _name;
          }
          /**
           * @dev See {IERC721Metadata-symbol}.
           */
          function symbol() public view virtual override returns (string memory) {
              return _symbol;
          }
          /**
           * @dev See {IERC721Metadata-tokenURI}.
           */
          function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
              _requireMinted(tokenId);
              string memory baseURI = _baseURI();
              return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
          }
          /**
           * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
           * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
           * by default, can be overridden in child contracts.
           */
          function _baseURI() internal view virtual returns (string memory) {
              return "";
          }
          /**
           * @dev See {IERC721-approve}.
           */
          function approve(address to, uint256 tokenId) public virtual override {
              address owner = ERC721.ownerOf(tokenId);
              require(to != owner, "ERC721: approval to current owner");
              require(
                  _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
                  "ERC721: approve caller is not token owner nor approved for all"
              );
              _approve(to, tokenId);
          }
          /**
           * @dev See {IERC721-getApproved}.
           */
          function getApproved(uint256 tokenId) public view virtual override returns (address) {
              _requireMinted(tokenId);
              return _tokenApprovals[tokenId];
          }
          /**
           * @dev See {IERC721-setApprovalForAll}.
           */
          function setApprovalForAll(address operator, bool approved) public virtual override {
              _setApprovalForAll(_msgSender(), operator, approved);
          }
          /**
           * @dev See {IERC721-isApprovedForAll}.
           */
          function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
              return _operatorApprovals[owner][operator];
          }
          /**
           * @dev See {IERC721-transferFrom}.
           */
          function transferFrom(
              address from,
              address to,
              uint256 tokenId
          ) public virtual override {
              //solhint-disable-next-line max-line-length
              require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner nor approved");
              _transfer(from, to, tokenId);
          }
          /**
           * @dev See {IERC721-safeTransferFrom}.
           */
          function safeTransferFrom(
              address from,
              address to,
              uint256 tokenId
          ) public virtual override {
              safeTransferFrom(from, to, tokenId, "");
          }
          /**
           * @dev See {IERC721-safeTransferFrom}.
           */
          function safeTransferFrom(
              address from,
              address to,
              uint256 tokenId,
              bytes memory data
          ) public virtual override {
              require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner nor approved");
              _safeTransfer(from, to, tokenId, data);
          }
          /**
           * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
           * are aware of the ERC721 protocol to prevent tokens from being forever locked.
           *
           * `data` is additional data, it has no specified format and it is sent in call to `to`.
           *
           * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
           * implement alternative mechanisms to perform token transfer, such as signature-based.
           *
           * Requirements:
           *
           * - `from` cannot be the zero address.
           * - `to` cannot be the zero address.
           * - `tokenId` token must exist and be owned by `from`.
           * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
           *
           * Emits a {Transfer} event.
           */
          function _safeTransfer(
              address from,
              address to,
              uint256 tokenId,
              bytes memory data
          ) internal virtual {
              _transfer(from, to, tokenId);
              require(_checkOnERC721Received(from, to, tokenId, data), "ERC721: transfer to non ERC721Receiver implementer");
          }
          /**
           * @dev Returns whether `tokenId` exists.
           *
           * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
           *
           * Tokens start existing when they are minted (`_mint`),
           * and stop existing when they are burned (`_burn`).
           */
          function _exists(uint256 tokenId) internal view virtual returns (bool) {
              return _owners[tokenId] != address(0);
          }
          /**
           * @dev Returns whether `spender` is allowed to manage `tokenId`.
           *
           * Requirements:
           *
           * - `tokenId` must exist.
           */
          function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
              address owner = ERC721.ownerOf(tokenId);
              return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);
          }
          /**
           * @dev Safely mints `tokenId` and transfers it to `to`.
           *
           * Requirements:
           *
           * - `tokenId` must not exist.
           * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
           *
           * Emits a {Transfer} event.
           */
          function _safeMint(address to, uint256 tokenId) internal virtual {
              _safeMint(to, tokenId, "");
          }
          /**
           * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
           * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
           */
          function _safeMint(
              address to,
              uint256 tokenId,
              bytes memory data
          ) internal virtual {
              _mint(to, tokenId);
              require(
                  _checkOnERC721Received(address(0), to, tokenId, data),
                  "ERC721: transfer to non ERC721Receiver implementer"
              );
          }
          /**
           * @dev Mints `tokenId` and transfers it to `to`.
           *
           * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
           *
           * Requirements:
           *
           * - `tokenId` must not exist.
           * - `to` cannot be the zero address.
           *
           * Emits a {Transfer} event.
           */
          function _mint(address to, uint256 tokenId) internal virtual {
              require(to != address(0), "ERC721: mint to the zero address");
              require(!_exists(tokenId), "ERC721: token already minted");
              _beforeTokenTransfer(address(0), to, tokenId);
              _balances[to] += 1;
              _owners[tokenId] = to;
              emit Transfer(address(0), to, tokenId);
              _afterTokenTransfer(address(0), to, tokenId);
          }
          /**
           * @dev Destroys `tokenId`.
           * The approval is cleared when the token is burned.
           *
           * Requirements:
           *
           * - `tokenId` must exist.
           *
           * Emits a {Transfer} event.
           */
          function _burn(uint256 tokenId) internal virtual {
              address owner = ERC721.ownerOf(tokenId);
              _beforeTokenTransfer(owner, address(0), tokenId);
              // Clear approvals
              _approve(address(0), tokenId);
              _balances[owner] -= 1;
              delete _owners[tokenId];
              emit Transfer(owner, address(0), tokenId);
              _afterTokenTransfer(owner, address(0), tokenId);
          }
          /**
           * @dev Transfers `tokenId` from `from` to `to`.
           *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
           *
           * Requirements:
           *
           * - `to` cannot be the zero address.
           * - `tokenId` token must be owned by `from`.
           *
           * Emits a {Transfer} event.
           */
          function _transfer(
              address from,
              address to,
              uint256 tokenId
          ) internal virtual {
              require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
              require(to != address(0), "ERC721: transfer to the zero address");
              _beforeTokenTransfer(from, to, tokenId);
              // Clear approvals from the previous owner
              _approve(address(0), tokenId);
              _balances[from] -= 1;
              _balances[to] += 1;
              _owners[tokenId] = to;
              emit Transfer(from, to, tokenId);
              _afterTokenTransfer(from, to, tokenId);
          }
          /**
           * @dev Approve `to` to operate on `tokenId`
           *
           * Emits an {Approval} event.
           */
          function _approve(address to, uint256 tokenId) internal virtual {
              _tokenApprovals[tokenId] = to;
              emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
          }
          /**
           * @dev Approve `operator` to operate on all of `owner` tokens
           *
           * Emits an {ApprovalForAll} event.
           */
          function _setApprovalForAll(
              address owner,
              address operator,
              bool approved
          ) internal virtual {
              require(owner != operator, "ERC721: approve to caller");
              _operatorApprovals[owner][operator] = approved;
              emit ApprovalForAll(owner, operator, approved);
          }
          /**
           * @dev Reverts if the `tokenId` has not been minted yet.
           */
          function _requireMinted(uint256 tokenId) internal view virtual {
              require(_exists(tokenId), "ERC721: invalid token ID");
          }
          /**
           * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
           * The call is not executed if the target address is not a contract.
           *
           * @param from address representing the previous owner of the given token ID
           * @param to target address that will receive the tokens
           * @param tokenId uint256 ID of the token to be transferred
           * @param data bytes optional data to send along with the call
           * @return bool whether the call correctly returned the expected magic value
           */
          function _checkOnERC721Received(
              address from,
              address to,
              uint256 tokenId,
              bytes memory data
          ) private returns (bool) {
              if (to.isContract()) {
                  try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
                      return retval == IERC721Receiver.onERC721Received.selector;
                  } catch (bytes memory reason) {
                      if (reason.length == 0) {
                          revert("ERC721: transfer to non ERC721Receiver implementer");
                      } else {
                          /// @solidity memory-safe-assembly
                          assembly {
                              revert(add(32, reason), mload(reason))
                          }
                      }
                  }
              } else {
                  return true;
              }
          }
          /**
           * @dev Hook that is called before any token transfer. This includes minting
           * and burning.
           *
           * Calling conditions:
           *
           * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
           * transferred to `to`.
           * - When `from` is zero, `tokenId` will be minted for `to`.
           * - When `to` is zero, ``from``'s `tokenId` 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 tokenId
          ) 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.
           * - `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 tokenId
          ) internal virtual {}
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
      pragma solidity ^0.8.0;
      import "./IERC165.sol";
      /**
       * @dev Implementation of the {IERC165} interface.
       *
       * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
       * for the additional interface id that will be supported. For example:
       *
       * ```solidity
       * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
       *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
       * }
       * ```
       *
       * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
       */
      abstract contract ERC165 is IERC165 {
          /**
           * @dev See {IERC165-supportsInterface}.
           */
          function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
              return interfaceId == type(IERC165).interfaceId;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.6.0) (interfaces/IERC2981.sol)
      pragma solidity ^0.8.0;
      import "../utils/introspection/IERC165.sol";
      /**
       * @dev Interface for the NFT Royalty Standard.
       *
       * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
       * support for royalty payments across all NFT marketplaces and ecosystem participants.
       *
       * _Available since v4.5._
       */
      interface IERC2981 is IERC165 {
          /**
           * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
           * exchange. The royalty amount is denominated and should be paid in that same unit of exchange.
           */
          function royaltyInfo(uint256 tokenId, uint256 salePrice)
              external
              view
              returns (address receiver, uint256 royaltyAmount);
      }
      // 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.7.0) (utils/Address.sol)
      pragma solidity ^0.8.1;
      /**
       * @dev Collection of functions related to the address type
       */
      library Address {
          /**
           * @dev Returns true if `account` is a contract.
           *
           * [IMPORTANT]
           * ====
           * It is unsafe to assume that an address for which this function returns
           * false is an externally-owned account (EOA) and not a contract.
           *
           * Among others, `isContract` will return false for the following
           * types of addresses:
           *
           *  - an externally-owned account
           *  - a contract in construction
           *  - an address where a contract will be created
           *  - an address where a contract lived, but was destroyed
           * ====
           *
           * [IMPORTANT]
           * ====
           * You shouldn't rely on `isContract` to protect against flash loan attacks!
           *
           * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
           * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
           * constructor.
           * ====
           */
          function isContract(address account) internal view returns (bool) {
              // This method relies on extcodesize/address.code.length, which returns 0
              // for contracts in construction, since the code is only stored at the end
              // of the constructor execution.
              return account.code.length > 0;
          }
          /**
           * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
           * `recipient`, forwarding all available gas and reverting on errors.
           *
           * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
           * of certain opcodes, possibly making contracts go over the 2300 gas limit
           * imposed by `transfer`, making them unable to receive funds via
           * `transfer`. {sendValue} removes this limitation.
           *
           * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
           *
           * IMPORTANT: because control is transferred to `recipient`, care must be
           * taken to not create reentrancy vulnerabilities. Consider using
           * {ReentrancyGuard} or the
           * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
           */
          function sendValue(address payable recipient, uint256 amount) internal {
              require(address(this).balance >= amount, "Address: insufficient balance");
              (bool success, ) = recipient.call{value: amount}("");
              require(success, "Address: unable to send value, recipient may have reverted");
          }
          /**
           * @dev Performs a Solidity function call using a low level `call`. A
           * plain `call` is an unsafe replacement for a function call: use this
           * function instead.
           *
           * If `target` reverts with a revert reason, it is bubbled up by this
           * function (like regular Solidity function calls).
           *
           * Returns the raw returned data. To convert to the expected return value,
           * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
           *
           * Requirements:
           *
           * - `target` must be a contract.
           * - calling `target` with `data` must not revert.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionCall(target, data, "Address: low-level call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
           * `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal returns (bytes memory) {
              return functionCallWithValue(target, data, 0, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but also transferring `value` wei to `target`.
           *
           * Requirements:
           *
           * - the calling contract must have an ETH balance of at least `value`.
           * - the called Solidity function must be `payable`.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(
              address target,
              bytes memory data,
              uint256 value
          ) internal returns (bytes memory) {
              return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
          }
          /**
           * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
           * with `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(
              address target,
              bytes memory data,
              uint256 value,
              string memory errorMessage
          ) internal returns (bytes memory) {
              require(address(this).balance >= value, "Address: insufficient balance for call");
              require(isContract(target), "Address: call to non-contract");
              (bool success, bytes memory returndata) = target.call{value: value}(data);
              return verifyCallResult(success, returndata, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
              return functionStaticCall(target, data, "Address: low-level static call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal view returns (bytes memory) {
              require(isContract(target), "Address: static call to non-contract");
              (bool success, bytes memory returndata) = target.staticcall(data);
              return verifyCallResult(success, returndata, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.4._
           */
          function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionDelegateCall(target, data, "Address: low-level delegate call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.4._
           */
          function functionDelegateCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal returns (bytes memory) {
              require(isContract(target), "Address: delegate call to non-contract");
              (bool success, bytes memory returndata) = target.delegatecall(data);
              return verifyCallResult(success, returndata, errorMessage);
          }
          /**
           * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
           * revert reason using the provided one.
           *
           * _Available since v4.3._
           */
          function verifyCallResult(
              bool success,
              bytes memory returndata,
              string memory errorMessage
          ) internal pure returns (bytes memory) {
              if (success) {
                  return returndata;
              } else {
                  // Look for revert reason and bubble it up if present
                  if (returndata.length > 0) {
                      // The easiest way to bubble the revert reason is using memory via assembly
                      /// @solidity memory-safe-assembly
                      assembly {
                          let returndata_size := mload(returndata)
                          revert(add(32, returndata), returndata_size)
                      }
                  } else {
                      revert(errorMessage);
                  }
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)
      pragma solidity ^0.8.0;
      import "../IERC721.sol";
      /**
       * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
       * @dev See https://eips.ethereum.org/EIPS/eip-721
       */
      interface IERC721Metadata is IERC721 {
          /**
           * @dev Returns the token collection name.
           */
          function name() external view returns (string memory);
          /**
           * @dev Returns the token collection symbol.
           */
          function symbol() external view returns (string memory);
          /**
           * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
           */
          function tokenURI(uint256 tokenId) external view returns (string memory);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)
      pragma solidity ^0.8.0;
      /**
       * @title ERC721 token receiver interface
       * @dev Interface for any contract that wants to support safeTransfers
       * from ERC721 asset contracts.
       */
      interface IERC721Receiver {
          /**
           * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
           * by `operator` from `from`, this function is called.
           *
           * It must return its Solidity selector to confirm the token transfer.
           * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
           *
           * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
           */
          function onERC721Received(
              address operator,
              address from,
              uint256 tokenId,
              bytes calldata data
          ) external returns (bytes4);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/IERC721.sol)
      pragma solidity ^0.8.0;
      import "../../utils/introspection/IERC165.sol";
      /**
       * @dev Required interface of an ERC721 compliant contract.
       */
      interface IERC721 is IERC165 {
          /**
           * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
           */
          event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
          /**
           * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
           */
          event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
          /**
           * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
           */
          event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
          /**
           * @dev Returns the number of tokens in ``owner``'s account.
           */
          function balanceOf(address owner) external view returns (uint256 balance);
          /**
           * @dev Returns the owner of the `tokenId` token.
           *
           * Requirements:
           *
           * - `tokenId` must exist.
           */
          function ownerOf(uint256 tokenId) external view returns (address owner);
          /**
           * @dev Safely transfers `tokenId` token from `from` to `to`.
           *
           * Requirements:
           *
           * - `from` cannot be the zero address.
           * - `to` cannot be the zero address.
           * - `tokenId` token must exist and be owned by `from`.
           * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
           * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
           *
           * Emits a {Transfer} event.
           */
          function safeTransferFrom(
              address from,
              address to,
              uint256 tokenId,
              bytes calldata data
          ) external;
          /**
           * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
           * are aware of the ERC721 protocol to prevent tokens from being forever locked.
           *
           * Requirements:
           *
           * - `from` cannot be the zero address.
           * - `to` cannot be the zero address.
           * - `tokenId` token must exist and be owned by `from`.
           * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
           * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
           *
           * Emits a {Transfer} event.
           */
          function safeTransferFrom(
              address from,
              address to,
              uint256 tokenId
          ) external;
          /**
           * @dev Transfers `tokenId` token from `from` to `to`.
           *
           * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
           *
           * Requirements:
           *
           * - `from` cannot be the zero address.
           * - `to` cannot be the zero address.
           * - `tokenId` token must be owned by `from`.
           * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
           *
           * Emits a {Transfer} event.
           */
          function transferFrom(
              address from,
              address to,
              uint256 tokenId
          ) external;
          /**
           * @dev Gives permission to `to` to transfer `tokenId` token to another account.
           * The approval is cleared when the token is transferred.
           *
           * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
           *
           * Requirements:
           *
           * - The caller must own the token or be an approved operator.
           * - `tokenId` must exist.
           *
           * Emits an {Approval} event.
           */
          function approve(address to, uint256 tokenId) external;
          /**
           * @dev Approve or remove `operator` as an operator for the caller.
           * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
           *
           * Requirements:
           *
           * - The `operator` cannot be the caller.
           *
           * Emits an {ApprovalForAll} event.
           */
          function setApprovalForAll(address operator, bool _approved) external;
          /**
           * @dev Returns the account approved for `tokenId` token.
           *
           * Requirements:
           *
           * - `tokenId` must exist.
           */
          function getApproved(uint256 tokenId) external view returns (address operator);
          /**
           * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
           *
           * See {setApprovalForAll}
           */
          function isApprovedForAll(address owner, address operator) external view returns (bool);
      }
      // 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/introspection/IERC165.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Interface of the ERC165 standard, as defined in the
       * https://eips.ethereum.org/EIPS/eip-165[EIP].
       *
       * Implementers can declare support of contract interfaces, which can then be
       * queried by others ({ERC165Checker}).
       *
       * For an implementation, see {ERC165}.
       */
      interface IERC165 {
          /**
           * @dev Returns true if this contract implements the interface defined by
           * `interfaceId`. See the corresponding
           * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
           * to learn more about how these ids are created.
           *
           * This function call must use less than 30 000 gas.
           */
          function supportsInterface(bytes4 interfaceId) external view returns (bool);
      }
      

      File 2 of 3: FiatTokenProxy
      pragma solidity ^0.4.24;
      
      // File: zos-lib/contracts/upgradeability/Proxy.sol
      
      /**
       * @title Proxy
       * @dev Implements delegation of calls to other contracts, with proper
       * forwarding of return values and bubbling of failures.
       * It defines a fallback function that delegates all calls to the address
       * returned by the abstract _implementation() internal function.
       */
      contract Proxy {
        /**
         * @dev Fallback function.
         * Implemented entirely in `_fallback`.
         */
        function () payable external {
          _fallback();
        }
      
        /**
         * @return The Address of the implementation.
         */
        function _implementation() internal view returns (address);
      
        /**
         * @dev Delegates execution to an implementation contract.
         * This is a low level function that doesn't return to its internal call site.
         * It will return to the external caller whatever the implementation returns.
         * @param implementation Address to delegate.
         */
        function _delegate(address implementation) internal {
          assembly {
            // Copy msg.data. We take full control of memory in this inline assembly
            // block because it will not return to Solidity code. We overwrite the
            // Solidity scratch pad at memory position 0.
            calldatacopy(0, 0, calldatasize)
      
            // Call the implementation.
            // out and outsize are 0 because we don't know the size yet.
            let result := delegatecall(gas, implementation, 0, calldatasize, 0, 0)
      
            // Copy the returned data.
            returndatacopy(0, 0, returndatasize)
      
            switch result
            // delegatecall returns 0 on error.
            case 0 { revert(0, returndatasize) }
            default { return(0, returndatasize) }
          }
        }
      
        /**
         * @dev Function that is run as the first thing in the fallback function.
         * Can be redefined in derived contracts to add functionality.
         * Redefinitions must call super._willFallback().
         */
        function _willFallback() internal {
        }
      
        /**
         * @dev fallback implementation.
         * Extracted to enable manual triggering.
         */
        function _fallback() internal {
          _willFallback();
          _delegate(_implementation());
        }
      }
      
      // File: openzeppelin-solidity/contracts/AddressUtils.sol
      
      /**
       * Utility library of inline functions on addresses
       */
      library AddressUtils {
      
        /**
         * Returns whether the target address is a contract
         * @dev This function will return false if invoked during the constructor of a contract,
         * as the code is not actually created until after the constructor finishes.
         * @param addr address to check
         * @return whether the target address is a contract
         */
        function isContract(address addr) internal view returns (bool) {
          uint256 size;
          // XXX Currently there is no better way to check if there is a contract in an address
          // than to check the size of the code at that address.
          // See https://ethereum.stackexchange.com/a/14016/36603
          // for more details about how this works.
          // TODO Check this again before the Serenity release, because all addresses will be
          // contracts then.
          // solium-disable-next-line security/no-inline-assembly
          assembly { size := extcodesize(addr) }
          return size > 0;
        }
      
      }
      
      // File: zos-lib/contracts/upgradeability/UpgradeabilityProxy.sol
      
      /**
       * @title UpgradeabilityProxy
       * @dev This contract implements a proxy that allows to change the
       * implementation address to which it will delegate.
       * Such a change is called an implementation upgrade.
       */
      contract UpgradeabilityProxy is Proxy {
        /**
         * @dev Emitted when the implementation is upgraded.
         * @param implementation Address of the new implementation.
         */
        event Upgraded(address implementation);
      
        /**
         * @dev Storage slot with the address of the current implementation.
         * This is the keccak-256 hash of "org.zeppelinos.proxy.implementation", and is
         * validated in the constructor.
         */
        bytes32 private constant IMPLEMENTATION_SLOT = 0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3;
      
        /**
         * @dev Contract constructor.
         * @param _implementation Address of the initial implementation.
         */
        constructor(address _implementation) public {
          assert(IMPLEMENTATION_SLOT == keccak256("org.zeppelinos.proxy.implementation"));
      
          _setImplementation(_implementation);
        }
      
        /**
         * @dev Returns the current implementation.
         * @return Address of the current implementation
         */
        function _implementation() internal view returns (address impl) {
          bytes32 slot = IMPLEMENTATION_SLOT;
          assembly {
            impl := sload(slot)
          }
        }
      
        /**
         * @dev Upgrades the proxy to a new implementation.
         * @param newImplementation Address of the new implementation.
         */
        function _upgradeTo(address newImplementation) internal {
          _setImplementation(newImplementation);
          emit Upgraded(newImplementation);
        }
      
        /**
         * @dev Sets the implementation address of the proxy.
         * @param newImplementation Address of the new implementation.
         */
        function _setImplementation(address newImplementation) private {
          require(AddressUtils.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address");
      
          bytes32 slot = IMPLEMENTATION_SLOT;
      
          assembly {
            sstore(slot, newImplementation)
          }
        }
      }
      
      // File: zos-lib/contracts/upgradeability/AdminUpgradeabilityProxy.sol
      
      /**
       * @title AdminUpgradeabilityProxy
       * @dev This contract combines an upgradeability proxy with an authorization
       * mechanism for administrative tasks.
       * All external functions in this contract must be guarded by the
       * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity
       * feature proposal that would enable this to be done automatically.
       */
      contract AdminUpgradeabilityProxy is UpgradeabilityProxy {
        /**
         * @dev Emitted when the administration has been transferred.
         * @param previousAdmin Address of the previous admin.
         * @param newAdmin Address of the new admin.
         */
        event AdminChanged(address previousAdmin, address newAdmin);
      
        /**
         * @dev Storage slot with the admin of the contract.
         * This is the keccak-256 hash of "org.zeppelinos.proxy.admin", and is
         * validated in the constructor.
         */
        bytes32 private constant ADMIN_SLOT = 0x10d6a54a4754c8869d6886b5f5d7fbfa5b4522237ea5c60d11bc4e7a1ff9390b;
      
        /**
         * @dev Modifier to check whether the `msg.sender` is the admin.
         * If it is, it will run the function. Otherwise, it will delegate the call
         * to the implementation.
         */
        modifier ifAdmin() {
          if (msg.sender == _admin()) {
            _;
          } else {
            _fallback();
          }
        }
      
        /**
         * Contract constructor.
         * It sets the `msg.sender` as the proxy administrator.
         * @param _implementation address of the initial implementation.
         */
        constructor(address _implementation) UpgradeabilityProxy(_implementation) public {
          assert(ADMIN_SLOT == keccak256("org.zeppelinos.proxy.admin"));
      
          _setAdmin(msg.sender);
        }
      
        /**
         * @return The address of the proxy admin.
         */
        function admin() external view ifAdmin returns (address) {
          return _admin();
        }
      
        /**
         * @return The address of the implementation.
         */
        function implementation() external view ifAdmin returns (address) {
          return _implementation();
        }
      
        /**
         * @dev Changes the admin of the proxy.
         * Only the current admin can call this function.
         * @param newAdmin Address to transfer proxy administration to.
         */
        function changeAdmin(address newAdmin) external ifAdmin {
          require(newAdmin != address(0), "Cannot change the admin of a proxy to the zero address");
          emit AdminChanged(_admin(), newAdmin);
          _setAdmin(newAdmin);
        }
      
        /**
         * @dev Upgrade the backing implementation of the proxy.
         * Only the admin can call this function.
         * @param newImplementation Address of the new implementation.
         */
        function upgradeTo(address newImplementation) external ifAdmin {
          _upgradeTo(newImplementation);
        }
      
        /**
         * @dev Upgrade the backing implementation of the proxy and call a function
         * on the new implementation.
         * This is useful to initialize the proxied contract.
         * @param newImplementation Address of the new implementation.
         * @param data Data to send as msg.data in the low level call.
         * It should include the signature and the parameters of the function to be
         * called, as described in
         * https://solidity.readthedocs.io/en/develop/abi-spec.html#function-selector-and-argument-encoding.
         */
        function upgradeToAndCall(address newImplementation, bytes data) payable external ifAdmin {
          _upgradeTo(newImplementation);
          require(address(this).call.value(msg.value)(data));
        }
      
        /**
         * @return The admin slot.
         */
        function _admin() internal view returns (address adm) {
          bytes32 slot = ADMIN_SLOT;
          assembly {
            adm := sload(slot)
          }
        }
      
        /**
         * @dev Sets the address of the proxy admin.
         * @param newAdmin Address of the new proxy admin.
         */
        function _setAdmin(address newAdmin) internal {
          bytes32 slot = ADMIN_SLOT;
      
          assembly {
            sstore(slot, newAdmin)
          }
        }
      
        /**
         * @dev Only fall back when the sender is not the admin.
         */
        function _willFallback() internal {
          require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin");
          super._willFallback();
        }
      }
      
      // File: contracts/FiatTokenProxy.sol
      
      /**
      * Copyright CENTRE SECZ 2018
      *
      * Permission is hereby granted, free of charge, to any person obtaining a copy 
      * of this software and associated documentation files (the "Software"), to deal 
      * in the Software without restriction, including without limitation the rights 
      * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
      * copies of the Software, and to permit persons to whom the Software is furnished to 
      * do so, subject to the following conditions:
      *
      * The above copyright notice and this permission notice shall be included in all 
      * copies or substantial portions of the Software.
      *
      * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
      * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
      * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
      * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
      * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
      * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      */
      
      pragma solidity ^0.4.24;
      
      
      /**
       * @title FiatTokenProxy
       * @dev This contract proxies FiatToken calls and enables FiatToken upgrades
      */ 
      contract FiatTokenProxy is AdminUpgradeabilityProxy {
          constructor(address _implementation) public AdminUpgradeabilityProxy(_implementation) {
          }
      }

      File 3 of 3: FiatTokenV2_1
      // File: @openzeppelin/contracts/math/SafeMath.sol
      
      // SPDX-License-Identifier: MIT
      
      pragma solidity ^0.6.0;
      
      /**
       * @dev Wrappers over Solidity's arithmetic operations with added overflow
       * checks.
       *
       * Arithmetic operations in Solidity wrap on overflow. This can easily result
       * in bugs, because programmers usually assume that an overflow raises an
       * error, which is the standard behavior in high level programming languages.
       * `SafeMath` restores this intuition by reverting the transaction when an
       * operation overflows.
       *
       * Using this library instead of the unchecked operations eliminates an entire
       * class of bugs, so it's recommended to use it always.
       */
      library SafeMath {
          /**
           * @dev Returns the addition of two unsigned integers, reverting on
           * overflow.
           *
           * Counterpart to Solidity's `+` operator.
           *
           * Requirements:
           *
           * - Addition cannot overflow.
           */
          function add(uint256 a, uint256 b) internal pure returns (uint256) {
              uint256 c = a + b;
              require(c >= a, "SafeMath: addition overflow");
      
              return c;
          }
      
          /**
           * @dev Returns the subtraction of two unsigned integers, reverting on
           * overflow (when the result is negative).
           *
           * Counterpart to Solidity's `-` operator.
           *
           * Requirements:
           *
           * - Subtraction cannot overflow.
           */
          function sub(uint256 a, uint256 b) internal pure returns (uint256) {
              return sub(a, b, "SafeMath: subtraction overflow");
          }
      
          /**
           * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
           * overflow (when the result is negative).
           *
           * Counterpart to Solidity's `-` operator.
           *
           * Requirements:
           *
           * - Subtraction cannot overflow.
           */
          function sub(
              uint256 a,
              uint256 b,
              string memory errorMessage
          ) internal pure returns (uint256) {
              require(b <= a, errorMessage);
              uint256 c = a - b;
      
              return c;
          }
      
          /**
           * @dev Returns the multiplication of two unsigned integers, reverting on
           * overflow.
           *
           * Counterpart to Solidity's `*` operator.
           *
           * Requirements:
           *
           * - Multiplication cannot overflow.
           */
          function mul(uint256 a, uint256 b) internal pure returns (uint256) {
              // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
              // benefit is lost if 'b' is also tested.
              // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
              if (a == 0) {
                  return 0;
              }
      
              uint256 c = a * b;
              require(c / a == b, "SafeMath: multiplication overflow");
      
              return c;
          }
      
          /**
           * @dev Returns the integer division of two unsigned integers. Reverts on
           * division by zero. The result is rounded towards zero.
           *
           * Counterpart to Solidity's `/` operator. Note: this function uses a
           * `revert` opcode (which leaves remaining gas untouched) while Solidity
           * uses an invalid opcode to revert (consuming all remaining gas).
           *
           * Requirements:
           *
           * - The divisor cannot be zero.
           */
          function div(uint256 a, uint256 b) internal pure returns (uint256) {
              return div(a, b, "SafeMath: division by zero");
          }
      
          /**
           * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
           * division by zero. The result is rounded towards zero.
           *
           * Counterpart to Solidity's `/` operator. Note: this function uses a
           * `revert` opcode (which leaves remaining gas untouched) while Solidity
           * uses an invalid opcode to revert (consuming all remaining gas).
           *
           * Requirements:
           *
           * - The divisor cannot be zero.
           */
          function div(
              uint256 a,
              uint256 b,
              string memory errorMessage
          ) internal pure returns (uint256) {
              require(b > 0, errorMessage);
              uint256 c = a / b;
              // assert(a == b * c + a % b); // There is no case in which this doesn't hold
      
              return c;
          }
      
          /**
           * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
           * Reverts when dividing by zero.
           *
           * Counterpart to Solidity's `%` operator. This function uses a `revert`
           * opcode (which leaves remaining gas untouched) while Solidity uses an
           * invalid opcode to revert (consuming all remaining gas).
           *
           * Requirements:
           *
           * - The divisor cannot be zero.
           */
          function mod(uint256 a, uint256 b) internal pure returns (uint256) {
              return mod(a, b, "SafeMath: modulo by zero");
          }
      
          /**
           * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
           * Reverts with custom message when dividing by zero.
           *
           * Counterpart to Solidity's `%` operator. This function uses a `revert`
           * opcode (which leaves remaining gas untouched) while Solidity uses an
           * invalid opcode to revert (consuming all remaining gas).
           *
           * Requirements:
           *
           * - The divisor cannot be zero.
           */
          function mod(
              uint256 a,
              uint256 b,
              string memory errorMessage
          ) internal pure returns (uint256) {
              require(b != 0, errorMessage);
              return a % b;
          }
      }
      
      // File: @openzeppelin/contracts/token/ERC20/IERC20.sol
      
      pragma solidity ^0.6.0;
      
      /**
       * @dev Interface of the ERC20 standard as defined in the EIP.
       */
      interface IERC20 {
          /**
           * @dev Returns the amount of tokens in existence.
           */
          function totalSupply() external view returns (uint256);
      
          /**
           * @dev Returns the amount of tokens owned by `account`.
           */
          function balanceOf(address account) external view returns (uint256);
      
          /**
           * @dev Moves `amount` tokens from the caller's account to `recipient`.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transfer(address recipient, uint256 amount)
              external
              returns (bool);
      
          /**
           * @dev Returns the remaining number of tokens that `spender` will be
           * allowed to spend on behalf of `owner` through {transferFrom}. This is
           * zero by default.
           *
           * This value changes when {approve} or {transferFrom} are called.
           */
          function allowance(address owner, address spender)
              external
              view
              returns (uint256);
      
          /**
           * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * IMPORTANT: Beware that changing an allowance with this method brings the risk
           * that someone may use both the old and the new allowance by unfortunate
           * transaction ordering. One possible solution to mitigate this race
           * condition is to first reduce the spender's allowance to 0 and set the
           * desired value afterwards:
           * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
           *
           * Emits an {Approval} event.
           */
          function approve(address spender, uint256 amount) external returns (bool);
      
          /**
           * @dev Moves `amount` tokens from `sender` to `recipient` using the
           * allowance mechanism. `amount` is then deducted from the caller's
           * allowance.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transferFrom(
              address sender,
              address recipient,
              uint256 amount
          ) external returns (bool);
      
          /**
           * @dev Emitted when `value` tokens are moved from one account (`from`) to
           * another (`to`).
           *
           * Note that `value` may be zero.
           */
          event Transfer(address indexed from, address indexed to, uint256 value);
      
          /**
           * @dev Emitted when the allowance of a `spender` for an `owner` is set by
           * a call to {approve}. `value` is the new allowance.
           */
          event Approval(
              address indexed owner,
              address indexed spender,
              uint256 value
          );
      }
      
      // File: contracts/v1/AbstractFiatTokenV1.sol
      
      /**
       * Copyright (c) 2018-2020 CENTRE SECZ
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy
       * of this software and associated documentation files (the "Software"), to deal
       * in the Software without restriction, including without limitation the rights
       * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       * copies of the Software, and to permit persons to whom the Software is
       * furnished to do so, subject to the following conditions:
       *
       * The above copyright notice and this permission notice shall be included in
       * copies or substantial portions of the Software.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       * SOFTWARE.
       */
      
      pragma solidity 0.6.12;
      
      abstract contract AbstractFiatTokenV1 is IERC20 {
          function _approve(
              address owner,
              address spender,
              uint256 value
          ) internal virtual;
      
          function _transfer(
              address from,
              address to,
              uint256 value
          ) internal virtual;
      }
      
      // File: contracts/v1/Ownable.sol
      
      /**
       * Copyright (c) 2018 zOS Global Limited.
       * Copyright (c) 2018-2020 CENTRE SECZ
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy
       * of this software and associated documentation files (the "Software"), to deal
       * in the Software without restriction, including without limitation the rights
       * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       * copies of the Software, and to permit persons to whom the Software is
       * furnished to do so, subject to the following conditions:
       *
       * The above copyright notice and this permission notice shall be included in
       * copies or substantial portions of the Software.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       * SOFTWARE.
       */
      pragma solidity 0.6.12;
      
      /**
       * @notice The Ownable contract has an owner address, and provides basic
       * authorization control functions
       * @dev Forked from https://github.com/OpenZeppelin/openzeppelin-labs/blob/3887ab77b8adafba4a26ace002f3a684c1a3388b/upgradeability_ownership/contracts/ownership/Ownable.sol
       * Modifications:
       * 1. Consolidate OwnableStorage into this contract (7/13/18)
       * 2. Reformat, conform to Solidity 0.6 syntax, and add error messages (5/13/20)
       * 3. Make public functions external (5/27/20)
       */
      contract Ownable {
          // Owner of the contract
          address private _owner;
      
          /**
           * @dev Event to show ownership has been transferred
           * @param previousOwner representing the address of the previous owner
           * @param newOwner representing the address of the new owner
           */
          event OwnershipTransferred(address previousOwner, address newOwner);
      
          /**
           * @dev The constructor sets the original owner of the contract to the sender account.
           */
          constructor() public {
              setOwner(msg.sender);
          }
      
          /**
           * @dev Tells the address of the owner
           * @return the address of the owner
           */
          function owner() external view returns (address) {
              return _owner;
          }
      
          /**
           * @dev Sets a new owner address
           */
          function setOwner(address newOwner) internal {
              _owner = newOwner;
          }
      
          /**
           * @dev Throws if called by any account other than the owner.
           */
          modifier onlyOwner() {
              require(msg.sender == _owner, "Ownable: caller is not the owner");
              _;
          }
      
          /**
           * @dev Allows the current owner to transfer control of the contract to a newOwner.
           * @param newOwner The address to transfer ownership to.
           */
          function transferOwnership(address newOwner) external onlyOwner {
              require(
                  newOwner != address(0),
                  "Ownable: new owner is the zero address"
              );
              emit OwnershipTransferred(_owner, newOwner);
              setOwner(newOwner);
          }
      }
      
      // File: contracts/v1/Pausable.sol
      
      /**
       * Copyright (c) 2016 Smart Contract Solutions, Inc.
       * Copyright (c) 2018-2020 CENTRE SECZ0
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy
       * of this software and associated documentation files (the "Software"), to deal
       * in the Software without restriction, including without limitation the rights
       * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       * copies of the Software, and to permit persons to whom the Software is
       * furnished to do so, subject to the following conditions:
       *
       * The above copyright notice and this permission notice shall be included in
       * copies or substantial portions of the Software.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       * SOFTWARE.
       */
      
      pragma solidity 0.6.12;
      
      /**
       * @notice Base contract which allows children to implement an emergency stop
       * mechanism
       * @dev Forked from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/feb665136c0dae9912e08397c1a21c4af3651ef3/contracts/lifecycle/Pausable.sol
       * Modifications:
       * 1. Added pauser role, switched pause/unpause to be onlyPauser (6/14/2018)
       * 2. Removed whenNotPause/whenPaused from pause/unpause (6/14/2018)
       * 3. Removed whenPaused (6/14/2018)
       * 4. Switches ownable library to use ZeppelinOS (7/12/18)
       * 5. Remove constructor (7/13/18)
       * 6. Reformat, conform to Solidity 0.6 syntax and add error messages (5/13/20)
       * 7. Make public functions external (5/27/20)
       */
      contract Pausable is Ownable {
          event Pause();
          event Unpause();
          event PauserChanged(address indexed newAddress);
      
          address public pauser;
          bool public paused = false;
      
          /**
           * @dev Modifier to make a function callable only when the contract is not paused.
           */
          modifier whenNotPaused() {
              require(!paused, "Pausable: paused");
              _;
          }
      
          /**
           * @dev throws if called by any account other than the pauser
           */
          modifier onlyPauser() {
              require(msg.sender == pauser, "Pausable: caller is not the pauser");
              _;
          }
      
          /**
           * @dev called by the owner to pause, triggers stopped state
           */
          function pause() external onlyPauser {
              paused = true;
              emit Pause();
          }
      
          /**
           * @dev called by the owner to unpause, returns to normal state
           */
          function unpause() external onlyPauser {
              paused = false;
              emit Unpause();
          }
      
          /**
           * @dev update the pauser role
           */
          function updatePauser(address _newPauser) external onlyOwner {
              require(
                  _newPauser != address(0),
                  "Pausable: new pauser is the zero address"
              );
              pauser = _newPauser;
              emit PauserChanged(pauser);
          }
      }
      
      // File: contracts/v1/Blacklistable.sol
      
      /**
       * Copyright (c) 2018-2020 CENTRE SECZ
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy
       * of this software and associated documentation files (the "Software"), to deal
       * in the Software without restriction, including without limitation the rights
       * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       * copies of the Software, and to permit persons to whom the Software is
       * furnished to do so, subject to the following conditions:
       *
       * The above copyright notice and this permission notice shall be included in
       * copies or substantial portions of the Software.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       * SOFTWARE.
       */
      
      pragma solidity 0.6.12;
      
      /**
       * @title Blacklistable Token
       * @dev Allows accounts to be blacklisted by a "blacklister" role
       */
      contract Blacklistable is Ownable {
          address public blacklister;
          mapping(address => bool) internal blacklisted;
      
          event Blacklisted(address indexed _account);
          event UnBlacklisted(address indexed _account);
          event BlacklisterChanged(address indexed newBlacklister);
      
          /**
           * @dev Throws if called by any account other than the blacklister
           */
          modifier onlyBlacklister() {
              require(
                  msg.sender == blacklister,
                  "Blacklistable: caller is not the blacklister"
              );
              _;
          }
      
          /**
           * @dev Throws if argument account is blacklisted
           * @param _account The address to check
           */
          modifier notBlacklisted(address _account) {
              require(
                  !blacklisted[_account],
                  "Blacklistable: account is blacklisted"
              );
              _;
          }
      
          /**
           * @dev Checks if account is blacklisted
           * @param _account The address to check
           */
          function isBlacklisted(address _account) external view returns (bool) {
              return blacklisted[_account];
          }
      
          /**
           * @dev Adds account to blacklist
           * @param _account The address to blacklist
           */
          function blacklist(address _account) external onlyBlacklister {
              blacklisted[_account] = true;
              emit Blacklisted(_account);
          }
      
          /**
           * @dev Removes account from blacklist
           * @param _account The address to remove from the blacklist
           */
          function unBlacklist(address _account) external onlyBlacklister {
              blacklisted[_account] = false;
              emit UnBlacklisted(_account);
          }
      
          function updateBlacklister(address _newBlacklister) external onlyOwner {
              require(
                  _newBlacklister != address(0),
                  "Blacklistable: new blacklister is the zero address"
              );
              blacklister = _newBlacklister;
              emit BlacklisterChanged(blacklister);
          }
      }
      
      // File: contracts/v1/FiatTokenV1.sol
      
      /**
       *
       * Copyright (c) 2018-2020 CENTRE SECZ
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy
       * of this software and associated documentation files (the "Software"), to deal
       * in the Software without restriction, including without limitation the rights
       * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       * copies of the Software, and to permit persons to whom the Software is
       * furnished to do so, subject to the following conditions:
       *
       * The above copyright notice and this permission notice shall be included in
       * copies or substantial portions of the Software.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       * SOFTWARE.
       */
      
      pragma solidity 0.6.12;
      
      /**
       * @title FiatToken
       * @dev ERC20 Token backed by fiat reserves
       */
      contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable {
          using SafeMath for uint256;
      
          string public name;
          string public symbol;
          uint8 public decimals;
          string public currency;
          address public masterMinter;
          bool internal initialized;
      
          mapping(address => uint256) internal balances;
          mapping(address => mapping(address => uint256)) internal allowed;
          uint256 internal totalSupply_ = 0;
          mapping(address => bool) internal minters;
          mapping(address => uint256) internal minterAllowed;
      
          event Mint(address indexed minter, address indexed to, uint256 amount);
          event Burn(address indexed burner, uint256 amount);
          event MinterConfigured(address indexed minter, uint256 minterAllowedAmount);
          event MinterRemoved(address indexed oldMinter);
          event MasterMinterChanged(address indexed newMasterMinter);
      
          function initialize(
              string memory tokenName,
              string memory tokenSymbol,
              string memory tokenCurrency,
              uint8 tokenDecimals,
              address newMasterMinter,
              address newPauser,
              address newBlacklister,
              address newOwner
          ) public {
              require(!initialized, "FiatToken: contract is already initialized");
              require(
                  newMasterMinter != address(0),
                  "FiatToken: new masterMinter is the zero address"
              );
              require(
                  newPauser != address(0),
                  "FiatToken: new pauser is the zero address"
              );
              require(
                  newBlacklister != address(0),
                  "FiatToken: new blacklister is the zero address"
              );
              require(
                  newOwner != address(0),
                  "FiatToken: new owner is the zero address"
              );
      
              name = tokenName;
              symbol = tokenSymbol;
              currency = tokenCurrency;
              decimals = tokenDecimals;
              masterMinter = newMasterMinter;
              pauser = newPauser;
              blacklister = newBlacklister;
              setOwner(newOwner);
              initialized = true;
          }
      
          /**
           * @dev Throws if called by any account other than a minter
           */
          modifier onlyMinters() {
              require(minters[msg.sender], "FiatToken: caller is not a minter");
              _;
          }
      
          /**
           * @dev Function to mint tokens
           * @param _to The address that will receive the minted tokens.
           * @param _amount The amount of tokens to mint. Must be less than or equal
           * to the minterAllowance of the caller.
           * @return A boolean that indicates if the operation was successful.
           */
          function mint(address _to, uint256 _amount)
              external
              whenNotPaused
              onlyMinters
              notBlacklisted(msg.sender)
              notBlacklisted(_to)
              returns (bool)
          {
              require(_to != address(0), "FiatToken: mint to the zero address");
              require(_amount > 0, "FiatToken: mint amount not greater than 0");
      
              uint256 mintingAllowedAmount = minterAllowed[msg.sender];
              require(
                  _amount <= mintingAllowedAmount,
                  "FiatToken: mint amount exceeds minterAllowance"
              );
      
              totalSupply_ = totalSupply_.add(_amount);
              balances[_to] = balances[_to].add(_amount);
              minterAllowed[msg.sender] = mintingAllowedAmount.sub(_amount);
              emit Mint(msg.sender, _to, _amount);
              emit Transfer(address(0), _to, _amount);
              return true;
          }
      
          /**
           * @dev Throws if called by any account other than the masterMinter
           */
          modifier onlyMasterMinter() {
              require(
                  msg.sender == masterMinter,
                  "FiatToken: caller is not the masterMinter"
              );
              _;
          }
      
          /**
           * @dev Get minter allowance for an account
           * @param minter The address of the minter
           */
          function minterAllowance(address minter) external view returns (uint256) {
              return minterAllowed[minter];
          }
      
          /**
           * @dev Checks if account is a minter
           * @param account The address to check
           */
          function isMinter(address account) external view returns (bool) {
              return minters[account];
          }
      
          /**
           * @notice Amount of remaining tokens spender is allowed to transfer on
           * behalf of the token owner
           * @param owner     Token owner's address
           * @param spender   Spender's address
           * @return Allowance amount
           */
          function allowance(address owner, address spender)
              external
              override
              view
              returns (uint256)
          {
              return allowed[owner][spender];
          }
      
          /**
           * @dev Get totalSupply of token
           */
          function totalSupply() external override view returns (uint256) {
              return totalSupply_;
          }
      
          /**
           * @dev Get token balance of an account
           * @param account address The account
           */
          function balanceOf(address account)
              external
              override
              view
              returns (uint256)
          {
              return balances[account];
          }
      
          /**
           * @notice Set spender's allowance over the caller's tokens to be a given
           * value.
           * @param spender   Spender's address
           * @param value     Allowance amount
           * @return True if successful
           */
          function approve(address spender, uint256 value)
              external
              override
              whenNotPaused
              notBlacklisted(msg.sender)
              notBlacklisted(spender)
              returns (bool)
          {
              _approve(msg.sender, spender, value);
              return true;
          }
      
          /**
           * @dev Internal function to set allowance
           * @param owner     Token owner's address
           * @param spender   Spender's address
           * @param value     Allowance amount
           */
          function _approve(
              address owner,
              address spender,
              uint256 value
          ) internal override {
              require(owner != address(0), "ERC20: approve from the zero address");
              require(spender != address(0), "ERC20: approve to the zero address");
              allowed[owner][spender] = value;
              emit Approval(owner, spender, value);
          }
      
          /**
           * @notice Transfer tokens by spending allowance
           * @param from  Payer's address
           * @param to    Payee's address
           * @param value Transfer amount
           * @return True if successful
           */
          function transferFrom(
              address from,
              address to,
              uint256 value
          )
              external
              override
              whenNotPaused
              notBlacklisted(msg.sender)
              notBlacklisted(from)
              notBlacklisted(to)
              returns (bool)
          {
              require(
                  value <= allowed[from][msg.sender],
                  "ERC20: transfer amount exceeds allowance"
              );
              _transfer(from, to, value);
              allowed[from][msg.sender] = allowed[from][msg.sender].sub(value);
              return true;
          }
      
          /**
           * @notice Transfer tokens from the caller
           * @param to    Payee's address
           * @param value Transfer amount
           * @return True if successful
           */
          function transfer(address to, uint256 value)
              external
              override
              whenNotPaused
              notBlacklisted(msg.sender)
              notBlacklisted(to)
              returns (bool)
          {
              _transfer(msg.sender, to, value);
              return true;
          }
      
          /**
           * @notice Internal function to process transfers
           * @param from  Payer's address
           * @param to    Payee's address
           * @param value Transfer amount
           */
          function _transfer(
              address from,
              address to,
              uint256 value
          ) internal override {
              require(from != address(0), "ERC20: transfer from the zero address");
              require(to != address(0), "ERC20: transfer to the zero address");
              require(
                  value <= balances[from],
                  "ERC20: transfer amount exceeds balance"
              );
      
              balances[from] = balances[from].sub(value);
              balances[to] = balances[to].add(value);
              emit Transfer(from, to, value);
          }
      
          /**
           * @dev Function to add/update a new minter
           * @param minter The address of the minter
           * @param minterAllowedAmount The minting amount allowed for the minter
           * @return True if the operation was successful.
           */
          function configureMinter(address minter, uint256 minterAllowedAmount)
              external
              whenNotPaused
              onlyMasterMinter
              returns (bool)
          {
              minters[minter] = true;
              minterAllowed[minter] = minterAllowedAmount;
              emit MinterConfigured(minter, minterAllowedAmount);
              return true;
          }
      
          /**
           * @dev Function to remove a minter
           * @param minter The address of the minter to remove
           * @return True if the operation was successful.
           */
          function removeMinter(address minter)
              external
              onlyMasterMinter
              returns (bool)
          {
              minters[minter] = false;
              minterAllowed[minter] = 0;
              emit MinterRemoved(minter);
              return true;
          }
      
          /**
           * @dev allows a minter to burn some of its own tokens
           * Validates that caller is a minter and that sender is not blacklisted
           * amount is less than or equal to the minter's account balance
           * @param _amount uint256 the amount of tokens to be burned
           */
          function burn(uint256 _amount)
              external
              whenNotPaused
              onlyMinters
              notBlacklisted(msg.sender)
          {
              uint256 balance = balances[msg.sender];
              require(_amount > 0, "FiatToken: burn amount not greater than 0");
              require(balance >= _amount, "FiatToken: burn amount exceeds balance");
      
              totalSupply_ = totalSupply_.sub(_amount);
              balances[msg.sender] = balance.sub(_amount);
              emit Burn(msg.sender, _amount);
              emit Transfer(msg.sender, address(0), _amount);
          }
      
          function updateMasterMinter(address _newMasterMinter) external onlyOwner {
              require(
                  _newMasterMinter != address(0),
                  "FiatToken: new masterMinter is the zero address"
              );
              masterMinter = _newMasterMinter;
              emit MasterMinterChanged(masterMinter);
          }
      }
      
      // File: @openzeppelin/contracts/utils/Address.sol
      
      pragma solidity ^0.6.2;
      
      /**
       * @dev Collection of functions related to the address type
       */
      library Address {
          /**
           * @dev Returns true if `account` is a contract.
           *
           * [IMPORTANT]
           * ====
           * It is unsafe to assume that an address for which this function returns
           * false is an externally-owned account (EOA) and not a contract.
           *
           * Among others, `isContract` will return false for the following
           * types of addresses:
           *
           *  - an externally-owned account
           *  - a contract in construction
           *  - an address where a contract will be created
           *  - an address where a contract lived, but was destroyed
           * ====
           */
          function isContract(address account) internal view returns (bool) {
              // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
              // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
              // for accounts without code, i.e. `keccak256('')`
              bytes32 codehash;
      
                  bytes32 accountHash
               = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
              // solhint-disable-next-line no-inline-assembly
              assembly {
                  codehash := extcodehash(account)
              }
              return (codehash != accountHash && codehash != 0x0);
          }
      
          /**
           * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
           * `recipient`, forwarding all available gas and reverting on errors.
           *
           * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
           * of certain opcodes, possibly making contracts go over the 2300 gas limit
           * imposed by `transfer`, making them unable to receive funds via
           * `transfer`. {sendValue} removes this limitation.
           *
           * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
           *
           * IMPORTANT: because control is transferred to `recipient`, care must be
           * taken to not create reentrancy vulnerabilities. Consider using
           * {ReentrancyGuard} or the
           * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
           */
          function sendValue(address payable recipient, uint256 amount) internal {
              require(
                  address(this).balance >= amount,
                  "Address: insufficient balance"
              );
      
              // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
              (bool success, ) = recipient.call{ value: amount }("");
              require(
                  success,
                  "Address: unable to send value, recipient may have reverted"
              );
          }
      
          /**
           * @dev Performs a Solidity function call using a low level `call`. A
           * plain`call` is an unsafe replacement for a function call: use this
           * function instead.
           *
           * If `target` reverts with a revert reason, it is bubbled up by this
           * function (like regular Solidity function calls).
           *
           * Returns the raw returned data. To convert to the expected return value,
           * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
           *
           * Requirements:
           *
           * - `target` must be a contract.
           * - calling `target` with `data` must not revert.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data)
              internal
              returns (bytes memory)
          {
              return functionCall(target, data, "Address: low-level call failed");
          }
      
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
           * `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal returns (bytes memory) {
              return _functionCallWithValue(target, data, 0, errorMessage);
          }
      
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but also transferring `value` wei to `target`.
           *
           * Requirements:
           *
           * - the calling contract must have an ETH balance of at least `value`.
           * - the called Solidity function must be `payable`.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(
              address target,
              bytes memory data,
              uint256 value
          ) internal returns (bytes memory) {
              return
                  functionCallWithValue(
                      target,
                      data,
                      value,
                      "Address: low-level call with value failed"
                  );
          }
      
          /**
           * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
           * with `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(
              address target,
              bytes memory data,
              uint256 value,
              string memory errorMessage
          ) internal returns (bytes memory) {
              require(
                  address(this).balance >= value,
                  "Address: insufficient balance for call"
              );
              return _functionCallWithValue(target, data, value, errorMessage);
          }
      
          function _functionCallWithValue(
              address target,
              bytes memory data,
              uint256 weiValue,
              string memory errorMessage
          ) private returns (bytes memory) {
              require(isContract(target), "Address: call to non-contract");
      
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = target.call{
                  value: weiValue
              }(data);
              if (success) {
                  return returndata;
              } else {
                  // Look for revert reason and bubble it up if present
                  if (returndata.length > 0) {
                      // The easiest way to bubble the revert reason is using memory via assembly
      
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          let returndata_size := mload(returndata)
                          revert(add(32, returndata), returndata_size)
                      }
                  } else {
                      revert(errorMessage);
                  }
              }
          }
      }
      
      // File: @openzeppelin/contracts/token/ERC20/SafeERC20.sol
      
      pragma solidity ^0.6.0;
      
      /**
       * @title SafeERC20
       * @dev Wrappers around ERC20 operations that throw on failure (when the token
       * contract returns false). Tokens that return no value (and instead revert or
       * throw on failure) are also supported, non-reverting calls are assumed to be
       * successful.
       * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
       * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
       */
      library SafeERC20 {
          using SafeMath for uint256;
          using Address for address;
      
          function safeTransfer(
              IERC20 token,
              address to,
              uint256 value
          ) internal {
              _callOptionalReturn(
                  token,
                  abi.encodeWithSelector(token.transfer.selector, to, value)
              );
          }
      
          function safeTransferFrom(
              IERC20 token,
              address from,
              address to,
              uint256 value
          ) internal {
              _callOptionalReturn(
                  token,
                  abi.encodeWithSelector(token.transferFrom.selector, from, to, value)
              );
          }
      
          /**
           * @dev Deprecated. This function has issues similar to the ones found in
           * {IERC20-approve}, and its usage is discouraged.
           *
           * Whenever possible, use {safeIncreaseAllowance} and
           * {safeDecreaseAllowance} instead.
           */
          function safeApprove(
              IERC20 token,
              address spender,
              uint256 value
          ) internal {
              // safeApprove should only be called when setting an initial allowance,
              // or when resetting it to zero. To increase and decrease it, use
              // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
              // solhint-disable-next-line max-line-length
              require(
                  (value == 0) || (token.allowance(address(this), spender) == 0),
                  "SafeERC20: approve from non-zero to non-zero allowance"
              );
              _callOptionalReturn(
                  token,
                  abi.encodeWithSelector(token.approve.selector, spender, value)
              );
          }
      
          function safeIncreaseAllowance(
              IERC20 token,
              address spender,
              uint256 value
          ) internal {
              uint256 newAllowance = token.allowance(address(this), spender).add(
                  value
              );
              _callOptionalReturn(
                  token,
                  abi.encodeWithSelector(
                      token.approve.selector,
                      spender,
                      newAllowance
                  )
              );
          }
      
          function safeDecreaseAllowance(
              IERC20 token,
              address spender,
              uint256 value
          ) internal {
              uint256 newAllowance = token.allowance(address(this), spender).sub(
                  value,
                  "SafeERC20: decreased allowance below zero"
              );
              _callOptionalReturn(
                  token,
                  abi.encodeWithSelector(
                      token.approve.selector,
                      spender,
                      newAllowance
                  )
              );
          }
      
          /**
           * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
           * on the return value: the return value is optional (but if data is returned, it must not be false).
           * @param token The token targeted by the call.
           * @param data The call data (encoded using abi.encode or one of its variants).
           */
          function _callOptionalReturn(IERC20 token, bytes memory data) private {
              // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
              // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
              // the target address contains contract code and also asserts for success in the low-level call.
      
              bytes memory returndata = address(token).functionCall(
                  data,
                  "SafeERC20: low-level call failed"
              );
              if (returndata.length > 0) {
                  // Return data is optional
                  // solhint-disable-next-line max-line-length
                  require(
                      abi.decode(returndata, (bool)),
                      "SafeERC20: ERC20 operation did not succeed"
                  );
              }
          }
      }
      
      // File: contracts/v1.1/Rescuable.sol
      
      /**
       * Copyright (c) 2018-2020 CENTRE SECZ
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy
       * of this software and associated documentation files (the "Software"), to deal
       * in the Software without restriction, including without limitation the rights
       * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       * copies of the Software, and to permit persons to whom the Software is
       * furnished to do so, subject to the following conditions:
       *
       * The above copyright notice and this permission notice shall be included in
       * copies or substantial portions of the Software.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       * SOFTWARE.
       */
      
      pragma solidity 0.6.12;
      
      contract Rescuable is Ownable {
          using SafeERC20 for IERC20;
      
          address private _rescuer;
      
          event RescuerChanged(address indexed newRescuer);
      
          /**
           * @notice Returns current rescuer
           * @return Rescuer's address
           */
          function rescuer() external view returns (address) {
              return _rescuer;
          }
      
          /**
           * @notice Revert if called by any account other than the rescuer.
           */
          modifier onlyRescuer() {
              require(msg.sender == _rescuer, "Rescuable: caller is not the rescuer");
              _;
          }
      
          /**
           * @notice Rescue ERC20 tokens locked up in this contract.
           * @param tokenContract ERC20 token contract address
           * @param to        Recipient address
           * @param amount    Amount to withdraw
           */
          function rescueERC20(
              IERC20 tokenContract,
              address to,
              uint256 amount
          ) external onlyRescuer {
              tokenContract.safeTransfer(to, amount);
          }
      
          /**
           * @notice Assign the rescuer role to a given address.
           * @param newRescuer New rescuer's address
           */
          function updateRescuer(address newRescuer) external onlyOwner {
              require(
                  newRescuer != address(0),
                  "Rescuable: new rescuer is the zero address"
              );
              _rescuer = newRescuer;
              emit RescuerChanged(newRescuer);
          }
      }
      
      // File: contracts/v1.1/FiatTokenV1_1.sol
      
      /**
       * Copyright (c) 2018-2020 CENTRE SECZ
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy
       * of this software and associated documentation files (the "Software"), to deal
       * in the Software without restriction, including without limitation the rights
       * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       * copies of the Software, and to permit persons to whom the Software is
       * furnished to do so, subject to the following conditions:
       *
       * The above copyright notice and this permission notice shall be included in
       * copies or substantial portions of the Software.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       * SOFTWARE.
       */
      
      pragma solidity 0.6.12;
      
      /**
       * @title FiatTokenV1_1
       * @dev ERC20 Token backed by fiat reserves
       */
      contract FiatTokenV1_1 is FiatTokenV1, Rescuable {
      
      }
      
      // File: contracts/v2/AbstractFiatTokenV2.sol
      
      /**
       * Copyright (c) 2018-2020 CENTRE SECZ
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy
       * of this software and associated documentation files (the "Software"), to deal
       * in the Software without restriction, including without limitation the rights
       * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       * copies of the Software, and to permit persons to whom the Software is
       * furnished to do so, subject to the following conditions:
       *
       * The above copyright notice and this permission notice shall be included in
       * copies or substantial portions of the Software.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       * SOFTWARE.
       */
      
      pragma solidity 0.6.12;
      
      abstract contract AbstractFiatTokenV2 is AbstractFiatTokenV1 {
          function _increaseAllowance(
              address owner,
              address spender,
              uint256 increment
          ) internal virtual;
      
          function _decreaseAllowance(
              address owner,
              address spender,
              uint256 decrement
          ) internal virtual;
      }
      
      // File: contracts/util/ECRecover.sol
      
      /**
       * Copyright (c) 2016-2019 zOS Global Limited
       * Copyright (c) 2018-2020 CENTRE SECZ
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy
       * of this software and associated documentation files (the "Software"), to deal
       * in the Software without restriction, including without limitation the rights
       * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       * copies of the Software, and to permit persons to whom the Software is
       * furnished to do so, subject to the following conditions:
       *
       * The above copyright notice and this permission notice shall be included in
       * copies or substantial portions of the Software.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       * SOFTWARE.
       */
      
      pragma solidity 0.6.12;
      
      /**
       * @title ECRecover
       * @notice A library that provides a safe ECDSA recovery function
       */
      library ECRecover {
          /**
           * @notice Recover signer's address from a signed message
           * @dev Adapted from: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/65e4ffde586ec89af3b7e9140bdc9235d1254853/contracts/cryptography/ECDSA.sol
           * Modifications: Accept v, r, and s as separate arguments
           * @param digest    Keccak-256 hash digest of the signed message
           * @param v         v of the signature
           * @param r         r of the signature
           * @param s         s of the signature
           * @return Signer address
           */
          function recover(
              bytes32 digest,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) internal pure returns (address) {
              // 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 (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): 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
              ) {
                  revert("ECRecover: invalid signature 's' value");
              }
      
              if (v != 27 && v != 28) {
                  revert("ECRecover: invalid signature 'v' value");
              }
      
              // If the signature is valid (and not malleable), return the signer address
              address signer = ecrecover(digest, v, r, s);
              require(signer != address(0), "ECRecover: invalid signature");
      
              return signer;
          }
      }
      
      // File: contracts/util/EIP712.sol
      
      /**
       * Copyright (c) 2018-2020 CENTRE SECZ
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy
       * of this software and associated documentation files (the "Software"), to deal
       * in the Software without restriction, including without limitation the rights
       * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       * copies of the Software, and to permit persons to whom the Software is
       * furnished to do so, subject to the following conditions:
       *
       * The above copyright notice and this permission notice shall be included in
       * copies or substantial portions of the Software.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       * SOFTWARE.
       */
      
      pragma solidity 0.6.12;
      
      /**
       * @title EIP712
       * @notice A library that provides EIP712 helper functions
       */
      library EIP712 {
          /**
           * @notice Make EIP712 domain separator
           * @param name      Contract name
           * @param version   Contract version
           * @return Domain separator
           */
          function makeDomainSeparator(string memory name, string memory version)
              internal
              view
              returns (bytes32)
          {
              uint256 chainId;
              assembly {
                  chainId := chainid()
              }
              return
                  keccak256(
                      abi.encode(
                          // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
                          0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f,
                          keccak256(bytes(name)),
                          keccak256(bytes(version)),
                          chainId,
                          address(this)
                      )
                  );
          }
      
          /**
           * @notice Recover signer's address from a EIP712 signature
           * @param domainSeparator   Domain separator
           * @param v                 v of the signature
           * @param r                 r of the signature
           * @param s                 s of the signature
           * @param typeHashAndData   Type hash concatenated with data
           * @return Signer's address
           */
          function recover(
              bytes32 domainSeparator,
              uint8 v,
              bytes32 r,
              bytes32 s,
              bytes memory typeHashAndData
          ) internal pure returns (address) {
              bytes32 digest = keccak256(
                  abi.encodePacked(
                      "\x19\x01",
                      domainSeparator,
                      keccak256(typeHashAndData)
                  )
              );
              return ECRecover.recover(digest, v, r, s);
          }
      }
      
      // File: contracts/v2/EIP712Domain.sol
      
      /**
       * Copyright (c) 2018-2020 CENTRE SECZ
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy
       * of this software and associated documentation files (the "Software"), to deal
       * in the Software without restriction, including without limitation the rights
       * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       * copies of the Software, and to permit persons to whom the Software is
       * furnished to do so, subject to the following conditions:
       *
       * The above copyright notice and this permission notice shall be included in
       * copies or substantial portions of the Software.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       * SOFTWARE.
       */
      
      pragma solidity 0.6.12;
      
      /**
       * @title EIP712 Domain
       */
      contract EIP712Domain {
          /**
           * @dev EIP712 Domain Separator
           */
          bytes32 public DOMAIN_SEPARATOR;
      }
      
      // File: contracts/v2/EIP3009.sol
      
      /**
       * Copyright (c) 2018-2020 CENTRE SECZ
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy
       * of this software and associated documentation files (the "Software"), to deal
       * in the Software without restriction, including without limitation the rights
       * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       * copies of the Software, and to permit persons to whom the Software is
       * furnished to do so, subject to the following conditions:
       *
       * The above copyright notice and this permission notice shall be included in
       * copies or substantial portions of the Software.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       * SOFTWARE.
       */
      
      pragma solidity 0.6.12;
      
      /**
       * @title EIP-3009
       * @notice Provide internal implementation for gas-abstracted transfers
       * @dev Contracts that inherit from this must wrap these with publicly
       * accessible functions, optionally adding modifiers where necessary
       */
      abstract contract EIP3009 is AbstractFiatTokenV2, EIP712Domain {
          // keccak256("TransferWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)")
          bytes32
              public constant TRANSFER_WITH_AUTHORIZATION_TYPEHASH = 0x7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267;
      
          // keccak256("ReceiveWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)")
          bytes32
              public constant RECEIVE_WITH_AUTHORIZATION_TYPEHASH = 0xd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de8;
      
          // keccak256("CancelAuthorization(address authorizer,bytes32 nonce)")
          bytes32
              public constant CANCEL_AUTHORIZATION_TYPEHASH = 0x158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a1597429;
      
          /**
           * @dev authorizer address => nonce => bool (true if nonce is used)
           */
          mapping(address => mapping(bytes32 => bool)) private _authorizationStates;
      
          event AuthorizationUsed(address indexed authorizer, bytes32 indexed nonce);
          event AuthorizationCanceled(
              address indexed authorizer,
              bytes32 indexed nonce
          );
      
          /**
           * @notice Returns the state of an authorization
           * @dev Nonces are randomly generated 32-byte data unique to the
           * authorizer's address
           * @param authorizer    Authorizer's address
           * @param nonce         Nonce of the authorization
           * @return True if the nonce is used
           */
          function authorizationState(address authorizer, bytes32 nonce)
              external
              view
              returns (bool)
          {
              return _authorizationStates[authorizer][nonce];
          }
      
          /**
           * @notice Execute a transfer with a signed authorization
           * @param from          Payer's address (Authorizer)
           * @param to            Payee's address
           * @param value         Amount to be transferred
           * @param validAfter    The time after which this is valid (unix time)
           * @param validBefore   The time before which this is valid (unix time)
           * @param nonce         Unique nonce
           * @param v             v of the signature
           * @param r             r of the signature
           * @param s             s of the signature
           */
          function _transferWithAuthorization(
              address from,
              address to,
              uint256 value,
              uint256 validAfter,
              uint256 validBefore,
              bytes32 nonce,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) internal {
              _requireValidAuthorization(from, nonce, validAfter, validBefore);
      
              bytes memory data = abi.encode(
                  TRANSFER_WITH_AUTHORIZATION_TYPEHASH,
                  from,
                  to,
                  value,
                  validAfter,
                  validBefore,
                  nonce
              );
              require(
                  EIP712.recover(DOMAIN_SEPARATOR, v, r, s, data) == from,
                  "FiatTokenV2: invalid signature"
              );
      
              _markAuthorizationAsUsed(from, nonce);
              _transfer(from, to, value);
          }
      
          /**
           * @notice Receive a transfer with a signed authorization from the payer
           * @dev This has an additional check to ensure that the payee's address
           * matches the caller of this function to prevent front-running attacks.
           * @param from          Payer's address (Authorizer)
           * @param to            Payee's address
           * @param value         Amount to be transferred
           * @param validAfter    The time after which this is valid (unix time)
           * @param validBefore   The time before which this is valid (unix time)
           * @param nonce         Unique nonce
           * @param v             v of the signature
           * @param r             r of the signature
           * @param s             s of the signature
           */
          function _receiveWithAuthorization(
              address from,
              address to,
              uint256 value,
              uint256 validAfter,
              uint256 validBefore,
              bytes32 nonce,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) internal {
              require(to == msg.sender, "FiatTokenV2: caller must be the payee");
              _requireValidAuthorization(from, nonce, validAfter, validBefore);
      
              bytes memory data = abi.encode(
                  RECEIVE_WITH_AUTHORIZATION_TYPEHASH,
                  from,
                  to,
                  value,
                  validAfter,
                  validBefore,
                  nonce
              );
              require(
                  EIP712.recover(DOMAIN_SEPARATOR, v, r, s, data) == from,
                  "FiatTokenV2: invalid signature"
              );
      
              _markAuthorizationAsUsed(from, nonce);
              _transfer(from, to, value);
          }
      
          /**
           * @notice Attempt to cancel an authorization
           * @param authorizer    Authorizer's address
           * @param nonce         Nonce of the authorization
           * @param v             v of the signature
           * @param r             r of the signature
           * @param s             s of the signature
           */
          function _cancelAuthorization(
              address authorizer,
              bytes32 nonce,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) internal {
              _requireUnusedAuthorization(authorizer, nonce);
      
              bytes memory data = abi.encode(
                  CANCEL_AUTHORIZATION_TYPEHASH,
                  authorizer,
                  nonce
              );
              require(
                  EIP712.recover(DOMAIN_SEPARATOR, v, r, s, data) == authorizer,
                  "FiatTokenV2: invalid signature"
              );
      
              _authorizationStates[authorizer][nonce] = true;
              emit AuthorizationCanceled(authorizer, nonce);
          }
      
          /**
           * @notice Check that an authorization is unused
           * @param authorizer    Authorizer's address
           * @param nonce         Nonce of the authorization
           */
          function _requireUnusedAuthorization(address authorizer, bytes32 nonce)
              private
              view
          {
              require(
                  !_authorizationStates[authorizer][nonce],
                  "FiatTokenV2: authorization is used or canceled"
              );
          }
      
          /**
           * @notice Check that authorization is valid
           * @param authorizer    Authorizer's address
           * @param nonce         Nonce of the authorization
           * @param validAfter    The time after which this is valid (unix time)
           * @param validBefore   The time before which this is valid (unix time)
           */
          function _requireValidAuthorization(
              address authorizer,
              bytes32 nonce,
              uint256 validAfter,
              uint256 validBefore
          ) private view {
              require(
                  now > validAfter,
                  "FiatTokenV2: authorization is not yet valid"
              );
              require(now < validBefore, "FiatTokenV2: authorization is expired");
              _requireUnusedAuthorization(authorizer, nonce);
          }
      
          /**
           * @notice Mark an authorization as used
           * @param authorizer    Authorizer's address
           * @param nonce         Nonce of the authorization
           */
          function _markAuthorizationAsUsed(address authorizer, bytes32 nonce)
              private
          {
              _authorizationStates[authorizer][nonce] = true;
              emit AuthorizationUsed(authorizer, nonce);
          }
      }
      
      // File: contracts/v2/EIP2612.sol
      
      /**
       * Copyright (c) 2018-2020 CENTRE SECZ
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy
       * of this software and associated documentation files (the "Software"), to deal
       * in the Software without restriction, including without limitation the rights
       * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       * copies of the Software, and to permit persons to whom the Software is
       * furnished to do so, subject to the following conditions:
       *
       * The above copyright notice and this permission notice shall be included in
       * copies or substantial portions of the Software.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       * SOFTWARE.
       */
      
      pragma solidity 0.6.12;
      
      /**
       * @title EIP-2612
       * @notice Provide internal implementation for gas-abstracted approvals
       */
      abstract contract EIP2612 is AbstractFiatTokenV2, EIP712Domain {
          // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")
          bytes32
              public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
      
          mapping(address => uint256) private _permitNonces;
      
          /**
           * @notice Nonces for permit
           * @param owner Token owner's address (Authorizer)
           * @return Next nonce
           */
          function nonces(address owner) external view returns (uint256) {
              return _permitNonces[owner];
          }
      
          /**
           * @notice Verify a signed approval permit and execute if valid
           * @param owner     Token owner's address (Authorizer)
           * @param spender   Spender's address
           * @param value     Amount of allowance
           * @param deadline  The time at which this expires (unix time)
           * @param v         v of the signature
           * @param r         r of the signature
           * @param s         s of the signature
           */
          function _permit(
              address owner,
              address spender,
              uint256 value,
              uint256 deadline,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) internal {
              require(deadline >= now, "FiatTokenV2: permit is expired");
      
              bytes memory data = abi.encode(
                  PERMIT_TYPEHASH,
                  owner,
                  spender,
                  value,
                  _permitNonces[owner]++,
                  deadline
              );
              require(
                  EIP712.recover(DOMAIN_SEPARATOR, v, r, s, data) == owner,
                  "EIP2612: invalid signature"
              );
      
              _approve(owner, spender, value);
          }
      }
      
      // File: contracts/v2/FiatTokenV2.sol
      
      /**
       * Copyright (c) 2018-2020 CENTRE SECZ
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy
       * of this software and associated documentation files (the "Software"), to deal
       * in the Software without restriction, including without limitation the rights
       * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       * copies of the Software, and to permit persons to whom the Software is
       * furnished to do so, subject to the following conditions:
       *
       * The above copyright notice and this permission notice shall be included in
       * copies or substantial portions of the Software.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       * SOFTWARE.
       */
      
      pragma solidity 0.6.12;
      
      /**
       * @title FiatToken V2
       * @notice ERC20 Token backed by fiat reserves, version 2
       */
      contract FiatTokenV2 is FiatTokenV1_1, EIP3009, EIP2612 {
          uint8 internal _initializedVersion;
      
          /**
           * @notice Initialize v2
           * @param newName   New token name
           */
          function initializeV2(string calldata newName) external {
              // solhint-disable-next-line reason-string
              require(initialized && _initializedVersion == 0);
              name = newName;
              DOMAIN_SEPARATOR = EIP712.makeDomainSeparator(newName, "2");
              _initializedVersion = 1;
          }
      
          /**
           * @notice Increase the allowance by a given increment
           * @param spender   Spender's address
           * @param increment Amount of increase in allowance
           * @return True if successful
           */
          function increaseAllowance(address spender, uint256 increment)
              external
              whenNotPaused
              notBlacklisted(msg.sender)
              notBlacklisted(spender)
              returns (bool)
          {
              _increaseAllowance(msg.sender, spender, increment);
              return true;
          }
      
          /**
           * @notice Decrease the allowance by a given decrement
           * @param spender   Spender's address
           * @param decrement Amount of decrease in allowance
           * @return True if successful
           */
          function decreaseAllowance(address spender, uint256 decrement)
              external
              whenNotPaused
              notBlacklisted(msg.sender)
              notBlacklisted(spender)
              returns (bool)
          {
              _decreaseAllowance(msg.sender, spender, decrement);
              return true;
          }
      
          /**
           * @notice Execute a transfer with a signed authorization
           * @param from          Payer's address (Authorizer)
           * @param to            Payee's address
           * @param value         Amount to be transferred
           * @param validAfter    The time after which this is valid (unix time)
           * @param validBefore   The time before which this is valid (unix time)
           * @param nonce         Unique nonce
           * @param v             v of the signature
           * @param r             r of the signature
           * @param s             s of the signature
           */
          function transferWithAuthorization(
              address from,
              address to,
              uint256 value,
              uint256 validAfter,
              uint256 validBefore,
              bytes32 nonce,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) external whenNotPaused notBlacklisted(from) notBlacklisted(to) {
              _transferWithAuthorization(
                  from,
                  to,
                  value,
                  validAfter,
                  validBefore,
                  nonce,
                  v,
                  r,
                  s
              );
          }
      
          /**
           * @notice Receive a transfer with a signed authorization from the payer
           * @dev This has an additional check to ensure that the payee's address
           * matches the caller of this function to prevent front-running attacks.
           * @param from          Payer's address (Authorizer)
           * @param to            Payee's address
           * @param value         Amount to be transferred
           * @param validAfter    The time after which this is valid (unix time)
           * @param validBefore   The time before which this is valid (unix time)
           * @param nonce         Unique nonce
           * @param v             v of the signature
           * @param r             r of the signature
           * @param s             s of the signature
           */
          function receiveWithAuthorization(
              address from,
              address to,
              uint256 value,
              uint256 validAfter,
              uint256 validBefore,
              bytes32 nonce,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) external whenNotPaused notBlacklisted(from) notBlacklisted(to) {
              _receiveWithAuthorization(
                  from,
                  to,
                  value,
                  validAfter,
                  validBefore,
                  nonce,
                  v,
                  r,
                  s
              );
          }
      
          /**
           * @notice Attempt to cancel an authorization
           * @dev Works only if the authorization is not yet used.
           * @param authorizer    Authorizer's address
           * @param nonce         Nonce of the authorization
           * @param v             v of the signature
           * @param r             r of the signature
           * @param s             s of the signature
           */
          function cancelAuthorization(
              address authorizer,
              bytes32 nonce,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) external whenNotPaused {
              _cancelAuthorization(authorizer, nonce, v, r, s);
          }
      
          /**
           * @notice Update allowance with a signed permit
           * @param owner       Token owner's address (Authorizer)
           * @param spender     Spender's address
           * @param value       Amount of allowance
           * @param deadline    Expiration time, seconds since the epoch
           * @param v           v of the signature
           * @param r           r of the signature
           * @param s           s of the signature
           */
          function permit(
              address owner,
              address spender,
              uint256 value,
              uint256 deadline,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) external whenNotPaused notBlacklisted(owner) notBlacklisted(spender) {
              _permit(owner, spender, value, deadline, v, r, s);
          }
      
          /**
           * @notice Internal function to increase the allowance by a given increment
           * @param owner     Token owner's address
           * @param spender   Spender's address
           * @param increment Amount of increase
           */
          function _increaseAllowance(
              address owner,
              address spender,
              uint256 increment
          ) internal override {
              _approve(owner, spender, allowed[owner][spender].add(increment));
          }
      
          /**
           * @notice Internal function to decrease the allowance by a given decrement
           * @param owner     Token owner's address
           * @param spender   Spender's address
           * @param decrement Amount of decrease
           */
          function _decreaseAllowance(
              address owner,
              address spender,
              uint256 decrement
          ) internal override {
              _approve(
                  owner,
                  spender,
                  allowed[owner][spender].sub(
                      decrement,
                      "ERC20: decreased allowance below zero"
                  )
              );
          }
      }
      
      // File: contracts/v2/FiatTokenV2_1.sol
      
      /**
       * Copyright (c) 2018-2020 CENTRE SECZ
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy
       * of this software and associated documentation files (the "Software"), to deal
       * in the Software without restriction, including without limitation the rights
       * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       * copies of the Software, and to permit persons to whom the Software is
       * furnished to do so, subject to the following conditions:
       *
       * The above copyright notice and this permission notice shall be included in
       * copies or substantial portions of the Software.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       * SOFTWARE.
       */
      
      pragma solidity 0.6.12;
      
      // solhint-disable func-name-mixedcase
      
      /**
       * @title FiatToken V2.1
       * @notice ERC20 Token backed by fiat reserves, version 2.1
       */
      contract FiatTokenV2_1 is FiatTokenV2 {
          /**
           * @notice Initialize v2.1
           * @param lostAndFound  The address to which the locked funds are sent
           */
          function initializeV2_1(address lostAndFound) external {
              // solhint-disable-next-line reason-string
              require(_initializedVersion == 1);
      
              uint256 lockedAmount = balances[address(this)];
              if (lockedAmount > 0) {
                  _transfer(address(this), lostAndFound, lockedAmount);
              }
              blacklisted[address(this)] = true;
      
              _initializedVersion = 2;
          }
      
          /**
           * @notice Version string for the EIP712 domain separator
           * @return Version string
           */
          function version() external view returns (string memory) {
              return "2";
          }
      }