ETH Price: $2,412.95 (-2.73%)
Gas: 3.66 Gwei

Transaction Decoder

Block:
18425364 at Oct-25-2023 06:06:35 AM +UTC
Transaction Fee:
0.00369099293930064 ETH $8.91
Gas Used:
339,120 Gas / 10.884032022 Gwei

Emitted Events:

124 PHAToken.Transfer( from=[Sender] 0x505119164fdb5a6b735c39fa7c21d54da7cf2c3b, to=ERC20Handler, value=100100000000000000000000 )
125 PHAToken.Approval( owner=[Sender] 0x505119164fdb5a6b735c39fa7c21d54da7cf2c3b, spender=ERC20Handler, value=0 )
126 ERC20Handler.Withdrawn( token=PHAToken, depositer=[Sender] 0x505119164fdb5a6b735c39fa7c21d54da7cf2c3b, amount=100100000000000000000000 )
127 Bridge.Deposit( destinationChainID=3, resourceID=00B14E071DDAD0B12BE5ACA6DFFC5F2584EA158D9B0CE73E1437115E97A32A3E, depositNonce=319 )

Account State Difference:

  Address   Before After State Difference Code
0.797169878876783397 Eth0.797203790876783397 Eth0.000033912
0x50511916...dA7cF2c3b
0.066994627061623246 Eth
Nonce: 56
0.063303634122322606 Eth
Nonce: 57
0.00369099293930064
0x6c5bA916...8C9d52f4E
0x8F92e735...8ea1A2Bb9
(Phala Network: Main Bridge Contract)
0xcd38b15a...55792e257
(Phala Network: ERC20 Bridge Handler)

Execution Trace

Bridge.deposit( destinationChainID=3, resourceID=00B14E071DDAD0B12BE5ACA6DFFC5F2584EA158D9B0CE73E1437115E97A32A3E, data=0x0000000000000000000000000000000000000000000015326E8F3F78599000000000000000000000000000000000000000000000000000000000000000000024000101005A9F6C46FDCE1463EEF2A8D6143B3282C3B6DDAC572D9CDE70BCFCFF27C9B26B )
  • ERC20Handler.deposit( resourceID=00B14E071DDAD0B12BE5ACA6DFFC5F2584EA158D9B0CE73E1437115E97A32A3E, destinationChainID=3, depositNonce=319, depositer=0x505119164FDB5a6b735c39Fa7c21D54dA7cF2c3b, data=0x0000000000000000000000000000000000000000000015326E8F3F78599000000000000000000000000000000000000000000000000000000000000000000024000101005A9F6C46FDCE1463EEF2A8D6143B3282C3B6DDAC572D9CDE70BCFCFF27C9B26B )
    • PHAToken.transferFrom( from=0x505119164FDB5a6b735c39Fa7c21D54dA7cF2c3b, to=0xcd38b15a419491c7c1238B0659f65c755792e257, value=100100000000000000000000 ) => ( True )
      File 1 of 3: Bridge
      pragma solidity 0.7.0;
      pragma experimental ABIEncoderV2;
      import "@openzeppelin/contracts/access/AccessControl.sol";
      import "./utils/Pausable.sol";
      import "./utils/SafeMath.sol";
      import "./interfaces/IDepositExecute.sol";
      import "./interfaces/IBridge.sol";
      import "./interfaces/IERCHandler.sol";
      import "./interfaces/IGenericHandler.sol";
      /**
          @title Facilitates deposits, creation and votiing of deposit proposals, and deposit executions.
          @author ChainSafe Systems.
       */
      contract Bridge is Pausable, AccessControl, SafeMath {
          uint8   public _chainID;
          uint256 public _relayerThreshold;
          uint256 public _totalRelayers;
          uint256 public _totalProposals;
          uint256 public _fee;
          uint256 public _expiry;
          enum Vote {No, Yes}
          enum ProposalStatus {Inactive, Active, Passed, Executed, Cancelled}
          struct Proposal {
              bytes32 _resourceID;
              bytes32 _dataHash;
              address[] _yesVotes;
              address[] _noVotes;
              ProposalStatus _status;
              uint256 _proposedBlock;
          }
          // destinationChainID => number of deposits
          mapping(uint8 => uint64) public _depositCounts;
          // resourceID => handler address
          mapping(bytes32 => address) public _resourceIDToHandlerAddress;
          // depositNonce => destinationChainID => bytes
          mapping(uint64 => mapping(uint8 => bytes)) public _depositRecords;
          // destinationChainID + depositNonce => dataHash => Proposal
          mapping(uint72 => mapping(bytes32 => Proposal)) public _proposals;
          // destinationChainID + depositNonce => dataHash => relayerAddress => bool
          mapping(uint72 => mapping(bytes32 => mapping(address => bool))) public _hasVotedOnProposal;
          event RelayerThresholdChanged(uint indexed newThreshold);
          event RelayerAdded(address indexed relayer);
          event RelayerRemoved(address indexed relayer);
          event Deposit(
              uint8   indexed destinationChainID,
              bytes32 indexed resourceID,
              uint64  indexed depositNonce
          );
          event ProposalEvent(
              uint8           indexed originChainID,
              uint64          indexed depositNonce,
              ProposalStatus  indexed status,
              bytes32 resourceID,
              bytes32 dataHash
          );
          event ProposalVote(
              uint8   indexed originChainID,
              uint64  indexed depositNonce,
              ProposalStatus indexed status,
              bytes32 resourceID
          );
          bytes32 public constant RELAYER_ROLE = keccak256("RELAYER_ROLE");
          modifier onlyAdmin() {
              _onlyAdmin();
              _;
          }
          modifier onlyAdminOrRelayer() {
              _onlyAdminOrRelayer();
              _;
          }
          modifier onlyRelayers() {
              _onlyRelayers();
              _;
          }
          function _onlyAdminOrRelayer() private {
              require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender) || hasRole(RELAYER_ROLE, msg.sender),
                  "sender is not relayer or admin");
          }
          function _onlyAdmin() private {
              require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "sender doesn't have admin role");
          }
          function _onlyRelayers() private {
              require(hasRole(RELAYER_ROLE, msg.sender), "sender doesn't have relayer role");
          }
          /**
              @notice Initializes Bridge, creates and grants {msg.sender} the admin role,
              creates and grants {initialRelayers} the relayer role.
              @param chainID ID of chain the Bridge contract exists on.
              @param initialRelayers Addresses that should be initially granted the relayer role.
              @param initialRelayerThreshold Number of votes needed for a deposit proposal to be considered passed.
           */
          constructor (uint8 chainID, address[] memory initialRelayers, uint initialRelayerThreshold, uint256 fee, uint256 expiry) public {
              _chainID = chainID;
              _relayerThreshold = initialRelayerThreshold;
              _fee = fee;
              _expiry = expiry;
              _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
              _setRoleAdmin(RELAYER_ROLE, DEFAULT_ADMIN_ROLE);
              uint initialRelayerCount = initialRelayers.length;
              for (uint i; i < initialRelayerCount; i++) {
                  grantRole(RELAYER_ROLE, initialRelayers[i]);
                  _totalRelayers++;
              }
          }
          /**
              @notice Returns true if {relayer} has the relayer role.
              @param relayer Address to check.
           */
          function isRelayer(address relayer) external view returns (bool) {
              return hasRole(RELAYER_ROLE, relayer);
          }
          /**
              @notice Removes admin role from {msg.sender} and grants it to {newAdmin}.
              @notice Only callable by an address that currently has the admin role.
              @param newAdmin Address that admin role will be granted to.
           */
          function renounceAdmin(address newAdmin) external onlyAdmin {
              grantRole(DEFAULT_ADMIN_ROLE, newAdmin);
              renounceRole(DEFAULT_ADMIN_ROLE, msg.sender);
          }
          /**
              @notice Pauses deposits, proposal creation and voting, and deposit executions.
              @notice Only callable by an address that currently has the admin role.
           */
          function adminPauseTransfers() external onlyAdmin {
              _pause();
          }
          /**
              @notice Unpauses deposits, proposal creation and voting, and deposit executions.
              @notice Only callable by an address that currently has the admin role.
           */
          function adminUnpauseTransfers() external onlyAdmin {
              _unpause();
          }
          /**
              @notice Modifies the number of votes required for a proposal to be considered passed.
              @notice Only callable by an address that currently has the admin role.
              @param newThreshold Value {_relayerThreshold} will be changed to.
              @notice Emits {RelayerThresholdChanged} event.
           */
          function adminChangeRelayerThreshold(uint newThreshold) external onlyAdmin {
              _relayerThreshold = newThreshold;
              emit RelayerThresholdChanged(newThreshold);
          }
          /**
              @notice Grants {relayerAddress} the relayer role and increases {_totalRelayer} count.
              @notice Only callable by an address that currently has the admin role.
              @param relayerAddress Address of relayer to be added.
              @notice Emits {RelayerAdded} event.
           */
          function adminAddRelayer(address relayerAddress) external onlyAdmin {
              require(!hasRole(RELAYER_ROLE, relayerAddress), "addr already has relayer role!");
              grantRole(RELAYER_ROLE, relayerAddress);
              emit RelayerAdded(relayerAddress);
              _totalRelayers++;
          }
          /**
              @notice Removes relayer role for {relayerAddress} and decreases {_totalRelayer} count.
              @notice Only callable by an address that currently has the admin role.
              @param relayerAddress Address of relayer to be removed.
              @notice Emits {RelayerRemoved} event.
           */
          function adminRemoveRelayer(address relayerAddress) external onlyAdmin {
              require(hasRole(RELAYER_ROLE, relayerAddress), "addr doesn't have relayer role!");
              revokeRole(RELAYER_ROLE, relayerAddress);
              emit RelayerRemoved(relayerAddress);
              _totalRelayers--;
          }
          /**
              @notice Sets a new resource for handler contracts that use the IERCHandler interface,
              and maps the {handlerAddress} to {resourceID} in {_resourceIDToHandlerAddress}.
              @notice Only callable by an address that currently has the admin role.
              @param handlerAddress Address of handler resource will be set for.
              @param resourceID ResourceID to be used when making deposits.
              @param tokenAddress Address of contract to be called when a deposit is made and a deposited is executed.
           */
          function adminSetResource(address handlerAddress, bytes32 resourceID, address tokenAddress) external onlyAdmin {
              _resourceIDToHandlerAddress[resourceID] = handlerAddress;
              IERCHandler handler = IERCHandler(handlerAddress);
              handler.setResource(resourceID, tokenAddress);
          }
          /**
              @notice Sets a new resource for handler contracts that use the IGenericHandler interface,
              and maps the {handlerAddress} to {resourceID} in {_resourceIDToHandlerAddress}.
              @notice Only callable by an address that currently has the admin role.
              @param handlerAddress Address of handler resource will be set for.
              @param resourceID ResourceID to be used when making deposits.
              @param contractAddress Address of contract to be called when a deposit is made and a deposited is executed.
           */
          function adminSetGenericResource(
              address handlerAddress,
              bytes32 resourceID,
              address contractAddress,
              bytes4 depositFunctionSig,
              bytes4 executeFunctionSig
          ) external onlyAdmin {
              _resourceIDToHandlerAddress[resourceID] = handlerAddress;
              IGenericHandler handler = IGenericHandler(handlerAddress);
              handler.setResource(resourceID, contractAddress, depositFunctionSig, executeFunctionSig);
          }
          /**
              @notice Sets a resource as burnable for handler contracts that use the IERCHandler interface.
              @notice Only callable by an address that currently has the admin role.
              @param handlerAddress Address of handler resource will be set for.
              @param tokenAddress Address of contract to be called when a deposit is made and a deposited is executed.
           */
          function adminSetBurnable(address handlerAddress, address tokenAddress) external onlyAdmin {
              IERCHandler handler = IERCHandler(handlerAddress);
              handler.setBurnable(tokenAddress);
          }
          /**
              @notice Sets a resource as burnable for handler contracts that use the IERCHandler interface.
              @notice Only callable by an address that currently has the admin role.
              @param handlerAddress Address of handler resource will be set for.
              @param tokenAddress Address of contract to be called when a deposit is made and a deposited is executed.
           */
          function adminSetDecimals(address handlerAddress, address tokenAddress, uint8 srcDecimals, uint8 destDecimals) external onlyAdmin {
              IERCHandler handler = IERCHandler(handlerAddress);
              handler.setDecimals(tokenAddress, srcDecimals, destDecimals);
          }
          /**
              @notice Sets a initial deposit nonce for dest chan, this is used to migrate to a new bridge contract.
              @param chainId Dest chain id.
              @param depositNonce Initial deposit nonce.
           */
          function adminSetDepositNonce(uint8 chainId, uint64 depositNonce) external onlyAdmin {
              _depositCounts[chainId] = depositNonce;
          }
          /**
              @notice Returns a proposal.
              @param originChainID Chain ID deposit originated from.
              @param depositNonce ID of proposal generated by proposal's origin Bridge contract.
              @param dataHash Hash of data to be provided when deposit proposal is executed.
              @return Proposal which consists of:
              - _dataHash Hash of data to be provided when deposit proposal is executed.
              - _yesVotes Number of votes in favor of proposal.
              - _noVotes Number of votes against proposal.
              - _status Current status of proposal.
           */
          function getProposal(uint8 originChainID, uint64 depositNonce, bytes32 dataHash) external view returns (Proposal memory) {
              uint72 nonceAndID = (uint72(depositNonce) << 8) | uint72(originChainID);
              return _proposals[nonceAndID][dataHash];
          }
          /**
              @notice Changes deposit fee.
              @notice Only callable by admin.
              @param newFee Value {_fee} will be updated to.
           */
          function adminChangeFee(uint newFee) external onlyAdmin {
              require(_fee != newFee, "Current fee is equal to new fee");
              _fee = newFee;
          }
          /**
              @notice Used to manually withdraw funds from ERC safes.
              @param handlerAddress Address of handler to withdraw from.
              @param tokenAddress Address of token to withdraw.
              @param recipient Address to withdraw tokens to.
              @param amountOrTokenID Either the amount of ERC20 tokens or the ERC721 token ID to withdraw.
           */
          function adminWithdraw(
              address handlerAddress,
              address tokenAddress,
              address recipient,
              uint256 amountOrTokenID
          ) external onlyAdmin {
              IERCHandler handler = IERCHandler(handlerAddress);
              handler.withdraw(tokenAddress, recipient, amountOrTokenID);
          }
          /**
              @notice Initiates a transfer using a specified handler contract.
              @notice Only callable when Bridge is not paused.
              @param destinationChainID ID of chain deposit will be bridged to.
              @param resourceID ResourceID used to find address of handler to be used for deposit.
              @param data Additional data to be passed to specified handler.
              @notice Emits {Deposit} event.
           */
          function deposit(uint8 destinationChainID, bytes32 resourceID, bytes calldata data) external payable whenNotPaused {
              require(msg.value == _fee, "Incorrect fee supplied");
              address handler = _resourceIDToHandlerAddress[resourceID];
              require(handler != address(0), "resourceID not mapped to handler");
              uint64 depositNonce = ++_depositCounts[destinationChainID];
              _depositRecords[depositNonce][destinationChainID] = data;
              IDepositExecute depositHandler = IDepositExecute(handler);
              depositHandler.deposit(resourceID, destinationChainID, depositNonce, msg.sender, data);
              emit Deposit(destinationChainID, resourceID, depositNonce);
          }
          /**
              @notice When called, {msg.sender} will be marked as voting in favor of proposal.
              @notice Only callable by relayers when Bridge is not paused.
              @param chainID ID of chain deposit originated from.
              @param depositNonce ID of deposited generated by origin Bridge contract.
              @param dataHash Hash of data provided when deposit was made.
              @notice Proposal must not have already been passed or executed.
              @notice {msg.sender} must not have already voted on proposal.
              @notice Emits {ProposalEvent} event with status indicating the proposal status.
              @notice Emits {ProposalVote} event.
           */
          function voteProposal(uint8 chainID, uint64 depositNonce, bytes32 resourceID, bytes32 dataHash) external onlyRelayers whenNotPaused {
              uint72 nonceAndID = (uint72(depositNonce) << 8) | uint72(chainID);
              Proposal storage proposal = _proposals[nonceAndID][dataHash];
              require(_resourceIDToHandlerAddress[resourceID] != address(0), "no handler for resourceID");
              require(uint(proposal._status) <= 1, "proposal already passed/executed/cancelled");
              require(!_hasVotedOnProposal[nonceAndID][dataHash][msg.sender], "relayer already voted");
              if (uint(proposal._status) == 0) {
                  ++_totalProposals;
                  _proposals[nonceAndID][dataHash] = Proposal({
                      _resourceID : resourceID,
                      _dataHash : dataHash,
                      _yesVotes : new address[](1),
                      _noVotes : new address[](0),
                      _status : ProposalStatus.Active,
                      _proposedBlock : block.number
                      });
                  proposal._yesVotes[0] = msg.sender;
                  emit ProposalEvent(chainID, depositNonce, ProposalStatus.Active, resourceID, dataHash);
              } else {
                  if (sub(block.number, proposal._proposedBlock) > _expiry) {
                      // if the number of blocks that has passed since this proposal was
                      // submitted exceeds the expiry threshold set, cancel the proposal
                      proposal._status = ProposalStatus.Cancelled;
                      emit ProposalEvent(chainID, depositNonce, ProposalStatus.Cancelled, resourceID, dataHash);
                  } else {
                      require(dataHash == proposal._dataHash, "datahash mismatch");
                      proposal._yesVotes.push(msg.sender);
                  }
              }
              if (proposal._status != ProposalStatus.Cancelled) {
                  _hasVotedOnProposal[nonceAndID][dataHash][msg.sender] = true;
                  emit ProposalVote(chainID, depositNonce, proposal._status, resourceID);
                  // If _depositThreshold is set to 1, then auto finalize
                  // or if _relayerThreshold has been exceeded
                  if (_relayerThreshold <= 1 || proposal._yesVotes.length >= _relayerThreshold) {
                      proposal._status = ProposalStatus.Passed;
                      emit ProposalEvent(chainID, depositNonce, ProposalStatus.Passed, resourceID, dataHash);
                  }
              }
          }
          /**
              @notice Executes a deposit proposal that is considered passed using a specified handler contract.
              @notice Only callable by relayers when Bridge is not paused.
              @param chainID ID of chain deposit originated from.
              @param depositNonce ID of deposited generated by origin Bridge contract.
              @param dataHash Hash of data originally provided when deposit was made.
              @notice Proposal must be past expiry threshold.
              @notice Emits {ProposalEvent} event with status {Cancelled}.
           */
          function cancelProposal(uint8 chainID, uint64 depositNonce, bytes32 dataHash) public onlyAdminOrRelayer {
              uint72 nonceAndID = (uint72(depositNonce) << 8) | uint72(chainID);
              Proposal storage proposal = _proposals[nonceAndID][dataHash];
              require(proposal._status != ProposalStatus.Cancelled, "Proposal already cancelled");
              require(sub(block.number, proposal._proposedBlock) > _expiry, "Proposal not at expiry threshold");
              proposal._status = ProposalStatus.Cancelled;
              emit ProposalEvent(chainID, depositNonce, ProposalStatus.Cancelled, proposal._resourceID, proposal._dataHash);
          }
          /**
              @notice Executes a deposit proposal that is considered passed using a specified handler contract.
              @notice Only callable by relayers when Bridge is not paused.
              @param chainID ID of chain deposit originated from.
              @param resourceID ResourceID to be used when making deposits.
              @param depositNonce ID of deposited generated by origin Bridge contract.
              @param data Data originally provided when deposit was made.
              @notice Proposal must have Passed status.
              @notice Hash of {data} must equal proposal's {dataHash}.
              @notice Emits {ProposalEvent} event with status {Executed}.
           */
          function executeProposal(uint8 chainID, uint64 depositNonce, bytes calldata data, bytes32 resourceID) external onlyRelayers whenNotPaused {
              address handler = _resourceIDToHandlerAddress[resourceID];
              uint72 nonceAndID = (uint72(depositNonce) << 8) | uint72(chainID);
              bytes32 dataHash = keccak256(abi.encodePacked(handler, data));
              Proposal storage proposal = _proposals[nonceAndID][dataHash];
              require(proposal._status == ProposalStatus.Passed, "proposal already transferred");
              require(dataHash == proposal._dataHash, "data doesn't match datahash");
              proposal._status = ProposalStatus.Executed;
              IDepositExecute depositHandler = IDepositExecute(_resourceIDToHandlerAddress[proposal._resourceID]);
              depositHandler.executeProposal(proposal._resourceID, data);
              emit ProposalEvent(chainID, depositNonce, proposal._status, proposal._resourceID, proposal._dataHash);
          }
          /**
              @notice Transfers eth in the contract to the specified addresses. The parameters addrs and amounts are mapped 1-1.
              This means that the address at index 0 for addrs will receive the amount (in WEI) from amounts at index 0.
              @param addrs Array of addresses to transfer {amounts} to.
              @param amounts Array of amonuts to transfer to {addrs}.
           */
          function transferFunds(address payable[] calldata addrs, uint[] calldata amounts) external onlyAdmin {
              require(addrs.length == amounts.length, "length of address and amounts dismatch");
              uint addrCount = addrs.length;
              for (uint i = 0; i < addrCount; i++) {
                  addrs[i].transfer(amounts[i]);
              }
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity >=0.6.0 <0.8.0;
      /**
       * @dev Library for managing
       * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
       * types.
       *
       * Sets have the following properties:
       *
       * - Elements are added, removed, and checked for existence in constant time
       * (O(1)).
       * - Elements are enumerated in O(n). No guarantees are made on the ordering.
       *
       * ```
       * contract Example {
       *     // Add the library methods
       *     using EnumerableSet for EnumerableSet.AddressSet;
       *
       *     // Declare a set state variable
       *     EnumerableSet.AddressSet private mySet;
       * }
       * ```
       *
       * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
       * and `uint256` (`UintSet`) are supported.
       */
      library EnumerableSet {
          // To implement this library for multiple types with as little code
          // repetition as possible, we write it in terms of a generic Set type with
          // bytes32 values.
          // The Set implementation uses private functions, and user-facing
          // implementations (such as AddressSet) are just wrappers around the
          // underlying Set.
          // This means that we can only create new EnumerableSets for types that fit
          // in bytes32.
          struct Set {
              // Storage of set values
              bytes32[] _values;
              // Position of the value in the `values` array, plus 1 because index 0
              // means a value is not in the set.
              mapping (bytes32 => uint256) _indexes;
          }
          /**
           * @dev Add a value to a set. O(1).
           *
           * Returns true if the value was added to the set, that is if it was not
           * already present.
           */
          function _add(Set storage set, bytes32 value) private returns (bool) {
              if (!_contains(set, value)) {
                  set._values.push(value);
                  // The value is stored at length-1, but we add 1 to all indexes
                  // and use 0 as a sentinel value
                  set._indexes[value] = set._values.length;
                  return true;
              } else {
                  return false;
              }
          }
          /**
           * @dev Removes a value from a set. O(1).
           *
           * Returns true if the value was removed from the set, that is if it was
           * present.
           */
          function _remove(Set storage set, bytes32 value) private returns (bool) {
              // We read and store the value's index to prevent multiple reads from the same storage slot
              uint256 valueIndex = set._indexes[value];
              if (valueIndex != 0) { // Equivalent to contains(set, value)
                  // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
                  // the array, and then remove the last element (sometimes called as 'swap and pop').
                  // This modifies the order of the array, as noted in {at}.
                  uint256 toDeleteIndex = valueIndex - 1;
                  uint256 lastIndex = set._values.length - 1;
                  // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
                  // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.
                  bytes32 lastvalue = set._values[lastIndex];
                  // Move the last value to the index where the value to delete is
                  set._values[toDeleteIndex] = lastvalue;
                  // Update the index for the moved value
                  set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based
                  // Delete the slot where the moved value was stored
                  set._values.pop();
                  // Delete the index for the deleted slot
                  delete set._indexes[value];
                  return true;
              } else {
                  return false;
              }
          }
          /**
           * @dev Returns true if the value is in the set. O(1).
           */
          function _contains(Set storage set, bytes32 value) private view returns (bool) {
              return set._indexes[value] != 0;
          }
          /**
           * @dev Returns the number of values on the set. O(1).
           */
          function _length(Set storage set) private view returns (uint256) {
              return set._values.length;
          }
         /**
          * @dev Returns the value stored at position `index` in the set. O(1).
          *
          * Note that there are no guarantees on the ordering of values inside the
          * array, and it may change when more values are added or removed.
          *
          * Requirements:
          *
          * - `index` must be strictly less than {length}.
          */
          function _at(Set storage set, uint256 index) private view returns (bytes32) {
              require(set._values.length > index, "EnumerableSet: index out of bounds");
              return set._values[index];
          }
          // Bytes32Set
          struct Bytes32Set {
              Set _inner;
          }
          /**
           * @dev Add a value to a set. O(1).
           *
           * Returns true if the value was added to the set, that is if it was not
           * already present.
           */
          function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
              return _add(set._inner, value);
          }
          /**
           * @dev Removes a value from a set. O(1).
           *
           * Returns true if the value was removed from the set, that is if it was
           * present.
           */
          function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
              return _remove(set._inner, value);
          }
          /**
           * @dev Returns true if the value is in the set. O(1).
           */
          function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
              return _contains(set._inner, value);
          }
          /**
           * @dev Returns the number of values in the set. O(1).
           */
          function length(Bytes32Set storage set) internal view returns (uint256) {
              return _length(set._inner);
          }
         /**
          * @dev Returns the value stored at position `index` in the set. O(1).
          *
          * Note that there are no guarantees on the ordering of values inside the
          * array, and it may change when more values are added or removed.
          *
          * Requirements:
          *
          * - `index` must be strictly less than {length}.
          */
          function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
              return _at(set._inner, index);
          }
          // AddressSet
          struct AddressSet {
              Set _inner;
          }
          /**
           * @dev Add a value to a set. O(1).
           *
           * Returns true if the value was added to the set, that is if it was not
           * already present.
           */
          function add(AddressSet storage set, address value) internal returns (bool) {
              return _add(set._inner, bytes32(uint256(uint160(value))));
          }
          /**
           * @dev Removes a value from a set. O(1).
           *
           * Returns true if the value was removed from the set, that is if it was
           * present.
           */
          function remove(AddressSet storage set, address value) internal returns (bool) {
              return _remove(set._inner, bytes32(uint256(uint160(value))));
          }
          /**
           * @dev Returns true if the value is in the set. O(1).
           */
          function contains(AddressSet storage set, address value) internal view returns (bool) {
              return _contains(set._inner, bytes32(uint256(uint160(value))));
          }
          /**
           * @dev Returns the number of values in the set. O(1).
           */
          function length(AddressSet storage set) internal view returns (uint256) {
              return _length(set._inner);
          }
         /**
          * @dev Returns the value stored at position `index` in the set. O(1).
          *
          * Note that there are no guarantees on the ordering of values inside the
          * array, and it may change when more values are added or removed.
          *
          * Requirements:
          *
          * - `index` must be strictly less than {length}.
          */
          function at(AddressSet storage set, uint256 index) internal view returns (address) {
              return address(uint160(uint256(_at(set._inner, index))));
          }
          // UintSet
          struct UintSet {
              Set _inner;
          }
          /**
           * @dev Add a value to a set. O(1).
           *
           * Returns true if the value was added to the set, that is if it was not
           * already present.
           */
          function add(UintSet storage set, uint256 value) internal returns (bool) {
              return _add(set._inner, bytes32(value));
          }
          /**
           * @dev Removes a value from a set. O(1).
           *
           * Returns true if the value was removed from the set, that is if it was
           * present.
           */
          function remove(UintSet storage set, uint256 value) internal returns (bool) {
              return _remove(set._inner, bytes32(value));
          }
          /**
           * @dev Returns true if the value is in the set. O(1).
           */
          function contains(UintSet storage set, uint256 value) internal view returns (bool) {
              return _contains(set._inner, bytes32(value));
          }
          /**
           * @dev Returns the number of values on the set. O(1).
           */
          function length(UintSet storage set) internal view returns (uint256) {
              return _length(set._inner);
          }
         /**
          * @dev Returns the value stored at position `index` in the set. O(1).
          *
          * Note that there are no guarantees on the ordering of values inside the
          * array, and it may change when more values are added or removed.
          *
          * Requirements:
          *
          * - `index` must be strictly less than {length}.
          */
          function at(UintSet storage set, uint256 index) internal view returns (uint256) {
              return uint256(_at(set._inner, index));
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity >=0.6.0 <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 GSN meta-transactions the account sending and
       * paying for execution may not be the actual sender (as far as an application
       * is concerned).
       *
       * This contract is only required for intermediate, library-like contracts.
       */
      abstract contract Context {
          function _msgSender() internal view virtual returns (address payable) {
              return msg.sender;
          }
          function _msgData() internal view virtual returns (bytes memory) {
              this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
              return msg.data;
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity >=0.6.2 <0.8.0;
      /**
       * @dev Collection of functions related to the address type
       */
      library Address {
          /**
           * @dev Returns true if `account` is a contract.
           *
           * [IMPORTANT]
           * ====
           * It is unsafe to assume that an address for which this function returns
           * false is an externally-owned account (EOA) and not a contract.
           *
           * Among others, `isContract` will return false for the following
           * types of addresses:
           *
           *  - an externally-owned account
           *  - a contract in construction
           *  - an address where a contract will be created
           *  - an address where a contract lived, but was destroyed
           * ====
           */
          function isContract(address account) internal view returns (bool) {
              // This method relies on extcodesize, which returns 0 for contracts in
              // construction, since the code is only stored at the end of the
              // constructor execution.
              uint256 size;
              // solhint-disable-next-line no-inline-assembly
              assembly { size := extcodesize(account) }
              return size > 0;
          }
          /**
           * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
           * `recipient`, forwarding all available gas and reverting on errors.
           *
           * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
           * of certain opcodes, possibly making contracts go over the 2300 gas limit
           * imposed by `transfer`, making them unable to receive funds via
           * `transfer`. {sendValue} removes this limitation.
           *
           * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
           *
           * IMPORTANT: because control is transferred to `recipient`, care must be
           * taken to not create reentrancy vulnerabilities. Consider using
           * {ReentrancyGuard} or the
           * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
           */
          function sendValue(address payable recipient, uint256 amount) internal {
              require(address(this).balance >= amount, "Address: insufficient balance");
              // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
              (bool success, ) = recipient.call{ value: amount }("");
              require(success, "Address: unable to send value, recipient may have reverted");
          }
          /**
           * @dev Performs a Solidity function call using a low level `call`. A
           * plain`call` is an unsafe replacement for a function call: use this
           * function instead.
           *
           * If `target` reverts with a revert reason, it is bubbled up by this
           * function (like regular Solidity function calls).
           *
           * Returns the raw returned data. To convert to the expected return value,
           * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
           *
           * Requirements:
           *
           * - `target` must be a contract.
           * - calling `target` with `data` must not revert.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCall(target, data, "Address: low-level call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
           * `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
              return functionCallWithValue(target, data, 0, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but also transferring `value` wei to `target`.
           *
           * Requirements:
           *
           * - the calling contract must have an ETH balance of at least `value`.
           * - the called Solidity function must be `payable`.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
              return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
          }
          /**
           * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
           * with `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
              require(address(this).balance >= value, "Address: insufficient balance for call");
              require(isContract(target), "Address: call to non-contract");
              // solhint-disable-next-line avoid-low-level-calls
              (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");
              // solhint-disable-next-line avoid-low-level-calls
              (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");
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = target.delegatecall(data);
              return _verifyCallResult(success, returndata, errorMessage);
          }
          function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private 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
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          let returndata_size := mload(returndata)
                          revert(add(32, returndata), returndata_size)
                      }
                  } else {
                      revert(errorMessage);
                  }
              }
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity >=0.6.0 <0.8.0;
      import "../utils/EnumerableSet.sol";
      import "../utils/Address.sol";
      import "../utils/Context.sol";
      /**
       * @dev Contract module that allows children to implement role-based access
       * control mechanisms.
       *
       * Roles are referred to by their `bytes32` identifier. These should be exposed
       * in the external API and be unique. The best way to achieve this is by
       * using `public constant` hash digests:
       *
       * ```
       * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
       * ```
       *
       * Roles can be used to represent a set of permissions. To restrict access to a
       * function call, use {hasRole}:
       *
       * ```
       * function foo() public {
       *     require(hasRole(MY_ROLE, msg.sender));
       *     ...
       * }
       * ```
       *
       * Roles can be granted and revoked dynamically via the {grantRole} and
       * {revokeRole} functions. Each role has an associated admin role, and only
       * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
       *
       * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
       * that only accounts with this role will be able to grant or revoke other
       * roles. More complex role relationships can be created by using
       * {_setRoleAdmin}.
       *
       * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
       * grant and revoke this role. Extra precautions should be taken to secure
       * accounts that have been granted it.
       */
      abstract contract AccessControl is Context {
          using EnumerableSet for EnumerableSet.AddressSet;
          using Address for address;
          struct RoleData {
              EnumerableSet.AddressSet members;
              bytes32 adminRole;
          }
          mapping (bytes32 => RoleData) private _roles;
          bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
          /**
           * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
           *
           * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
           * {RoleAdminChanged} not being emitted signaling this.
           *
           * _Available since v3.1._
           */
          event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
          /**
           * @dev Emitted when `account` is granted `role`.
           *
           * `sender` is the account that originated the contract call, an admin role
           * bearer except when using {_setupRole}.
           */
          event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
          /**
           * @dev Emitted when `account` is revoked `role`.
           *
           * `sender` is the account that originated the contract call:
           *   - if using `revokeRole`, it is the admin role bearer
           *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
           */
          event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
          /**
           * @dev Returns `true` if `account` has been granted `role`.
           */
          function hasRole(bytes32 role, address account) public view returns (bool) {
              return _roles[role].members.contains(account);
          }
          /**
           * @dev Returns the number of accounts that have `role`. Can be used
           * together with {getRoleMember} to enumerate all bearers of a role.
           */
          function getRoleMemberCount(bytes32 role) public view returns (uint256) {
              return _roles[role].members.length();
          }
          /**
           * @dev Returns one of the accounts that have `role`. `index` must be a
           * value between 0 and {getRoleMemberCount}, non-inclusive.
           *
           * Role bearers are not sorted in any particular way, and their ordering may
           * change at any point.
           *
           * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
           * you perform all queries on the same block. See the following
           * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
           * for more information.
           */
          function getRoleMember(bytes32 role, uint256 index) public view returns (address) {
              return _roles[role].members.at(index);
          }
          /**
           * @dev Returns the admin role that controls `role`. See {grantRole} and
           * {revokeRole}.
           *
           * To change a role's admin, use {_setRoleAdmin}.
           */
          function getRoleAdmin(bytes32 role) public view returns (bytes32) {
              return _roles[role].adminRole;
          }
          /**
           * @dev Grants `role` to `account`.
           *
           * If `account` had not been already granted `role`, emits a {RoleGranted}
           * event.
           *
           * Requirements:
           *
           * - the caller must have ``role``'s admin role.
           */
          function grantRole(bytes32 role, address account) public virtual {
              require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to grant");
              _grantRole(role, account);
          }
          /**
           * @dev Revokes `role` from `account`.
           *
           * If `account` had been granted `role`, emits a {RoleRevoked} event.
           *
           * Requirements:
           *
           * - the caller must have ``role``'s admin role.
           */
          function revokeRole(bytes32 role, address account) public virtual {
              require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to revoke");
              _revokeRole(role, account);
          }
          /**
           * @dev Revokes `role` from the calling account.
           *
           * Roles are often managed via {grantRole} and {revokeRole}: this function's
           * purpose is to provide a mechanism for accounts to lose their privileges
           * if they are compromised (such as when a trusted device is misplaced).
           *
           * If the calling account had been granted `role`, emits a {RoleRevoked}
           * event.
           *
           * Requirements:
           *
           * - the caller must be `account`.
           */
          function renounceRole(bytes32 role, address account) public virtual {
              require(account == _msgSender(), "AccessControl: can only renounce roles for self");
              _revokeRole(role, account);
          }
          /**
           * @dev Grants `role` to `account`.
           *
           * If `account` had not been already granted `role`, emits a {RoleGranted}
           * event. Note that unlike {grantRole}, this function doesn't perform any
           * checks on the calling account.
           *
           * [WARNING]
           * ====
           * This function should only be called from the constructor when setting
           * up the initial roles for the system.
           *
           * Using this function in any other way is effectively circumventing the admin
           * system imposed by {AccessControl}.
           * ====
           */
          function _setupRole(bytes32 role, address account) internal virtual {
              _grantRole(role, account);
          }
          /**
           * @dev Sets `adminRole` as ``role``'s admin role.
           *
           * Emits a {RoleAdminChanged} event.
           */
          function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
              emit RoleAdminChanged(role, _roles[role].adminRole, adminRole);
              _roles[role].adminRole = adminRole;
          }
          function _grantRole(bytes32 role, address account) private {
              if (_roles[role].members.add(account)) {
                  emit RoleGranted(role, account, _msgSender());
              }
          }
          function _revokeRole(bytes32 role, address account) private {
              if (_roles[role].members.remove(account)) {
                  emit RoleRevoked(role, account, _msgSender());
              }
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.7.0;
      /**
       * @dev Wrappers over Solidity's arithmetic operations with added overflow
       * checks.
       *
       * note that this is a stripped down version of open zeppelin's safemath
       * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/math/SafeMath.sol
       */
      contract SafeMath {
          /**
           * @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;
          }
      }// SPDX-License-Identifier: MIT
      pragma solidity 0.7.0;
      /**
       * @dev Contract module which allows children to implement an emergency stop
       * mechanism that can be triggered by an authorized account.
       *
       * This is a stripped down version of Open zeppelin's Pausable contract.
       * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/EnumerableSet.sol
       *
       */
      contract Pausable {
          /**
           * @dev Emitted when the pause is triggered by `account`.
           */
          event Paused(address account);
          /**
           * @dev Emitted when the pause is lifted by `account`.
           */
          event Unpaused(address account);
          bool private _paused;
          /**
           * @dev Initializes the contract in unpaused state.
           */
          constructor () {
              _paused = false;
          }
          /**
           * @dev Returns true if the contract is paused, and false otherwise.
           */
          function paused() public view returns (bool) {
              return _paused;
          }
          /**
           * @dev Modifier to make a function callable only when the contract is not paused.
           *
           * Requirements:
           *
           * - The contract must not be paused.
           */
          modifier whenNotPaused() {
              _whenNotPaused();
              _;
          }
          function _whenNotPaused() private view {
              require(!_paused, "Pausable: paused");
          }
          /**
           * @dev Modifier to make a function callable only when the contract is not paused.
           *
           * Requirements:
           *
           * - The contract must not be paused.
           */
          modifier whenPaused() {
              _whenPaused();
              _;
          }
          function _whenPaused() private view {
              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(msg.sender);
          }
          /**
           * @dev Returns to normal state.
           *
           * Requirements:
           *
           * - The contract must be paused.
           */
          function _unpause() internal virtual whenPaused {
              _paused = false;
              emit Unpaused(msg.sender);
          }
      }pragma solidity 0.7.0;
      /**
          @title Interface for handler that handles generic deposits and deposit executions.
          @author ChainSafe Systems.
       */
      interface IGenericHandler {
          /**
              @notice Correlates {resourceID} with {contractAddress}, {depositFunctionSig}, and {executeFunctionSig}.
              @param resourceID ResourceID to be used when making deposits.
              @param contractAddress Address of contract to be called when a deposit is made and a deposited is executed.
              @param depositFunctionSig Function signature of method to be called in {contractAddress} when a deposit is made.
              @param executeFunctionSig Function signature of method to be called in {contractAddress} when a deposit is executed.
           */
          function setResource(bytes32 resourceID, address contractAddress, bytes4 depositFunctionSig, bytes4 executeFunctionSig) external;
      }pragma solidity 0.7.0;
      /**
          @title Interface to be used with handlers that support ERC20s and ERC721s.
          @author ChainSafe Systems.
       */
      interface IERCHandler {
          /**
              @notice Correlates {resourceID} with {contractAddress}.
              @param resourceID ResourceID to be used when making deposits.
              @param contractAddress Address of contract to be called when a deposit is made and a deposited is executed.
           */
          function setResource(bytes32 resourceID, address contractAddress) external;
          /**
              @notice Marks {contractAddress} as mintable/burnable.
              @param contractAddress Address of contract to be used when making or executing deposits.
           */
          function setBurnable(address contractAddress) external;
          /**
              @notice Set {contractAddress} decimals on dest chain.
              @param contractAddress Address of contract to be used when making or executing deposits.
              @param srcDecimals Decimals of this token on source chain
              @param destDecimals Decimals of this token on dest chain.
           */
          function setDecimals(address contractAddress, uint8 srcDecimals, uint8 destDecimals) external;
          /**
              @notice Used to manually release funds from ERC safes.
              @param tokenAddress Address of token contract to release.
              @param recipient Address to release tokens to.
              @param amountOrTokenID Either the amount of ERC20 tokens or the ERC721 token ID to release.
           */
          function withdraw(address tokenAddress, address recipient, uint256 amountOrTokenID) external;
      }
      pragma solidity 0.7.0;
      /**
          @title Interface for handler contracts that support deposits and deposit executions.
          @author ChainSafe Systems.
       */
      interface IDepositExecute {
          /**
              @notice It is intended that deposit are made using the Bridge contract.
              @param destinationChainID Chain ID deposit is expected to be bridged to.
              @param depositNonce This value is generated as an ID by the Bridge contract.
              @param depositer Address of account making the deposit in the Bridge contract.
              @param data Consists of additional data needed for a specific deposit.
           */
          function deposit(bytes32 resourceID, uint8 destinationChainID, uint64 depositNonce, address depositer, bytes calldata data) external;
          /**
              @notice It is intended that proposals are executed by the Bridge contract.
              @param data Consists of additional data needed for a specific deposit execution.
           */
          function executeProposal(bytes32 resourceID, bytes calldata data) external;
      }
      pragma solidity 0.7.0;
      /**
          @title Interface for Bridge contract.
          @author ChainSafe Systems.
       */
      interface IBridge {
          /**
              @notice Exposing getter for {_chainID} instead of forcing the use of call.
              @return uint8 The {_chainID} that is currently set for the Bridge contract.
           */
          function _chainID() external returns (uint8);
      }

      File 2 of 3: ERC20Handler
      pragma solidity 0.7.0;
      pragma experimental ABIEncoderV2;
      import "../interfaces/IDepositExecute.sol";
      import "./HandlerHelpers.sol";
      import "../ERC20Safe.sol";
      import "@openzeppelin/contracts/presets/ERC20PresetMinterPauser.sol";
      import "@openzeppelin/contracts/math/SafeMath.sol";
      /**
          @title Handles ERC20 deposits and deposit executions.
          @author ChainSafe Systems.
          @notice This contract is intended to be used with the Bridge contract.
       */
      contract ERC20Handler is IDepositExecute, HandlerHelpers, ERC20Safe {
          using SafeMath for uint256;
          struct DepositRecord {
              address _tokenAddress;
              uint8    _lenDestinationRecipientAddress;
              uint8   _destinationChainID;
              bytes32 _resourceID;
              bytes   _destinationRecipientAddress;
              address _depositer;
              uint    _amount;
          }
          // depositNonce => Deposit Record
          mapping (uint8 => mapping(uint64 => DepositRecord)) public _depositRecords;
          event Deposited(address indexed token, address indexed recipient, uint256 amount);
          event Withdrawn(address indexed token, address indexed depositer, uint256 amount);
          /**
              @param bridgeAddress Contract address of previously deployed Bridge.
              @param initialResourceIDs Resource IDs are used to identify a specific contract address.
              These are the Resource IDs this contract will initially support.
              @param initialContractAddresses These are the addresses the {initialResourceIDs} will point to, and are the contracts that will be
              called to perform various deposit calls.
              @param burnableContractAddresses These addresses will be set as burnable and when {deposit} is called, the deposited token will be burned.
              When {executeProposal} is called, new tokens will be minted.
              @dev {initialResourceIDs} and {initialContractAddresses} must have the same length (one resourceID for every address).
              Also, these arrays must be ordered in the way that {initialResourceIDs}[0] is the intended resourceID for {initialContractAddresses}[0].
           */
          constructor(
              address          bridgeAddress,
              bytes32[] memory initialResourceIDs,
              address[] memory initialContractAddresses,
              address[] memory burnableContractAddresses
          ) public {
              require(initialResourceIDs.length == initialContractAddresses.length,
                  "initialResourceIDs and initialContractAddresses len mismatch");
              _bridgeAddress = bridgeAddress;
              uint256 initialCount = initialResourceIDs.length;
              for (uint256 i = 0; i < initialCount; i++) {
                  _setResource(initialResourceIDs[i], initialContractAddresses[i]);
              }
              uint256 burnableCount = burnableContractAddresses.length;
              for (uint256 i = 0; i < burnableCount; i++) {
                  _setBurnable(burnableContractAddresses[i]);
              }
          }
          /**
              @param depositNonce This ID will have been generated by the Bridge contract.
              @param destId ID of chain deposit will be bridged to.
              @return DepositRecord which consists of:
              - _tokenAddress Address used when {deposit} was executed.
              - _destinationChainID ChainID deposited tokens are intended to end up on.
              - _resourceID ResourceID used when {deposit} was executed.
              - _lenDestinationRecipientAddress Used to parse recipient's address from {_destinationRecipientAddress}
              - _destinationRecipientAddress Address tokens are intended to be deposited to on desitnation chain.
              - _depositer Address that initially called {deposit} in the Bridge contract.
              - _amount Amount of tokens that were deposited.
          */
          function getDepositRecord(uint64 depositNonce, uint8 destId) external view returns (DepositRecord memory) {
              return _depositRecords[destId][depositNonce];
          }
          /**
              @notice A deposit is initiatied by making a deposit in the Bridge contract.
              @param destinationChainID Chain ID of chain tokens are expected to be bridged to.
              @param depositNonce This value is generated as an ID by the Bridge contract.
              @param depositer Address of account making the deposit in the Bridge contract.
              @param data Consists of: {resourceID}, {amount}, {lenRecipientAddress}, and {recipientAddress}
              all padded to 32 bytes.
              @notice Data passed into the function should be constructed as follows:
              amount                      uint256     bytes   0 - 32
              recipientAddress length     uint256     bytes  32 - 64
              recipientAddress            bytes       bytes  64 - END
              @dev Depending if the corresponding {tokenAddress} for the parsed {resourceID} is
              marked true in {_burnList}, deposited tokens will be burned, if not, they will be locked.
           */
          function deposit(
              bytes32 resourceID,
              uint8   destinationChainID,
              uint64  depositNonce,
              address depositer,
              bytes   calldata data
          ) external override onlyBridge {
              bytes   memory recipientAddress;
              uint256        amount;
              uint256        lenRecipientAddress;
              assembly {
                  amount := calldataload(0xC4)
                  recipientAddress := mload(0x40)
                  lenRecipientAddress := calldataload(0xE4)
                  mstore(0x40, add(0x20, add(recipientAddress, lenRecipientAddress)))
                  calldatacopy(
                      recipientAddress, // copy to destinationRecipientAddress
                      0xE4, // copy from calldata @ 0x104
                      sub(calldatasize(), 0xE4) // copy size (calldatasize - 0x104)
                  )
              }
              address tokenAddress = _resourceIDToTokenContractAddress[resourceID];
              require(_contractWhitelist[tokenAddress], "provided tokenAddress is not whitelisted");
              if (_burnList[tokenAddress]) {
                  burnERC20(tokenAddress, depositer, amount);
              } else {
                  lockERC20(tokenAddress, depositer, address(this), amount);
              }
              _depositRecords[destinationChainID][depositNonce] = DepositRecord(
                  tokenAddress,
                  uint8(lenRecipientAddress),
                  destinationChainID,
                  resourceID,
                  recipientAddress,
                  depositer,
                  toDestBalance(tokenAddress, amount)
              );
              emit Withdrawn(tokenAddress, depositer, amount);
          }
          /**
              @notice Proposal execution should be initiated when a proposal is finalized in the Bridge contract.
              by a relayer on the deposit's destination chain.
              @param data Consists of {resourceID}, {amount}, {lenDestinationRecipientAddress},
              and {destinationRecipientAddress} all padded to 32 bytes.
              @notice Data passed into the function should be constructed as follows:
              amount                                 uint256     bytes  0 - 32
              destinationRecipientAddress length     uint256     bytes  32 - 64
              destinationRecipientAddress            bytes       bytes  64 - END
           */
          function executeProposal(bytes32 resourceID, bytes calldata data) external override onlyBridge {
              uint256       amount;
              bytes  memory destinationRecipientAddress;
              assembly {
                  amount := calldataload(0x64)
                  destinationRecipientAddress := mload(0x40)
                  let lenDestinationRecipientAddress := calldataload(0x84)
                  mstore(0x40, add(0x20, add(destinationRecipientAddress, lenDestinationRecipientAddress)))
                  // in the calldata the destinationRecipientAddress is stored at 0xC4 after accounting for the function signature and length declaration
                  calldatacopy(
                      destinationRecipientAddress, // copy to destinationRecipientAddress
                      0x84, // copy from calldata @ 0x84
                      sub(calldatasize(), 0x84) // copy size to the end of calldata
                  )
              }
              bytes20 recipientAddress;
              address tokenAddress = _resourceIDToTokenContractAddress[resourceID];
              assembly {
                  recipientAddress := mload(add(destinationRecipientAddress, 0x20))
              }
              require(_contractWhitelist[tokenAddress], "provided tokenAddress is not whitelisted");
              if (_burnList[tokenAddress]) {
                  mintERC20(tokenAddress, address(recipientAddress), toSrcBalance(tokenAddress, amount));
              } else {
                  releaseERC20(tokenAddress, address(recipientAddress), toSrcBalance(tokenAddress, amount));
              }
              emit Deposited(tokenAddress, address(recipientAddress), toSrcBalance(tokenAddress, amount));
          }
          /**
              @notice Used to manually release ERC20 tokens from ERC20Safe.
              @param tokenAddress Address of token contract to release.
              @param recipient Address to release tokens to.
              @param amount The amount of ERC20 tokens to release.
           */
          function withdraw(address tokenAddress, address recipient, uint amount) external override onlyBridge {
              releaseERC20(tokenAddress, recipient, amount);
          }
          function toDestBalance(address tokenAddress, uint256 amount) internal returns(uint256) {
              Decimals memory decimals = _decimals[tokenAddress];
              // We are not allowed decimals not set or not set correctly
              require(decimals.srcDecimals != 0 && decimals.destDecimals != 0, "Wrong decimals");
              if (decimals.destDecimals >= decimals.srcDecimals) {
                  return amount.mul(10 ** (decimals.destDecimals - decimals.srcDecimals));
              } else {
                  return amount.div(10 ** (decimals.srcDecimals - decimals.destDecimals));
              }
          }
          function toSrcBalance(address tokenAddress, uint256 amount) internal returns(uint256) {
              Decimals memory decimals = _decimals[tokenAddress];
              // We are not allowed decimals not set or not set correctly
              require(decimals.srcDecimals != 0 && decimals.destDecimals != 0, "Wrong decimals");
              if (decimals.destDecimals >= decimals.srcDecimals) {
                  return amount.div(10 ** (decimals.destDecimals - decimals.srcDecimals));
              } else {
                  return amount.mul(10 ** (decimals.srcDecimals - decimals.destDecimals));
              }
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity >=0.6.0 <0.8.0;
      import "./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 () internal {
              _paused = false;
          }
          /**
           * @dev Returns true if the contract is paused, and false otherwise.
           */
          function paused() public view virtual returns (bool) {
              return _paused;
          }
          /**
           * @dev Modifier to make a function callable only when the contract is not paused.
           *
           * Requirements:
           *
           * - The contract must not be paused.
           */
          modifier whenNotPaused() {
              require(!paused(), "Pausable: paused");
              _;
          }
          /**
           * @dev Modifier to make a function callable only when the contract is paused.
           *
           * Requirements:
           *
           * - The contract must be paused.
           */
          modifier whenPaused() {
              require(paused(), "Pausable: not paused");
              _;
          }
          /**
           * @dev Triggers stopped state.
           *
           * Requirements:
           *
           * - The contract must not be paused.
           */
          function _pause() internal virtual whenNotPaused {
              _paused = true;
              emit Paused(_msgSender());
          }
          /**
           * @dev Returns to normal state.
           *
           * Requirements:
           *
           * - The contract must be paused.
           */
          function _unpause() internal virtual whenPaused {
              _paused = false;
              emit Unpaused(_msgSender());
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity >=0.6.0 <0.8.0;
      /**
       * @dev Library for managing
       * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
       * types.
       *
       * Sets have the following properties:
       *
       * - Elements are added, removed, and checked for existence in constant time
       * (O(1)).
       * - Elements are enumerated in O(n). No guarantees are made on the ordering.
       *
       * ```
       * contract Example {
       *     // Add the library methods
       *     using EnumerableSet for EnumerableSet.AddressSet;
       *
       *     // Declare a set state variable
       *     EnumerableSet.AddressSet private mySet;
       * }
       * ```
       *
       * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
       * and `uint256` (`UintSet`) are supported.
       */
      library EnumerableSet {
          // To implement this library for multiple types with as little code
          // repetition as possible, we write it in terms of a generic Set type with
          // bytes32 values.
          // The Set implementation uses private functions, and user-facing
          // implementations (such as AddressSet) are just wrappers around the
          // underlying Set.
          // This means that we can only create new EnumerableSets for types that fit
          // in bytes32.
          struct Set {
              // Storage of set values
              bytes32[] _values;
              // Position of the value in the `values` array, plus 1 because index 0
              // means a value is not in the set.
              mapping (bytes32 => uint256) _indexes;
          }
          /**
           * @dev Add a value to a set. O(1).
           *
           * Returns true if the value was added to the set, that is if it was not
           * already present.
           */
          function _add(Set storage set, bytes32 value) private returns (bool) {
              if (!_contains(set, value)) {
                  set._values.push(value);
                  // The value is stored at length-1, but we add 1 to all indexes
                  // and use 0 as a sentinel value
                  set._indexes[value] = set._values.length;
                  return true;
              } else {
                  return false;
              }
          }
          /**
           * @dev Removes a value from a set. O(1).
           *
           * Returns true if the value was removed from the set, that is if it was
           * present.
           */
          function _remove(Set storage set, bytes32 value) private returns (bool) {
              // We read and store the value's index to prevent multiple reads from the same storage slot
              uint256 valueIndex = set._indexes[value];
              if (valueIndex != 0) { // Equivalent to contains(set, value)
                  // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
                  // the array, and then remove the last element (sometimes called as 'swap and pop').
                  // This modifies the order of the array, as noted in {at}.
                  uint256 toDeleteIndex = valueIndex - 1;
                  uint256 lastIndex = set._values.length - 1;
                  // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
                  // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.
                  bytes32 lastvalue = set._values[lastIndex];
                  // Move the last value to the index where the value to delete is
                  set._values[toDeleteIndex] = lastvalue;
                  // Update the index for the moved value
                  set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based
                  // Delete the slot where the moved value was stored
                  set._values.pop();
                  // Delete the index for the deleted slot
                  delete set._indexes[value];
                  return true;
              } else {
                  return false;
              }
          }
          /**
           * @dev Returns true if the value is in the set. O(1).
           */
          function _contains(Set storage set, bytes32 value) private view returns (bool) {
              return set._indexes[value] != 0;
          }
          /**
           * @dev Returns the number of values on the set. O(1).
           */
          function _length(Set storage set) private view returns (uint256) {
              return set._values.length;
          }
         /**
          * @dev Returns the value stored at position `index` in the set. O(1).
          *
          * Note that there are no guarantees on the ordering of values inside the
          * array, and it may change when more values are added or removed.
          *
          * Requirements:
          *
          * - `index` must be strictly less than {length}.
          */
          function _at(Set storage set, uint256 index) private view returns (bytes32) {
              require(set._values.length > index, "EnumerableSet: index out of bounds");
              return set._values[index];
          }
          // Bytes32Set
          struct Bytes32Set {
              Set _inner;
          }
          /**
           * @dev Add a value to a set. O(1).
           *
           * Returns true if the value was added to the set, that is if it was not
           * already present.
           */
          function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
              return _add(set._inner, value);
          }
          /**
           * @dev Removes a value from a set. O(1).
           *
           * Returns true if the value was removed from the set, that is if it was
           * present.
           */
          function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
              return _remove(set._inner, value);
          }
          /**
           * @dev Returns true if the value is in the set. O(1).
           */
          function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
              return _contains(set._inner, value);
          }
          /**
           * @dev Returns the number of values in the set. O(1).
           */
          function length(Bytes32Set storage set) internal view returns (uint256) {
              return _length(set._inner);
          }
         /**
          * @dev Returns the value stored at position `index` in the set. O(1).
          *
          * Note that there are no guarantees on the ordering of values inside the
          * array, and it may change when more values are added or removed.
          *
          * Requirements:
          *
          * - `index` must be strictly less than {length}.
          */
          function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
              return _at(set._inner, index);
          }
          // AddressSet
          struct AddressSet {
              Set _inner;
          }
          /**
           * @dev Add a value to a set. O(1).
           *
           * Returns true if the value was added to the set, that is if it was not
           * already present.
           */
          function add(AddressSet storage set, address value) internal returns (bool) {
              return _add(set._inner, bytes32(uint256(uint160(value))));
          }
          /**
           * @dev Removes a value from a set. O(1).
           *
           * Returns true if the value was removed from the set, that is if it was
           * present.
           */
          function remove(AddressSet storage set, address value) internal returns (bool) {
              return _remove(set._inner, bytes32(uint256(uint160(value))));
          }
          /**
           * @dev Returns true if the value is in the set. O(1).
           */
          function contains(AddressSet storage set, address value) internal view returns (bool) {
              return _contains(set._inner, bytes32(uint256(uint160(value))));
          }
          /**
           * @dev Returns the number of values in the set. O(1).
           */
          function length(AddressSet storage set) internal view returns (uint256) {
              return _length(set._inner);
          }
         /**
          * @dev Returns the value stored at position `index` in the set. O(1).
          *
          * Note that there are no guarantees on the ordering of values inside the
          * array, and it may change when more values are added or removed.
          *
          * Requirements:
          *
          * - `index` must be strictly less than {length}.
          */
          function at(AddressSet storage set, uint256 index) internal view returns (address) {
              return address(uint160(uint256(_at(set._inner, index))));
          }
          // UintSet
          struct UintSet {
              Set _inner;
          }
          /**
           * @dev Add a value to a set. O(1).
           *
           * Returns true if the value was added to the set, that is if it was not
           * already present.
           */
          function add(UintSet storage set, uint256 value) internal returns (bool) {
              return _add(set._inner, bytes32(value));
          }
          /**
           * @dev Removes a value from a set. O(1).
           *
           * Returns true if the value was removed from the set, that is if it was
           * present.
           */
          function remove(UintSet storage set, uint256 value) internal returns (bool) {
              return _remove(set._inner, bytes32(value));
          }
          /**
           * @dev Returns true if the value is in the set. O(1).
           */
          function contains(UintSet storage set, uint256 value) internal view returns (bool) {
              return _contains(set._inner, bytes32(value));
          }
          /**
           * @dev Returns the number of values on the set. O(1).
           */
          function length(UintSet storage set) internal view returns (uint256) {
              return _length(set._inner);
          }
         /**
          * @dev Returns the value stored at position `index` in the set. O(1).
          *
          * Note that there are no guarantees on the ordering of values inside the
          * array, and it may change when more values are added or removed.
          *
          * Requirements:
          *
          * - `index` must be strictly less than {length}.
          */
          function at(UintSet storage set, uint256 index) internal view returns (uint256) {
              return uint256(_at(set._inner, index));
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity >=0.6.0 <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 GSN meta-transactions the account sending and
       * paying for execution may not be the actual sender (as far as an application
       * is concerned).
       *
       * This contract is only required for intermediate, library-like contracts.
       */
      abstract contract Context {
          function _msgSender() internal view virtual returns (address payable) {
              return msg.sender;
          }
          function _msgData() internal view virtual returns (bytes memory) {
              this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
              return msg.data;
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity >=0.6.2 <0.8.0;
      /**
       * @dev Collection of functions related to the address type
       */
      library Address {
          /**
           * @dev Returns true if `account` is a contract.
           *
           * [IMPORTANT]
           * ====
           * It is unsafe to assume that an address for which this function returns
           * false is an externally-owned account (EOA) and not a contract.
           *
           * Among others, `isContract` will return false for the following
           * types of addresses:
           *
           *  - an externally-owned account
           *  - a contract in construction
           *  - an address where a contract will be created
           *  - an address where a contract lived, but was destroyed
           * ====
           */
          function isContract(address account) internal view returns (bool) {
              // This method relies on extcodesize, which returns 0 for contracts in
              // construction, since the code is only stored at the end of the
              // constructor execution.
              uint256 size;
              // solhint-disable-next-line no-inline-assembly
              assembly { size := extcodesize(account) }
              return size > 0;
          }
          /**
           * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
           * `recipient`, forwarding all available gas and reverting on errors.
           *
           * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
           * of certain opcodes, possibly making contracts go over the 2300 gas limit
           * imposed by `transfer`, making them unable to receive funds via
           * `transfer`. {sendValue} removes this limitation.
           *
           * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
           *
           * IMPORTANT: because control is transferred to `recipient`, care must be
           * taken to not create reentrancy vulnerabilities. Consider using
           * {ReentrancyGuard} or the
           * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
           */
          function sendValue(address payable recipient, uint256 amount) internal {
              require(address(this).balance >= amount, "Address: insufficient balance");
              // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
              (bool success, ) = recipient.call{ value: amount }("");
              require(success, "Address: unable to send value, recipient may have reverted");
          }
          /**
           * @dev Performs a Solidity function call using a low level `call`. A
           * plain`call` is an unsafe replacement for a function call: use this
           * function instead.
           *
           * If `target` reverts with a revert reason, it is bubbled up by this
           * function (like regular Solidity function calls).
           *
           * Returns the raw returned data. To convert to the expected return value,
           * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
           *
           * Requirements:
           *
           * - `target` must be a contract.
           * - calling `target` with `data` must not revert.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCall(target, data, "Address: low-level call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
           * `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
              return functionCallWithValue(target, data, 0, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but also transferring `value` wei to `target`.
           *
           * Requirements:
           *
           * - the calling contract must have an ETH balance of at least `value`.
           * - the called Solidity function must be `payable`.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
              return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
          }
          /**
           * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
           * with `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
              require(address(this).balance >= value, "Address: insufficient balance for call");
              require(isContract(target), "Address: call to non-contract");
              // solhint-disable-next-line avoid-low-level-calls
              (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");
              // solhint-disable-next-line avoid-low-level-calls
              (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");
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = target.delegatecall(data);
              return _verifyCallResult(success, returndata, errorMessage);
          }
          function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private 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
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          let returndata_size := mload(returndata)
                          revert(add(32, returndata), returndata_size)
                      }
                  } else {
                      revert(errorMessage);
                  }
              }
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity >=0.6.0 <0.8.0;
      /**
       * @dev Interface of the ERC20 standard as defined in the EIP.
       */
      interface IERC20 {
          /**
           * @dev Returns the amount of tokens in existence.
           */
          function totalSupply() external view returns (uint256);
          /**
           * @dev Returns the amount of tokens owned by `account`.
           */
          function balanceOf(address account) external view returns (uint256);
          /**
           * @dev Moves `amount` tokens from the caller's account to `recipient`.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transfer(address recipient, uint256 amount) external returns (bool);
          /**
           * @dev Returns the remaining number of tokens that `spender` will be
           * allowed to spend on behalf of `owner` through {transferFrom}. This is
           * zero by default.
           *
           * This value changes when {approve} or {transferFrom} are called.
           */
          function allowance(address owner, address spender) external view returns (uint256);
          /**
           * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * IMPORTANT: Beware that changing an allowance with this method brings the risk
           * that someone may use both the old and the new allowance by unfortunate
           * transaction ordering. One possible solution to mitigate this race
           * condition is to first reduce the spender's allowance to 0 and set the
           * desired value afterwards:
           * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
           *
           * Emits an {Approval} event.
           */
          function approve(address spender, uint256 amount) external returns (bool);
          /**
           * @dev Moves `amount` tokens from `sender` to `recipient` using the
           * allowance mechanism. `amount` is then deducted from the caller's
           * allowance.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
          /**
           * @dev Emitted when `value` tokens are moved from one account (`from`) to
           * another (`to`).
           *
           * Note that `value` may be zero.
           */
          event Transfer(address indexed from, address indexed to, uint256 value);
          /**
           * @dev Emitted when the allowance of a `spender` for an `owner` is set by
           * a call to {approve}. `value` is the new allowance.
           */
          event Approval(address indexed owner, address indexed spender, uint256 value);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity >=0.6.0 <0.8.0;
      import "./ERC20.sol";
      import "../../utils/Pausable.sol";
      /**
       * @dev ERC20 token with pausable token transfers, minting and burning.
       *
       * Useful for scenarios such as preventing trades until the end of an evaluation
       * period, or having an emergency switch for freezing all token transfers in the
       * event of a large bug.
       */
      abstract contract ERC20Pausable is ERC20, Pausable {
          /**
           * @dev See {ERC20-_beforeTokenTransfer}.
           *
           * Requirements:
           *
           * - the contract must not be paused.
           */
          function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override {
              super._beforeTokenTransfer(from, to, amount);
              require(!paused(), "ERC20Pausable: token transfer while paused");
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity >=0.6.0 <0.8.0;
      import "../../utils/Context.sol";
      import "./ERC20.sol";
      /**
       * @dev Extension of {ERC20} that allows token holders to destroy both their own
       * tokens and those that they have an allowance for, in a way that can be
       * recognized off-chain (via event analysis).
       */
      abstract contract ERC20Burnable is Context, ERC20 {
          using SafeMath for uint256;
          /**
           * @dev Destroys `amount` tokens from the caller.
           *
           * See {ERC20-_burn}.
           */
          function burn(uint256 amount) public virtual {
              _burn(_msgSender(), amount);
          }
          /**
           * @dev Destroys `amount` tokens from `account`, deducting from the caller's
           * allowance.
           *
           * See {ERC20-_burn} and {ERC20-allowance}.
           *
           * Requirements:
           *
           * - the caller must have allowance for ``accounts``'s tokens of at least
           * `amount`.
           */
          function burnFrom(address account, uint256 amount) public virtual {
              uint256 decreasedAllowance = allowance(account, _msgSender()).sub(amount, "ERC20: burn amount exceeds allowance");
              _approve(account, _msgSender(), decreasedAllowance);
              _burn(account, amount);
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity >=0.6.0 <0.8.0;
      import "../../utils/Context.sol";
      import "./IERC20.sol";
      import "../../math/SafeMath.sol";
      /**
       * @dev Implementation of the {IERC20} interface.
       *
       * This implementation is agnostic to the way tokens are created. This means
       * that a supply mechanism has to be added in a derived contract using {_mint}.
       * For a generic mechanism see {ERC20PresetMinterPauser}.
       *
       * TIP: For a detailed writeup see our guide
       * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
       * to implement supply mechanisms].
       *
       * We have followed general OpenZeppelin guidelines: functions revert instead
       * of returning `false` on failure. This behavior is nonetheless conventional
       * and does not conflict with the expectations of ERC20 applications.
       *
       * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
       * This allows applications to reconstruct the allowance for all accounts just
       * by listening to said events. Other implementations of the EIP may not emit
       * these events, as it isn't required by the specification.
       *
       * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
       * functions have been added to mitigate the well-known issues around setting
       * allowances. See {IERC20-approve}.
       */
      contract ERC20 is Context, IERC20 {
          using SafeMath for uint256;
          mapping (address => uint256) private _balances;
          mapping (address => mapping (address => uint256)) private _allowances;
          uint256 private _totalSupply;
          string private _name;
          string private _symbol;
          uint8 private _decimals;
          /**
           * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
           * a default value of 18.
           *
           * To select a different value for {decimals}, use {_setupDecimals}.
           *
           * All three of these values are immutable: they can only be set once during
           * construction.
           */
          constructor (string memory name_, string memory symbol_) public {
              _name = name_;
              _symbol = symbol_;
              _decimals = 18;
          }
          /**
           * @dev Returns the name of the token.
           */
          function name() public view virtual returns (string memory) {
              return _name;
          }
          /**
           * @dev Returns the symbol of the token, usually a shorter version of the
           * name.
           */
          function symbol() public view virtual returns (string memory) {
              return _symbol;
          }
          /**
           * @dev Returns the number of decimals used to get its user representation.
           * For example, if `decimals` equals `2`, a balance of `505` tokens should
           * be displayed to a user as `5,05` (`505 / 10 ** 2`).
           *
           * Tokens usually opt for a value of 18, imitating the relationship between
           * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
           * called.
           *
           * NOTE: This information is only used for _display_ purposes: it in
           * no way affects any of the arithmetic of the contract, including
           * {IERC20-balanceOf} and {IERC20-transfer}.
           */
          function decimals() public view virtual returns (uint8) {
              return _decimals;
          }
          /**
           * @dev See {IERC20-totalSupply}.
           */
          function totalSupply() public view virtual override returns (uint256) {
              return _totalSupply;
          }
          /**
           * @dev See {IERC20-balanceOf}.
           */
          function balanceOf(address account) public view virtual override returns (uint256) {
              return _balances[account];
          }
          /**
           * @dev See {IERC20-transfer}.
           *
           * Requirements:
           *
           * - `recipient` cannot be the zero address.
           * - the caller must have a balance of at least `amount`.
           */
          function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
              _transfer(_msgSender(), recipient, amount);
              return true;
          }
          /**
           * @dev See {IERC20-allowance}.
           */
          function allowance(address owner, address spender) public view virtual override returns (uint256) {
              return _allowances[owner][spender];
          }
          /**
           * @dev See {IERC20-approve}.
           *
           * Requirements:
           *
           * - `spender` cannot be the zero address.
           */
          function approve(address spender, uint256 amount) public virtual override returns (bool) {
              _approve(_msgSender(), spender, amount);
              return true;
          }
          /**
           * @dev See {IERC20-transferFrom}.
           *
           * Emits an {Approval} event indicating the updated allowance. This is not
           * required by the EIP. See the note at the beginning of {ERC20}.
           *
           * Requirements:
           *
           * - `sender` and `recipient` cannot be the zero address.
           * - `sender` must have a balance of at least `amount`.
           * - the caller must have allowance for ``sender``'s tokens of at least
           * `amount`.
           */
          function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
              _transfer(sender, recipient, amount);
              _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
              return true;
          }
          /**
           * @dev Atomically increases the allowance granted to `spender` by the caller.
           *
           * This is an alternative to {approve} that can be used as a mitigation for
           * problems described in {IERC20-approve}.
           *
           * Emits an {Approval} event indicating the updated allowance.
           *
           * Requirements:
           *
           * - `spender` cannot be the zero address.
           */
          function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
              _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
              return true;
          }
          /**
           * @dev Atomically decreases the allowance granted to `spender` by the caller.
           *
           * This is an alternative to {approve} that can be used as a mitigation for
           * problems described in {IERC20-approve}.
           *
           * Emits an {Approval} event indicating the updated allowance.
           *
           * Requirements:
           *
           * - `spender` cannot be the zero address.
           * - `spender` must have allowance for the caller of at least
           * `subtractedValue`.
           */
          function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
              _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
              return true;
          }
          /**
           * @dev Moves tokens `amount` from `sender` to `recipient`.
           *
           * This is internal function is equivalent to {transfer}, and can be used to
           * e.g. implement automatic token fees, slashing mechanisms, etc.
           *
           * Emits a {Transfer} event.
           *
           * Requirements:
           *
           * - `sender` cannot be the zero address.
           * - `recipient` cannot be the zero address.
           * - `sender` must have a balance of at least `amount`.
           */
          function _transfer(address sender, address recipient, uint256 amount) internal virtual {
              require(sender != address(0), "ERC20: transfer from the zero address");
              require(recipient != address(0), "ERC20: transfer to the zero address");
              _beforeTokenTransfer(sender, recipient, amount);
              _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
              _balances[recipient] = _balances[recipient].add(amount);
              emit Transfer(sender, recipient, amount);
          }
          /** @dev Creates `amount` tokens and assigns them to `account`, increasing
           * the total supply.
           *
           * Emits a {Transfer} event with `from` set to the zero address.
           *
           * Requirements:
           *
           * - `to` cannot be the zero address.
           */
          function _mint(address account, uint256 amount) internal virtual {
              require(account != address(0), "ERC20: mint to the zero address");
              _beforeTokenTransfer(address(0), account, amount);
              _totalSupply = _totalSupply.add(amount);
              _balances[account] = _balances[account].add(amount);
              emit Transfer(address(0), account, amount);
          }
          /**
           * @dev Destroys `amount` tokens from `account`, reducing the
           * total supply.
           *
           * Emits a {Transfer} event with `to` set to the zero address.
           *
           * Requirements:
           *
           * - `account` cannot be the zero address.
           * - `account` must have at least `amount` tokens.
           */
          function _burn(address account, uint256 amount) internal virtual {
              require(account != address(0), "ERC20: burn from the zero address");
              _beforeTokenTransfer(account, address(0), amount);
              _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
              _totalSupply = _totalSupply.sub(amount);
              emit Transfer(account, address(0), amount);
          }
          /**
           * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
           *
           * This internal function is equivalent to `approve`, and can be used to
           * e.g. set automatic allowances for certain subsystems, etc.
           *
           * Emits an {Approval} event.
           *
           * Requirements:
           *
           * - `owner` cannot be the zero address.
           * - `spender` cannot be the zero address.
           */
          function _approve(address owner, address spender, uint256 amount) internal virtual {
              require(owner != address(0), "ERC20: approve from the zero address");
              require(spender != address(0), "ERC20: approve to the zero address");
              _allowances[owner][spender] = amount;
              emit Approval(owner, spender, amount);
          }
          /**
           * @dev Sets {decimals} to a value other than the default one of 18.
           *
           * WARNING: This function should only be called from the constructor. Most
           * applications that interact with token contracts will not expect
           * {decimals} to ever change, and may work incorrectly if it does.
           */
          function _setupDecimals(uint8 decimals_) internal virtual {
              _decimals = decimals_;
          }
          /**
           * @dev Hook that is called before any transfer of tokens. This includes
           * minting and burning.
           *
           * Calling conditions:
           *
           * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
           * will be to transferred to `to`.
           * - when `from` is zero, `amount` tokens will be minted for `to`.
           * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
           * - `from` and `to` are never both zero.
           *
           * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
           */
          function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity >=0.6.0 <0.8.0;
      import "../access/AccessControl.sol";
      import "../utils/Context.sol";
      import "../token/ERC20/ERC20.sol";
      import "../token/ERC20/ERC20Burnable.sol";
      import "../token/ERC20/ERC20Pausable.sol";
      /**
       * @dev {ERC20} token, including:
       *
       *  - ability for holders to burn (destroy) their tokens
       *  - a minter role that allows for token minting (creation)
       *  - a pauser role that allows to stop all token transfers
       *
       * This contract uses {AccessControl} to lock permissioned functions using the
       * different roles - head to its documentation for details.
       *
       * The account that deploys the contract will be granted the minter and pauser
       * roles, as well as the default admin role, which will let it grant both minter
       * and pauser roles to other accounts.
       */
      contract ERC20PresetMinterPauser is Context, AccessControl, ERC20Burnable, ERC20Pausable {
          bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
          bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
          /**
           * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE` and `PAUSER_ROLE` to the
           * account that deploys the contract.
           *
           * See {ERC20-constructor}.
           */
          constructor(string memory name, string memory symbol) public ERC20(name, symbol) {
              _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
              _setupRole(MINTER_ROLE, _msgSender());
              _setupRole(PAUSER_ROLE, _msgSender());
          }
          /**
           * @dev Creates `amount` new tokens for `to`.
           *
           * See {ERC20-_mint}.
           *
           * Requirements:
           *
           * - the caller must have the `MINTER_ROLE`.
           */
          function mint(address to, uint256 amount) public virtual {
              require(hasRole(MINTER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have minter role to mint");
              _mint(to, amount);
          }
          /**
           * @dev Pauses all token transfers.
           *
           * See {ERC20Pausable} and {Pausable-_pause}.
           *
           * Requirements:
           *
           * - the caller must have the `PAUSER_ROLE`.
           */
          function pause() public virtual {
              require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have pauser role to pause");
              _pause();
          }
          /**
           * @dev Unpauses all token transfers.
           *
           * See {ERC20Pausable} and {Pausable-_unpause}.
           *
           * Requirements:
           *
           * - the caller must have the `PAUSER_ROLE`.
           */
          function unpause() public virtual {
              require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have pauser role to unpause");
              _unpause();
          }
          function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override(ERC20, ERC20Pausable) {
              super._beforeTokenTransfer(from, to, amount);
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity >=0.6.0 <0.8.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, with an overflow flag.
           *
           * _Available since v3.4._
           */
          function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
              uint256 c = a + b;
              if (c < a) return (false, 0);
              return (true, c);
          }
          /**
           * @dev Returns the substraction of two unsigned integers, with an overflow flag.
           *
           * _Available since v3.4._
           */
          function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
              if (b > a) return (false, 0);
              return (true, a - b);
          }
          /**
           * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
           *
           * _Available since v3.4._
           */
          function tryMul(uint256 a, uint256 b) internal pure returns (bool, 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 (true, 0);
              uint256 c = a * b;
              if (c / a != b) return (false, 0);
              return (true, c);
          }
          /**
           * @dev Returns the division of two unsigned integers, with a division by zero flag.
           *
           * _Available since v3.4._
           */
          function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
              if (b == 0) return (false, 0);
              return (true, a / b);
          }
          /**
           * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
           *
           * _Available since v3.4._
           */
          function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
              if (b == 0) return (false, 0);
              return (true, a % b);
          }
          /**
           * @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) {
              require(b <= a, "SafeMath: subtraction overflow");
              return a - b;
          }
          /**
           * @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) {
              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, reverting 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) {
              require(b > 0, "SafeMath: division by zero");
              return a / b;
          }
          /**
           * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
           * reverting 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) {
              require(b > 0, "SafeMath: modulo by zero");
              return a % b;
          }
          /**
           * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
           * overflow (when the result is negative).
           *
           * CAUTION: This function is deprecated because it requires allocating memory for the error
           * message unnecessarily. For custom revert reasons use {trySub}.
           *
           * 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);
              return a - b;
          }
          /**
           * @dev Returns the integer division of two unsigned integers, reverting with custom message on
           * division by zero. The result is rounded towards zero.
           *
           * CAUTION: This function is deprecated because it requires allocating memory for the error
           * message unnecessarily. For custom revert reasons use {tryDiv}.
           *
           * 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);
              return a / b;
          }
          /**
           * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
           * reverting with custom message when dividing by zero.
           *
           * CAUTION: This function is deprecated because it requires allocating memory for the error
           * message unnecessarily. For custom revert reasons use {tryMod}.
           *
           * Counterpart to Solidity's `%` operator. This function uses a `revert`
           * opcode (which leaves remaining gas untouched) while Solidity uses an
           * invalid opcode to revert (consuming all remaining gas).
           *
           * Requirements:
           *
           * - The divisor cannot be zero.
           */
          function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              require(b > 0, errorMessage);
              return a % b;
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity >=0.6.0 <0.8.0;
      import "../utils/EnumerableSet.sol";
      import "../utils/Address.sol";
      import "../utils/Context.sol";
      /**
       * @dev Contract module that allows children to implement role-based access
       * control mechanisms.
       *
       * Roles are referred to by their `bytes32` identifier. These should be exposed
       * in the external API and be unique. The best way to achieve this is by
       * using `public constant` hash digests:
       *
       * ```
       * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
       * ```
       *
       * Roles can be used to represent a set of permissions. To restrict access to a
       * function call, use {hasRole}:
       *
       * ```
       * function foo() public {
       *     require(hasRole(MY_ROLE, msg.sender));
       *     ...
       * }
       * ```
       *
       * Roles can be granted and revoked dynamically via the {grantRole} and
       * {revokeRole} functions. Each role has an associated admin role, and only
       * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
       *
       * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
       * that only accounts with this role will be able to grant or revoke other
       * roles. More complex role relationships can be created by using
       * {_setRoleAdmin}.
       *
       * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
       * grant and revoke this role. Extra precautions should be taken to secure
       * accounts that have been granted it.
       */
      abstract contract AccessControl is Context {
          using EnumerableSet for EnumerableSet.AddressSet;
          using Address for address;
          struct RoleData {
              EnumerableSet.AddressSet members;
              bytes32 adminRole;
          }
          mapping (bytes32 => RoleData) private _roles;
          bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
          /**
           * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
           *
           * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
           * {RoleAdminChanged} not being emitted signaling this.
           *
           * _Available since v3.1._
           */
          event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
          /**
           * @dev Emitted when `account` is granted `role`.
           *
           * `sender` is the account that originated the contract call, an admin role
           * bearer except when using {_setupRole}.
           */
          event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
          /**
           * @dev Emitted when `account` is revoked `role`.
           *
           * `sender` is the account that originated the contract call:
           *   - if using `revokeRole`, it is the admin role bearer
           *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
           */
          event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
          /**
           * @dev Returns `true` if `account` has been granted `role`.
           */
          function hasRole(bytes32 role, address account) public view returns (bool) {
              return _roles[role].members.contains(account);
          }
          /**
           * @dev Returns the number of accounts that have `role`. Can be used
           * together with {getRoleMember} to enumerate all bearers of a role.
           */
          function getRoleMemberCount(bytes32 role) public view returns (uint256) {
              return _roles[role].members.length();
          }
          /**
           * @dev Returns one of the accounts that have `role`. `index` must be a
           * value between 0 and {getRoleMemberCount}, non-inclusive.
           *
           * Role bearers are not sorted in any particular way, and their ordering may
           * change at any point.
           *
           * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
           * you perform all queries on the same block. See the following
           * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
           * for more information.
           */
          function getRoleMember(bytes32 role, uint256 index) public view returns (address) {
              return _roles[role].members.at(index);
          }
          /**
           * @dev Returns the admin role that controls `role`. See {grantRole} and
           * {revokeRole}.
           *
           * To change a role's admin, use {_setRoleAdmin}.
           */
          function getRoleAdmin(bytes32 role) public view returns (bytes32) {
              return _roles[role].adminRole;
          }
          /**
           * @dev Grants `role` to `account`.
           *
           * If `account` had not been already granted `role`, emits a {RoleGranted}
           * event.
           *
           * Requirements:
           *
           * - the caller must have ``role``'s admin role.
           */
          function grantRole(bytes32 role, address account) public virtual {
              require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to grant");
              _grantRole(role, account);
          }
          /**
           * @dev Revokes `role` from `account`.
           *
           * If `account` had been granted `role`, emits a {RoleRevoked} event.
           *
           * Requirements:
           *
           * - the caller must have ``role``'s admin role.
           */
          function revokeRole(bytes32 role, address account) public virtual {
              require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to revoke");
              _revokeRole(role, account);
          }
          /**
           * @dev Revokes `role` from the calling account.
           *
           * Roles are often managed via {grantRole} and {revokeRole}: this function's
           * purpose is to provide a mechanism for accounts to lose their privileges
           * if they are compromised (such as when a trusted device is misplaced).
           *
           * If the calling account had been granted `role`, emits a {RoleRevoked}
           * event.
           *
           * Requirements:
           *
           * - the caller must be `account`.
           */
          function renounceRole(bytes32 role, address account) public virtual {
              require(account == _msgSender(), "AccessControl: can only renounce roles for self");
              _revokeRole(role, account);
          }
          /**
           * @dev Grants `role` to `account`.
           *
           * If `account` had not been already granted `role`, emits a {RoleGranted}
           * event. Note that unlike {grantRole}, this function doesn't perform any
           * checks on the calling account.
           *
           * [WARNING]
           * ====
           * This function should only be called from the constructor when setting
           * up the initial roles for the system.
           *
           * Using this function in any other way is effectively circumventing the admin
           * system imposed by {AccessControl}.
           * ====
           */
          function _setupRole(bytes32 role, address account) internal virtual {
              _grantRole(role, account);
          }
          /**
           * @dev Sets `adminRole` as ``role``'s admin role.
           *
           * Emits a {RoleAdminChanged} event.
           */
          function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
              emit RoleAdminChanged(role, _roles[role].adminRole, adminRole);
              _roles[role].adminRole = adminRole;
          }
          function _grantRole(bytes32 role, address account) private {
              if (_roles[role].members.add(account)) {
                  emit RoleGranted(role, account, _msgSender());
              }
          }
          function _revokeRole(bytes32 role, address account) private {
              if (_roles[role].members.remove(account)) {
                  emit RoleRevoked(role, account, _msgSender());
              }
          }
      }
      pragma solidity 0.7.0;
      /**
          @title Interface to be used with handlers that support ERC20s and ERC721s.
          @author ChainSafe Systems.
       */
      interface IERCHandler {
          /**
              @notice Correlates {resourceID} with {contractAddress}.
              @param resourceID ResourceID to be used when making deposits.
              @param contractAddress Address of contract to be called when a deposit is made and a deposited is executed.
           */
          function setResource(bytes32 resourceID, address contractAddress) external;
          /**
              @notice Marks {contractAddress} as mintable/burnable.
              @param contractAddress Address of contract to be used when making or executing deposits.
           */
          function setBurnable(address contractAddress) external;
          /**
              @notice Set {contractAddress} decimals on dest chain.
              @param contractAddress Address of contract to be used when making or executing deposits.
              @param srcDecimals Decimals of this token on source chain
              @param destDecimals Decimals of this token on dest chain.
           */
          function setDecimals(address contractAddress, uint8 srcDecimals, uint8 destDecimals) external;
          /**
              @notice Used to manually release funds from ERC safes.
              @param tokenAddress Address of token contract to release.
              @param recipient Address to release tokens to.
              @param amountOrTokenID Either the amount of ERC20 tokens or the ERC721 token ID to release.
           */
          function withdraw(address tokenAddress, address recipient, uint256 amountOrTokenID) external;
      }
      pragma solidity 0.7.0;
      /**
          @title Interface for handler contracts that support deposits and deposit executions.
          @author ChainSafe Systems.
       */
      interface IDepositExecute {
          /**
              @notice It is intended that deposit are made using the Bridge contract.
              @param destinationChainID Chain ID deposit is expected to be bridged to.
              @param depositNonce This value is generated as an ID by the Bridge contract.
              @param depositer Address of account making the deposit in the Bridge contract.
              @param data Consists of additional data needed for a specific deposit.
           */
          function deposit(bytes32 resourceID, uint8 destinationChainID, uint64 depositNonce, address depositer, bytes calldata data) external;
          /**
              @notice It is intended that proposals are executed by the Bridge contract.
              @param data Consists of additional data needed for a specific deposit execution.
           */
          function executeProposal(bytes32 resourceID, bytes calldata data) external;
      }
      pragma solidity 0.7.0;
      import "../interfaces/IERCHandler.sol";
      /**
          @title Function used across handler contracts.
          @author ChainSafe Systems.
          @notice This contract is intended to be used with the Bridge contract.
       */
      contract HandlerHelpers is IERCHandler {
          struct Decimals {
              uint8 srcDecimals;
              uint8 destDecimals;
          }
          address public _bridgeAddress;
          // resourceID => token contract address
          mapping (bytes32 => address) public _resourceIDToTokenContractAddress;
          // token contract address => resourceID
          mapping (address => bytes32) public _tokenContractAddressToResourceID;
          // token contract address => is whitelisted
          mapping (address => bool) public _contractWhitelist;
          // token contract address => is burnable
          mapping (address => bool) public _burnList;
          // token contract address => decimals
          mapping (address => Decimals) public _decimals;
          modifier onlyBridge() {
              _onlyBridge();
              _;
          }
          function _onlyBridge() private {
              require(msg.sender == _bridgeAddress, "sender must be bridge contract");
          }
          /**
              @notice First verifies {_resourceIDToContractAddress}[{resourceID}] and
              {_contractAddressToResourceID}[{contractAddress}] are not already set,
              then sets {_resourceIDToContractAddress} with {contractAddress},
              {_contractAddressToResourceID} with {resourceID},
              and {_contractWhitelist} to true for {contractAddress}.
              @param resourceID ResourceID to be used when making deposits.
              @param contractAddress Address of contract to be called when a deposit is made and a deposited is executed.
           */
          function setResource(bytes32 resourceID, address contractAddress) external override onlyBridge {
              _setResource(resourceID, contractAddress);
          }
          /**
              @notice First verifies {contractAddress} is whitelisted, then sets {_burnList}[{contractAddress}]
              to true.
              @param contractAddress Address of contract to be used when making or executing deposits.
           */
          function setBurnable(address contractAddress) external override onlyBridge{
              _setBurnable(contractAddress);
          }
          /**
              @notice First verifies {contractAddress} is whitelisted, then sets {_decimals}[{contractAddress}]
              to it's decimals value.
              @param contractAddress Address of contract to be used when making or eexcuting deposits.
              @param srcDecimals Decimals of this token on source chain
              @param destDecimals Decimals of this token on dest chain.
           */
          function setDecimals(address contractAddress, uint8 srcDecimals, uint8 destDecimals) external override onlyBridge {
              _setDecimals(contractAddress, srcDecimals, destDecimals);
          }
          /**
              @notice Used to manually release funds from ERC safes.
              @param tokenAddress Address of token contract to release.
              @param recipient Address to release tokens to.
              @param amountOrTokenID Either the amount of ERC20 tokens or the ERC721 token ID to release.
           */
          function withdraw(address tokenAddress, address recipient, uint256 amountOrTokenID) external virtual override {}
          function _setResource(bytes32 resourceID, address contractAddress) internal {
              _resourceIDToTokenContractAddress[resourceID] = contractAddress;
              _tokenContractAddressToResourceID[contractAddress] = resourceID;
              _contractWhitelist[contractAddress] = true;
          }
          function _setBurnable(address contractAddress) internal {
              require(_contractWhitelist[contractAddress], "provided contract is not whitelisted");
              _burnList[contractAddress] = true;
          }
          function _setDecimals(address contractAddress, uint8 srcDecimals, uint8 destDecimals) internal {
              require(_contractWhitelist[contractAddress], "provided contract is not whitelisted");
              _decimals[contractAddress] = Decimals({
                  srcDecimals: srcDecimals,
                  destDecimals: destDecimals
              });
          }
      }
      pragma solidity 0.7.0;
      import "@openzeppelin/contracts/math/SafeMath.sol";
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      import "@openzeppelin/contracts/presets/ERC20PresetMinterPauser.sol";
      import "@openzeppelin/contracts/token/ERC20/ERC20Burnable.sol";
      /**
          @title Manages deposited ERC20s.
          @author ChainSafe Systems.
          @notice This contract is intended to be used with ERC20Handler contract.
       */
      contract ERC20Safe {
          using SafeMath for uint256;
          /**
              @notice Used to gain custody of deposited token.
              @param tokenAddress Address of ERC20 to transfer.
              @param owner Address of current token owner.
              @param recipient Address to transfer tokens to.
              @param amount Amount of tokens to transfer.
           */
          function lockERC20(address tokenAddress, address owner, address recipient, uint256 amount) internal {
              IERC20 erc20 = IERC20(tokenAddress);
              _safeTransferFrom(erc20, owner, recipient, amount);
          }
          /**
              @notice Transfers custody of token to recipient.
              @param tokenAddress Address of ERC20 to transfer.
              @param recipient Address to transfer tokens to.
              @param amount Amount of tokens to transfer.
           */
          function releaseERC20(address tokenAddress, address recipient, uint256 amount) internal {
              IERC20 erc20 = IERC20(tokenAddress);
              _safeTransfer(erc20, recipient, amount);
          }
          /**
              @notice Used to create new ERC20s.
              @param tokenAddress Address of ERC20 to transfer.
              @param recipient Address to mint token to.
              @param amount Amount of token to mint.
           */
          function mintERC20(address tokenAddress, address recipient, uint256 amount) internal {
              ERC20PresetMinterPauser erc20 = ERC20PresetMinterPauser(tokenAddress);
              erc20.mint(recipient, amount);
          }
          /**
              @notice Used to burn ERC20s.
              @param tokenAddress Address of ERC20 to burn.
              @param owner Current owner of tokens.
              @param amount Amount of tokens to burn.
           */
          function burnERC20(address tokenAddress, address owner, uint256 amount) internal {
              ERC20Burnable erc20 = ERC20Burnable(tokenAddress);
              erc20.burnFrom(owner, amount);
          }
          /**
              @notice used to transfer ERC20s safely
              @param token Token instance to transfer
              @param to Address to transfer token to
              @param value Amount of token to transfer
           */
          function _safeTransfer(IERC20 token, address to, uint256 value) private {
              _safeCall(token, abi.encodeWithSelector(token.transfer.selector, to, value));
          }
          /**
              @notice used to transfer ERC20s safely
              @param token Token instance to transfer
              @param from Address to transfer token from
              @param to Address to transfer token to
              @param value Amount of token to transfer
           */
          function _safeTransferFrom(IERC20 token, address from, address to, uint256 value) private {
              _safeCall(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
          }
          /**
              @notice used to make calls to ERC20s safely
              @param token Token instance call targets
              @param data encoded call data
           */
          function _safeCall(IERC20 token, bytes memory data) private {        
              (bool success, bytes memory returndata) = address(token).call(data);
              require(success, "ERC20: call failed");
              if (returndata.length > 0) {
                  require(abi.decode(returndata, (bool)), "ERC20: operation did not succeed");
              }
          }
      }
      

      File 3 of 3: PHAToken
      pragma solidity ^0.5.0;
      
      
      contract Context {
          // Empty internal constructor, to prevent people from mistakenly deploying
          // an instance of this contract, which should be used via inheritance.
          constructor () internal { }
          // solhint-disable-previous-line no-empty-blocks
      
          function _msgSender() internal view returns (address payable) {
              return msg.sender;
          }
      
          function _msgData() internal view returns (bytes memory) {
              this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
              return msg.data;
          }
      }
      
      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);
      }
      
      library SafeMath {
          /**
           * @dev Returns the addition of two unsigned integers, reverting on
           * overflow.
           *
           * Counterpart to Solidity's `+` operator.
           *
           * Requirements:
           * - Addition cannot overflow.
           */
          function add(uint256 a, uint256 b) internal pure returns (uint256) {
              uint256 c = a + b;
              require(c >= a, "SafeMath: addition overflow");
      
              return c;
          }
      
          /**
           * @dev Returns the subtraction of two unsigned integers, reverting on
           * overflow (when the result is negative).
           *
           * Counterpart to Solidity's `-` operator.
           *
           * Requirements:
           * - Subtraction cannot overflow.
           */
          function sub(uint256 a, uint256 b) internal pure returns (uint256) {
              return sub(a, b, "SafeMath: subtraction overflow");
          }
      
          /**
           * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
           * overflow (when the result is negative).
           *
           * Counterpart to Solidity's `-` operator.
           *
           * Requirements:
           * - Subtraction cannot overflow.
           *
           * _Available since v2.4.0._
           */
          function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              require(b <= a, errorMessage);
              uint256 c = a - b;
      
              return c;
          }
      
          /**
           * @dev Returns the multiplication of two unsigned integers, reverting on
           * overflow.
           *
           * Counterpart to Solidity's `*` operator.
           *
           * Requirements:
           * - Multiplication cannot overflow.
           */
          function mul(uint256 a, uint256 b) internal pure returns (uint256) {
              // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
              // benefit is lost if 'b' is also tested.
              // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
              if (a == 0) {
                  return 0;
              }
      
              uint256 c = a * b;
              require(c / a == b, "SafeMath: multiplication overflow");
      
              return c;
          }
      
          /**
           * @dev Returns the integer division of two unsigned integers. Reverts on
           * division by zero. The result is rounded towards zero.
           *
           * Counterpart to Solidity's `/` operator. Note: this function uses a
           * `revert` opcode (which leaves remaining gas untouched) while Solidity
           * uses an invalid opcode to revert (consuming all remaining gas).
           *
           * Requirements:
           * - The divisor cannot be zero.
           */
          function div(uint256 a, uint256 b) internal pure returns (uint256) {
              return div(a, b, "SafeMath: division by zero");
          }
      
          /**
           * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
           * division by zero. The result is rounded towards zero.
           *
           * Counterpart to Solidity's `/` operator. Note: this function uses a
           * `revert` opcode (which leaves remaining gas untouched) while Solidity
           * uses an invalid opcode to revert (consuming all remaining gas).
           *
           * Requirements:
           * - The divisor cannot be zero.
           *
           * _Available since v2.4.0._
           */
          function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              // Solidity only automatically asserts when dividing by 0
              require(b > 0, errorMessage);
              uint256 c = a / b;
              // assert(a == b * c + a % b); // There is no case in which this doesn't hold
      
              return c;
          }
      
          /**
           * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
           * Reverts when dividing by zero.
           *
           * Counterpart to Solidity's `%` operator. This function uses a `revert`
           * opcode (which leaves remaining gas untouched) while Solidity uses an
           * invalid opcode to revert (consuming all remaining gas).
           *
           * Requirements:
           * - The divisor cannot be zero.
           */
          function mod(uint256 a, uint256 b) internal pure returns (uint256) {
              return mod(a, b, "SafeMath: modulo by zero");
          }
      
          /**
           * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
           * Reverts with custom message when dividing by zero.
           *
           * Counterpart to Solidity's `%` operator. This function uses a `revert`
           * opcode (which leaves remaining gas untouched) while Solidity uses an
           * invalid opcode to revert (consuming all remaining gas).
           *
           * Requirements:
           * - The divisor cannot be zero.
           *
           * _Available since v2.4.0._
           */
          function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              require(b != 0, errorMessage);
              return a % b;
          }
      }
      
      contract ERC20 is Context, IERC20 {
          using SafeMath for uint256;
      
          mapping (address => uint256) private _balances;
      
          mapping (address => mapping (address => uint256)) private _allowances;
      
          uint256 private _totalSupply;
      
          /**
           * @dev See {IERC20-totalSupply}.
           */
          function totalSupply() public view returns (uint256) {
              return _totalSupply;
          }
      
          /**
           * @dev See {IERC20-balanceOf}.
           */
          function balanceOf(address account) public view returns (uint256) {
              return _balances[account];
          }
      
          /**
           * @dev See {IERC20-transfer}.
           *
           * Requirements:
           *
           * - `recipient` cannot be the zero address.
           * - the caller must have a balance of at least `amount`.
           */
          function transfer(address recipient, uint256 amount) public returns (bool) {
              _transfer(_msgSender(), recipient, amount);
              return true;
          }
      
          /**
           * @dev See {IERC20-allowance}.
           */
          function allowance(address owner, address spender) public view returns (uint256) {
              return _allowances[owner][spender];
          }
      
          /**
           * @dev See {IERC20-approve}.
           *
           * Requirements:
           *
           * - `spender` cannot be the zero address.
           */
          function approve(address spender, uint256 amount) public returns (bool) {
              _approve(_msgSender(), spender, amount);
              return true;
          }
      
          /**
           * @dev See {IERC20-transferFrom}.
           *
           * Emits an {Approval} event indicating the updated allowance. This is not
           * required by the EIP. See the note at the beginning of {ERC20};
           *
           * Requirements:
           * - `sender` and `recipient` cannot be the zero address.
           * - `sender` must have a balance of at least `amount`.
           * - the caller must have allowance for `sender`'s tokens of at least
           * `amount`.
           */
          function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
              _transfer(sender, recipient, amount);
              _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
              return true;
          }
      
          /**
           * @dev Atomically increases the allowance granted to `spender` by the caller.
           *
           * This is an alternative to {approve} that can be used as a mitigation for
           * problems described in {IERC20-approve}.
           *
           * Emits an {Approval} event indicating the updated allowance.
           *
           * Requirements:
           *
           * - `spender` cannot be the zero address.
           */
          function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
              _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
              return true;
          }
      
          /**
           * @dev Atomically decreases the allowance granted to `spender` by the caller.
           *
           * This is an alternative to {approve} that can be used as a mitigation for
           * problems described in {IERC20-approve}.
           *
           * Emits an {Approval} event indicating the updated allowance.
           *
           * Requirements:
           *
           * - `spender` cannot be the zero address.
           * - `spender` must have allowance for the caller of at least
           * `subtractedValue`.
           */
          function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
              _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
              return true;
          }
      
          /**
           * @dev Moves tokens `amount` from `sender` to `recipient`.
           *
           * This is internal function is equivalent to {transfer}, and can be used to
           * e.g. implement automatic token fees, slashing mechanisms, etc.
           *
           * Emits a {Transfer} event.
           *
           * Requirements:
           *
           * - `sender` cannot be the zero address.
           * - `recipient` cannot be the zero address.
           * - `sender` must have a balance of at least `amount`.
           */
          function _transfer(address sender, address recipient, uint256 amount) internal {
              require(sender != address(0), "ERC20: transfer from the zero address");
              require(recipient != address(0), "ERC20: transfer to the zero address");
      
              _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
              _balances[recipient] = _balances[recipient].add(amount);
              emit Transfer(sender, recipient, amount);
          }
      
          /** @dev Creates `amount` tokens and assigns them to `account`, increasing
           * the total supply.
           *
           * Emits a {Transfer} event with `from` set to the zero address.
           *
           * Requirements
           *
           * - `to` cannot be the zero address.
           */
          function _mint(address account, uint256 amount) internal {
              require(account != address(0), "ERC20: mint to the zero address");
      
              _totalSupply = _totalSupply.add(amount);
              _balances[account] = _balances[account].add(amount);
              emit Transfer(address(0), account, amount);
          }
      
          /**
           * @dev Destroys `amount` tokens from `account`, reducing the
           * total supply.
           *
           * Emits a {Transfer} event with `to` set to the zero address.
           *
           * Requirements
           *
           * - `account` cannot be the zero address.
           * - `account` must have at least `amount` tokens.
           */
          function _burn(address account, uint256 amount) internal {
              require(account != address(0), "ERC20: burn from the zero address");
      
              _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
              _totalSupply = _totalSupply.sub(amount);
              emit Transfer(account, address(0), amount);
          }
      
          /**
           * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
           *
           * This is internal function is equivalent to `approve`, and can be used to
           * e.g. set automatic allowances for certain subsystems, etc.
           *
           * Emits an {Approval} event.
           *
           * Requirements:
           *
           * - `owner` cannot be the zero address.
           * - `spender` cannot be the zero address.
           */
          function _approve(address owner, address spender, uint256 amount) internal {
              require(owner != address(0), "ERC20: approve from the zero address");
              require(spender != address(0), "ERC20: approve to the zero address");
      
              _allowances[owner][spender] = amount;
              emit Approval(owner, spender, amount);
          }
      
          /**
           * @dev Destroys `amount` tokens from `account`.`amount` is then deducted
           * from the caller's allowance.
           *
           * See {_burn} and {_approve}.
           */
          function _burnFrom(address account, uint256 amount) internal {
              _burn(account, amount);
              _approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance"));
          }
      }
      
      contract ERC20Detailed is IERC20 {
          string private _name;
          string private _symbol;
          uint8 private _decimals;
      
          /**
           * @dev Sets the values for `name`, `symbol`, and `decimals`. All three of
           * these values are immutable: they can only be set once during
           * construction.
           */
          constructor (string memory name, string memory symbol, uint8 decimals) public {
              _name = name;
              _symbol = symbol;
              _decimals = decimals;
          }
      
          /**
           * @dev Returns the name of the token.
           */
          function name() public view returns (string memory) {
              return _name;
          }
      
          /**
           * @dev Returns the symbol of the token, usually a shorter version of the
           * name.
           */
          function symbol() public view returns (string memory) {
              return _symbol;
          }
      
          /**
           * @dev Returns the number of decimals used to get its user representation.
           * For example, if `decimals` equals `2`, a balance of `505` tokens should
           * be displayed to a user as `5,05` (`505 / 10 ** 2`).
           *
           * Tokens usually opt for a value of 18, imitating the relationship between
           * Ether and Wei.
           *
           * NOTE: This information is only used for _display_ purposes: it in
           * no way affects any of the arithmetic of the contract, including
           * {IERC20-balanceOf} and {IERC20-transfer}.
           */
          function decimals() public view returns (uint8) {
              return _decimals;
          }
      }
      
      contract Ownable is Context {
          address private _owner;
      
          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
      
          /**
           * @dev Initializes the contract setting the deployer as the initial owner.
           */
          constructor () internal {
              address msgSender = _msgSender();
              _owner = msgSender;
              emit OwnershipTransferred(address(0), msgSender);
          }
      
          /**
           * @dev Returns the address of the current owner.
           */
          function owner() public view returns (address) {
              return _owner;
          }
      
          /**
           * @dev Throws if called by any account other than the owner.
           */
          modifier onlyOwner() {
              require(isOwner(), "Ownable: caller is not the owner");
              _;
          }
      
          /**
           * @dev Returns true if the caller is the current owner.
           */
          function isOwner() public view returns (bool) {
              return _msgSender() == _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 onlyOwner {
              emit OwnershipTransferred(_owner, address(0));
              _owner = address(0);
          }
      
          /**
           * @dev Transfers ownership of the contract to a new account (`newOwner`).
           * Can only be called by the current owner.
           */
          function transferOwnership(address newOwner) public onlyOwner {
              _transferOwnership(newOwner);
          }
      
          /**
           * @dev Transfers ownership of the contract to a new account (`newOwner`).
           */
          function _transferOwnership(address newOwner) internal {
              require(newOwner != address(0), "Ownable: new owner is the zero address");
              emit OwnershipTransferred(_owner, newOwner);
              _owner = newOwner;
          }
      }
      
      library Roles {
          struct Role {
              mapping (address => bool) bearer;
          }
      
          /**
           * @dev Give an account access to this role.
           */
          function add(Role storage role, address account) internal {
              require(!has(role, account), "Roles: account already has role");
              role.bearer[account] = true;
          }
      
          /**
           * @dev Remove an account's access to this role.
           */
          function remove(Role storage role, address account) internal {
              require(has(role, account), "Roles: account does not have role");
              role.bearer[account] = false;
          }
      
          /**
           * @dev Check if an account has this role.
           * @return bool
           */
          function has(Role storage role, address account) internal view returns (bool) {
              require(account != address(0), "Roles: account is the zero address");
              return role.bearer[account];
          }
      }
      
      contract PauserRole is Context {
          using Roles for Roles.Role;
      
          event PauserAdded(address indexed account);
          event PauserRemoved(address indexed account);
      
          Roles.Role private _pausers;
      
          constructor () internal {
              _addPauser(_msgSender());
          }
      
          modifier onlyPauser() {
              require(isPauser(_msgSender()), "PauserRole: caller does not have the Pauser role");
              _;
          }
      
          function isPauser(address account) public view returns (bool) {
              return _pausers.has(account);
          }
      
          function addPauser(address account) public onlyPauser {
              _addPauser(account);
          }
      
          function renouncePauser() public {
              _removePauser(_msgSender());
          }
      
          function _addPauser(address account) internal {
              _pausers.add(account);
              emit PauserAdded(account);
          }
      
          function _removePauser(address account) internal {
              _pausers.remove(account);
              emit PauserRemoved(account);
          }
      }
      
      contract Pausable is Context, PauserRole {
          /**
           * @dev Emitted when the pause is triggered by a pauser (`account`).
           */
          event Paused(address account);
      
          /**
           * @dev Emitted when the pause is lifted by a pauser (`account`).
           */
          event Unpaused(address account);
      
          bool private _paused;
      
          /**
           * @dev Initializes the contract in unpaused state. Assigns the Pauser role
           * to the deployer.
           */
          constructor () internal {
              _paused = false;
          }
      
          /**
           * @dev Returns true if the contract is paused, and false otherwise.
           */
          function paused() public view returns (bool) {
              return _paused;
          }
      
          /**
           * @dev Modifier to make a function callable only when the contract is not paused.
           */
          modifier whenNotPaused() {
              require(!_paused, "Pausable: paused");
              _;
          }
      
          /**
           * @dev Modifier to make a function callable only when the contract is paused.
           */
          modifier whenPaused() {
              require(_paused, "Pausable: not paused");
              _;
          }
      
          /**
           * @dev Called by a pauser to pause, triggers stopped state.
           */
          function pause() public onlyPauser whenNotPaused {
              _paused = true;
              emit Paused(_msgSender());
          }
      
          /**
           * @dev Called by a pauser to unpause, returns to normal state.
           */
          function unpause() public onlyPauser whenPaused {
              _paused = false;
              emit Unpaused(_msgSender());
          }
      }
      
      contract OwnedPausalbe is Pausable, Ownable {
          modifier onlyOwnerOrNotPaused() {
              if (!isOwner()) {
                  require(!paused(), "Pausable: paused");
              }
              _;
          }
      }
      
      contract PHAToken is ERC20, ERC20Detailed, OwnedPausalbe {
          constructor(uint256 initialSupply) ERC20Detailed("Phala", "PHA", 18) public {
              _mint(msg.sender, initialSupply);
              pause();
          }
      
          function transfer(address to, uint256 value) public onlyOwnerOrNotPaused returns (bool) {
              return super.transfer(to, value);
          }
      
          function transferFrom(address from, address to, uint256 value) public returns (bool) {
              if (from != owner()) {
                  require(!paused(), "Pausable: paused");
              }
              return super.transferFrom(from, to, value);
          }
      
          function approve(address spender, uint256 value) public onlyOwnerOrNotPaused returns (bool) {
              return super.approve(spender, value);
          }
      
          function increaseAllowance(address spender, uint256 addedValue) public onlyOwnerOrNotPaused returns (bool) {
              return super.increaseAllowance(spender, addedValue);
          }
      
          function decreaseAllowance(address spender, uint256 subtractedValue) public onlyOwnerOrNotPaused returns (bool) {
              return super.decreaseAllowance(spender, subtractedValue);
          }
      }