ETH Price: $2,241.42 (-1.15%)

Transaction Decoder

Block:
12935128 at Jul-31-2021 07:16:23 PM +UTC
Transaction Fee:
0.017037605 ETH $38.19
Gas Used:
262,117 Gas / 65 Gwei

Emitted Events:

123 Dai.Transfer( src=[Sender] 0x5a28124ae1f80c15eb682bee0ef3a7df705e25ae, dst=ERC20Handler, wad=6902000000000000000000 )
124 MainnetBridge.Deposit( destinationChainID=2, resourceID=00000000000000000000006B175474E89094C44DA98B954EEDEAC495271D0F01, depositNonce=599 )

Account State Difference:

  Address   Before After State Difference Code
(Spark Pool)
61.51327198882027346 Eth61.53030959382027346 Eth0.017037605
0x5a28124a...f705e25AE
0.04699895 Eth
Nonce: 1
0.029961345 Eth
Nonce: 2
0.017037605
0x6B175474...495271d0F
0x7D0e6373...7fb897679
0xf4684EB7...5ABa34b87

Execution Trace

MainnetBridge.deposit( destinationChainID=2, resourceID=00000000000000000000006B175474E89094C44DA98B954EEDEAC495271D0F01, data=0x000000000000000000000000000000000000000000000176287DCFA30218000000000000000000000000000000000000000000000000000000000000000000145A28124AE1F80C15EB682BEE0EF3A7DF705E25AE )
  • ERC20Handler.deposit( resourceID=00000000000000000000006B175474E89094C44DA98B954EEDEAC495271D0F01, destinationChainID=2, depositNonce=599, depositer=0x5a28124aE1F80c15EB682BEE0Ef3a7df705e25AE, data=0x000000000000000000000000000000000000000000000176287DCFA30218000000000000000000000000000000000000000000000000000000000000000000145A28124AE1F80C15EB682BEE0EF3A7DF705E25AE )
    • Dai.transferFrom( src=0x5a28124aE1F80c15EB682BEE0Ef3a7df705e25AE, dst=0xf4684EB75659Bec9C3c3b19f075a6fd5ABa34b87, wad=6902000000000000000000 ) => ( True )
      File 1 of 3: MainnetBridge
      pragma solidity 0.6.12;
      pragma experimental ABIEncoderV2;
      import "./Bridge.sol";
      contract MainnetBridge is Bridge {
       
        // The mainnet bridge isn't altered at all.
        // However we need to call the _deposit and _executeProposal functions
        // that have been made internal.
        constructor (uint8 chainID, address[] memory initialRelayers, uint initialRelayerThreshold, uint256 fee, uint256 expiry, address forwarder) Bridge(chainID, initialRelayers, initialRelayerThreshold, fee, expiry, forwarder) public {}
        function deposit(uint8 destinationChainID, bytes32 resourceID, bytes calldata data) external whenNotPaused {
          _deposit(destinationChainID, resourceID, data);
        }
        function executeProposal(uint8 chainID, uint64 depositNonce, bytes calldata data, bytes32 resourceID) external onlyRelayers whenNotPaused {
          _executeProposal(chainID, depositNonce, data, resourceID);
        }
      }// SPDX-License-Identifier: MIT
      pragma solidity ^0.6.0;
      import "@openzeppelin/contracts/math/SafeMath.sol";
      ////
      // NOTE: This has been edited to use OZ's SafeMath while
      // preserving the ability to inherit from it.
      // This is a shim to keep most of Chainbridge's code intact.
      ////
      import "@openzeppelin/contracts/math/SafeMath.sol";
      /**
       * @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 SafeMathBase {
          /**
           * @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 SafeMath.sub(a, b);
          }
      }// SPDX-License-Identifier: MIT
      pragma solidity ^0.6.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 () internal {
              _paused = false;
          }
          /**
           * @dev Returns true if the contract is paused, and false otherwise.
           */
          function paused() public view returns (bool) {
              return _paused;
          }
          /**
           * @dev Modifier to make a function callable only when the contract is not paused.
           *
           * Requirements:
           *
           * - The contract must not be paused.
           */
          modifier whenNotPaused() {
              _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);
          }
      }// This code was taken from the chainbridge-solidity project listed below,
      // licensed under MIT. Slight modifications (downgraded to 0.6.0), branched from
      // the v4.0.1 tag. 
      // 
      // https://github.com/OpenZeppelin/openzeppelin-contracts/
      pragma solidity ^0.6.0;
      import "@openzeppelin/contracts/utils/Context.sol";
      /*
       * @dev Context variant with ERC2771 support.
       */
      contract ERC2771Context is Context {
          address immutable _trustedForwarder;
          constructor(address trustedForwarder) internal {
              _trustedForwarder = trustedForwarder;
          }
          function isTrustedForwarder(address forwarder) public view virtual returns(bool) {
              return forwarder == _trustedForwarder;
          }
          function _msgSender() internal view virtual override(Context) returns (address payable sender) {
              if (isTrustedForwarder(msg.sender)) {
                  // The assembly code is more direct than the Solidity version using `abi.decode`.
                  assembly { sender := shr(96, calldataload(sub(calldatasize(), 20))) }
              } else {
                  return super._msgSender();
              }
          }
          function _msgData() internal view virtual override(Context) returns (bytes memory) {
              if (isTrustedForwarder(msg.sender)) {
                  return bytes(msg.data[:msg.data.length-20]);
              } else {
                  return super._msgData();
              }
          }
      }// This code was taken from the chainbridge-solidity project listed below,
      // licensed under MIT. Branched from the v3.0.1 tag.
      // We've replaced use of msgSender with msg.sender everywhere. We need to do this because we use access control in the 
      // constructor of Bridge.sol but _msgSender() cannot be called from a constructor as it accesses `immutable` vars.
      // https://forum.openzeppelin.com/t/erc2771context-typeerror-immutable-variables-cannot-be-read-during-contract-creation-time/8513
      // Also removed inheritance of Context.sol since we dont need it now.
      // Note that since we've replaced _msgSender with msg.sender in here care should
      // be take when using a Forwarder contract to call functions in this method. In this
      // case the msg.sender will be the Forwarder contract instead of the admin. 
      // 
      // https://github.com/OpenZeppelin/openzeppelin-contracts/
      pragma solidity ^0.6.0;
      import "@openzeppelin/contracts/utils/EnumerableSet.sol";
      import "@openzeppelin/contracts/utils/Address.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 {
          using EnumerableSet for EnumerableSet.AddressSet;
          using Address for address;
          struct RoleData {
              EnumerableSet.AddressSet members;
              bytes32 adminRole;
          }
          mapping (bytes32 => RoleData) private _roles;
          bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
          /**
           * @dev Emitted when `account` is granted `role`.
           *
           * `sender` is the account that originated the contract call, an admin role
           * bearer except when using {_setupRole}.
           */
          event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
          /**
           * @dev Emitted when `account` is revoked `role`.
           *
           * `sender` is the account that originated the contract call:
           *   - if using `revokeRole`, it is the admin role bearer
           *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
           */
          event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
          /**
           * @dev Returns `true` if `account` has been granted `role`.
           */
          function hasRole(bytes32 role, address account) public view returns (bool) {
              return _roles[role].members.contains(account);
          }
          /**
           * @dev Returns the number of accounts that have `role`. Can be used
           * together with {getRoleMember} to enumerate all bearers of a role.
           */
          function getRoleMemberCount(bytes32 role) public view returns (uint256) {
              return _roles[role].members.length();
          }
          /**
           * @dev Returns one of the accounts that have `role`. `index` must be a
           * value between 0 and {getRoleMemberCount}, non-inclusive.
           *
           * Role bearers are not sorted in any particular way, and their ordering may
           * change at any point.
           *
           * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
           * you perform all queries on the same block. See the following
           * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
           * for more information.
           */
          function getRoleMember(bytes32 role, uint256 index) public view returns (address) {
              return _roles[role].members.at(index);
          }
          /**
           * @dev Returns the admin role that controls `role`. See {grantRole} and
           * {revokeRole}.
           *
           * To change a role's admin, use {_setRoleAdmin}.
           */
          function getRoleAdmin(bytes32 role) public view returns (bytes32) {
              return _roles[role].adminRole;
          }
          /**
           * @dev Grants `role` to `account`.
           *
           * If `account` had not been already granted `role`, emits a {RoleGranted}
           * event.
           *
           * Requirements:
           *
           * - the caller must have ``role``'s admin role.
           */
          function grantRole(bytes32 role, address account) public virtual {
              require(hasRole(_roles[role].adminRole, msg.sender), "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, msg.sender), "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 == msg.sender, "AccessControl: can only renounce roles for self");
              _revokeRole(role, account);
          }
          /**
           * @dev Grants `role` to `account`.
           *
           * If `account` had not been already granted `role`, emits a {RoleGranted}
           * event. Note that unlike {grantRole}, this function doesn't perform any
           * checks on the calling account.
           *
           * [WARNING]
           * ====
           * This function should only be called from the constructor when setting
           * up the initial roles for the system.
           *
           * Using this function in any other way is effectively circumventing the admin
           * system imposed by {AccessControl}.
           * ====
           */
          function _setupRole(bytes32 role, address account) internal virtual {
              _grantRole(role, account);
          }
          /**
           * @dev Sets `adminRole` as ``role``'s admin role.
           */
          function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
              _roles[role].adminRole = adminRole;
          }
          function _grantRole(bytes32 role, address account) private {
              if (_roles[role].members.add(account)) {
                  emit RoleGranted(role, account, msg.sender);
              }
          }
          function _revokeRole(bytes32 role, address account) private {
              if (_roles[role].members.remove(account)) {
                  emit RoleRevoked(role, account, msg.sender);
              }
          }
      }// This code was taken from the chainbridge-solidity project listed below,
      // licensed under GPL v3. We've made slight modifications, branched from
      // the v1.0.0 tag. 
      // 
      // https://github.com/ChainSafe/chainbridge-solidity.git
      pragma solidity ^0.6.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;
      }// This code was taken from the chainbridge-solidity project listed below,
      // licensed under GPL v3. We've made slight modifications, branched from
      // the v1.0.0 tag. 
      // 
      // https://github.com/ChainSafe/chainbridge-solidity.git
      pragma solidity ^0.6.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 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;
      }
      // This code was taken from the chainbridge-solidity project listed below,
      // licensed under GPL v3. We've made slight modifications, branched from
      // the v1.0.0 tag. 
      // 
      // https://github.com/ChainSafe/chainbridge-solidity.git
      pragma solidity ^0.6.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;
      }
      // This code was taken from the chainbridge-solidity project listed below,
      // licensed under GPL v3. We've made slight modifications, branched from
      // the v1.0.0 tag. 
      // 
      // https://github.com/ChainSafe/chainbridge-solidity.git
      pragma solidity ^0.6.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);
      }// This code was taken from the chainbridge-solidity project listed below,
      // licensed under GPL v3. We've made slight modifications, branched from
      // the v1.0.0 tag. 
      // 
      // https://github.com/ChainSafe/chainbridge-solidity.git
      pragma solidity 0.6.12;
      pragma experimental ABIEncoderV2;
      import "./utils/AccessControl.sol";
      import "./utils/SafeMathBase.sol";
      import "./utils/Pausable.sol";
      import "./utils/ERC2771Context.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, SafeMathBase, ERC2771Context {
          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, _msgSender()) || hasRole(RELAYER_ROLE, _msgSender()),
                  "sender is not relayer or admin");
          }
          function _onlyAdmin() private {
              require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "sender doesn't have admin role");
          }
          function _onlyRelayers() private {
              require(hasRole(RELAYER_ROLE, _msgSender()), "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,
              address forwarder
          ) ERC2771Context(forwarder) public {
              _chainID = chainID;
              _relayerThreshold = initialRelayerThreshold;
              _fee = fee;
              _expiry = expiry;
              _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
              _setRoleAdmin(RELAYER_ROLE, DEFAULT_ADMIN_ROLE);
              for (uint i; i < initialRelayers.length; 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 {_msgSender()} 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, _msgSender());
          }
          /**
              @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 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 memory data) internal 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, _msgSender(), data);
              emit Deposit(destinationChainID, resourceID, depositNonce);
          }
          /**
              @notice When called, {_msgSender()} 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 {_msgSender()} 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];
              address msgSender = _msgSender();
              require(_resourceIDToHandlerAddress[resourceID] != address(0), "no handler for resourceID");
              require(uint(proposal._status) <= 1, "proposal already passed/executed/cancelled");
              require(!_hasVotedOnProposal[nonceAndID][dataHash][msgSender], "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] = msgSender;
                  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(msgSender);
                  }
              }
              if (proposal._status != ProposalStatus.Cancelled) {
                  _hasVotedOnProposal[nonceAndID][dataHash][msgSender] = 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 memory data, bytes32 resourceID) internal 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.Inactive, "proposal is not active");
              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 {
              for (uint i = 0; i < addrs.length; 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;
      /**
       * @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;
          }
      }
      

      File 2 of 3: ERC20Handler
      pragma solidity 0.6.12;
      pragma experimental ABIEncoderV2;
      import "../interfaces/IDepositExecute.sol";
      import "./HandlerHelpers.sol";
      import "../ERC20Safe.sol";
      import "@openzeppelin/contracts/presets/ERC20PresetMinterPauser.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 {
          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;
          /**
              @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;
              for (uint256 i = 0; i < initialResourceIDs.length; i++) {
                  _setResource(initialResourceIDs[i], initialContractAddresses[i]);
              }
              for (uint256 i = 0; i < burnableContractAddresses.length; 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(), 0xE) // 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,
                  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), amount);
              } else {
                  releaseERC20(tokenAddress, address(recipientAddress), 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);
          }
      }
      // This code was taken from the chainbridge-solidity project listed below,
      // licensed under GPL v3. We've made slight modifications, branched from
      // the v1.0.0 tag. 
      // 
      // https://github.com/ChainSafe/chainbridge-solidity.git
      pragma solidity ^0.6.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 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;
      }
      // This code was taken from the chainbridge-solidity project listed below,
      // licensed under GPL v3. We've made slight modifications, branched from
      // the v1.0.0 tag. 
      // 
      // https://github.com/ChainSafe/chainbridge-solidity.git
      pragma solidity ^0.6.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.6.12;
      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 {
          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;
          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 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;
          }
      }
      pragma solidity 0.6.12;
      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 transfer tokens into the safe to fund proposals.
              @param tokenAddress Address of ERC20 to transfer.
              @param owner Address of current token owner.
              @param amount Amount of tokens to transfer.
           */
          function fundERC20(address tokenAddress, address owner, uint256 amount) public {
              IERC20 erc20 = IERC20(tokenAddress);
              _safeTransferFrom(erc20, owner, address(this), amount);
          }
          /**
              @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");
              }
          }
      }
      // 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());
              }
          }
      }
      

      File 3 of 3: Dai
      // hevm: flattened sources of /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/dai.sol
      pragma solidity =0.5.12;
      
      ////// /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/lib.sol
      // This program is free software: you can redistribute it and/or modify
      // it under the terms of the GNU General Public License as published by
      // the Free Software Foundation, either version 3 of the License, or
      // (at your option) any later version.
      
      // This program is distributed in the hope that it will be useful,
      // but WITHOUT ANY WARRANTY; without even the implied warranty of
      // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      // GNU General Public License for more details.
      
      // You should have received a copy of the GNU General Public License
      // along with this program.  If not, see <http://www.gnu.org/licenses/>.
      
      /* pragma solidity 0.5.12; */
      
      contract LibNote {
          event LogNote(
              bytes4   indexed  sig,
              address  indexed  usr,
              bytes32  indexed  arg1,
              bytes32  indexed  arg2,
              bytes             data
          ) anonymous;
      
          modifier note {
              _;
              assembly {
                  // log an 'anonymous' event with a constant 6 words of calldata
                  // and four indexed topics: selector, caller, arg1 and arg2
                  let mark := msize                         // end of memory ensures zero
                  mstore(0x40, add(mark, 288))              // update free memory pointer
                  mstore(mark, 0x20)                        // bytes type data offset
                  mstore(add(mark, 0x20), 224)              // bytes size (padded)
                  calldatacopy(add(mark, 0x40), 0, 224)     // bytes payload
                  log4(mark, 288,                           // calldata
                       shl(224, shr(224, calldataload(0))), // msg.sig
                       caller,                              // msg.sender
                       calldataload(4),                     // arg1
                       calldataload(36)                     // arg2
                      )
              }
          }
      }
      
      ////// /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/dai.sol
      // Copyright (C) 2017, 2018, 2019 dbrock, rain, mrchico
      
      // This program is free software: you can redistribute it and/or modify
      // it under the terms of the GNU Affero General Public License as published by
      // the Free Software Foundation, either version 3 of the License, or
      // (at your option) any later version.
      //
      // This program is distributed in the hope that it will be useful,
      // but WITHOUT ANY WARRANTY; without even the implied warranty of
      // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      // GNU Affero General Public License for more details.
      //
      // You should have received a copy of the GNU Affero General Public License
      // along with this program.  If not, see <https://www.gnu.org/licenses/>.
      
      /* pragma solidity 0.5.12; */
      
      /* import "./lib.sol"; */
      
      contract Dai is LibNote {
          // --- Auth ---
          mapping (address => uint) public wards;
          function rely(address guy) external note auth { wards[guy] = 1; }
          function deny(address guy) external note auth { wards[guy] = 0; }
          modifier auth {
              require(wards[msg.sender] == 1, "Dai/not-authorized");
              _;
          }
      
          // --- ERC20 Data ---
          string  public constant name     = "Dai Stablecoin";
          string  public constant symbol   = "DAI";
          string  public constant version  = "1";
          uint8   public constant decimals = 18;
          uint256 public totalSupply;
      
          mapping (address => uint)                      public balanceOf;
          mapping (address => mapping (address => uint)) public allowance;
          mapping (address => uint)                      public nonces;
      
          event Approval(address indexed src, address indexed guy, uint wad);
          event Transfer(address indexed src, address indexed dst, uint wad);
      
          // --- Math ---
          function add(uint x, uint y) internal pure returns (uint z) {
              require((z = x + y) >= x);
          }
          function sub(uint x, uint y) internal pure returns (uint z) {
              require((z = x - y) <= x);
          }
      
          // --- EIP712 niceties ---
          bytes32 public DOMAIN_SEPARATOR;
          // bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address holder,address spender,uint256 nonce,uint256 expiry,bool allowed)");
          bytes32 public constant PERMIT_TYPEHASH = 0xea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb;
      
          constructor(uint256 chainId_) public {
              wards[msg.sender] = 1;
              DOMAIN_SEPARATOR = keccak256(abi.encode(
                  keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                  keccak256(bytes(name)),
                  keccak256(bytes(version)),
                  chainId_,
                  address(this)
              ));
          }
      
          // --- Token ---
          function transfer(address dst, uint wad) external returns (bool) {
              return transferFrom(msg.sender, dst, wad);
          }
          function transferFrom(address src, address dst, uint wad)
              public returns (bool)
          {
              require(balanceOf[src] >= wad, "Dai/insufficient-balance");
              if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) {
                  require(allowance[src][msg.sender] >= wad, "Dai/insufficient-allowance");
                  allowance[src][msg.sender] = sub(allowance[src][msg.sender], wad);
              }
              balanceOf[src] = sub(balanceOf[src], wad);
              balanceOf[dst] = add(balanceOf[dst], wad);
              emit Transfer(src, dst, wad);
              return true;
          }
          function mint(address usr, uint wad) external auth {
              balanceOf[usr] = add(balanceOf[usr], wad);
              totalSupply    = add(totalSupply, wad);
              emit Transfer(address(0), usr, wad);
          }
          function burn(address usr, uint wad) external {
              require(balanceOf[usr] >= wad, "Dai/insufficient-balance");
              if (usr != msg.sender && allowance[usr][msg.sender] != uint(-1)) {
                  require(allowance[usr][msg.sender] >= wad, "Dai/insufficient-allowance");
                  allowance[usr][msg.sender] = sub(allowance[usr][msg.sender], wad);
              }
              balanceOf[usr] = sub(balanceOf[usr], wad);
              totalSupply    = sub(totalSupply, wad);
              emit Transfer(usr, address(0), wad);
          }
          function approve(address usr, uint wad) external returns (bool) {
              allowance[msg.sender][usr] = wad;
              emit Approval(msg.sender, usr, wad);
              return true;
          }
      
          // --- Alias ---
          function push(address usr, uint wad) external {
              transferFrom(msg.sender, usr, wad);
          }
          function pull(address usr, uint wad) external {
              transferFrom(usr, msg.sender, wad);
          }
          function move(address src, address dst, uint wad) external {
              transferFrom(src, dst, wad);
          }
      
          // --- Approve by signature ---
          function permit(address holder, address spender, uint256 nonce, uint256 expiry,
                          bool allowed, uint8 v, bytes32 r, bytes32 s) external
          {
              bytes32 digest =
                  keccak256(abi.encodePacked(
                      "\x19\x01",
                      DOMAIN_SEPARATOR,
                      keccak256(abi.encode(PERMIT_TYPEHASH,
                                           holder,
                                           spender,
                                           nonce,
                                           expiry,
                                           allowed))
              ));
      
              require(holder != address(0), "Dai/invalid-address-0");
              require(holder == ecrecover(digest, v, r, s), "Dai/invalid-permit");
              require(expiry == 0 || now <= expiry, "Dai/permit-expired");
              require(nonce == nonces[holder]++, "Dai/invalid-nonce");
              uint wad = allowed ? uint(-1) : 0;
              allowance[holder][spender] = wad;
              emit Approval(holder, spender, wad);
          }
      }