ETH Price: $2,238.62 (-1.19%)
Gas: 0.46 Gwei

Transaction Decoder

Block:
11712692 at Jan-23-2021 03:43:01 PM +UTC
Transaction Fee:
0.018699753 ETH $41.86
Gas Used:
256,161 Gas / 73 Gwei

Emitted Events:

234 BaseRegistrarImplementation.Transfer( from=0x00000000...000000000, to=[Receiver] ETHRegistrarController, tokenId=102636122634934747982534024207144672723666485790198695215555987896547919638124 )
235 ENSRegistryWithFallback.NewOwner( node=93CDEB708B7545DC668EB9280176169D1C33CFD8ED6F04690A0BCC88A93FC4AE, label=E2E9FC9D9A2233A5389A499C485874813874B5F6C70FE23C2B4A8D8602C1AE6C, owner=[Receiver] ETHRegistrarController )
236 BaseRegistrarImplementation.NameRegistered( id=102636122634934747982534024207144672723666485790198695215555987896547919638124, owner=[Receiver] ETHRegistrarController, expires=1642973533 )
237 ENSRegistryWithFallback.NewResolver( node=7FFCE06A614563A5AB664E3624F40A4EADEE4F81EE96B6F243BEF9C95C4713BC, resolver=PublicResolver )
238 PublicResolver.AddressChanged( node=7FFCE06A614563A5AB664E3624F40A4EADEE4F81EE96B6F243BEF9C95C4713BC, coinType=60, newAddress=[Sender] 0xa058f019548930c61e9f324edd6ed224f9c7c2ae )
239 PublicResolver.AddrChanged( node=7FFCE06A614563A5AB664E3624F40A4EADEE4F81EE96B6F243BEF9C95C4713BC, a=[Sender] 0xa058f019548930c61e9f324edd6ed224f9c7c2ae )
240 ENSRegistryWithFallback.NewOwner( node=93CDEB708B7545DC668EB9280176169D1C33CFD8ED6F04690A0BCC88A93FC4AE, label=E2E9FC9D9A2233A5389A499C485874813874B5F6C70FE23C2B4A8D8602C1AE6C, owner=[Sender] 0xa058f019548930c61e9f324edd6ed224f9c7c2ae )
241 BaseRegistrarImplementation.Transfer( from=[Receiver] ETHRegistrarController, to=[Sender] 0xa058f019548930c61e9f324edd6ed224f9c7c2ae, tokenId=102636122634934747982534024207144672723666485790198695215555987896547919638124 )
242 ETHRegistrarController.NameRegistered( name=yatra, label=E2E9FC9D9A2233A5389A499C485874813874B5F6C70FE23C2B4A8D8602C1AE6C, owner=[Sender] 0xa058f019548930c61e9f324edd6ed224f9c7c2ae, cost=4053622338460487, expires=1642973533 )

Account State Difference:

  Address   Before After State Difference Code
0x00000000...A6C7d2e1e
(ENS: Registry with Fallback)
0x283Af0B2...A627EB7F5
(ENS: Old ETH Registrar Controller)
5,188.453870564509309628 Eth5,188.457924186847770115 Eth0.004053622338460487
0x4976fb03...78EBaBa41
(ENS: Public Resolver 2)
0x57f1887a...Af147eA85
(Spark Pool)
45.384586536528639331 Eth45.403286289528639331 Eth0.018699753
0xa058F019...4f9C7c2aE
0.04727891 Eth
Nonce: 21
0.024525534661539513 Eth
Nonce: 22
0.022753375338460487

Execution Trace

ETH 0.004458984572306535 ETHRegistrarController.registerWithConfig( name=yatra, owner=0xa058F019548930c61e9F324EDd6eD224f9C7c2aE, duration=31556952, secret=46E62BA2D020D2DB37D382598C8BE637850BBACB3770C6CCF38D4D3FDDFD358A, resolver=0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41, addr=0xa058F019548930c61e9F324EDd6eD224f9C7c2aE )
  • BaseRegistrarImplementation.available( id=102636122634934747982534024207144672723666485790198695215555987896547919638124 ) => ( True )
  • BaseRegistrarImplementation.nameExpires( id=102636122634934747982534024207144672723666485790198695215555987896547919638124 ) => ( 0 )
  • LinearPremiumPriceOracle.price( name=yatra, expires=0, duration=31556952 ) => ( 4053622338460487 )
    • EACAggregatorProxy.STATICCALL( )
      • AccessControlledAggregator.STATICCALL( )
      • BaseRegistrarImplementation.register( id=102636122634934747982534024207144672723666485790198695215555987896547919638124, owner=0x283Af0B28c62C092C9727F1Ee09c02CA627EB7F5, duration=31556952 ) => ( 1642973533 )
        • ENSRegistryWithFallback.owner( node=93CDEB708B7545DC668EB9280176169D1C33CFD8ED6F04690A0BCC88A93FC4AE ) => ( 0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85 )
        • ENSRegistryWithFallback.setSubnodeOwner( node=93CDEB708B7545DC668EB9280176169D1C33CFD8ED6F04690A0BCC88A93FC4AE, label=E2E9FC9D9A2233A5389A499C485874813874B5F6C70FE23C2B4A8D8602C1AE6C, owner=0x283Af0B28c62C092C9727F1Ee09c02CA627EB7F5 ) => ( 7FFCE06A614563A5AB664E3624F40A4EADEE4F81EE96B6F243BEF9C95C4713BC )
        • BaseRegistrarImplementation.STATICCALL( )
        • BaseRegistrarImplementation.STATICCALL( )
        • ENSRegistryWithFallback.setResolver( node=7FFCE06A614563A5AB664E3624F40A4EADEE4F81EE96B6F243BEF9C95C4713BC, resolver=0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41 )
        • PublicResolver.setAddr( node=7FFCE06A614563A5AB664E3624F40A4EADEE4F81EE96B6F243BEF9C95C4713BC, a=0xa058F019548930c61e9F324EDd6eD224f9C7c2aE )
          • ENSRegistryWithFallback.owner( node=7FFCE06A614563A5AB664E3624F40A4EADEE4F81EE96B6F243BEF9C95C4713BC ) => ( 0x283Af0B28c62C092C9727F1Ee09c02CA627EB7F5 )
          • ENSRegistryWithFallback.owner( node=7FFCE06A614563A5AB664E3624F40A4EADEE4F81EE96B6F243BEF9C95C4713BC ) => ( 0x283Af0B28c62C092C9727F1Ee09c02CA627EB7F5 )
          • BaseRegistrarImplementation.reclaim( id=102636122634934747982534024207144672723666485790198695215555987896547919638124, owner=0xa058F019548930c61e9F324EDd6eD224f9C7c2aE )
            • ENSRegistryWithFallback.owner( node=93CDEB708B7545DC668EB9280176169D1C33CFD8ED6F04690A0BCC88A93FC4AE ) => ( 0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85 )
            • ENSRegistryWithFallback.setSubnodeOwner( node=93CDEB708B7545DC668EB9280176169D1C33CFD8ED6F04690A0BCC88A93FC4AE, label=E2E9FC9D9A2233A5389A499C485874813874B5F6C70FE23C2B4A8D8602C1AE6C, owner=0xa058F019548930c61e9F324EDd6eD224f9C7c2aE ) => ( 7FFCE06A614563A5AB664E3624F40A4EADEE4F81EE96B6F243BEF9C95C4713BC )
            • BaseRegistrarImplementation.transferFrom( from=0x283Af0B28c62C092C9727F1Ee09c02CA627EB7F5, to=0xa058F019548930c61e9F324EDd6eD224f9C7c2aE, tokenId=102636122634934747982534024207144672723666485790198695215555987896547919638124 )
            • ETH 0.000405362233846048 0xa058f019548930c61e9f324edd6ed224f9c7c2ae.CALL( )
              File 1 of 7: ETHRegistrarController
              // File: @ensdomains/ethregistrar/contracts/PriceOracle.sol
              
              pragma solidity >=0.4.24;
              
              interface PriceOracle {
                  /**
                   * @dev Returns the price to register or renew a name.
                   * @param name The name being registered or renewed.
                   * @param expires When the name presently expires (0 if this is a new registration).
                   * @param duration How long the name is being registered or extended for, in seconds.
                   * @return The price of this renewal or registration, in wei.
                   */
                  function price(string calldata name, uint expires, uint duration) external view returns(uint);
              }
              
              // File: @ensdomains/ens/contracts/ENS.sol
              
              pragma solidity >=0.4.24;
              
              interface ENS {
              
                  // Logged when the owner of a node assigns a new owner to a subnode.
                  event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner);
              
                  // Logged when the owner of a node transfers ownership to a new account.
                  event Transfer(bytes32 indexed node, address owner);
              
                  // Logged when the resolver for a node changes.
                  event NewResolver(bytes32 indexed node, address resolver);
              
                  // Logged when the TTL of a node changes
                  event NewTTL(bytes32 indexed node, uint64 ttl);
              
                  // Logged when an operator is added or removed.
                  event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
              
                  function setRecord(bytes32 node, address owner, address resolver, uint64 ttl) external;
                  function setSubnodeRecord(bytes32 node, bytes32 label, address owner, address resolver, uint64 ttl) external;
                  function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external returns(bytes32);
                  function setResolver(bytes32 node, address resolver) external;
                  function setOwner(bytes32 node, address owner) external;
                  function setTTL(bytes32 node, uint64 ttl) external;
                  function setApprovalForAll(address operator, bool approved) external;
                  function owner(bytes32 node) external view returns (address);
                  function resolver(bytes32 node) external view returns (address);
                  function ttl(bytes32 node) external view returns (uint64);
                  function recordExists(bytes32 node) external view returns (bool);
                  function isApprovedForAll(address owner, address operator) external view returns (bool);
              }
              
              // File: openzeppelin-solidity/contracts/introspection/IERC165.sol
              
              pragma solidity ^0.5.0;
              
              /**
               * @title IERC165
               * @dev https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md
               */
              interface IERC165 {
                  /**
                   * @notice Query if a contract implements an interface
                   * @param interfaceId The interface identifier, as specified in ERC-165
                   * @dev Interface identification is specified in ERC-165. This function
                   * uses less than 30,000 gas.
                   */
                  function supportsInterface(bytes4 interfaceId) external view returns (bool);
              }
              
              // File: openzeppelin-solidity/contracts/token/ERC721/IERC721.sol
              
              pragma solidity ^0.5.0;
              
              
              /**
               * @title ERC721 Non-Fungible Token Standard basic interface
               * @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
               */
              contract IERC721 is IERC165 {
                  event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
                  event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
                  event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
              
                  function balanceOf(address owner) public view returns (uint256 balance);
                  function ownerOf(uint256 tokenId) public view returns (address owner);
              
                  function approve(address to, uint256 tokenId) public;
                  function getApproved(uint256 tokenId) public view returns (address operator);
              
                  function setApprovalForAll(address operator, bool _approved) public;
                  function isApprovedForAll(address owner, address operator) public view returns (bool);
              
                  function transferFrom(address from, address to, uint256 tokenId) public;
                  function safeTransferFrom(address from, address to, uint256 tokenId) public;
              
                  function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public;
              }
              
              // File: openzeppelin-solidity/contracts/ownership/Ownable.sol
              
              pragma solidity ^0.5.0;
              
              /**
               * @title Ownable
               * @dev The Ownable contract has an owner address, and provides basic authorization control
               * functions, this simplifies the implementation of "user permissions".
               */
              contract Ownable {
                  address private _owner;
              
                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
              
                  /**
                   * @dev The Ownable constructor sets the original `owner` of the contract to the sender
                   * account.
                   */
                  constructor () internal {
                      _owner = msg.sender;
                      emit OwnershipTransferred(address(0), _owner);
                  }
              
                  /**
                   * @return the address of the owner.
                   */
                  function owner() public view returns (address) {
                      return _owner;
                  }
              
                  /**
                   * @dev Throws if called by any account other than the owner.
                   */
                  modifier onlyOwner() {
                      require(isOwner());
                      _;
                  }
              
                  /**
                   * @return true if `msg.sender` is the owner of the contract.
                   */
                  function isOwner() public view returns (bool) {
                      return msg.sender == _owner;
                  }
              
                  /**
                   * @dev Allows the current owner to relinquish control of the contract.
                   * @notice Renouncing to ownership will leave the contract without an owner.
                   * It will not be possible to call the functions with the `onlyOwner`
                   * modifier anymore.
                   */
                  function renounceOwnership() public onlyOwner {
                      emit OwnershipTransferred(_owner, address(0));
                      _owner = address(0);
                  }
              
                  /**
                   * @dev Allows the current owner to transfer control of the contract to a newOwner.
                   * @param newOwner The address to transfer ownership to.
                   */
                  function transferOwnership(address newOwner) public onlyOwner {
                      _transferOwnership(newOwner);
                  }
              
                  /**
                   * @dev Transfers control of the contract to a newOwner.
                   * @param newOwner The address to transfer ownership to.
                   */
                  function _transferOwnership(address newOwner) internal {
                      require(newOwner != address(0));
                      emit OwnershipTransferred(_owner, newOwner);
                      _owner = newOwner;
                  }
              }
              
              // File: @ensdomains/ethregistrar/contracts/BaseRegistrar.sol
              
              pragma solidity >=0.4.24;
              
              
              
              
              contract BaseRegistrar is IERC721, Ownable {
                  uint constant public GRACE_PERIOD = 90 days;
              
                  event ControllerAdded(address indexed controller);
                  event ControllerRemoved(address indexed controller);
                  event NameMigrated(uint256 indexed id, address indexed owner, uint expires);
                  event NameRegistered(uint256 indexed id, address indexed owner, uint expires);
                  event NameRenewed(uint256 indexed id, uint expires);
              
                  // The ENS registry
                  ENS public ens;
              
                  // The namehash of the TLD this registrar owns (eg, .eth)
                  bytes32 public baseNode;
              
                  // A map of addresses that are authorised to register and renew names.
                  mapping(address=>bool) public controllers;
              
                  // Authorises a controller, who can register and renew domains.
                  function addController(address controller) external;
              
                  // Revoke controller permission for an address.
                  function removeController(address controller) external;
              
                  // Set the resolver for the TLD this registrar manages.
                  function setResolver(address resolver) external;
              
                  // Returns the expiration timestamp of the specified label hash.
                  function nameExpires(uint256 id) external view returns(uint);
              
                  // Returns true iff the specified name is available for registration.
                  function available(uint256 id) public view returns(bool);
              
                  /**
                   * @dev Register a name.
                   */
                  function register(uint256 id, address owner, uint duration) external returns(uint);
              
                  function renew(uint256 id, uint duration) external returns(uint);
              
                  /**
                   * @dev Reclaim ownership of a name in ENS, if you own it in the registrar.
                   */
                  function reclaim(uint256 id, address owner) external;
              }
              
              // File: @ensdomains/ethregistrar/contracts/StringUtils.sol
              
              pragma solidity >=0.4.24;
              
              library StringUtils {
                  /**
                   * @dev Returns the length of a given string
                   *
                   * @param s The string to measure the length of
                   * @return The length of the input string
                   */
                  function strlen(string memory s) internal pure returns (uint) {
                      uint len;
                      uint i = 0;
                      uint bytelength = bytes(s).length;
                      for(len = 0; i < bytelength; len++) {
                          byte b = bytes(s)[i];
                          if(b < 0x80) {
                              i += 1;
                          } else if (b < 0xE0) {
                              i += 2;
                          } else if (b < 0xF0) {
                              i += 3;
                          } else if (b < 0xF8) {
                              i += 4;
                          } else if (b < 0xFC) {
                              i += 5;
                          } else {
                              i += 6;
                          }
                      }
                      return len;
                  }
              }
              
              // File: @ensdomains/resolver/contracts/Resolver.sol
              
              pragma solidity >=0.4.25;
              
              /**
               * A generic resolver interface which includes all the functions including the ones deprecated
               */
              interface Resolver{
                  event AddrChanged(bytes32 indexed node, address a);
                  event AddressChanged(bytes32 indexed node, uint coinType, bytes newAddress);
                  event NameChanged(bytes32 indexed node, string name);
                  event ABIChanged(bytes32 indexed node, uint256 indexed contentType);
                  event PubkeyChanged(bytes32 indexed node, bytes32 x, bytes32 y);
                  event TextChanged(bytes32 indexed node, string indexed indexedKey, string key);
                  event ContenthashChanged(bytes32 indexed node, bytes hash);
                  /* Deprecated events */
                  event ContentChanged(bytes32 indexed node, bytes32 hash);
              
                  function ABI(bytes32 node, uint256 contentTypes) external view returns (uint256, bytes memory);
                  function addr(bytes32 node) external view returns (address);
                  function addr(bytes32 node, uint coinType) external view returns(bytes memory);
                  function contenthash(bytes32 node) external view returns (bytes memory);
                  function dnsrr(bytes32 node) external view returns (bytes memory);
                  function name(bytes32 node) external view returns (string memory);
                  function pubkey(bytes32 node) external view returns (bytes32 x, bytes32 y);
                  function text(bytes32 node, string calldata key) external view returns (string memory);
                  function interfaceImplementer(bytes32 node, bytes4 interfaceID) external view returns (address);
              
                  function setABI(bytes32 node, uint256 contentType, bytes calldata data) external;
                  function setAddr(bytes32 node, address addr) external;
                  function setAddr(bytes32 node, uint coinType, bytes calldata a) external;
                  function setContenthash(bytes32 node, bytes calldata hash) external;
                  function setDnsrr(bytes32 node, bytes calldata data) external;
                  function setName(bytes32 node, string calldata _name) external;
                  function setPubkey(bytes32 node, bytes32 x, bytes32 y) external;
                  function setText(bytes32 node, string calldata key, string calldata value) external;
                  function setInterface(bytes32 node, bytes4 interfaceID, address implementer) external;
              
                  function supportsInterface(bytes4 interfaceID) external pure returns (bool);
              
                  /* Deprecated functions */
                  function content(bytes32 node) external view returns (bytes32);
                  function multihash(bytes32 node) external view returns (bytes memory);
                  function setContent(bytes32 node, bytes32 hash) external;
                  function setMultihash(bytes32 node, bytes calldata hash) external;
              }
              
              // File: @ensdomains/ethregistrar/contracts/ETHRegistrarController.sol
              
              pragma solidity ^0.5.0;
              
              
              
              
              
              
              /**
               * @dev A registrar controller for registering and renewing names at fixed cost.
               */
              contract ETHRegistrarController is Ownable {
                  using StringUtils for *;
              
                  uint constant public MIN_REGISTRATION_DURATION = 28 days;
              
                  bytes4 constant private INTERFACE_META_ID = bytes4(keccak256("supportsInterface(bytes4)"));
                  bytes4 constant private COMMITMENT_CONTROLLER_ID = bytes4(
                      keccak256("rentPrice(string,uint256)") ^
                      keccak256("available(string)") ^
                      keccak256("makeCommitment(string,address,bytes32)") ^
                      keccak256("commit(bytes32)") ^
                      keccak256("register(string,address,uint256,bytes32)") ^
                      keccak256("renew(string,uint256)")
                  );
              
                  bytes4 constant private COMMITMENT_WITH_CONFIG_CONTROLLER_ID = bytes4(
                      keccak256("registerWithConfig(string,address,uint256,bytes32,address,address)") ^
                      keccak256("makeCommitmentWithConfig(string,address,bytes32,address,address)")
                  );
              
                  BaseRegistrar base;
                  PriceOracle prices;
                  uint public minCommitmentAge;
                  uint public maxCommitmentAge;
              
                  mapping(bytes32=>uint) public commitments;
              
                  event NameRegistered(string name, bytes32 indexed label, address indexed owner, uint cost, uint expires);
                  event NameRenewed(string name, bytes32 indexed label, uint cost, uint expires);
                  event NewPriceOracle(address indexed oracle);
              
                  constructor(BaseRegistrar _base, PriceOracle _prices, uint _minCommitmentAge, uint _maxCommitmentAge) public {
                      require(_maxCommitmentAge > _minCommitmentAge);
              
                      base = _base;
                      prices = _prices;
                      minCommitmentAge = _minCommitmentAge;
                      maxCommitmentAge = _maxCommitmentAge;
                  }
              
                  function rentPrice(string memory name, uint duration) view public returns(uint) {
                      bytes32 hash = keccak256(bytes(name));
                      return prices.price(name, base.nameExpires(uint256(hash)), duration);
                  }
              
                  function valid(string memory name) public pure returns(bool) {
                      return name.strlen() >= 3;
                  }
              
                  function available(string memory name) public view returns(bool) {
                      bytes32 label = keccak256(bytes(name));
                      return valid(name) && base.available(uint256(label));
                  }
              
                  function makeCommitment(string memory name, address owner, bytes32 secret) pure public returns(bytes32) {
                      return makeCommitmentWithConfig(name, owner, secret, address(0), address(0));
                  }
              
                  function makeCommitmentWithConfig(string memory name, address owner, bytes32 secret, address resolver, address addr) pure public returns(bytes32) {
                      bytes32 label = keccak256(bytes(name));
                      if (resolver == address(0) && addr == address(0)) {
                          return keccak256(abi.encodePacked(label, owner, secret));
                      }
                      require(resolver != address(0));
                      return keccak256(abi.encodePacked(label, owner, resolver, addr, secret));
                  }
              
                  function commit(bytes32 commitment) public {
                      require(commitments[commitment] + maxCommitmentAge < now);
                      commitments[commitment] = now;
                  }
              
                  function register(string calldata name, address owner, uint duration, bytes32 secret) external payable {
                    registerWithConfig(name, owner, duration, secret, address(0), address(0));
                  }
              
                  function registerWithConfig(string memory name, address owner, uint duration, bytes32 secret, address resolver, address addr) public payable {
                      bytes32 commitment = makeCommitmentWithConfig(name, owner, secret, resolver, addr);
                      uint cost = _consumeCommitment(name, duration, commitment);
              
                      bytes32 label = keccak256(bytes(name));
                      uint256 tokenId = uint256(label);
              
                      uint expires;
                      if(resolver != address(0)) {
                          // Set this contract as the (temporary) owner, giving it
                          // permission to set up the resolver.
                          expires = base.register(tokenId, address(this), duration);
              
                          // The nodehash of this label
                          bytes32 nodehash = keccak256(abi.encodePacked(base.baseNode(), label));
              
                          // Set the resolver
                          base.ens().setResolver(nodehash, resolver);
              
                          // Configure the resolver
                          if (addr != address(0)) {
                              Resolver(resolver).setAddr(nodehash, addr);
                          }
              
                          // Now transfer full ownership to the expeceted owner
                          base.reclaim(tokenId, owner);
                          base.transferFrom(address(this), owner, tokenId);
                      } else {
                          require(addr == address(0));
                          expires = base.register(tokenId, owner, duration);
                      }
              
                      emit NameRegistered(name, label, owner, cost, expires);
              
                      // Refund any extra payment
                      if(msg.value > cost) {
                          msg.sender.transfer(msg.value - cost);
                      }
                  }
              
                  function renew(string calldata name, uint duration) external payable {
                      uint cost = rentPrice(name, duration);
                      require(msg.value >= cost);
              
                      bytes32 label = keccak256(bytes(name));
                      uint expires = base.renew(uint256(label), duration);
              
                      if(msg.value > cost) {
                          msg.sender.transfer(msg.value - cost);
                      }
              
                      emit NameRenewed(name, label, cost, expires);
                  }
              
                  function setPriceOracle(PriceOracle _prices) public onlyOwner {
                      prices = _prices;
                      emit NewPriceOracle(address(prices));
                  }
              
                  function setCommitmentAges(uint _minCommitmentAge, uint _maxCommitmentAge) public onlyOwner {
                      minCommitmentAge = _minCommitmentAge;
                      maxCommitmentAge = _maxCommitmentAge;
                  }
              
                  function withdraw() public onlyOwner {
                      msg.sender.transfer(address(this).balance);
                  }
              
                  function supportsInterface(bytes4 interfaceID) external pure returns (bool) {
                      return interfaceID == INTERFACE_META_ID ||
                             interfaceID == COMMITMENT_CONTROLLER_ID ||
                             interfaceID == COMMITMENT_WITH_CONFIG_CONTROLLER_ID;
                  }
              
                  function _consumeCommitment(string memory name, uint duration, bytes32 commitment) internal returns (uint256) {
                      // Require a valid commitment
                      require(commitments[commitment] + minCommitmentAge <= now);
              
                      // If the commitment is too old, or the name is registered, stop
                      require(commitments[commitment] + maxCommitmentAge > now);
                      require(available(name));
              
                      delete(commitments[commitment]);
              
                      uint cost = rentPrice(name, duration);
                      require(duration >= MIN_REGISTRATION_DURATION);
                      require(msg.value >= cost);
              
                      return cost;
                  }
              }

              File 2 of 7: BaseRegistrarImplementation
              // File: @ensdomains/ens/contracts/ENS.sol
              
              pragma solidity >=0.4.24;
              
              interface ENS {
              
                  // Logged when the owner of a node assigns a new owner to a subnode.
                  event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner);
              
                  // Logged when the owner of a node transfers ownership to a new account.
                  event Transfer(bytes32 indexed node, address owner);
              
                  // Logged when the resolver for a node changes.
                  event NewResolver(bytes32 indexed node, address resolver);
              
                  // Logged when the TTL of a node changes
                  event NewTTL(bytes32 indexed node, uint64 ttl);
              
                  // Logged when an operator is added or removed.
                  event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
              
                  function setRecord(bytes32 node, address owner, address resolver, uint64 ttl) external;
                  function setSubnodeRecord(bytes32 node, bytes32 label, address owner, address resolver, uint64 ttl) external;
                  function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external returns(bytes32);
                  function setResolver(bytes32 node, address resolver) external;
                  function setOwner(bytes32 node, address owner) external;
                  function setTTL(bytes32 node, uint64 ttl) external;
                  function setApprovalForAll(address operator, bool approved) external;
                  function owner(bytes32 node) external view returns (address);
                  function resolver(bytes32 node) external view returns (address);
                  function ttl(bytes32 node) external view returns (uint64);
                  function recordExists(bytes32 node) external view returns (bool);
                  function isApprovedForAll(address owner, address operator) external view returns (bool);
              }
              
              // File: openzeppelin-solidity/contracts/introspection/IERC165.sol
              
              pragma solidity ^0.5.0;
              
              /**
               * @title IERC165
               * @dev https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md
               */
              interface IERC165 {
                  /**
                   * @notice Query if a contract implements an interface
                   * @param interfaceId The interface identifier, as specified in ERC-165
                   * @dev Interface identification is specified in ERC-165. This function
                   * uses less than 30,000 gas.
                   */
                  function supportsInterface(bytes4 interfaceId) external view returns (bool);
              }
              
              // File: openzeppelin-solidity/contracts/token/ERC721/IERC721.sol
              
              pragma solidity ^0.5.0;
              
              
              /**
               * @title ERC721 Non-Fungible Token Standard basic interface
               * @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
               */
              contract IERC721 is IERC165 {
                  event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
                  event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
                  event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
              
                  function balanceOf(address owner) public view returns (uint256 balance);
                  function ownerOf(uint256 tokenId) public view returns (address owner);
              
                  function approve(address to, uint256 tokenId) public;
                  function getApproved(uint256 tokenId) public view returns (address operator);
              
                  function setApprovalForAll(address operator, bool _approved) public;
                  function isApprovedForAll(address owner, address operator) public view returns (bool);
              
                  function transferFrom(address from, address to, uint256 tokenId) public;
                  function safeTransferFrom(address from, address to, uint256 tokenId) public;
              
                  function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public;
              }
              
              // File: openzeppelin-solidity/contracts/token/ERC721/IERC721Receiver.sol
              
              pragma solidity ^0.5.0;
              
              /**
               * @title ERC721 token receiver interface
               * @dev Interface for any contract that wants to support safeTransfers
               * from ERC721 asset contracts.
               */
              contract IERC721Receiver {
                  /**
                   * @notice Handle the receipt of an NFT
                   * @dev The ERC721 smart contract calls this function on the recipient
                   * after a `safeTransfer`. This function MUST return the function selector,
                   * otherwise the caller will revert the transaction. The selector to be
                   * returned can be obtained as `this.onERC721Received.selector`. This
                   * function MAY throw to revert and reject the transfer.
                   * Note: the ERC721 contract address is always the message sender.
                   * @param operator The address which called `safeTransferFrom` function
                   * @param from The address which previously owned the token
                   * @param tokenId The NFT identifier which is being transferred
                   * @param data Additional data with no specified format
                   * @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
                   */
                  function onERC721Received(address operator, address from, uint256 tokenId, bytes memory data)
                  public returns (bytes4);
              }
              
              // File: openzeppelin-solidity/contracts/math/SafeMath.sol
              
              pragma solidity ^0.5.0;
              
              /**
               * @title SafeMath
               * @dev Unsigned math operations with safety checks that revert on error
               */
              library SafeMath {
                  /**
                  * @dev Multiplies two unsigned integers, reverts on overflow.
                  */
                  function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                      // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                      // benefit is lost if 'b' is also tested.
                      // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
                      if (a == 0) {
                          return 0;
                      }
              
                      uint256 c = a * b;
                      require(c / a == b);
              
                      return c;
                  }
              
                  /**
                  * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero.
                  */
                  function div(uint256 a, uint256 b) internal pure returns (uint256) {
                      // Solidity only automatically asserts when dividing by 0
                      require(b > 0);
                      uint256 c = a / b;
                      // assert(a == b * c + a % b); // There is no case in which this doesn't hold
              
                      return c;
                  }
              
                  /**
                  * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend).
                  */
                  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                      require(b <= a);
                      uint256 c = a - b;
              
                      return c;
                  }
              
                  /**
                  * @dev Adds two unsigned integers, reverts on overflow.
                  */
                  function add(uint256 a, uint256 b) internal pure returns (uint256) {
                      uint256 c = a + b;
                      require(c >= a);
              
                      return c;
                  }
              
                  /**
                  * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo),
                  * reverts when dividing by zero.
                  */
                  function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                      require(b != 0);
                      return a % b;
                  }
              }
              
              // File: openzeppelin-solidity/contracts/utils/Address.sol
              
              pragma solidity ^0.5.0;
              
              /**
               * Utility library of inline functions on addresses
               */
              library Address {
                  /**
                   * Returns whether the target address is a contract
                   * @dev This function will return false if invoked during the constructor of a contract,
                   * as the code is not actually created until after the constructor finishes.
                   * @param account address of the account to check
                   * @return whether the target address is a contract
                   */
                  function isContract(address account) internal view returns (bool) {
                      uint256 size;
                      // XXX Currently there is no better way to check if there is a contract in an address
                      // than to check the size of the code at that address.
                      // See https://ethereum.stackexchange.com/a/14016/36603
                      // for more details about how this works.
                      // TODO Check this again before the Serenity release, because all addresses will be
                      // contracts then.
                      // solhint-disable-next-line no-inline-assembly
                      assembly { size := extcodesize(account) }
                      return size > 0;
                  }
              }
              
              // File: openzeppelin-solidity/contracts/introspection/ERC165.sol
              
              pragma solidity ^0.5.0;
              
              
              /**
               * @title ERC165
               * @author Matt Condon (@shrugs)
               * @dev Implements ERC165 using a lookup table.
               */
              contract ERC165 is IERC165 {
                  bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
                  /**
                   * 0x01ffc9a7 ===
                   *     bytes4(keccak256('supportsInterface(bytes4)'))
                   */
              
                  /**
                   * @dev a mapping of interface id to whether or not it's supported
                   */
                  mapping(bytes4 => bool) private _supportedInterfaces;
              
                  /**
                   * @dev A contract implementing SupportsInterfaceWithLookup
                   * implement ERC165 itself
                   */
                  constructor () internal {
                      _registerInterface(_INTERFACE_ID_ERC165);
                  }
              
                  /**
                   * @dev implement supportsInterface(bytes4) using a lookup table
                   */
                  function supportsInterface(bytes4 interfaceId) external view returns (bool) {
                      return _supportedInterfaces[interfaceId];
                  }
              
                  /**
                   * @dev internal method for registering an interface
                   */
                  function _registerInterface(bytes4 interfaceId) internal {
                      require(interfaceId != 0xffffffff);
                      _supportedInterfaces[interfaceId] = true;
                  }
              }
              
              // File: openzeppelin-solidity/contracts/token/ERC721/ERC721.sol
              
              pragma solidity ^0.5.0;
              
              
              
              
              
              
              /**
               * @title ERC721 Non-Fungible Token Standard basic implementation
               * @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
               */
              contract ERC721 is ERC165, IERC721 {
                  using SafeMath for uint256;
                  using Address for address;
              
                  // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
                  // which can be also obtained as `IERC721Receiver(0).onERC721Received.selector`
                  bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;
              
                  // Mapping from token ID to owner
                  mapping (uint256 => address) private _tokenOwner;
              
                  // Mapping from token ID to approved address
                  mapping (uint256 => address) private _tokenApprovals;
              
                  // Mapping from owner to number of owned token
                  mapping (address => uint256) private _ownedTokensCount;
              
                  // Mapping from owner to operator approvals
                  mapping (address => mapping (address => bool)) private _operatorApprovals;
              
                  bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
                  /*
                   * 0x80ac58cd ===
                   *     bytes4(keccak256('balanceOf(address)')) ^
                   *     bytes4(keccak256('ownerOf(uint256)')) ^
                   *     bytes4(keccak256('approve(address,uint256)')) ^
                   *     bytes4(keccak256('getApproved(uint256)')) ^
                   *     bytes4(keccak256('setApprovalForAll(address,bool)')) ^
                   *     bytes4(keccak256('isApprovedForAll(address,address)')) ^
                   *     bytes4(keccak256('transferFrom(address,address,uint256)')) ^
                   *     bytes4(keccak256('safeTransferFrom(address,address,uint256)')) ^
                   *     bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)'))
                   */
              
                  constructor () public {
                      // register the supported interfaces to conform to ERC721 via ERC165
                      _registerInterface(_INTERFACE_ID_ERC721);
                  }
              
                  /**
                   * @dev Gets the balance of the specified address
                   * @param owner address to query the balance of
                   * @return uint256 representing the amount owned by the passed address
                   */
                  function balanceOf(address owner) public view returns (uint256) {
                      require(owner != address(0));
                      return _ownedTokensCount[owner];
                  }
              
                  /**
                   * @dev Gets the owner of the specified token ID
                   * @param tokenId uint256 ID of the token to query the owner of
                   * @return owner address currently marked as the owner of the given token ID
                   */
                  function ownerOf(uint256 tokenId) public view returns (address) {
                      address owner = _tokenOwner[tokenId];
                      require(owner != address(0));
                      return owner;
                  }
              
                  /**
                   * @dev Approves another address to transfer the given token ID
                   * The zero address indicates there is no approved address.
                   * There can only be one approved address per token at a given time.
                   * Can only be called by the token owner or an approved operator.
                   * @param to address to be approved for the given token ID
                   * @param tokenId uint256 ID of the token to be approved
                   */
                  function approve(address to, uint256 tokenId) public {
                      address owner = ownerOf(tokenId);
                      require(to != owner);
                      require(msg.sender == owner || isApprovedForAll(owner, msg.sender));
              
                      _tokenApprovals[tokenId] = to;
                      emit Approval(owner, to, tokenId);
                  }
              
                  /**
                   * @dev Gets the approved address for a token ID, or zero if no address set
                   * Reverts if the token ID does not exist.
                   * @param tokenId uint256 ID of the token to query the approval of
                   * @return address currently approved for the given token ID
                   */
                  function getApproved(uint256 tokenId) public view returns (address) {
                      require(_exists(tokenId));
                      return _tokenApprovals[tokenId];
                  }
              
                  /**
                   * @dev Sets or unsets the approval of a given operator
                   * An operator is allowed to transfer all tokens of the sender on their behalf
                   * @param to operator address to set the approval
                   * @param approved representing the status of the approval to be set
                   */
                  function setApprovalForAll(address to, bool approved) public {
                      require(to != msg.sender);
                      _operatorApprovals[msg.sender][to] = approved;
                      emit ApprovalForAll(msg.sender, to, approved);
                  }
              
                  /**
                   * @dev Tells whether an operator is approved by a given owner
                   * @param owner owner address which you want to query the approval of
                   * @param operator operator address which you want to query the approval of
                   * @return bool whether the given operator is approved by the given owner
                   */
                  function isApprovedForAll(address owner, address operator) public view returns (bool) {
                      return _operatorApprovals[owner][operator];
                  }
              
                  /**
                   * @dev Transfers the ownership of a given token ID to another address
                   * Usage of this method is discouraged, use `safeTransferFrom` whenever possible
                   * Requires the msg sender to be the owner, approved, or operator
                   * @param from current owner of the token
                   * @param to address to receive the ownership of the given token ID
                   * @param tokenId uint256 ID of the token to be transferred
                  */
                  function transferFrom(address from, address to, uint256 tokenId) public {
                      require(_isApprovedOrOwner(msg.sender, tokenId));
              
                      _transferFrom(from, to, tokenId);
                  }
              
                  /**
                   * @dev Safely transfers the ownership of a given token ID to another address
                   * If the target address is a contract, it must implement `onERC721Received`,
                   * which is called upon a safe transfer, and return the magic value
                   * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
                   * the transfer is reverted.
                   *
                   * Requires the msg sender to be the owner, approved, or operator
                   * @param from current owner of the token
                   * @param to address to receive the ownership of the given token ID
                   * @param tokenId uint256 ID of the token to be transferred
                  */
                  function safeTransferFrom(address from, address to, uint256 tokenId) public {
                      safeTransferFrom(from, to, tokenId, "");
                  }
              
                  /**
                   * @dev Safely transfers the ownership of a given token ID to another address
                   * If the target address is a contract, it must implement `onERC721Received`,
                   * which is called upon a safe transfer, and return the magic value
                   * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
                   * the transfer is reverted.
                   * Requires the msg sender to be the owner, approved, or operator
                   * @param from current owner of the token
                   * @param to address to receive the ownership of the given token ID
                   * @param tokenId uint256 ID of the token to be transferred
                   * @param _data bytes data to send along with a safe transfer check
                   */
                  function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public {
                      transferFrom(from, to, tokenId);
                      require(_checkOnERC721Received(from, to, tokenId, _data));
                  }
              
                  /**
                   * @dev Returns whether the specified token exists
                   * @param tokenId uint256 ID of the token to query the existence of
                   * @return whether the token exists
                   */
                  function _exists(uint256 tokenId) internal view returns (bool) {
                      address owner = _tokenOwner[tokenId];
                      return owner != address(0);
                  }
              
                  /**
                   * @dev Returns whether the given spender can transfer a given token ID
                   * @param spender address of the spender to query
                   * @param tokenId uint256 ID of the token to be transferred
                   * @return bool whether the msg.sender is approved for the given token ID,
                   *    is an operator of the owner, or is the owner of the token
                   */
                  function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) {
                      address owner = ownerOf(tokenId);
                      return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
                  }
              
                  /**
                   * @dev Internal function to mint a new token
                   * Reverts if the given token ID already exists
                   * @param to The address that will own the minted token
                   * @param tokenId uint256 ID of the token to be minted
                   */
                  function _mint(address to, uint256 tokenId) internal {
                      require(to != address(0));
                      require(!_exists(tokenId));
              
                      _tokenOwner[tokenId] = to;
                      _ownedTokensCount[to] = _ownedTokensCount[to].add(1);
              
                      emit Transfer(address(0), to, tokenId);
                  }
              
                  /**
                   * @dev Internal function to burn a specific token
                   * Reverts if the token does not exist
                   * Deprecated, use _burn(uint256) instead.
                   * @param owner owner of the token to burn
                   * @param tokenId uint256 ID of the token being burned
                   */
                  function _burn(address owner, uint256 tokenId) internal {
                      require(ownerOf(tokenId) == owner);
              
                      _clearApproval(tokenId);
              
                      _ownedTokensCount[owner] = _ownedTokensCount[owner].sub(1);
                      _tokenOwner[tokenId] = address(0);
              
                      emit Transfer(owner, address(0), tokenId);
                  }
              
                  /**
                   * @dev Internal function to burn a specific token
                   * Reverts if the token does not exist
                   * @param tokenId uint256 ID of the token being burned
                   */
                  function _burn(uint256 tokenId) internal {
                      _burn(ownerOf(tokenId), tokenId);
                  }
              
                  /**
                   * @dev Internal function to transfer ownership of a given token ID to another address.
                   * As opposed to transferFrom, this imposes no restrictions on msg.sender.
                   * @param from current owner of the token
                   * @param to address to receive the ownership of the given token ID
                   * @param tokenId uint256 ID of the token to be transferred
                  */
                  function _transferFrom(address from, address to, uint256 tokenId) internal {
                      require(ownerOf(tokenId) == from);
                      require(to != address(0));
              
                      _clearApproval(tokenId);
              
                      _ownedTokensCount[from] = _ownedTokensCount[from].sub(1);
                      _ownedTokensCount[to] = _ownedTokensCount[to].add(1);
              
                      _tokenOwner[tokenId] = to;
              
                      emit Transfer(from, to, tokenId);
                  }
              
                  /**
                   * @dev Internal function to invoke `onERC721Received` on a target address
                   * The call is not executed if the target address is not a contract
                   * @param from address representing the previous owner of the given token ID
                   * @param to target address that will receive the tokens
                   * @param tokenId uint256 ID of the token to be transferred
                   * @param _data bytes optional data to send along with the call
                   * @return whether the call correctly returned the expected magic value
                   */
                  function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data)
                      internal returns (bool)
                  {
                      if (!to.isContract()) {
                          return true;
                      }
              
                      bytes4 retval = IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, _data);
                      return (retval == _ERC721_RECEIVED);
                  }
              
                  /**
                   * @dev Private function to clear current approval of a given token ID
                   * @param tokenId uint256 ID of the token to be transferred
                   */
                  function _clearApproval(uint256 tokenId) private {
                      if (_tokenApprovals[tokenId] != address(0)) {
                          _tokenApprovals[tokenId] = address(0);
                      }
                  }
              }
              
              // File: openzeppelin-solidity/contracts/ownership/Ownable.sol
              
              pragma solidity ^0.5.0;
              
              /**
               * @title Ownable
               * @dev The Ownable contract has an owner address, and provides basic authorization control
               * functions, this simplifies the implementation of "user permissions".
               */
              contract Ownable {
                  address private _owner;
              
                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
              
                  /**
                   * @dev The Ownable constructor sets the original `owner` of the contract to the sender
                   * account.
                   */
                  constructor () internal {
                      _owner = msg.sender;
                      emit OwnershipTransferred(address(0), _owner);
                  }
              
                  /**
                   * @return the address of the owner.
                   */
                  function owner() public view returns (address) {
                      return _owner;
                  }
              
                  /**
                   * @dev Throws if called by any account other than the owner.
                   */
                  modifier onlyOwner() {
                      require(isOwner());
                      _;
                  }
              
                  /**
                   * @return true if `msg.sender` is the owner of the contract.
                   */
                  function isOwner() public view returns (bool) {
                      return msg.sender == _owner;
                  }
              
                  /**
                   * @dev Allows the current owner to relinquish control of the contract.
                   * @notice Renouncing to ownership will leave the contract without an owner.
                   * It will not be possible to call the functions with the `onlyOwner`
                   * modifier anymore.
                   */
                  function renounceOwnership() public onlyOwner {
                      emit OwnershipTransferred(_owner, address(0));
                      _owner = address(0);
                  }
              
                  /**
                   * @dev Allows the current owner to transfer control of the contract to a newOwner.
                   * @param newOwner The address to transfer ownership to.
                   */
                  function transferOwnership(address newOwner) public onlyOwner {
                      _transferOwnership(newOwner);
                  }
              
                  /**
                   * @dev Transfers control of the contract to a newOwner.
                   * @param newOwner The address to transfer ownership to.
                   */
                  function _transferOwnership(address newOwner) internal {
                      require(newOwner != address(0));
                      emit OwnershipTransferred(_owner, newOwner);
                      _owner = newOwner;
                  }
              }
              
              // File: @ensdomains/ethregistrar/contracts/BaseRegistrar.sol
              
              pragma solidity >=0.4.24;
              
              
              
              
              contract BaseRegistrar is IERC721, Ownable {
                  uint constant public GRACE_PERIOD = 90 days;
              
                  event ControllerAdded(address indexed controller);
                  event ControllerRemoved(address indexed controller);
                  event NameMigrated(uint256 indexed id, address indexed owner, uint expires);
                  event NameRegistered(uint256 indexed id, address indexed owner, uint expires);
                  event NameRenewed(uint256 indexed id, uint expires);
              
                  // The ENS registry
                  ENS public ens;
              
                  // The namehash of the TLD this registrar owns (eg, .eth)
                  bytes32 public baseNode;
              
                  // A map of addresses that are authorised to register and renew names.
                  mapping(address=>bool) public controllers;
              
                  // Authorises a controller, who can register and renew domains.
                  function addController(address controller) external;
              
                  // Revoke controller permission for an address.
                  function removeController(address controller) external;
              
                  // Set the resolver for the TLD this registrar manages.
                  function setResolver(address resolver) external;
              
                  // Returns the expiration timestamp of the specified label hash.
                  function nameExpires(uint256 id) external view returns(uint);
              
                  // Returns true iff the specified name is available for registration.
                  function available(uint256 id) public view returns(bool);
              
                  /**
                   * @dev Register a name.
                   */
                  function register(uint256 id, address owner, uint duration) external returns(uint);
              
                  function renew(uint256 id, uint duration) external returns(uint);
              
                  /**
                   * @dev Reclaim ownership of a name in ENS, if you own it in the registrar.
                   */
                  function reclaim(uint256 id, address owner) external;
              }
              
              // File: @ensdomains/ethregistrar/contracts/BaseRegistrarImplementation.sol
              
              pragma solidity ^0.5.0;
              
              
              
              
              contract BaseRegistrarImplementation is BaseRegistrar, ERC721 {
                  // A map of expiry times
                  mapping(uint256=>uint) expiries;
              
                  bytes4 constant private INTERFACE_META_ID = bytes4(keccak256("supportsInterface(bytes4)"));
                  bytes4 constant private ERC721_ID = bytes4(
                      keccak256("balanceOf(address)") ^
                      keccak256("ownerOf(uint256)") ^
                      keccak256("approve(address,uint256)") ^
                      keccak256("getApproved(uint256)") ^
                      keccak256("setApprovalForAll(address,bool)") ^
                      keccak256("isApprovedForAll(address,address)") ^
                      keccak256("transferFrom(address,address,uint256)") ^
                      keccak256("safeTransferFrom(address,address,uint256)") ^
                      keccak256("safeTransferFrom(address,address,uint256,bytes)")
                  );
                  bytes4 constant private RECLAIM_ID = bytes4(keccak256("reclaim(uint256,address)"));
              
                  constructor(ENS _ens, bytes32 _baseNode) public {
                      ens = _ens;
                      baseNode = _baseNode;
                  }
              
                  modifier live {
                      require(ens.owner(baseNode) == address(this));
                      _;
                  }
              
                  modifier onlyController {
                      require(controllers[msg.sender]);
                      _;
                  }
              
                  /**
                   * @dev Gets the owner of the specified token ID. Names become unowned
                   *      when their registration expires.
                   * @param tokenId uint256 ID of the token to query the owner of
                   * @return address currently marked as the owner of the given token ID
                   */
                  function ownerOf(uint256 tokenId) public view returns (address) {
                      require(expiries[tokenId] > now);
                      return super.ownerOf(tokenId);
                  }
              
                  // Authorises a controller, who can register and renew domains.
                  function addController(address controller) external onlyOwner {
                      controllers[controller] = true;
                      emit ControllerAdded(controller);
                  }
              
                  // Revoke controller permission for an address.
                  function removeController(address controller) external onlyOwner {
                      controllers[controller] = false;
                      emit ControllerRemoved(controller);
                  }
              
                  // Set the resolver for the TLD this registrar manages.
                  function setResolver(address resolver) external onlyOwner {
                      ens.setResolver(baseNode, resolver);
                  }
              
                  // Returns the expiration timestamp of the specified id.
                  function nameExpires(uint256 id) external view returns(uint) {
                      return expiries[id];
                  }
              
                  // Returns true iff the specified name is available for registration.
                  function available(uint256 id) public view returns(bool) {
                      // Not available if it's registered here or in its grace period.
                      return expiries[id] + GRACE_PERIOD < now;
                  }
              
                  /**
                   * @dev Register a name.
                   * @param id The token ID (keccak256 of the label).
                   * @param owner The address that should own the registration.
                   * @param duration Duration in seconds for the registration.
                   */
                  function register(uint256 id, address owner, uint duration) external returns(uint) {
                    return _register(id, owner, duration, true);
                  }
              
                  /**
                   * @dev Register a name, without modifying the registry.
                   * @param id The token ID (keccak256 of the label).
                   * @param owner The address that should own the registration.
                   * @param duration Duration in seconds for the registration.
                   */
                  function registerOnly(uint256 id, address owner, uint duration) external returns(uint) {
                    return _register(id, owner, duration, false);
                  }
              
                  function _register(uint256 id, address owner, uint duration, bool updateRegistry) internal live onlyController returns(uint) {
                      require(available(id));
                      require(now + duration + GRACE_PERIOD > now + GRACE_PERIOD); // Prevent future overflow
              
                      expiries[id] = now + duration;
                      if(_exists(id)) {
                          // Name was previously owned, and expired
                          _burn(id);
                      }
                      _mint(owner, id);
                      if(updateRegistry) {
                          ens.setSubnodeOwner(baseNode, bytes32(id), owner);
                      }
              
                      emit NameRegistered(id, owner, now + duration);
              
                      return now + duration;
                  }
              
                  function renew(uint256 id, uint duration) external live onlyController returns(uint) {
                      require(expiries[id] + GRACE_PERIOD >= now); // Name must be registered here or in grace period
                      require(expiries[id] + duration + GRACE_PERIOD > duration + GRACE_PERIOD); // Prevent future overflow
              
                      expiries[id] += duration;
                      emit NameRenewed(id, expiries[id]);
                      return expiries[id];
                  }
              
                  /**
                   * @dev Reclaim ownership of a name in ENS, if you own it in the registrar.
                   */
                  function reclaim(uint256 id, address owner) external live {
                      require(_isApprovedOrOwner(msg.sender, id));
                      ens.setSubnodeOwner(baseNode, bytes32(id), owner);
                  }
              
                  function supportsInterface(bytes4 interfaceID) external view returns (bool) {
                      return interfaceID == INTERFACE_META_ID ||
                             interfaceID == ERC721_ID ||
                             interfaceID == RECLAIM_ID;
                  }
              }

              File 3 of 7: ENSRegistryWithFallback
              // File: @ensdomains/ens/contracts/ENS.sol
              
              pragma solidity >=0.4.24;
              
              interface ENS {
              
                  // Logged when the owner of a node assigns a new owner to a subnode.
                  event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner);
              
                  // Logged when the owner of a node transfers ownership to a new account.
                  event Transfer(bytes32 indexed node, address owner);
              
                  // Logged when the resolver for a node changes.
                  event NewResolver(bytes32 indexed node, address resolver);
              
                  // Logged when the TTL of a node changes
                  event NewTTL(bytes32 indexed node, uint64 ttl);
              
                  // Logged when an operator is added or removed.
                  event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
              
                  function setRecord(bytes32 node, address owner, address resolver, uint64 ttl) external;
                  function setSubnodeRecord(bytes32 node, bytes32 label, address owner, address resolver, uint64 ttl) external;
                  function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external returns(bytes32);
                  function setResolver(bytes32 node, address resolver) external;
                  function setOwner(bytes32 node, address owner) external;
                  function setTTL(bytes32 node, uint64 ttl) external;
                  function setApprovalForAll(address operator, bool approved) external;
                  function owner(bytes32 node) external view returns (address);
                  function resolver(bytes32 node) external view returns (address);
                  function ttl(bytes32 node) external view returns (uint64);
                  function recordExists(bytes32 node) external view returns (bool);
                  function isApprovedForAll(address owner, address operator) external view returns (bool);
              }
              
              // File: @ensdomains/ens/contracts/ENSRegistry.sol
              
              pragma solidity ^0.5.0;
              
              
              /**
               * The ENS registry contract.
               */
              contract ENSRegistry is ENS {
              
                  struct Record {
                      address owner;
                      address resolver;
                      uint64 ttl;
                  }
              
                  mapping (bytes32 => Record) records;
                  mapping (address => mapping(address => bool)) operators;
              
                  // Permits modifications only by the owner of the specified node.
                  modifier authorised(bytes32 node) {
                      address owner = records[node].owner;
                      require(owner == msg.sender || operators[owner][msg.sender]);
                      _;
                  }
              
                  /**
                   * @dev Constructs a new ENS registrar.
                   */
                  constructor() public {
                      records[0x0].owner = msg.sender;
                  }
              
                  /**
                   * @dev Sets the record for a node.
                   * @param node The node to update.
                   * @param owner The address of the new owner.
                   * @param resolver The address of the resolver.
                   * @param ttl The TTL in seconds.
                   */
                  function setRecord(bytes32 node, address owner, address resolver, uint64 ttl) external {
                      setOwner(node, owner);
                      _setResolverAndTTL(node, resolver, ttl);
                  }
              
                  /**
                   * @dev Sets the record for a subnode.
                   * @param node The parent node.
                   * @param label The hash of the label specifying the subnode.
                   * @param owner The address of the new owner.
                   * @param resolver The address of the resolver.
                   * @param ttl The TTL in seconds.
                   */
                  function setSubnodeRecord(bytes32 node, bytes32 label, address owner, address resolver, uint64 ttl) external {
                      bytes32 subnode = setSubnodeOwner(node, label, owner);
                      _setResolverAndTTL(subnode, resolver, ttl);
                  }
              
                  /**
                   * @dev Transfers ownership of a node to a new address. May only be called by the current owner of the node.
                   * @param node The node to transfer ownership of.
                   * @param owner The address of the new owner.
                   */
                  function setOwner(bytes32 node, address owner) public authorised(node) {
                      _setOwner(node, owner);
                      emit Transfer(node, owner);
                  }
              
                  /**
                   * @dev Transfers ownership of a subnode keccak256(node, label) to a new address. May only be called by the owner of the parent node.
                   * @param node The parent node.
                   * @param label The hash of the label specifying the subnode.
                   * @param owner The address of the new owner.
                   */
                  function setSubnodeOwner(bytes32 node, bytes32 label, address owner) public authorised(node) returns(bytes32) {
                      bytes32 subnode = keccak256(abi.encodePacked(node, label));
                      _setOwner(subnode, owner);
                      emit NewOwner(node, label, owner);
                      return subnode;
                  }
              
                  /**
                   * @dev Sets the resolver address for the specified node.
                   * @param node The node to update.
                   * @param resolver The address of the resolver.
                   */
                  function setResolver(bytes32 node, address resolver) public authorised(node) {
                      emit NewResolver(node, resolver);
                      records[node].resolver = resolver;
                  }
              
                  /**
                   * @dev Sets the TTL for the specified node.
                   * @param node The node to update.
                   * @param ttl The TTL in seconds.
                   */
                  function setTTL(bytes32 node, uint64 ttl) public authorised(node) {
                      emit NewTTL(node, ttl);
                      records[node].ttl = ttl;
                  }
              
                  /**
                   * @dev Enable or disable approval for a third party ("operator") to manage
                   *  all of `msg.sender`'s ENS records. Emits the ApprovalForAll event.
                   * @param operator Address to add to the set of authorized operators.
                   * @param approved True if the operator is approved, false to revoke approval.
                   */
                  function setApprovalForAll(address operator, bool approved) external {
                      operators[msg.sender][operator] = approved;
                      emit ApprovalForAll(msg.sender, operator, approved);
                  }
              
                  /**
                   * @dev Returns the address that owns the specified node.
                   * @param node The specified node.
                   * @return address of the owner.
                   */
                  function owner(bytes32 node) public view returns (address) {
                      address addr = records[node].owner;
                      if (addr == address(this)) {
                          return address(0x0);
                      }
              
                      return addr;
                  }
              
                  /**
                   * @dev Returns the address of the resolver for the specified node.
                   * @param node The specified node.
                   * @return address of the resolver.
                   */
                  function resolver(bytes32 node) public view returns (address) {
                      return records[node].resolver;
                  }
              
                  /**
                   * @dev Returns the TTL of a node, and any records associated with it.
                   * @param node The specified node.
                   * @return ttl of the node.
                   */
                  function ttl(bytes32 node) public view returns (uint64) {
                      return records[node].ttl;
                  }
              
                  /**
                   * @dev Returns whether a record has been imported to the registry.
                   * @param node The specified node.
                   * @return Bool if record exists
                   */
                  function recordExists(bytes32 node) public view returns (bool) {
                      return records[node].owner != address(0x0);
                  }
              
                  /**
                   * @dev Query if an address is an authorized operator for another address.
                   * @param owner The address that owns the records.
                   * @param operator The address that acts on behalf of the owner.
                   * @return True if `operator` is an approved operator for `owner`, false otherwise.
                   */
                  function isApprovedForAll(address owner, address operator) external view returns (bool) {
                      return operators[owner][operator];
                  }
              
                  function _setOwner(bytes32 node, address owner) internal {
                      records[node].owner = owner;
                  }
              
                  function _setResolverAndTTL(bytes32 node, address resolver, uint64 ttl) internal {
                      if(resolver != records[node].resolver) {
                          records[node].resolver = resolver;
                          emit NewResolver(node, resolver);
                      }
              
                      if(ttl != records[node].ttl) {
                          records[node].ttl = ttl;
                          emit NewTTL(node, ttl);
                      }
                  }
              }
              
              // File: @ensdomains/ens/contracts/ENSRegistryWithFallback.sol
              
              pragma solidity ^0.5.0;
              
              
              
              /**
               * The ENS registry contract.
               */
              contract ENSRegistryWithFallback is ENSRegistry {
              
                  ENS public old;
              
                  /**
                   * @dev Constructs a new ENS registrar.
                   */
                  constructor(ENS _old) public ENSRegistry() {
                      old = _old;
                  }
              
                  /**
                   * @dev Returns the address of the resolver for the specified node.
                   * @param node The specified node.
                   * @return address of the resolver.
                   */
                  function resolver(bytes32 node) public view returns (address) {
                      if (!recordExists(node)) {
                          return old.resolver(node);
                      }
              
                      return super.resolver(node);
                  }
              
                  /**
                   * @dev Returns the address that owns the specified node.
                   * @param node The specified node.
                   * @return address of the owner.
                   */
                  function owner(bytes32 node) public view returns (address) {
                      if (!recordExists(node)) {
                          return old.owner(node);
                      }
              
                      return super.owner(node);
                  }
              
                  /**
                   * @dev Returns the TTL of a node, and any records associated with it.
                   * @param node The specified node.
                   * @return ttl of the node.
                   */
                  function ttl(bytes32 node) public view returns (uint64) {
                      if (!recordExists(node)) {
                          return old.ttl(node);
                      }
              
                      return super.ttl(node);
                  }
              
                  function _setOwner(bytes32 node, address owner) internal {
                      address addr = owner;
                      if (addr == address(0x0)) {
                          addr = address(this);
                      }
              
                      super._setOwner(node, addr);
                  }
              }

              File 4 of 7: PublicResolver
              // File: @ensdomains/ens/contracts/ENS.sol
              
              pragma solidity >=0.4.24;
              
              interface ENS {
              
                  // Logged when the owner of a node assigns a new owner to a subnode.
                  event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner);
              
                  // Logged when the owner of a node transfers ownership to a new account.
                  event Transfer(bytes32 indexed node, address owner);
              
                  // Logged when the resolver for a node changes.
                  event NewResolver(bytes32 indexed node, address resolver);
              
                  // Logged when the TTL of a node changes
                  event NewTTL(bytes32 indexed node, uint64 ttl);
              
              
                  function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external;
                  function setResolver(bytes32 node, address resolver) external;
                  function setOwner(bytes32 node, address owner) external;
                  function setTTL(bytes32 node, uint64 ttl) external;
                  function owner(bytes32 node) external view returns (address);
                  function resolver(bytes32 node) external view returns (address);
                  function ttl(bytes32 node) external view returns (uint64);
              
              }
              
              // File: contracts/ResolverBase.sol
              
              pragma solidity ^0.5.0;
              
              contract ResolverBase {
                  bytes4 private constant INTERFACE_META_ID = 0x01ffc9a7;
              
                  function supportsInterface(bytes4 interfaceID) public pure returns(bool) {
                      return interfaceID == INTERFACE_META_ID;
                  }
              
                  function isAuthorised(bytes32 node) internal view returns(bool);
              
                  modifier authorised(bytes32 node) {
                      require(isAuthorised(node));
                      _;
                  }
              
                  function bytesToAddress(bytes memory b) internal pure returns(address payable a) {
                      require(b.length == 20);
                      assembly {
                          a := div(mload(add(b, 32)), exp(256, 12))
                      }
                  }
              
                  function addressToBytes(address a) internal pure returns(bytes memory b) {
                      b = new bytes(20);
                      assembly {
                          mstore(add(b, 32), mul(a, exp(256, 12)))
                      }
                  }
              }
              
              // File: contracts/profiles/ABIResolver.sol
              
              pragma solidity ^0.5.0;
              
              
              contract ABIResolver is ResolverBase {
                  bytes4 constant private ABI_INTERFACE_ID = 0x2203ab56;
              
                  event ABIChanged(bytes32 indexed node, uint256 indexed contentType);
              
                  mapping(bytes32=>mapping(uint256=>bytes)) abis;
              
                  /**
                   * Sets the ABI associated with an ENS node.
                   * Nodes may have one ABI of each content type. To remove an ABI, set it to
                   * the empty string.
                   * @param node The node to update.
                   * @param contentType The content type of the ABI
                   * @param data The ABI data.
                   */
                  function setABI(bytes32 node, uint256 contentType, bytes calldata data) external authorised(node) {
                      // Content types must be powers of 2
                      require(((contentType - 1) & contentType) == 0);
              
                      abis[node][contentType] = data;
                      emit ABIChanged(node, contentType);
                  }
              
                  /**
                   * Returns the ABI associated with an ENS node.
                   * Defined in EIP205.
                   * @param node The ENS node to query
                   * @param contentTypes A bitwise OR of the ABI formats accepted by the caller.
                   * @return contentType The content type of the return value
                   * @return data The ABI data
                   */
                  function ABI(bytes32 node, uint256 contentTypes) external view returns (uint256, bytes memory) {
                      mapping(uint256=>bytes) storage abiset = abis[node];
              
                      for (uint256 contentType = 1; contentType <= contentTypes; contentType <<= 1) {
                          if ((contentType & contentTypes) != 0 && abiset[contentType].length > 0) {
                              return (contentType, abiset[contentType]);
                          }
                      }
              
                      return (0, bytes(""));
                  }
              
                  function supportsInterface(bytes4 interfaceID) public pure returns(bool) {
                      return interfaceID == ABI_INTERFACE_ID || super.supportsInterface(interfaceID);
                  }
              }
              
              // File: contracts/profiles/AddrResolver.sol
              
              pragma solidity ^0.5.0;
              
              
              contract AddrResolver is ResolverBase {
                  bytes4 constant private ADDR_INTERFACE_ID = 0x3b3b57de;
                  bytes4 constant private ADDRESS_INTERFACE_ID = 0xf1cb7e06;
                  uint constant private COIN_TYPE_ETH = 60;
              
                  event AddrChanged(bytes32 indexed node, address a);
                  event AddressChanged(bytes32 indexed node, uint coinType, bytes newAddress);
              
                  mapping(bytes32=>mapping(uint=>bytes)) _addresses;
              
                  /**
                   * Sets the address associated with an ENS node.
                   * May only be called by the owner of that node in the ENS registry.
                   * @param node The node to update.
                   * @param a The address to set.
                   */
                  function setAddr(bytes32 node, address a) external authorised(node) {
                      setAddr(node, COIN_TYPE_ETH, addressToBytes(a));
                  }
              
                  /**
                   * Returns the address associated with an ENS node.
                   * @param node The ENS node to query.
                   * @return The associated address.
                   */
                  function addr(bytes32 node) public view returns (address payable) {
                      bytes memory a = addr(node, COIN_TYPE_ETH);
                      if(a.length == 0) {
                          return address(0);
                      }
                      return bytesToAddress(a);
                  }
              
                  function setAddr(bytes32 node, uint coinType, bytes memory a) public authorised(node) {
                      emit AddressChanged(node, coinType, a);
                      if(coinType == COIN_TYPE_ETH) {
                          emit AddrChanged(node, bytesToAddress(a));
                      }
                      _addresses[node][coinType] = a;
                  }
              
                  function addr(bytes32 node, uint coinType) public view returns(bytes memory) {
                      return _addresses[node][coinType];
                  }
              
                  function supportsInterface(bytes4 interfaceID) public pure returns(bool) {
                      return interfaceID == ADDR_INTERFACE_ID || interfaceID == ADDRESS_INTERFACE_ID || super.supportsInterface(interfaceID);
                  }
              }
              
              // File: contracts/profiles/ContentHashResolver.sol
              
              pragma solidity ^0.5.0;
              
              
              contract ContentHashResolver is ResolverBase {
                  bytes4 constant private CONTENT_HASH_INTERFACE_ID = 0xbc1c58d1;
              
                  event ContenthashChanged(bytes32 indexed node, bytes hash);
              
                  mapping(bytes32=>bytes) hashes;
              
                  /**
                   * Sets the contenthash associated with an ENS node.
                   * May only be called by the owner of that node in the ENS registry.
                   * @param node The node to update.
                   * @param hash The contenthash to set
                   */
                  function setContenthash(bytes32 node, bytes calldata hash) external authorised(node) {
                      hashes[node] = hash;
                      emit ContenthashChanged(node, hash);
                  }
              
                  /**
                   * Returns the contenthash associated with an ENS node.
                   * @param node The ENS node to query.
                   * @return The associated contenthash.
                   */
                  function contenthash(bytes32 node) external view returns (bytes memory) {
                      return hashes[node];
                  }
              
                  function supportsInterface(bytes4 interfaceID) public pure returns(bool) {
                      return interfaceID == CONTENT_HASH_INTERFACE_ID || super.supportsInterface(interfaceID);
                  }
              }
              
              // File: @ensdomains/dnssec-oracle/contracts/BytesUtils.sol
              
              pragma solidity >0.4.23;
              
              library BytesUtils {
                  /*
                  * @dev Returns the keccak-256 hash of a byte range.
                  * @param self The byte string to hash.
                  * @param offset The position to start hashing at.
                  * @param len The number of bytes to hash.
                  * @return The hash of the byte range.
                  */
                  function keccak(bytes memory self, uint offset, uint len) internal pure returns (bytes32 ret) {
                      require(offset + len <= self.length);
                      assembly {
                          ret := keccak256(add(add(self, 32), offset), len)
                      }
                  }
              
              
                  /*
                  * @dev Returns a positive number if `other` comes lexicographically after
                  *      `self`, a negative number if it comes before, or zero if the
                  *      contents of the two bytes are equal.
                  * @param self The first bytes to compare.
                  * @param other The second bytes to compare.
                  * @return The result of the comparison.
                  */
                  function compare(bytes memory self, bytes memory other) internal pure returns (int) {
                      return compare(self, 0, self.length, other, 0, other.length);
                  }
              
                  /*
                  * @dev Returns a positive number if `other` comes lexicographically after
                  *      `self`, a negative number if it comes before, or zero if the
                  *      contents of the two bytes are equal. Comparison is done per-rune,
                  *      on unicode codepoints.
                  * @param self The first bytes to compare.
                  * @param offset The offset of self.
                  * @param len    The length of self.
                  * @param other The second bytes to compare.
                  * @param otheroffset The offset of the other string.
                  * @param otherlen    The length of the other string.
                  * @return The result of the comparison.
                  */
                  function compare(bytes memory self, uint offset, uint len, bytes memory other, uint otheroffset, uint otherlen) internal pure returns (int) {
                      uint shortest = len;
                      if (otherlen < len)
                      shortest = otherlen;
              
                      uint selfptr;
                      uint otherptr;
              
                      assembly {
                          selfptr := add(self, add(offset, 32))
                          otherptr := add(other, add(otheroffset, 32))
                      }
                      for (uint idx = 0; idx < shortest; idx += 32) {
                          uint a;
                          uint b;
                          assembly {
                              a := mload(selfptr)
                              b := mload(otherptr)
                          }
                          if (a != b) {
                              // Mask out irrelevant bytes and check again
                              uint mask;
                              if (shortest > 32) {
                                  mask = uint256(- 1); // aka 0xffffff....
                              } else {
                                  mask = ~(2 ** (8 * (32 - shortest + idx)) - 1);
                              }
                              uint diff = (a & mask) - (b & mask);
                              if (diff != 0)
                              return int(diff);
                          }
                          selfptr += 32;
                          otherptr += 32;
                      }
              
                      return int(len) - int(otherlen);
                  }
              
                  /*
                  * @dev Returns true if the two byte ranges are equal.
                  * @param self The first byte range to compare.
                  * @param offset The offset into the first byte range.
                  * @param other The second byte range to compare.
                  * @param otherOffset The offset into the second byte range.
                  * @param len The number of bytes to compare
                  * @return True if the byte ranges are equal, false otherwise.
                  */
                  function equals(bytes memory self, uint offset, bytes memory other, uint otherOffset, uint len) internal pure returns (bool) {
                      return keccak(self, offset, len) == keccak(other, otherOffset, len);
                  }
              
                  /*
                  * @dev Returns true if the two byte ranges are equal with offsets.
                  * @param self The first byte range to compare.
                  * @param offset The offset into the first byte range.
                  * @param other The second byte range to compare.
                  * @param otherOffset The offset into the second byte range.
                  * @return True if the byte ranges are equal, false otherwise.
                  */
                  function equals(bytes memory self, uint offset, bytes memory other, uint otherOffset) internal pure returns (bool) {
                      return keccak(self, offset, self.length - offset) == keccak(other, otherOffset, other.length - otherOffset);
                  }
              
                  /*
                  * @dev Compares a range of 'self' to all of 'other' and returns True iff
                  *      they are equal.
                  * @param self The first byte range to compare.
                  * @param offset The offset into the first byte range.
                  * @param other The second byte range to compare.
                  * @return True if the byte ranges are equal, false otherwise.
                  */
                  function equals(bytes memory self, uint offset, bytes memory other) internal pure returns (bool) {
                      return self.length >= offset + other.length && equals(self, offset, other, 0, other.length);
                  }
              
                  /*
                  * @dev Returns true if the two byte ranges are equal.
                  * @param self The first byte range to compare.
                  * @param other The second byte range to compare.
                  * @return True if the byte ranges are equal, false otherwise.
                  */
                  function equals(bytes memory self, bytes memory other) internal pure returns(bool) {
                      return self.length == other.length && equals(self, 0, other, 0, self.length);
                  }
              
                  /*
                  * @dev Returns the 8-bit number at the specified index of self.
                  * @param self The byte string.
                  * @param idx The index into the bytes
                  * @return The specified 8 bits of the string, interpreted as an integer.
                  */
                  function readUint8(bytes memory self, uint idx) internal pure returns (uint8 ret) {
                      return uint8(self[idx]);
                  }
              
                  /*
                  * @dev Returns the 16-bit number at the specified index of self.
                  * @param self The byte string.
                  * @param idx The index into the bytes
                  * @return The specified 16 bits of the string, interpreted as an integer.
                  */
                  function readUint16(bytes memory self, uint idx) internal pure returns (uint16 ret) {
                      require(idx + 2 <= self.length);
                      assembly {
                          ret := and(mload(add(add(self, 2), idx)), 0xFFFF)
                      }
                  }
              
                  /*
                  * @dev Returns the 32-bit number at the specified index of self.
                  * @param self The byte string.
                  * @param idx The index into the bytes
                  * @return The specified 32 bits of the string, interpreted as an integer.
                  */
                  function readUint32(bytes memory self, uint idx) internal pure returns (uint32 ret) {
                      require(idx + 4 <= self.length);
                      assembly {
                          ret := and(mload(add(add(self, 4), idx)), 0xFFFFFFFF)
                      }
                  }
              
                  /*
                  * @dev Returns the 32 byte value at the specified index of self.
                  * @param self The byte string.
                  * @param idx The index into the bytes
                  * @return The specified 32 bytes of the string.
                  */
                  function readBytes32(bytes memory self, uint idx) internal pure returns (bytes32 ret) {
                      require(idx + 32 <= self.length);
                      assembly {
                          ret := mload(add(add(self, 32), idx))
                      }
                  }
              
                  /*
                  * @dev Returns the 32 byte value at the specified index of self.
                  * @param self The byte string.
                  * @param idx The index into the bytes
                  * @return The specified 32 bytes of the string.
                  */
                  function readBytes20(bytes memory self, uint idx) internal pure returns (bytes20 ret) {
                      require(idx + 20 <= self.length);
                      assembly {
                          ret := and(mload(add(add(self, 32), idx)), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000)
                      }
                  }
              
                  /*
                  * @dev Returns the n byte value at the specified index of self.
                  * @param self The byte string.
                  * @param idx The index into the bytes.
                  * @param len The number of bytes.
                  * @return The specified 32 bytes of the string.
                  */
                  function readBytesN(bytes memory self, uint idx, uint len) internal pure returns (bytes32 ret) {
                      require(len <= 32);
                      require(idx + len <= self.length);
                      assembly {
                          let mask := not(sub(exp(256, sub(32, len)), 1))
                          ret := and(mload(add(add(self, 32), idx)),  mask)
                      }
                  }
              
                  function memcpy(uint dest, uint src, uint len) private pure {
                      // Copy word-length chunks while possible
                      for (; len >= 32; len -= 32) {
                          assembly {
                              mstore(dest, mload(src))
                          }
                          dest += 32;
                          src += 32;
                      }
              
                      // Copy remaining bytes
                      uint mask = 256 ** (32 - len) - 1;
                      assembly {
                          let srcpart := and(mload(src), not(mask))
                          let destpart := and(mload(dest), mask)
                          mstore(dest, or(destpart, srcpart))
                      }
                  }
              
                  /*
                  * @dev Copies a substring into a new byte string.
                  * @param self The byte string to copy from.
                  * @param offset The offset to start copying at.
                  * @param len The number of bytes to copy.
                  */
                  function substring(bytes memory self, uint offset, uint len) internal pure returns(bytes memory) {
                      require(offset + len <= self.length);
              
                      bytes memory ret = new bytes(len);
                      uint dest;
                      uint src;
              
                      assembly {
                          dest := add(ret, 32)
                          src := add(add(self, 32), offset)
                      }
                      memcpy(dest, src, len);
              
                      return ret;
                  }
              
                  // Maps characters from 0x30 to 0x7A to their base32 values.
                  // 0xFF represents invalid characters in that range.
                  bytes constant base32HexTable = hex'00010203040506070809FFFFFFFFFFFFFF0A0B0C0D0E0F101112131415161718191A1B1C1D1E1FFFFFFFFFFFFFFFFFFFFF0A0B0C0D0E0F101112131415161718191A1B1C1D1E1F';
              
                  /**
                   * @dev Decodes unpadded base32 data of up to one word in length.
                   * @param self The data to decode.
                   * @param off Offset into the string to start at.
                   * @param len Number of characters to decode.
                   * @return The decoded data, left aligned.
                   */
                  function base32HexDecodeWord(bytes memory self, uint off, uint len) internal pure returns(bytes32) {
                      require(len <= 52);
              
                      uint ret = 0;
                      uint8 decoded;
                      for(uint i = 0; i < len; i++) {
                          bytes1 char = self[off + i];
                          require(char >= 0x30 && char <= 0x7A);
                          decoded = uint8(base32HexTable[uint(uint8(char)) - 0x30]);
                          require(decoded <= 0x20);
                          if(i == len - 1) {
                              break;
                          }
                          ret = (ret << 5) | decoded;
                      }
              
                      uint bitlen = len * 5;
                      if(len % 8 == 0) {
                          // Multiple of 8 characters, no padding
                          ret = (ret << 5) | decoded;
                      } else if(len % 8 == 2) {
                          // Two extra characters - 1 byte
                          ret = (ret << 3) | (decoded >> 2);
                          bitlen -= 2;
                      } else if(len % 8 == 4) {
                          // Four extra characters - 2 bytes
                          ret = (ret << 1) | (decoded >> 4);
                          bitlen -= 4;
                      } else if(len % 8 == 5) {
                          // Five extra characters - 3 bytes
                          ret = (ret << 4) | (decoded >> 1);
                          bitlen -= 1;
                      } else if(len % 8 == 7) {
                          // Seven extra characters - 4 bytes
                          ret = (ret << 2) | (decoded >> 3);
                          bitlen -= 3;
                      } else {
                          revert();
                      }
              
                      return bytes32(ret << (256 - bitlen));
                  }
              }
              
              // File: @ensdomains/buffer/contracts/Buffer.sol
              
              pragma solidity >0.4.18;
              
              /**
              * @dev A library for working with mutable byte buffers in Solidity.
              *
              * Byte buffers are mutable and expandable, and provide a variety of primitives
              * for writing to them. At any time you can fetch a bytes object containing the
              * current contents of the buffer. The bytes object should not be stored between
              * operations, as it may change due to resizing of the buffer.
              */
              library Buffer {
                  /**
                  * @dev Represents a mutable buffer. Buffers have a current value (buf) and
                  *      a capacity. The capacity may be longer than the current value, in
                  *      which case it can be extended without the need to allocate more memory.
                  */
                  struct buffer {
                      bytes buf;
                      uint capacity;
                  }
              
                  /**
                  * @dev Initializes a buffer with an initial capacity.
                  * @param buf The buffer to initialize.
                  * @param capacity The number of bytes of space to allocate the buffer.
                  * @return The buffer, for chaining.
                  */
                  function init(buffer memory buf, uint capacity) internal pure returns(buffer memory) {
                      if (capacity % 32 != 0) {
                          capacity += 32 - (capacity % 32);
                      }
                      // Allocate space for the buffer data
                      buf.capacity = capacity;
                      assembly {
                          let ptr := mload(0x40)
                          mstore(buf, ptr)
                          mstore(ptr, 0)
                          mstore(0x40, add(32, add(ptr, capacity)))
                      }
                      return buf;
                  }
              
                  /**
                  * @dev Initializes a new buffer from an existing bytes object.
                  *      Changes to the buffer may mutate the original value.
                  * @param b The bytes object to initialize the buffer with.
                  * @return A new buffer.
                  */
                  function fromBytes(bytes memory b) internal pure returns(buffer memory) {
                      buffer memory buf;
                      buf.buf = b;
                      buf.capacity = b.length;
                      return buf;
                  }
              
                  function resize(buffer memory buf, uint capacity) private pure {
                      bytes memory oldbuf = buf.buf;
                      init(buf, capacity);
                      append(buf, oldbuf);
                  }
              
                  function max(uint a, uint b) private pure returns(uint) {
                      if (a > b) {
                          return a;
                      }
                      return b;
                  }
              
                  /**
                  * @dev Sets buffer length to 0.
                  * @param buf The buffer to truncate.
                  * @return The original buffer, for chaining..
                  */
                  function truncate(buffer memory buf) internal pure returns (buffer memory) {
                      assembly {
                          let bufptr := mload(buf)
                          mstore(bufptr, 0)
                      }
                      return buf;
                  }
              
                  /**
                  * @dev Writes a byte string to a buffer. Resizes if doing so would exceed
                  *      the capacity of the buffer.
                  * @param buf The buffer to append to.
                  * @param off The start offset to write to.
                  * @param data The data to append.
                  * @param len The number of bytes to copy.
                  * @return The original buffer, for chaining.
                  */
                  function write(buffer memory buf, uint off, bytes memory data, uint len) internal pure returns(buffer memory) {
                      require(len <= data.length);
              
                      if (off + len > buf.capacity) {
                          resize(buf, max(buf.capacity, len + off) * 2);
                      }
              
                      uint dest;
                      uint src;
                      assembly {
                          // Memory address of the buffer data
                          let bufptr := mload(buf)
                          // Length of existing buffer data
                          let buflen := mload(bufptr)
                          // Start address = buffer address + offset + sizeof(buffer length)
                          dest := add(add(bufptr, 32), off)
                          // Update buffer length if we're extending it
                          if gt(add(len, off), buflen) {
                              mstore(bufptr, add(len, off))
                          }
                          src := add(data, 32)
                      }
              
                      // Copy word-length chunks while possible
                      for (; len >= 32; len -= 32) {
                          assembly {
                              mstore(dest, mload(src))
                          }
                          dest += 32;
                          src += 32;
                      }
              
                      // Copy remaining bytes
                      uint mask = 256 ** (32 - len) - 1;
                      assembly {
                          let srcpart := and(mload(src), not(mask))
                          let destpart := and(mload(dest), mask)
                          mstore(dest, or(destpart, srcpart))
                      }
              
                      return buf;
                  }
              
                  /**
                  * @dev Appends a byte string to a buffer. Resizes if doing so would exceed
                  *      the capacity of the buffer.
                  * @param buf The buffer to append to.
                  * @param data The data to append.
                  * @param len The number of bytes to copy.
                  * @return The original buffer, for chaining.
                  */
                  function append(buffer memory buf, bytes memory data, uint len) internal pure returns (buffer memory) {
                      return write(buf, buf.buf.length, data, len);
                  }
              
                  /**
                  * @dev Appends a byte string to a buffer. Resizes if doing so would exceed
                  *      the capacity of the buffer.
                  * @param buf The buffer to append to.
                  * @param data The data to append.
                  * @return The original buffer, for chaining.
                  */
                  function append(buffer memory buf, bytes memory data) internal pure returns (buffer memory) {
                      return write(buf, buf.buf.length, data, data.length);
                  }
              
                  /**
                  * @dev Writes a byte to the buffer. Resizes if doing so would exceed the
                  *      capacity of the buffer.
                  * @param buf The buffer to append to.
                  * @param off The offset to write the byte at.
                  * @param data The data to append.
                  * @return The original buffer, for chaining.
                  */
                  function writeUint8(buffer memory buf, uint off, uint8 data) internal pure returns(buffer memory) {
                      if (off >= buf.capacity) {
                          resize(buf, buf.capacity * 2);
                      }
              
                      assembly {
                          // Memory address of the buffer data
                          let bufptr := mload(buf)
                          // Length of existing buffer data
                          let buflen := mload(bufptr)
                          // Address = buffer address + sizeof(buffer length) + off
                          let dest := add(add(bufptr, off), 32)
                          mstore8(dest, data)
                          // Update buffer length if we extended it
                          if eq(off, buflen) {
                              mstore(bufptr, add(buflen, 1))
                          }
                      }
                      return buf;
                  }
              
                  /**
                  * @dev Appends a byte to the buffer. Resizes if doing so would exceed the
                  *      capacity of the buffer.
                  * @param buf The buffer to append to.
                  * @param data The data to append.
                  * @return The original buffer, for chaining.
                  */
                  function appendUint8(buffer memory buf, uint8 data) internal pure returns(buffer memory) {
                      return writeUint8(buf, buf.buf.length, data);
                  }
              
                  /**
                  * @dev Writes up to 32 bytes to the buffer. Resizes if doing so would
                  *      exceed the capacity of the buffer.
                  * @param buf The buffer to append to.
                  * @param off The offset to write at.
                  * @param data The data to append.
                  * @param len The number of bytes to write (left-aligned).
                  * @return The original buffer, for chaining.
                  */
                  function write(buffer memory buf, uint off, bytes32 data, uint len) private pure returns(buffer memory) {
                      if (len + off > buf.capacity) {
                          resize(buf, (len + off) * 2);
                      }
              
                      uint mask = 256 ** len - 1;
                      // Right-align data
                      data = data >> (8 * (32 - len));
                      assembly {
                          // Memory address of the buffer data
                          let bufptr := mload(buf)
                          // Address = buffer address + sizeof(buffer length) + off + len
                          let dest := add(add(bufptr, off), len)
                          mstore(dest, or(and(mload(dest), not(mask)), data))
                          // Update buffer length if we extended it
                          if gt(add(off, len), mload(bufptr)) {
                              mstore(bufptr, add(off, len))
                          }
                      }
                      return buf;
                  }
              
                  /**
                  * @dev Writes a bytes20 to the buffer. Resizes if doing so would exceed the
                  *      capacity of the buffer.
                  * @param buf The buffer to append to.
                  * @param off The offset to write at.
                  * @param data The data to append.
                  * @return The original buffer, for chaining.
                  */
                  function writeBytes20(buffer memory buf, uint off, bytes20 data) internal pure returns (buffer memory) {
                      return write(buf, off, bytes32(data), 20);
                  }
              
                  /**
                  * @dev Appends a bytes20 to the buffer. Resizes if doing so would exceed
                  *      the capacity of the buffer.
                  * @param buf The buffer to append to.
                  * @param data The data to append.
                  * @return The original buffer, for chhaining.
                  */
                  function appendBytes20(buffer memory buf, bytes20 data) internal pure returns (buffer memory) {
                      return write(buf, buf.buf.length, bytes32(data), 20);
                  }
              
                  /**
                  * @dev Appends a bytes32 to the buffer. Resizes if doing so would exceed
                  *      the capacity of the buffer.
                  * @param buf The buffer to append to.
                  * @param data The data to append.
                  * @return The original buffer, for chaining.
                  */
                  function appendBytes32(buffer memory buf, bytes32 data) internal pure returns (buffer memory) {
                      return write(buf, buf.buf.length, data, 32);
                  }
              
                  /**
                  * @dev Writes an integer to the buffer. Resizes if doing so would exceed
                  *      the capacity of the buffer.
                  * @param buf The buffer to append to.
                  * @param off The offset to write at.
                  * @param data The data to append.
                  * @param len The number of bytes to write (right-aligned).
                  * @return The original buffer, for chaining.
                  */
                  function writeInt(buffer memory buf, uint off, uint data, uint len) private pure returns(buffer memory) {
                      if (len + off > buf.capacity) {
                          resize(buf, (len + off) * 2);
                      }
              
                      uint mask = 256 ** len - 1;
                      assembly {
                          // Memory address of the buffer data
                          let bufptr := mload(buf)
                          // Address = buffer address + off + sizeof(buffer length) + len
                          let dest := add(add(bufptr, off), len)
                          mstore(dest, or(and(mload(dest), not(mask)), data))
                          // Update buffer length if we extended it
                          if gt(add(off, len), mload(bufptr)) {
                              mstore(bufptr, add(off, len))
                          }
                      }
                      return buf;
                  }
              
                  /**
                   * @dev Appends a byte to the end of the buffer. Resizes if doing so would
                   * exceed the capacity of the buffer.
                   * @param buf The buffer to append to.
                   * @param data The data to append.
                   * @return The original buffer.
                   */
                  function appendInt(buffer memory buf, uint data, uint len) internal pure returns(buffer memory) {
                      return writeInt(buf, buf.buf.length, data, len);
                  }
              }
              
              // File: @ensdomains/dnssec-oracle/contracts/RRUtils.sol
              
              pragma solidity >0.4.23;
              
              
              
              /**
              * @dev RRUtils is a library that provides utilities for parsing DNS resource records.
              */
              library RRUtils {
                  using BytesUtils for *;
                  using Buffer for *;
              
                  /**
                  * @dev Returns the number of bytes in the DNS name at 'offset' in 'self'.
                  * @param self The byte array to read a name from.
                  * @param offset The offset to start reading at.
                  * @return The length of the DNS name at 'offset', in bytes.
                  */
                  function nameLength(bytes memory self, uint offset) internal pure returns(uint) {
                      uint idx = offset;
                      while (true) {
                          assert(idx < self.length);
                          uint labelLen = self.readUint8(idx);
                          idx += labelLen + 1;
                          if (labelLen == 0) {
                              break;
                          }
                      }
                      return idx - offset;
                  }
              
                  /**
                  * @dev Returns a DNS format name at the specified offset of self.
                  * @param self The byte array to read a name from.
                  * @param offset The offset to start reading at.
                  * @return The name.
                  */
                  function readName(bytes memory self, uint offset) internal pure returns(bytes memory ret) {
                      uint len = nameLength(self, offset);
                      return self.substring(offset, len);
                  }
              
                  /**
                  * @dev Returns the number of labels in the DNS name at 'offset' in 'self'.
                  * @param self The byte array to read a name from.
                  * @param offset The offset to start reading at.
                  * @return The number of labels in the DNS name at 'offset', in bytes.
                  */
                  function labelCount(bytes memory self, uint offset) internal pure returns(uint) {
                      uint count = 0;
                      while (true) {
                          assert(offset < self.length);
                          uint labelLen = self.readUint8(offset);
                          offset += labelLen + 1;
                          if (labelLen == 0) {
                              break;
                          }
                          count += 1;
                      }
                      return count;
                  }
              
                  /**
                  * @dev An iterator over resource records.
                  */
                  struct RRIterator {
                      bytes data;
                      uint offset;
                      uint16 dnstype;
                      uint16 class;
                      uint32 ttl;
                      uint rdataOffset;
                      uint nextOffset;
                  }
              
                  /**
                  * @dev Begins iterating over resource records.
                  * @param self The byte string to read from.
                  * @param offset The offset to start reading at.
                  * @return An iterator object.
                  */
                  function iterateRRs(bytes memory self, uint offset) internal pure returns (RRIterator memory ret) {
                      ret.data = self;
                      ret.nextOffset = offset;
                      next(ret);
                  }
              
                  /**
                  * @dev Returns true iff there are more RRs to iterate.
                  * @param iter The iterator to check.
                  * @return True iff the iterator has finished.
                  */
                  function done(RRIterator memory iter) internal pure returns(bool) {
                      return iter.offset >= iter.data.length;
                  }
              
                  /**
                  * @dev Moves the iterator to the next resource record.
                  * @param iter The iterator to advance.
                  */
                  function next(RRIterator memory iter) internal pure {
                      iter.offset = iter.nextOffset;
                      if (iter.offset >= iter.data.length) {
                          return;
                      }
              
                      // Skip the name
                      uint off = iter.offset + nameLength(iter.data, iter.offset);
              
                      // Read type, class, and ttl
                      iter.dnstype = iter.data.readUint16(off);
                      off += 2;
                      iter.class = iter.data.readUint16(off);
                      off += 2;
                      iter.ttl = iter.data.readUint32(off);
                      off += 4;
              
                      // Read the rdata
                      uint rdataLength = iter.data.readUint16(off);
                      off += 2;
                      iter.rdataOffset = off;
                      iter.nextOffset = off + rdataLength;
                  }
              
                  /**
                  * @dev Returns the name of the current record.
                  * @param iter The iterator.
                  * @return A new bytes object containing the owner name from the RR.
                  */
                  function name(RRIterator memory iter) internal pure returns(bytes memory) {
                      return iter.data.substring(iter.offset, nameLength(iter.data, iter.offset));
                  }
              
                  /**
                  * @dev Returns the rdata portion of the current record.
                  * @param iter The iterator.
                  * @return A new bytes object containing the RR's RDATA.
                  */
                  function rdata(RRIterator memory iter) internal pure returns(bytes memory) {
                      return iter.data.substring(iter.rdataOffset, iter.nextOffset - iter.rdataOffset);
                  }
              
                  /**
                  * @dev Checks if a given RR type exists in a type bitmap.
                  * @param self The byte string to read the type bitmap from.
                  * @param offset The offset to start reading at.
                  * @param rrtype The RR type to check for.
                  * @return True if the type is found in the bitmap, false otherwise.
                  */
                  function checkTypeBitmap(bytes memory self, uint offset, uint16 rrtype) internal pure returns (bool) {
                      uint8 typeWindow = uint8(rrtype >> 8);
                      uint8 windowByte = uint8((rrtype & 0xff) / 8);
                      uint8 windowBitmask = uint8(uint8(1) << (uint8(7) - uint8(rrtype & 0x7)));
                      for (uint off = offset; off < self.length;) {
                          uint8 window = self.readUint8(off);
                          uint8 len = self.readUint8(off + 1);
                          if (typeWindow < window) {
                              // We've gone past our window; it's not here.
                              return false;
                          } else if (typeWindow == window) {
                              // Check this type bitmap
                              if (len * 8 <= windowByte) {
                                  // Our type is past the end of the bitmap
                                  return false;
                              }
                              return (self.readUint8(off + windowByte + 2) & windowBitmask) != 0;
                          } else {
                              // Skip this type bitmap
                              off += len + 2;
                          }
                      }
              
                      return false;
                  }
              
                  function compareNames(bytes memory self, bytes memory other) internal pure returns (int) {
                      if (self.equals(other)) {
                          return 0;
                      }
              
                      uint off;
                      uint otheroff;
                      uint prevoff;
                      uint otherprevoff;
                      uint counts = labelCount(self, 0);
                      uint othercounts = labelCount(other, 0);
              
                      // Keep removing labels from the front of the name until both names are equal length
                      while (counts > othercounts) {
                          prevoff = off;
                          off = progress(self, off);
                          counts--;
                      }
              
                      while (othercounts > counts) {
                          otherprevoff = otheroff;
                          otheroff = progress(other, otheroff);
                          othercounts--;
                      }
              
                      // Compare the last nonequal labels to each other
                      while (counts > 0 && !self.equals(off, other, otheroff)) {
                          prevoff = off;
                          off = progress(self, off);
                          otherprevoff = otheroff;
                          otheroff = progress(other, otheroff);
                          counts -= 1;
                      }
              
                      if (off == 0) {
                          return -1;
                      }
                      if(otheroff == 0) {
                          return 1;
                      }
              
                      return self.compare(prevoff + 1, self.readUint8(prevoff), other, otherprevoff + 1, other.readUint8(otherprevoff));
                  }
              
                  function progress(bytes memory body, uint off) internal pure returns(uint) {
                      return off + 1 + body.readUint8(off);
                  }
              }
              
              // File: contracts/profiles/DNSResolver.sol
              
              pragma solidity ^0.5.0;
              
              
              
              contract DNSResolver is ResolverBase {
                  using RRUtils for *;
                  using BytesUtils for bytes;
              
                  bytes4 constant private DNS_RECORD_INTERFACE_ID = 0xa8fa5682;
              
                  // DNSRecordChanged is emitted whenever a given node/name/resource's RRSET is updated.
                  event DNSRecordChanged(bytes32 indexed node, bytes name, uint16 resource, bytes record);
                  // DNSRecordDeleted is emitted whenever a given node/name/resource's RRSET is deleted.
                  event DNSRecordDeleted(bytes32 indexed node, bytes name, uint16 resource);
                  // DNSZoneCleared is emitted whenever a given node's zone information is cleared.
                  event DNSZoneCleared(bytes32 indexed node);
              
                  // Version the mapping for each zone.  This allows users who have lost
                  // track of their entries to effectively delete an entire zone by bumping
                  // the version number.
                  // node => version
                  mapping(bytes32=>uint256) private versions;
              
                  // The records themselves.  Stored as binary RRSETs
                  // node => version => name => resource => data
                  mapping(bytes32=>mapping(uint256=>mapping(bytes32=>mapping(uint16=>bytes)))) private records;
              
                  // Count of number of entries for a given name.  Required for DNS resolvers
                  // when resolving wildcards.
                  // node => version => name => number of records
                  mapping(bytes32=>mapping(uint256=>mapping(bytes32=>uint16))) private nameEntriesCount;
              
                  /**
                   * Set one or more DNS records.  Records are supplied in wire-format.
                   * Records with the same node/name/resource must be supplied one after the
                   * other to ensure the data is updated correctly. For example, if the data
                   * was supplied:
                   *     a.example.com IN A 1.2.3.4
                   *     a.example.com IN A 5.6.7.8
                   *     www.example.com IN CNAME a.example.com.
                   * then this would store the two A records for a.example.com correctly as a
                   * single RRSET, however if the data was supplied:
                   *     a.example.com IN A 1.2.3.4
                   *     www.example.com IN CNAME a.example.com.
                   *     a.example.com IN A 5.6.7.8
                   * then this would store the first A record, the CNAME, then the second A
                   * record which would overwrite the first.
                   *
                   * @param node the namehash of the node for which to set the records
                   * @param data the DNS wire format records to set
                   */
                  function setDNSRecords(bytes32 node, bytes calldata data) external authorised(node) {
                      uint16 resource = 0;
                      uint256 offset = 0;
                      bytes memory name;
                      bytes memory value;
                      bytes32 nameHash;
                      // Iterate over the data to add the resource records
                      for (RRUtils.RRIterator memory iter = data.iterateRRs(0); !iter.done(); iter.next()) {
                          if (resource == 0) {
                              resource = iter.dnstype;
                              name = iter.name();
                              nameHash = keccak256(abi.encodePacked(name));
                              value = bytes(iter.rdata());
                          } else {
                              bytes memory newName = iter.name();
                              if (resource != iter.dnstype || !name.equals(newName)) {
                                  setDNSRRSet(node, name, resource, data, offset, iter.offset - offset, value.length == 0);
                                  resource = iter.dnstype;
                                  offset = iter.offset;
                                  name = newName;
                                  nameHash = keccak256(name);
                                  value = bytes(iter.rdata());
                              }
                          }
                      }
                      if (name.length > 0) {
                          setDNSRRSet(node, name, resource, data, offset, data.length - offset, value.length == 0);
                      }
                  }
              
                  /**
                   * Obtain a DNS record.
                   * @param node the namehash of the node for which to fetch the record
                   * @param name the keccak-256 hash of the fully-qualified name for which to fetch the record
                   * @param resource the ID of the resource as per https://en.wikipedia.org/wiki/List_of_DNS_record_types
                   * @return the DNS record in wire format if present, otherwise empty
                   */
                  function dnsRecord(bytes32 node, bytes32 name, uint16 resource) public view returns (bytes memory) {
                      return records[node][versions[node]][name][resource];
                  }
              
                  /**
                   * Check if a given node has records.
                   * @param node the namehash of the node for which to check the records
                   * @param name the namehash of the node for which to check the records
                   */
                  function hasDNSRecords(bytes32 node, bytes32 name) public view returns (bool) {
                      return (nameEntriesCount[node][versions[node]][name] != 0);
                  }
              
                  /**
                   * Clear all information for a DNS zone.
                   * @param node the namehash of the node for which to clear the zone
                   */
                  function clearDNSZone(bytes32 node) public authorised(node) {
                      versions[node]++;
                      emit DNSZoneCleared(node);
                  }
              
                  function supportsInterface(bytes4 interfaceID) public pure returns(bool) {
                      return interfaceID == DNS_RECORD_INTERFACE_ID || super.supportsInterface(interfaceID);
                  }
              
                  function setDNSRRSet(
                      bytes32 node,
                      bytes memory name,
                      uint16 resource,
                      bytes memory data,
                      uint256 offset,
                      uint256 size,
                      bool deleteRecord) private
                  {
                      uint256 version = versions[node];
                      bytes32 nameHash = keccak256(name);
                      bytes memory rrData = data.substring(offset, size);
                      if (deleteRecord) {
                          if (records[node][version][nameHash][resource].length != 0) {
                              nameEntriesCount[node][version][nameHash]--;
                          }
                          delete(records[node][version][nameHash][resource]);
                          emit DNSRecordDeleted(node, name, resource);
                      } else {
                          if (records[node][version][nameHash][resource].length == 0) {
                              nameEntriesCount[node][version][nameHash]++;
                          }
                          records[node][version][nameHash][resource] = rrData;
                          emit DNSRecordChanged(node, name, resource, rrData);
                      }
                  }
              }
              
              // File: contracts/profiles/InterfaceResolver.sol
              
              pragma solidity ^0.5.0;
              
              
              
              contract InterfaceResolver is ResolverBase, AddrResolver {
                  bytes4 constant private INTERFACE_INTERFACE_ID = bytes4(keccak256("interfaceImplementer(bytes32,bytes4)"));
                  bytes4 private constant INTERFACE_META_ID = 0x01ffc9a7;
              
                  event InterfaceChanged(bytes32 indexed node, bytes4 indexed interfaceID, address implementer);
              
                  mapping(bytes32=>mapping(bytes4=>address)) interfaces;
              
                  /**
                   * Sets an interface associated with a name.
                   * Setting the address to 0 restores the default behaviour of querying the contract at `addr()` for interface support.
                   * @param node The node to update.
                   * @param interfaceID The EIP 168 interface ID.
                   * @param implementer The address of a contract that implements this interface for this node.
                   */
                  function setInterface(bytes32 node, bytes4 interfaceID, address implementer) external authorised(node) {
                      interfaces[node][interfaceID] = implementer;
                      emit InterfaceChanged(node, interfaceID, implementer);
                  }
              
                  /**
                   * Returns the address of a contract that implements the specified interface for this name.
                   * If an implementer has not been set for this interfaceID and name, the resolver will query
                   * the contract at `addr()`. If `addr()` is set, a contract exists at that address, and that
                   * contract implements EIP168 and returns `true` for the specified interfaceID, its address
                   * will be returned.
                   * @param node The ENS node to query.
                   * @param interfaceID The EIP 168 interface ID to check for.
                   * @return The address that implements this interface, or 0 if the interface is unsupported.
                   */
                  function interfaceImplementer(bytes32 node, bytes4 interfaceID) external view returns (address) {
                      address implementer = interfaces[node][interfaceID];
                      if(implementer != address(0)) {
                          return implementer;
                      }
              
                      address a = addr(node);
                      if(a == address(0)) {
                          return address(0);
                      }
              
                      (bool success, bytes memory returnData) = a.staticcall(abi.encodeWithSignature("supportsInterface(bytes4)", INTERFACE_META_ID));
                      if(!success || returnData.length < 32 || returnData[31] == 0) {
                          // EIP 168 not supported by target
                          return address(0);
                      }
              
                      (success, returnData) = a.staticcall(abi.encodeWithSignature("supportsInterface(bytes4)", interfaceID));
                      if(!success || returnData.length < 32 || returnData[31] == 0) {
                          // Specified interface not supported by target
                          return address(0);
                      }
              
                      return a;
                  }
              
                  function supportsInterface(bytes4 interfaceID) public pure returns(bool) {
                      return interfaceID == INTERFACE_INTERFACE_ID || super.supportsInterface(interfaceID);
                  }
              }
              
              // File: contracts/profiles/NameResolver.sol
              
              pragma solidity ^0.5.0;
              
              
              contract NameResolver is ResolverBase {
                  bytes4 constant private NAME_INTERFACE_ID = 0x691f3431;
              
                  event NameChanged(bytes32 indexed node, string name);
              
                  mapping(bytes32=>string) names;
              
                  /**
                   * Sets the name associated with an ENS node, for reverse records.
                   * May only be called by the owner of that node in the ENS registry.
                   * @param node The node to update.
                   * @param name The name to set.
                   */
                  function setName(bytes32 node, string calldata name) external authorised(node) {
                      names[node] = name;
                      emit NameChanged(node, name);
                  }
              
                  /**
                   * Returns the name associated with an ENS node, for reverse records.
                   * Defined in EIP181.
                   * @param node The ENS node to query.
                   * @return The associated name.
                   */
                  function name(bytes32 node) external view returns (string memory) {
                      return names[node];
                  }
              
                  function supportsInterface(bytes4 interfaceID) public pure returns(bool) {
                      return interfaceID == NAME_INTERFACE_ID || super.supportsInterface(interfaceID);
                  }
              }
              
              // File: contracts/profiles/PubkeyResolver.sol
              
              pragma solidity ^0.5.0;
              
              
              contract PubkeyResolver is ResolverBase {
                  bytes4 constant private PUBKEY_INTERFACE_ID = 0xc8690233;
              
                  event PubkeyChanged(bytes32 indexed node, bytes32 x, bytes32 y);
              
                  struct PublicKey {
                      bytes32 x;
                      bytes32 y;
                  }
              
                  mapping(bytes32=>PublicKey) pubkeys;
              
                  /**
                   * Sets the SECP256k1 public key associated with an ENS node.
                   * @param node The ENS node to query
                   * @param x the X coordinate of the curve point for the public key.
                   * @param y the Y coordinate of the curve point for the public key.
                   */
                  function setPubkey(bytes32 node, bytes32 x, bytes32 y) external authorised(node) {
                      pubkeys[node] = PublicKey(x, y);
                      emit PubkeyChanged(node, x, y);
                  }
              
                  /**
                   * Returns the SECP256k1 public key associated with an ENS node.
                   * Defined in EIP 619.
                   * @param node The ENS node to query
                   * @return x, y the X and Y coordinates of the curve point for the public key.
                   */
                  function pubkey(bytes32 node) external view returns (bytes32 x, bytes32 y) {
                      return (pubkeys[node].x, pubkeys[node].y);
                  }
              
                  function supportsInterface(bytes4 interfaceID) public pure returns(bool) {
                      return interfaceID == PUBKEY_INTERFACE_ID || super.supportsInterface(interfaceID);
                  }
              }
              
              // File: contracts/profiles/TextResolver.sol
              
              pragma solidity ^0.5.0;
              
              
              contract TextResolver is ResolverBase {
                  bytes4 constant private TEXT_INTERFACE_ID = 0x59d1d43c;
              
                  event TextChanged(bytes32 indexed node, string indexed indexedKey, string key);
              
                  mapping(bytes32=>mapping(string=>string)) texts;
              
                  /**
                   * Sets the text data associated with an ENS node and key.
                   * May only be called by the owner of that node in the ENS registry.
                   * @param node The node to update.
                   * @param key The key to set.
                   * @param value The text data value to set.
                   */
                  function setText(bytes32 node, string calldata key, string calldata value) external authorised(node) {
                      texts[node][key] = value;
                      emit TextChanged(node, key, key);
                  }
              
                  /**
                   * Returns the text data associated with an ENS node and key.
                   * @param node The ENS node to query.
                   * @param key The text data key to query.
                   * @return The associated text data.
                   */
                  function text(bytes32 node, string calldata key) external view returns (string memory) {
                      return texts[node][key];
                  }
              
                  function supportsInterface(bytes4 interfaceID) public pure returns(bool) {
                      return interfaceID == TEXT_INTERFACE_ID || super.supportsInterface(interfaceID);
                  }
              }
              
              // File: contracts/PublicResolver.sol
              
              pragma solidity ^0.5.0;
              pragma experimental ABIEncoderV2;
              
              
              
              
              
              
              
              
              
              
              /**
               * A simple resolver anyone can use; only allows the owner of a node to set its
               * address.
               */
              contract PublicResolver is ABIResolver, AddrResolver, ContentHashResolver, DNSResolver, InterfaceResolver, NameResolver, PubkeyResolver, TextResolver {
                  ENS ens;
              
                  /**
                   * A mapping of authorisations. An address that is authorised for a name
                   * may make any changes to the name that the owner could, but may not update
                   * the set of authorisations.
                   * (node, owner, caller) => isAuthorised
                   */
                  mapping(bytes32=>mapping(address=>mapping(address=>bool))) public authorisations;
              
                  event AuthorisationChanged(bytes32 indexed node, address indexed owner, address indexed target, bool isAuthorised);
              
                  constructor(ENS _ens) public {
                      ens = _ens;
                  }
              
                  /**
                   * @dev Sets or clears an authorisation.
                   * Authorisations are specific to the caller. Any account can set an authorisation
                   * for any name, but the authorisation that is checked will be that of the
                   * current owner of a name. Thus, transferring a name effectively clears any
                   * existing authorisations, and new authorisations can be set in advance of
                   * an ownership transfer if desired.
                   *
                   * @param node The name to change the authorisation on.
                   * @param target The address that is to be authorised or deauthorised.
                   * @param isAuthorised True if the address should be authorised, or false if it should be deauthorised.
                   */
                  function setAuthorisation(bytes32 node, address target, bool isAuthorised) external {
                      authorisations[node][msg.sender][target] = isAuthorised;
                      emit AuthorisationChanged(node, msg.sender, target, isAuthorised);
                  }
              
                  function isAuthorised(bytes32 node) internal view returns(bool) {
                      address owner = ens.owner(node);
                      return owner == msg.sender || authorisations[node][owner][msg.sender];
                  }
              
                  function multicall(bytes[] calldata data) external returns(bytes[] memory results) {
                      results = new bytes[](data.length);
                      for(uint i = 0; i < data.length; i++) {
                          (bool success, bytes memory result) = address(this).delegatecall(data[i]);
                          require(success);
                          results[i] = result;
                      }
                      return results;
                  }
              }

              File 5 of 7: LinearPremiumPriceOracle
              // File: contracts/SafeMath.sol
              
              pragma solidity >=0.4.24;
              
              /**
               * @title SafeMath
               * @dev Unsigned math operations with safety checks that revert on error
               */
              library SafeMath {
                  /**
                  * @dev Multiplies two unsigned integers, reverts on overflow.
                  */
                  function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                      // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                      // benefit is lost if 'b' is also tested.
                      // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
                      if (a == 0) {
                          return 0;
                      }
              
                      uint256 c = a * b;
                      require(c / a == b);
              
                      return c;
                  }
              
                  /**
                  * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero.
                  */
                  function div(uint256 a, uint256 b) internal pure returns (uint256) {
                      // Solidity only automatically asserts when dividing by 0
                      require(b > 0);
                      uint256 c = a / b;
                      // assert(a == b * c + a % b); // There is no case in which this doesn't hold
              
                      return c;
                  }
              
                  /**
                  * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend).
                  */
                  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                      require(b <= a);
                      uint256 c = a - b;
              
                      return c;
                  }
              
                  /**
                  * @dev Adds two unsigned integers, reverts on overflow.
                  */
                  function add(uint256 a, uint256 b) internal pure returns (uint256) {
                      uint256 c = a + b;
                      require(c >= a);
              
                      return c;
                  }
              
                  /**
                  * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo),
                  * reverts when dividing by zero.
                  */
                  function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                      require(b != 0);
                      return a % b;
                  }
              }
              
              // File: contracts/PriceOracle.sol
              
              pragma solidity >=0.4.24;
              
              interface PriceOracle {
                  /**
                   * @dev Returns the price to register or renew a name.
                   * @param name The name being registered or renewed.
                   * @param expires When the name presently expires (0 if this is a new registration).
                   * @param duration How long the name is being registered or extended for, in seconds.
                   * @return The price of this renewal or registration, in wei.
                   */
                  function price(string calldata name, uint expires, uint duration) external view returns(uint);
              }
              
              // File: contracts/StringUtils.sol
              
              pragma solidity >=0.4.24;
              
              library StringUtils {
                  /**
                   * @dev Returns the length of a given string
                   *
                   * @param s The string to measure the length of
                   * @return The length of the input string
                   */
                  function strlen(string memory s) internal pure returns (uint) {
                      uint len;
                      uint i = 0;
                      uint bytelength = bytes(s).length;
                      for(len = 0; i < bytelength; len++) {
                          byte b = bytes(s)[i];
                          if(b < 0x80) {
                              i += 1;
                          } else if (b < 0xE0) {
                              i += 2;
                          } else if (b < 0xF0) {
                              i += 3;
                          } else if (b < 0xF8) {
                              i += 4;
                          } else if (b < 0xFC) {
                              i += 5;
                          } else {
                              i += 6;
                          }
                      }
                      return len;
                  }
              }
              
              // File: openzeppelin-solidity/contracts/ownership/Ownable.sol
              
              pragma solidity ^0.5.0;
              
              /**
               * @title Ownable
               * @dev The Ownable contract has an owner address, and provides basic authorization control
               * functions, this simplifies the implementation of "user permissions".
               */
              contract Ownable {
                  address private _owner;
              
                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
              
                  /**
                   * @dev The Ownable constructor sets the original `owner` of the contract to the sender
                   * account.
                   */
                  constructor () internal {
                      _owner = msg.sender;
                      emit OwnershipTransferred(address(0), _owner);
                  }
              
                  /**
                   * @return the address of the owner.
                   */
                  function owner() public view returns (address) {
                      return _owner;
                  }
              
                  /**
                   * @dev Throws if called by any account other than the owner.
                   */
                  modifier onlyOwner() {
                      require(isOwner());
                      _;
                  }
              
                  /**
                   * @return true if `msg.sender` is the owner of the contract.
                   */
                  function isOwner() public view returns (bool) {
                      return msg.sender == _owner;
                  }
              
                  /**
                   * @dev Allows the current owner to relinquish control of the contract.
                   * @notice Renouncing to ownership will leave the contract without an owner.
                   * It will not be possible to call the functions with the `onlyOwner`
                   * modifier anymore.
                   */
                  function renounceOwnership() public onlyOwner {
                      emit OwnershipTransferred(_owner, address(0));
                      _owner = address(0);
                  }
              
                  /**
                   * @dev Allows the current owner to transfer control of the contract to a newOwner.
                   * @param newOwner The address to transfer ownership to.
                   */
                  function transferOwnership(address newOwner) public onlyOwner {
                      _transferOwnership(newOwner);
                  }
              
                  /**
                   * @dev Transfers control of the contract to a newOwner.
                   * @param newOwner The address to transfer ownership to.
                   */
                  function _transferOwnership(address newOwner) internal {
                      require(newOwner != address(0));
                      emit OwnershipTransferred(_owner, newOwner);
                      _owner = newOwner;
                  }
              }
              
              // File: contracts/StablePriceOracle.sol
              
              pragma solidity >=0.5.0;
              
              
              
              
              
              interface AggregatorInterface {
                function latestAnswer() external view returns (int256);
              }
              
              
              // StablePriceOracle sets a price in USD, based on an oracle.
              contract StablePriceOracle is Ownable, PriceOracle {
                  using SafeMath for *;
                  using StringUtils for *;
              
                  // Rent in base price units by length. Element 0 is for 1-length names, and so on.
                  uint[] public rentPrices;
              
                  // Oracle address
                  AggregatorInterface public usdOracle;
              
                  event OracleChanged(address oracle);
              
                  event RentPriceChanged(uint[] prices);
              
                  bytes4 constant private INTERFACE_META_ID = bytes4(keccak256("supportsInterface(bytes4)"));
                  bytes4 constant private ORACLE_ID = bytes4(keccak256("price(string,uint256,uint256)") ^ keccak256("premium(string,uint256,uint256)"));
              
                  constructor(AggregatorInterface _usdOracle, uint[] memory _rentPrices) public {
                      usdOracle = _usdOracle;
                      setPrices(_rentPrices);
                  }
              
                  function price(string calldata name, uint expires, uint duration) external view returns(uint) {
                      uint len = name.strlen();
                      if(len > rentPrices.length) {
                          len = rentPrices.length;
                      }
                      require(len > 0);
                      
                      uint basePrice = rentPrices[len - 1].mul(duration);
                      basePrice = basePrice.add(_premium(name, expires, duration));
              
                      return attoUSDToWei(basePrice);
                  }
              
                  /**
                   * @dev Sets rent prices.
                   * @param _rentPrices The price array. Each element corresponds to a specific
                   *                    name length; names longer than the length of the array
                   *                    default to the price of the last element. Values are
                   *                    in base price units, equal to one attodollar (1e-18
                   *                    dollar) each.
                   */
                  function setPrices(uint[] memory _rentPrices) public onlyOwner {
                      rentPrices = _rentPrices;
                      emit RentPriceChanged(_rentPrices);
                  }
              
                  /**
                   * @dev Sets the price oracle address
                   * @param _usdOracle The address of the price oracle to use.
                   */
                  function setOracle(AggregatorInterface _usdOracle) public onlyOwner {
                      usdOracle = _usdOracle;
                      emit OracleChanged(address(_usdOracle));
                  }
              
                  /**
                   * @dev Returns the pricing premium in wei.
                   */
                  function premium(string calldata name, uint expires, uint duration) external view returns(uint) {
                      return attoUSDToWei(_premium(name, expires, duration));
                  }
              
                  /**
                   * @dev Returns the pricing premium in internal base units.
                   */
                  function _premium(string memory name, uint expires, uint duration) internal view returns(uint) {
                      return 0;
                  }
              
                  function attoUSDToWei(uint amount) internal view returns(uint) {
                      uint ethPrice = uint(usdOracle.latestAnswer());
                      return amount.mul(1e8).div(ethPrice);
                  }
              
                  function weiToAttoUSD(uint amount) internal view returns(uint) {
                      uint ethPrice = uint(usdOracle.latestAnswer());
                      return amount.mul(ethPrice).div(1e8);
                  }
              
                  function supportsInterface(bytes4 interfaceID) public view returns (bool) {
                      return interfaceID == INTERFACE_META_ID || interfaceID == ORACLE_ID;
                  }
              }
              
              // File: contracts/LinearPremiumPriceOracle.sol
              
              pragma solidity >=0.5.0;
              
              
              
              contract LinearPremiumPriceOracle is StablePriceOracle {
                  using SafeMath for *;
              
                  uint GRACE_PERIOD = 90 days;
              
                  uint public initialPremium;
                  uint public premiumDecreaseRate;
              
                  bytes4 constant private TIME_UNTIL_PREMIUM_ID = bytes4(keccak256("timeUntilPremium(uint,uint"));
              
                  constructor(AggregatorInterface _usdOracle, uint[] memory _rentPrices, uint _initialPremium, uint _premiumDecreaseRate) public
                      StablePriceOracle(_usdOracle, _rentPrices)
                  {
                      initialPremium = _initialPremium;
                      premiumDecreaseRate = _premiumDecreaseRate;
                  }
              
                  function _premium(string memory name, uint expires, uint /*duration*/) internal view returns(uint) {
                      expires = expires.add(GRACE_PERIOD);
                      if(expires > now) {
                          // No premium for renewals
                          return 0;
                      }
              
                      // Calculate the discount off the maximum premium
                      uint discount = premiumDecreaseRate.mul(now.sub(expires));
              
                      // If we've run out the premium period, return 0.
                      if(discount > initialPremium) {
                          return 0;
                      }
                      
                      return initialPremium - discount;
                  }
              
                  /**
                   * @dev Returns the timestamp at which a name with the specified expiry date will have
                   *      the specified re-registration price premium.
                   * @param expires The timestamp at which the name expires.
                   * @param amount The amount, in wei, the caller is willing to pay
                   * @return The timestamp at which the premium for this domain will be `amount`.
                   */
                  function timeUntilPremium(uint expires, uint amount) external view returns(uint) {
                      amount = weiToAttoUSD(amount);
                      require(amount <= initialPremium);
              
                      expires = expires.add(GRACE_PERIOD);
              
                      uint discount = initialPremium.sub(amount);
                      uint duration = discount.div(premiumDecreaseRate);
                      return expires.add(duration);
                  }
              
                  function supportsInterface(bytes4 interfaceID) public view returns (bool) {
                      return (interfaceID == TIME_UNTIL_PREMIUM_ID) || super.supportsInterface(interfaceID);
                  }
              }

              File 6 of 7: EACAggregatorProxy
              pragma solidity 0.6.6;
              
              
              /**
               * @title The Owned contract
               * @notice A contract with helpers for basic contract ownership.
               */
              contract Owned {
              
                address payable public owner;
                address private pendingOwner;
              
                event OwnershipTransferRequested(
                  address indexed from,
                  address indexed to
                );
                event OwnershipTransferred(
                  address indexed from,
                  address indexed to
                );
              
                constructor() public {
                  owner = msg.sender;
                }
              
                /**
                 * @dev Allows an owner to begin transferring ownership to a new address,
                 * pending.
                 */
                function transferOwnership(address _to)
                  external
                  onlyOwner()
                {
                  pendingOwner = _to;
              
                  emit OwnershipTransferRequested(owner, _to);
                }
              
                /**
                 * @dev Allows an ownership transfer to be completed by the recipient.
                 */
                function acceptOwnership()
                  external
                {
                  require(msg.sender == pendingOwner, "Must be proposed owner");
              
                  address oldOwner = owner;
                  owner = msg.sender;
                  pendingOwner = address(0);
              
                  emit OwnershipTransferred(oldOwner, msg.sender);
                }
              
                /**
                 * @dev Reverts if called by anyone other than the contract owner.
                 */
                modifier onlyOwner() {
                  require(msg.sender == owner, "Only callable by owner");
                  _;
                }
              
              }
              
              interface AggregatorInterface {
                function latestAnswer() external view returns (int256);
                function latestTimestamp() external view returns (uint256);
                function latestRound() external view returns (uint256);
                function getAnswer(uint256 roundId) external view returns (int256);
                function getTimestamp(uint256 roundId) external view returns (uint256);
              
                event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt);
                event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt);
              }
              
              interface AggregatorV3Interface {
              
                function decimals() external view returns (uint8);
                function description() external view returns (string memory);
                function version() external view returns (uint256);
              
                // getRoundData and latestRoundData should both raise "No data present"
                // if they do not have data to report, instead of returning unset values
                // which could be misinterpreted as actual reported values.
                function getRoundData(uint80 _roundId)
                  external
                  view
                  returns (
                    uint80 roundId,
                    int256 answer,
                    uint256 startedAt,
                    uint256 updatedAt,
                    uint80 answeredInRound
                  );
                function latestRoundData()
                  external
                  view
                  returns (
                    uint80 roundId,
                    int256 answer,
                    uint256 startedAt,
                    uint256 updatedAt,
                    uint80 answeredInRound
                  );
              
              }
              
              interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface
              {
              }
              
              /**
               * @title A trusted proxy for updating where current answers are read from
               * @notice This contract provides a consistent address for the
               * CurrentAnwerInterface but delegates where it reads from to the owner, who is
               * trusted to update it.
               */
              contract AggregatorProxy is AggregatorV2V3Interface, Owned {
              
                struct Phase {
                  uint16 id;
                  AggregatorV2V3Interface aggregator;
                }
                Phase private currentPhase;
                AggregatorV2V3Interface public proposedAggregator;
                mapping(uint16 => AggregatorV2V3Interface) public phaseAggregators;
              
                uint256 constant private PHASE_OFFSET = 64;
                uint256 constant private PHASE_SIZE = 16;
                uint256 constant private MAX_ID = 2**(PHASE_OFFSET+PHASE_SIZE) - 1;
              
                constructor(address _aggregator) public Owned() {
                  setAggregator(_aggregator);
                }
              
                /**
                 * @notice Reads the current answer from aggregator delegated to.
                 *
                 * @dev #[deprecated] Use latestRoundData instead. This does not error if no
                 * answer has been reached, it will simply return 0. Either wait to point to
                 * an already answered Aggregator or use the recommended latestRoundData
                 * instead which includes better verification information.
                 */
                function latestAnswer()
                  public
                  view
                  virtual
                  override
                  returns (int256 answer)
                {
                  return currentPhase.aggregator.latestAnswer();
                }
              
                /**
                 * @notice Reads the last updated height from aggregator delegated to.
                 *
                 * @dev #[deprecated] Use latestRoundData instead. This does not error if no
                 * answer has been reached, it will simply return 0. Either wait to point to
                 * an already answered Aggregator or use the recommended latestRoundData
                 * instead which includes better verification information.
                 */
                function latestTimestamp()
                  public
                  view
                  virtual
                  override
                  returns (uint256 updatedAt)
                {
                  return currentPhase.aggregator.latestTimestamp();
                }
              
                /**
                 * @notice get past rounds answers
                 * @param _roundId the answer number to retrieve the answer for
                 *
                 * @dev #[deprecated] Use getRoundData instead. This does not error if no
                 * answer has been reached, it will simply return 0. Either wait to point to
                 * an already answered Aggregator or use the recommended getRoundData
                 * instead which includes better verification information.
                 */
                function getAnswer(uint256 _roundId)
                  public
                  view
                  virtual
                  override
                  returns (int256 answer)
                {
                  if (_roundId > MAX_ID) return 0;
              
                  (uint16 phaseId, uint64 aggregatorRoundId) = parseIds(_roundId);
                  AggregatorV2V3Interface aggregator = phaseAggregators[phaseId];
                  if (address(aggregator) == address(0)) return 0;
              
                  return aggregator.getAnswer(aggregatorRoundId);
                }
              
                /**
                 * @notice get block timestamp when an answer was last updated
                 * @param _roundId the answer number to retrieve the updated timestamp for
                 *
                 * @dev #[deprecated] Use getRoundData instead. This does not error if no
                 * answer has been reached, it will simply return 0. Either wait to point to
                 * an already answered Aggregator or use the recommended getRoundData
                 * instead which includes better verification information.
                 */
                function getTimestamp(uint256 _roundId)
                  public
                  view
                  virtual
                  override
                  returns (uint256 updatedAt)
                {
                  if (_roundId > MAX_ID) return 0;
              
                  (uint16 phaseId, uint64 aggregatorRoundId) = parseIds(_roundId);
                  AggregatorV2V3Interface aggregator = phaseAggregators[phaseId];
                  if (address(aggregator) == address(0)) return 0;
              
                  return aggregator.getTimestamp(aggregatorRoundId);
                }
              
                /**
                 * @notice get the latest completed round where the answer was updated. This
                 * ID includes the proxy's phase, to make sure round IDs increase even when
                 * switching to a newly deployed aggregator.
                 *
                 * @dev #[deprecated] Use latestRoundData instead. This does not error if no
                 * answer has been reached, it will simply return 0. Either wait to point to
                 * an already answered Aggregator or use the recommended latestRoundData
                 * instead which includes better verification information.
                 */
                function latestRound()
                  public
                  view
                  virtual
                  override
                  returns (uint256 roundId)
                {
                  Phase memory phase = currentPhase; // cache storage reads
                  return addPhase(phase.id, uint64(phase.aggregator.latestRound()));
                }
              
                /**
                 * @notice get data about a round. Consumers are encouraged to check
                 * that they're receiving fresh data by inspecting the updatedAt and
                 * answeredInRound return values.
                 * Note that different underlying implementations of AggregatorV3Interface
                 * have slightly different semantics for some of the return values. Consumers
                 * should determine what implementations they expect to receive
                 * data from and validate that they can properly handle return data from all
                 * of them.
                 * @param _roundId the requested round ID as presented through the proxy, this
                 * is made up of the aggregator's round ID with the phase ID encoded in the
                 * two highest order bytes
                 * @return roundId is the round ID from the aggregator for which the data was
                 * retrieved combined with an phase to ensure that round IDs get larger as
                 * time moves forward.
                 * @return answer is the answer for the given round
                 * @return startedAt is the timestamp when the round was started.
                 * (Only some AggregatorV3Interface implementations return meaningful values)
                 * @return updatedAt is the timestamp when the round last was updated (i.e.
                 * answer was last computed)
                 * @return answeredInRound is the round ID of the round in which the answer
                 * was computed.
                 * (Only some AggregatorV3Interface implementations return meaningful values)
                 * @dev Note that answer and updatedAt may change between queries.
                 */
                function getRoundData(uint80 _roundId)
                  public
                  view
                  virtual
                  override
                  returns (
                    uint80 roundId,
                    int256 answer,
                    uint256 startedAt,
                    uint256 updatedAt,
                    uint80 answeredInRound
                  )
                {
                  (uint16 phaseId, uint64 aggregatorRoundId) = parseIds(_roundId);
              
                  (
                    uint80 roundId,
                    int256 answer,
                    uint256 startedAt,
                    uint256 updatedAt,
                    uint80 ansIn
                  ) = phaseAggregators[phaseId].getRoundData(aggregatorRoundId);
              
                  return addPhaseIds(roundId, answer, startedAt, updatedAt, ansIn, phaseId);
                }
              
                /**
                 * @notice get data about the latest round. Consumers are encouraged to check
                 * that they're receiving fresh data by inspecting the updatedAt and
                 * answeredInRound return values.
                 * Note that different underlying implementations of AggregatorV3Interface
                 * have slightly different semantics for some of the return values. Consumers
                 * should determine what implementations they expect to receive
                 * data from and validate that they can properly handle return data from all
                 * of them.
                 * @return roundId is the round ID from the aggregator for which the data was
                 * retrieved combined with an phase to ensure that round IDs get larger as
                 * time moves forward.
                 * @return answer is the answer for the given round
                 * @return startedAt is the timestamp when the round was started.
                 * (Only some AggregatorV3Interface implementations return meaningful values)
                 * @return updatedAt is the timestamp when the round last was updated (i.e.
                 * answer was last computed)
                 * @return answeredInRound is the round ID of the round in which the answer
                 * was computed.
                 * (Only some AggregatorV3Interface implementations return meaningful values)
                 * @dev Note that answer and updatedAt may change between queries.
                 */
                function latestRoundData()
                  public
                  view
                  virtual
                  override
                  returns (
                    uint80 roundId,
                    int256 answer,
                    uint256 startedAt,
                    uint256 updatedAt,
                    uint80 answeredInRound
                  )
                {
                  Phase memory current = currentPhase; // cache storage reads
              
                  (
                    uint80 roundId,
                    int256 answer,
                    uint256 startedAt,
                    uint256 updatedAt,
                    uint80 ansIn
                  ) = current.aggregator.latestRoundData();
              
                  return addPhaseIds(roundId, answer, startedAt, updatedAt, ansIn, current.id);
                }
              
                /**
                 * @notice Used if an aggregator contract has been proposed.
                 * @param _roundId the round ID to retrieve the round data for
                 * @return roundId is the round ID for which data was retrieved
                 * @return answer is the answer for the given round
                 * @return startedAt is the timestamp when the round was started.
                 * (Only some AggregatorV3Interface implementations return meaningful values)
                 * @return updatedAt is the timestamp when the round last was updated (i.e.
                 * answer was last computed)
                 * @return answeredInRound is the round ID of the round in which the answer
                 * was computed.
                */
                function proposedGetRoundData(uint80 _roundId)
                  public
                  view
                  virtual
                  hasProposal()
                  returns (
                    uint80 roundId,
                    int256 answer,
                    uint256 startedAt,
                    uint256 updatedAt,
                    uint80 answeredInRound
                  )
                {
                  return proposedAggregator.getRoundData(_roundId);
                }
              
                /**
                 * @notice Used if an aggregator contract has been proposed.
                 * @return roundId is the round ID for which data was retrieved
                 * @return answer is the answer for the given round
                 * @return startedAt is the timestamp when the round was started.
                 * (Only some AggregatorV3Interface implementations return meaningful values)
                 * @return updatedAt is the timestamp when the round last was updated (i.e.
                 * answer was last computed)
                 * @return answeredInRound is the round ID of the round in which the answer
                 * was computed.
                */
                function proposedLatestRoundData()
                  public
                  view
                  virtual
                  hasProposal()
                  returns (
                    uint80 roundId,
                    int256 answer,
                    uint256 startedAt,
                    uint256 updatedAt,
                    uint80 answeredInRound
                  )
                {
                  return proposedAggregator.latestRoundData();
                }
              
                /**
                 * @notice returns the current phase's aggregator address.
                 */
                function aggregator()
                  external
                  view
                  returns (address)
                {
                  return address(currentPhase.aggregator);
                }
              
                /**
                 * @notice returns the current phase's ID.
                 */
                function phaseId()
                  external
                  view
                  returns (uint16)
                {
                  return currentPhase.id;
                }
              
                /**
                 * @notice represents the number of decimals the aggregator responses represent.
                 */
                function decimals()
                  external
                  view
                  override
                  returns (uint8)
                {
                  return currentPhase.aggregator.decimals();
                }
              
                /**
                 * @notice the version number representing the type of aggregator the proxy
                 * points to.
                 */
                function version()
                  external
                  view
                  override
                  returns (uint256)
                {
                  return currentPhase.aggregator.version();
                }
              
                /**
                 * @notice returns the description of the aggregator the proxy points to.
                 */
                function description()
                  external
                  view
                  override
                  returns (string memory)
                {
                  return currentPhase.aggregator.description();
                }
              
                /**
                 * @notice Allows the owner to propose a new address for the aggregator
                 * @param _aggregator The new address for the aggregator contract
                 */
                function proposeAggregator(address _aggregator)
                  external
                  onlyOwner()
                {
                  proposedAggregator = AggregatorV2V3Interface(_aggregator);
                }
              
                /**
                 * @notice Allows the owner to confirm and change the address
                 * to the proposed aggregator
                 * @dev Reverts if the given address doesn't match what was previously
                 * proposed
                 * @param _aggregator The new address for the aggregator contract
                 */
                function confirmAggregator(address _aggregator)
                  external
                  onlyOwner()
                {
                  require(_aggregator == address(proposedAggregator), "Invalid proposed aggregator");
                  delete proposedAggregator;
                  setAggregator(_aggregator);
                }
              
              
                /*
                 * Internal
                 */
              
                function setAggregator(address _aggregator)
                  internal
                {
                  uint16 id = currentPhase.id + 1;
                  currentPhase = Phase(id, AggregatorV2V3Interface(_aggregator));
                  phaseAggregators[id] = AggregatorV2V3Interface(_aggregator);
                }
              
                function addPhase(
                  uint16 _phase,
                  uint64 _originalId
                )
                  internal
                  view
                  returns (uint80)
                {
                  return uint80(uint256(_phase) << PHASE_OFFSET | _originalId);
                }
              
                function parseIds(
                  uint256 _roundId
                )
                  internal
                  view
                  returns (uint16, uint64)
                {
                  uint16 phaseId = uint16(_roundId >> PHASE_OFFSET);
                  uint64 aggregatorRoundId = uint64(_roundId);
              
                  return (phaseId, aggregatorRoundId);
                }
              
                function addPhaseIds(
                    uint80 roundId,
                    int256 answer,
                    uint256 startedAt,
                    uint256 updatedAt,
                    uint80 answeredInRound,
                    uint16 phaseId
                )
                  internal
                  view
                  returns (uint80, int256, uint256, uint256, uint80)
                {
                  return (
                    addPhase(phaseId, uint64(roundId)),
                    answer,
                    startedAt,
                    updatedAt,
                    addPhase(phaseId, uint64(answeredInRound))
                  );
                }
              
                /*
                 * Modifiers
                 */
              
                modifier hasProposal() {
                  require(address(proposedAggregator) != address(0), "No proposed aggregator present");
                  _;
                }
              
              }
              
              interface AccessControllerInterface {
                function hasAccess(address user, bytes calldata data) external view returns (bool);
              }
              
              /**
               * @title External Access Controlled Aggregator Proxy
               * @notice A trusted proxy for updating where current answers are read from
               * @notice This contract provides a consistent address for the
               * Aggregator and AggregatorV3Interface but delegates where it reads from to the owner, who is
               * trusted to update it.
               * @notice Only access enabled addresses are allowed to access getters for
               * aggregated answers and round information.
               */
              contract EACAggregatorProxy is AggregatorProxy {
              
                AccessControllerInterface public accessController;
              
                constructor(
                  address _aggregator,
                  address _accessController
                )
                  public
                  AggregatorProxy(_aggregator)
                {
                  setController(_accessController);
                }
              
                /**
                 * @notice Allows the owner to update the accessController contract address.
                 * @param _accessController The new address for the accessController contract
                 */
                function setController(address _accessController)
                  public
                  onlyOwner()
                {
                  accessController = AccessControllerInterface(_accessController);
                }
              
                /**
                 * @notice Reads the current answer from aggregator delegated to.
                 * @dev overridden function to add the checkAccess() modifier
                 *
                 * @dev #[deprecated] Use latestRoundData instead. This does not error if no
                 * answer has been reached, it will simply return 0. Either wait to point to
                 * an already answered Aggregator or use the recommended latestRoundData
                 * instead which includes better verification information.
                 */
                function latestAnswer()
                  public
                  view
                  override
                  checkAccess()
                  returns (int256)
                {
                  return super.latestAnswer();
                }
              
                /**
                 * @notice get the latest completed round where the answer was updated. This
                 * ID includes the proxy's phase, to make sure round IDs increase even when
                 * switching to a newly deployed aggregator.
                 *
                 * @dev #[deprecated] Use latestRoundData instead. This does not error if no
                 * answer has been reached, it will simply return 0. Either wait to point to
                 * an already answered Aggregator or use the recommended latestRoundData
                 * instead which includes better verification information.
                 */
                function latestTimestamp()
                  public
                  view
                  override
                  checkAccess()
                  returns (uint256)
                {
                  return super.latestTimestamp();
                }
              
                /**
                 * @notice get past rounds answers
                 * @param _roundId the answer number to retrieve the answer for
                 * @dev overridden function to add the checkAccess() modifier
                 *
                 * @dev #[deprecated] Use getRoundData instead. This does not error if no
                 * answer has been reached, it will simply return 0. Either wait to point to
                 * an already answered Aggregator or use the recommended getRoundData
                 * instead which includes better verification information.
                 */
                function getAnswer(uint256 _roundId)
                  public
                  view
                  override
                  checkAccess()
                  returns (int256)
                {
                  return super.getAnswer(_roundId);
                }
              
                /**
                 * @notice get block timestamp when an answer was last updated
                 * @param _roundId the answer number to retrieve the updated timestamp for
                 * @dev overridden function to add the checkAccess() modifier
                 *
                 * @dev #[deprecated] Use getRoundData instead. This does not error if no
                 * answer has been reached, it will simply return 0. Either wait to point to
                 * an already answered Aggregator or use the recommended getRoundData
                 * instead which includes better verification information.
                 */
                function getTimestamp(uint256 _roundId)
                  public
                  view
                  override
                  checkAccess()
                  returns (uint256)
                {
                  return super.getTimestamp(_roundId);
                }
              
                /**
                 * @notice get the latest completed round where the answer was updated
                 * @dev overridden function to add the checkAccess() modifier
                 *
                 * @dev #[deprecated] Use latestRoundData instead. This does not error if no
                 * answer has been reached, it will simply return 0. Either wait to point to
                 * an already answered Aggregator or use the recommended latestRoundData
                 * instead which includes better verification information.
                 */
                function latestRound()
                  public
                  view
                  override
                  checkAccess()
                  returns (uint256)
                {
                  return super.latestRound();
                }
              
                /**
                 * @notice get data about a round. Consumers are encouraged to check
                 * that they're receiving fresh data by inspecting the updatedAt and
                 * answeredInRound return values.
                 * Note that different underlying implementations of AggregatorV3Interface
                 * have slightly different semantics for some of the return values. Consumers
                 * should determine what implementations they expect to receive
                 * data from and validate that they can properly handle return data from all
                 * of them.
                 * @param _roundId the round ID to retrieve the round data for
                 * @return roundId is the round ID from the aggregator for which the data was
                 * retrieved combined with a phase to ensure that round IDs get larger as
                 * time moves forward.
                 * @return answer is the answer for the given round
                 * @return startedAt is the timestamp when the round was started.
                 * (Only some AggregatorV3Interface implementations return meaningful values)
                 * @return updatedAt is the timestamp when the round last was updated (i.e.
                 * answer was last computed)
                 * @return answeredInRound is the round ID of the round in which the answer
                 * was computed.
                 * (Only some AggregatorV3Interface implementations return meaningful values)
                 * @dev Note that answer and updatedAt may change between queries.
                 */
                function getRoundData(uint80 _roundId)
                  public
                  view
                  checkAccess()
                  override
                  returns (
                    uint80 roundId,
                    int256 answer,
                    uint256 startedAt,
                    uint256 updatedAt,
                    uint80 answeredInRound
                  )
                {
                  return super.getRoundData(_roundId);
                }
              
                /**
                 * @notice get data about the latest round. Consumers are encouraged to check
                 * that they're receiving fresh data by inspecting the updatedAt and
                 * answeredInRound return values.
                 * Note that different underlying implementations of AggregatorV3Interface
                 * have slightly different semantics for some of the return values. Consumers
                 * should determine what implementations they expect to receive
                 * data from and validate that they can properly handle return data from all
                 * of them.
                 * @return roundId is the round ID from the aggregator for which the data was
                 * retrieved combined with a phase to ensure that round IDs get larger as
                 * time moves forward.
                 * @return answer is the answer for the given round
                 * @return startedAt is the timestamp when the round was started.
                 * (Only some AggregatorV3Interface implementations return meaningful values)
                 * @return updatedAt is the timestamp when the round last was updated (i.e.
                 * answer was last computed)
                 * @return answeredInRound is the round ID of the round in which the answer
                 * was computed.
                 * (Only some AggregatorV3Interface implementations return meaningful values)
                 * @dev Note that answer and updatedAt may change between queries.
                 */
                function latestRoundData()
                  public
                  view
                  checkAccess()
                  override
                  returns (
                    uint80 roundId,
                    int256 answer,
                    uint256 startedAt,
                    uint256 updatedAt,
                    uint80 answeredInRound
                  )
                {
                  return super.latestRoundData();
                }
              
                /**
                 * @notice Used if an aggregator contract has been proposed.
                 * @param _roundId the round ID to retrieve the round data for
                 * @return roundId is the round ID for which data was retrieved
                 * @return answer is the answer for the given round
                 * @return startedAt is the timestamp when the round was started.
                 * (Only some AggregatorV3Interface implementations return meaningful values)
                 * @return updatedAt is the timestamp when the round last was updated (i.e.
                 * answer was last computed)
                 * @return answeredInRound is the round ID of the round in which the answer
                 * was computed.
                */
                function proposedGetRoundData(uint80 _roundId)
                  public
                  view
                  checkAccess()
                  hasProposal()
                  override
                  returns (
                    uint80 roundId,
                    int256 answer,
                    uint256 startedAt,
                    uint256 updatedAt,
                    uint80 answeredInRound
                  )
                {
                  return super.proposedGetRoundData(_roundId);
                }
              
                /**
                 * @notice Used if an aggregator contract has been proposed.
                 * @return roundId is the round ID for which data was retrieved
                 * @return answer is the answer for the given round
                 * @return startedAt is the timestamp when the round was started.
                 * (Only some AggregatorV3Interface implementations return meaningful values)
                 * @return updatedAt is the timestamp when the round last was updated (i.e.
                 * answer was last computed)
                 * @return answeredInRound is the round ID of the round in which the answer
                 * was computed.
                */
                function proposedLatestRoundData()
                  public
                  view
                  checkAccess()
                  hasProposal()
                  override
                  returns (
                    uint80 roundId,
                    int256 answer,
                    uint256 startedAt,
                    uint256 updatedAt,
                    uint80 answeredInRound
                  )
                {
                  return super.proposedLatestRoundData();
                }
              
                /**
                 * @dev reverts if the caller does not have access by the accessController
                 * contract or is the contract itself.
                 */
                modifier checkAccess() {
                  AccessControllerInterface ac = accessController;
                  require(address(ac) == address(0) || ac.hasAccess(msg.sender, msg.data), "No access");
                  _;
                }
              }

              File 7 of 7: AccessControlledAggregator
              pragma solidity 0.6.6;
              
              
              /**
               * @dev Wrappers over Solidity's arithmetic operations with added overflow
               * checks.
               *
               * Arithmetic operations in Solidity wrap on overflow. This can easily result
               * in bugs, because programmers usually assume that an overflow raises an
               * error, which is the standard behavior in high level programming languages.
               * `SafeMath` restores this intuition by reverting the transaction when an
               * operation overflows.
               *
               * Using this library instead of the unchecked operations eliminates an entire
               * class of bugs, so it's recommended to use it always.
               */
              library SafeMath {
                /**
                  * @dev Returns the addition of two unsigned integers, reverting on
                  * overflow.
                  *
                  * Counterpart to Solidity's `+` operator.
                  *
                  * Requirements:
                  * - Addition cannot overflow.
                  */
                function add(uint256 a, uint256 b) internal pure returns (uint256) {
                  uint256 c = a + b;
                  require(c >= a, "SafeMath: addition overflow");
              
                  return c;
                }
              
                /**
                  * @dev Returns the subtraction of two unsigned integers, reverting on
                  * overflow (when the result is negative).
                  *
                  * Counterpart to Solidity's `-` operator.
                  *
                  * Requirements:
                  * - Subtraction cannot overflow.
                  */
                function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                  require(b <= a, "SafeMath: subtraction overflow");
                  uint256 c = a - b;
              
                  return c;
                }
              
                /**
                  * @dev Returns the multiplication of two unsigned integers, reverting on
                  * overflow.
                  *
                  * Counterpart to Solidity's `*` operator.
                  *
                  * Requirements:
                  * - Multiplication cannot overflow.
                  */
                function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                  // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                  // benefit is lost if 'b' is also tested.
                  // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
                  if (a == 0) {
                    return 0;
                  }
              
                  uint256 c = a * b;
                  require(c / a == b, "SafeMath: multiplication overflow");
              
                  return c;
                }
              
                /**
                  * @dev Returns the integer division of two unsigned integers. Reverts on
                  * division by zero. The result is rounded towards zero.
                  *
                  * Counterpart to Solidity's `/` operator. Note: this function uses a
                  * `revert` opcode (which leaves remaining gas untouched) while Solidity
                  * uses an invalid opcode to revert (consuming all remaining gas).
                  *
                  * Requirements:
                  * - The divisor cannot be zero.
                  */
                function div(uint256 a, uint256 b) internal pure returns (uint256) {
                  // Solidity only automatically asserts when dividing by 0
                  require(b > 0, "SafeMath: division by zero");
                  uint256 c = a / b;
                  // assert(a == b * c + a % b); // There is no case in which this doesn't hold
              
                  return c;
                }
              
                /**
                  * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                  * Reverts when dividing by zero.
                  *
                  * Counterpart to Solidity's `%` operator. This function uses a `revert`
                  * opcode (which leaves remaining gas untouched) while Solidity uses an
                  * invalid opcode to revert (consuming all remaining gas).
                  *
                  * Requirements:
                  * - The divisor cannot be zero.
                  */
                function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                  require(b != 0, "SafeMath: modulo by zero");
                  return a % b;
                }
              }
              
              library SignedSafeMath {
                int256 constant private _INT256_MIN = -2**255;
              
                /**
                 * @dev Multiplies two signed integers, reverts on overflow.
                 */
                function mul(int256 a, int256 b) internal pure returns (int256) {
                  // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                  // benefit is lost if 'b' is also tested.
                  // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                  if (a == 0) {
                    return 0;
                  }
              
                  require(!(a == -1 && b == _INT256_MIN), "SignedSafeMath: multiplication overflow");
              
                  int256 c = a * b;
                  require(c / a == b, "SignedSafeMath: multiplication overflow");
              
                  return c;
                }
              
                /**
                 * @dev Integer division of two signed integers truncating the quotient, reverts on division by zero.
                 */
                function div(int256 a, int256 b) internal pure returns (int256) {
                  require(b != 0, "SignedSafeMath: division by zero");
                  require(!(b == -1 && a == _INT256_MIN), "SignedSafeMath: division overflow");
              
                  int256 c = a / b;
              
                  return c;
                }
              
                /**
                 * @dev Subtracts two signed integers, reverts on overflow.
                 */
                function sub(int256 a, int256 b) internal pure returns (int256) {
                  int256 c = a - b;
                  require((b >= 0 && c <= a) || (b < 0 && c > a), "SignedSafeMath: subtraction overflow");
              
                  return c;
                }
              
                /**
                 * @dev Adds two signed integers, reverts on overflow.
                 */
                function add(int256 a, int256 b) internal pure returns (int256) {
                  int256 c = a + b;
                  require((b >= 0 && c >= a) || (b < 0 && c < a), "SignedSafeMath: addition overflow");
              
                  return c;
                }
              
                /**
                 * @notice Computes average of two signed integers, ensuring that the computation
                 * doesn't overflow.
                 * @dev If the result is not an integer, it is rounded towards zero. For example,
                 * avg(-3, -4) = -3
                 */
                function avg(int256 _a, int256 _b)
                  internal
                  pure
                  returns (int256)
                {
                  if ((_a < 0 && _b > 0) || (_a > 0 && _b < 0)) {
                    return add(_a, _b) / 2;
                  }
                  int256 remainder = (_a % 2 + _b % 2) / 2;
                  return add(add(_a / 2, _b / 2), remainder);
                }
              }
              
              library Median {
                using SignedSafeMath for int256;
              
                int256 constant INT_MAX = 2**255-1;
              
                /**
                 * @notice Returns the sorted middle, or the average of the two middle indexed items if the
                 * array has an even number of elements.
                 * @dev The list passed as an argument isn't modified.
                 * @dev This algorithm has expected runtime O(n), but for adversarially chosen inputs
                 * the runtime is O(n^2).
                 * @param list The list of elements to compare
                 */
                function calculate(int256[] memory list)
                  internal
                  pure
                  returns (int256)
                {
                  return calculateInplace(copy(list));
                }
              
                /**
                 * @notice See documentation for function calculate.
                 * @dev The list passed as an argument may be permuted.
                 */
                function calculateInplace(int256[] memory list)
                  internal
                  pure
                  returns (int256)
                {
                  require(0 < list.length, "list must not be empty");
                  uint256 len = list.length;
                  uint256 middleIndex = len / 2;
                  if (len % 2 == 0) {
                    int256 median1;
                    int256 median2;
                    (median1, median2) = quickselectTwo(list, 0, len - 1, middleIndex - 1, middleIndex);
                    return SignedSafeMath.avg(median1, median2);
                  } else {
                    return quickselect(list, 0, len - 1, middleIndex);
                  }
                }
              
                /**
                 * @notice Maximum length of list that shortSelectTwo can handle
                 */
                uint256 constant SHORTSELECTTWO_MAX_LENGTH = 7;
              
                /**
                 * @notice Select the k1-th and k2-th element from list of length at most 7
                 * @dev Uses an optimal sorting network
                 */
                function shortSelectTwo(
                  int256[] memory list,
                  uint256 lo,
                  uint256 hi,
                  uint256 k1,
                  uint256 k2
                )
                  private
                  pure
                  returns (int256 k1th, int256 k2th)
                {
                  // Uses an optimal sorting network (https://en.wikipedia.org/wiki/Sorting_network)
                  // for lists of length 7. Network layout is taken from
                  // http://jgamble.ripco.net/cgi-bin/nw.cgi?inputs=7&algorithm=hibbard&output=svg
              
                  uint256 len = hi + 1 - lo;
                  int256 x0 = list[lo + 0];
                  int256 x1 = 1 < len ? list[lo + 1] : INT_MAX;
                  int256 x2 = 2 < len ? list[lo + 2] : INT_MAX;
                  int256 x3 = 3 < len ? list[lo + 3] : INT_MAX;
                  int256 x4 = 4 < len ? list[lo + 4] : INT_MAX;
                  int256 x5 = 5 < len ? list[lo + 5] : INT_MAX;
                  int256 x6 = 6 < len ? list[lo + 6] : INT_MAX;
              
                  if (x0 > x1) {(x0, x1) = (x1, x0);}
                  if (x2 > x3) {(x2, x3) = (x3, x2);}
                  if (x4 > x5) {(x4, x5) = (x5, x4);}
                  if (x0 > x2) {(x0, x2) = (x2, x0);}
                  if (x1 > x3) {(x1, x3) = (x3, x1);}
                  if (x4 > x6) {(x4, x6) = (x6, x4);}
                  if (x1 > x2) {(x1, x2) = (x2, x1);}
                  if (x5 > x6) {(x5, x6) = (x6, x5);}
                  if (x0 > x4) {(x0, x4) = (x4, x0);}
                  if (x1 > x5) {(x1, x5) = (x5, x1);}
                  if (x2 > x6) {(x2, x6) = (x6, x2);}
                  if (x1 > x4) {(x1, x4) = (x4, x1);}
                  if (x3 > x6) {(x3, x6) = (x6, x3);}
                  if (x2 > x4) {(x2, x4) = (x4, x2);}
                  if (x3 > x5) {(x3, x5) = (x5, x3);}
                  if (x3 > x4) {(x3, x4) = (x4, x3);}
              
                  uint256 index1 = k1 - lo;
                  if (index1 == 0) {k1th = x0;}
                  else if (index1 == 1) {k1th = x1;}
                  else if (index1 == 2) {k1th = x2;}
                  else if (index1 == 3) {k1th = x3;}
                  else if (index1 == 4) {k1th = x4;}
                  else if (index1 == 5) {k1th = x5;}
                  else if (index1 == 6) {k1th = x6;}
                  else {revert("k1 out of bounds");}
              
                  uint256 index2 = k2 - lo;
                  if (k1 == k2) {return (k1th, k1th);}
                  else if (index2 == 0) {return (k1th, x0);}
                  else if (index2 == 1) {return (k1th, x1);}
                  else if (index2 == 2) {return (k1th, x2);}
                  else if (index2 == 3) {return (k1th, x3);}
                  else if (index2 == 4) {return (k1th, x4);}
                  else if (index2 == 5) {return (k1th, x5);}
                  else if (index2 == 6) {return (k1th, x6);}
                  else {revert("k2 out of bounds");}
                }
              
                /**
                 * @notice Selects the k-th ranked element from list, looking only at indices between lo and hi
                 * (inclusive). Modifies list in-place.
                 */
                function quickselect(int256[] memory list, uint256 lo, uint256 hi, uint256 k)
                  private
                  pure
                  returns (int256 kth)
                {
                  require(lo <= k);
                  require(k <= hi);
                  while (lo < hi) {
                    if (hi - lo < SHORTSELECTTWO_MAX_LENGTH) {
                      int256 ignore;
                      (kth, ignore) = shortSelectTwo(list, lo, hi, k, k);
                      return kth;
                    }
                    uint256 pivotIndex = partition(list, lo, hi);
                    if (k <= pivotIndex) {
                      // since pivotIndex < (original hi passed to partition),
                      // termination is guaranteed in this case
                      hi = pivotIndex;
                    } else {
                      // since (original lo passed to partition) <= pivotIndex,
                      // termination is guaranteed in this case
                      lo = pivotIndex + 1;
                    }
                  }
                  return list[lo];
                }
              
                /**
                 * @notice Selects the k1-th and k2-th ranked elements from list, looking only at indices between
                 * lo and hi (inclusive). Modifies list in-place.
                 */
                function quickselectTwo(
                  int256[] memory list,
                  uint256 lo,
                  uint256 hi,
                  uint256 k1,
                  uint256 k2
                )
                  internal // for testing
                  pure
                  returns (int256 k1th, int256 k2th)
                {
                  require(k1 < k2);
                  require(lo <= k1 && k1 <= hi);
                  require(lo <= k2 && k2 <= hi);
              
                  while (true) {
                    if (hi - lo < SHORTSELECTTWO_MAX_LENGTH) {
                      return shortSelectTwo(list, lo, hi, k1, k2);
                    }
                    uint256 pivotIdx = partition(list, lo, hi);
                    if (k2 <= pivotIdx) {
                      hi = pivotIdx;
                    } else if (pivotIdx < k1) {
                      lo = pivotIdx + 1;
                    } else {
                      assert(k1 <= pivotIdx && pivotIdx < k2);
                      k1th = quickselect(list, lo, pivotIdx, k1);
                      k2th = quickselect(list, pivotIdx + 1, hi, k2);
                      return (k1th, k2th);
                    }
                  }
                }
              
                /**
                 * @notice Partitions list in-place using Hoare's partitioning scheme.
                 * Only elements of list between indices lo and hi (inclusive) will be modified.
                 * Returns an index i, such that:
                 * - lo <= i < hi
                 * - forall j in [lo, i]. list[j] <= list[i]
                 * - forall j in [i, hi]. list[i] <= list[j]
                 */
                function partition(int256[] memory list, uint256 lo, uint256 hi)
                  private
                  pure
                  returns (uint256)
                {
                  // We don't care about overflow of the addition, because it would require a list
                  // larger than any feasible computer's memory.
                  int256 pivot = list[(lo + hi) / 2];
                  lo -= 1; // this can underflow. that's intentional.
                  hi += 1;
                  while (true) {
                    do {
                      lo += 1;
                    } while (list[lo] < pivot);
                    do {
                      hi -= 1;
                    } while (list[hi] > pivot);
                    if (lo < hi) {
                      (list[lo], list[hi]) = (list[hi], list[lo]);
                    } else {
                      // Let orig_lo and orig_hi be the original values of lo and hi passed to partition.
                      // Then, hi < orig_hi, because hi decreases *strictly* monotonically
                      // in each loop iteration and
                      // - either list[orig_hi] > pivot, in which case the first loop iteration
                      //   will achieve hi < orig_hi;
                      // - or list[orig_hi] <= pivot, in which case at least two loop iterations are
                      //   needed:
                      //   - lo will have to stop at least once in the interval
                      //     [orig_lo, (orig_lo + orig_hi)/2]
                      //   - (orig_lo + orig_hi)/2 < orig_hi
                      return hi;
                    }
                  }
                }
              
                /**
                 * @notice Makes an in-memory copy of the array passed in
                 * @param list Reference to the array to be copied
                 */
                function copy(int256[] memory list)
                  private
                  pure
                  returns(int256[] memory)
                {
                  int256[] memory list2 = new int256[](list.length);
                  for (uint256 i = 0; i < list.length; i++) {
                    list2[i] = list[i];
                  }
                  return list2;
                }
              }
              
              /**
               * @title The Owned contract
               * @notice A contract with helpers for basic contract ownership.
               */
              contract Owned {
              
                address payable public owner;
                address private pendingOwner;
              
                event OwnershipTransferRequested(
                  address indexed from,
                  address indexed to
                );
                event OwnershipTransferred(
                  address indexed from,
                  address indexed to
                );
              
                constructor() public {
                  owner = msg.sender;
                }
              
                /**
                 * @dev Allows an owner to begin transferring ownership to a new address,
                 * pending.
                 */
                function transferOwnership(address _to)
                  external
                  onlyOwner()
                {
                  pendingOwner = _to;
              
                  emit OwnershipTransferRequested(owner, _to);
                }
              
                /**
                 * @dev Allows an ownership transfer to be completed by the recipient.
                 */
                function acceptOwnership()
                  external
                {
                  require(msg.sender == pendingOwner, "Must be proposed owner");
              
                  address oldOwner = owner;
                  owner = msg.sender;
                  pendingOwner = address(0);
              
                  emit OwnershipTransferred(oldOwner, msg.sender);
                }
              
                /**
                 * @dev Reverts if called by anyone other than the contract owner.
                 */
                modifier onlyOwner() {
                  require(msg.sender == owner, "Only callable by owner");
                  _;
                }
              
              }
              
              /**
               * @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.
               *
               * This library is a version of Open Zeppelin's SafeMath, modified to support
               * unsigned 128 bit integers.
               */
              library SafeMath128 {
                /**
                  * @dev Returns the addition of two unsigned integers, reverting on
                  * overflow.
                  *
                  * Counterpart to Solidity's `+` operator.
                  *
                  * Requirements:
                  * - Addition cannot overflow.
                  */
                function add(uint128 a, uint128 b) internal pure returns (uint128) {
                  uint128 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(uint128 a, uint128 b) internal pure returns (uint128) {
                  require(b <= a, "SafeMath: subtraction overflow");
                  uint128 c = a - b;
              
                  return c;
                }
              
                /**
                  * @dev Returns the multiplication of two unsigned integers, reverting on
                  * overflow.
                  *
                  * Counterpart to Solidity's `*` operator.
                  *
                  * Requirements:
                  * - Multiplication cannot overflow.
                  */
                function mul(uint128 a, uint128 b) internal pure returns (uint128) {
                  // 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-solidity/pull/522
                  if (a == 0) {
                    return 0;
                  }
              
                  uint128 c = a * b;
                  require(c / a == b, "SafeMath: multiplication overflow");
              
                  return c;
                }
              
                /**
                  * @dev Returns the integer division of two unsigned integers. Reverts on
                  * division by zero. The result is rounded towards zero.
                  *
                  * Counterpart to Solidity's `/` operator. Note: this function uses a
                  * `revert` opcode (which leaves remaining gas untouched) while Solidity
                  * uses an invalid opcode to revert (consuming all remaining gas).
                  *
                  * Requirements:
                  * - The divisor cannot be zero.
                  */
                function div(uint128 a, uint128 b) internal pure returns (uint128) {
                  // Solidity only automatically asserts when dividing by 0
                  require(b > 0, "SafeMath: division by zero");
                  uint128 c = a / b;
                  // assert(a == b * c + a % b); // There is no case in which this doesn't hold
              
                  return c;
                }
              
                /**
                  * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                  * Reverts when dividing by zero.
                  *
                  * Counterpart to Solidity's `%` operator. This function uses a `revert`
                  * opcode (which leaves remaining gas untouched) while Solidity uses an
                  * invalid opcode to revert (consuming all remaining gas).
                  *
                  * Requirements:
                  * - The divisor cannot be zero.
                  */
                function mod(uint128 a, uint128 b) internal pure returns (uint128) {
                  require(b != 0, "SafeMath: modulo by zero");
                  return a % b;
                }
              }
              
              /**
               * @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.
               *
               * This library is a version of Open Zeppelin's SafeMath, modified to support
               * unsigned 32 bit integers.
               */
              library SafeMath32 {
                /**
                  * @dev Returns the addition of two unsigned integers, reverting on
                  * overflow.
                  *
                  * Counterpart to Solidity's `+` operator.
                  *
                  * Requirements:
                  * - Addition cannot overflow.
                  */
                function add(uint32 a, uint32 b) internal pure returns (uint32) {
                  uint32 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(uint32 a, uint32 b) internal pure returns (uint32) {
                  require(b <= a, "SafeMath: subtraction overflow");
                  uint32 c = a - b;
              
                  return c;
                }
              
                /**
                  * @dev Returns the multiplication of two unsigned integers, reverting on
                  * overflow.
                  *
                  * Counterpart to Solidity's `*` operator.
                  *
                  * Requirements:
                  * - Multiplication cannot overflow.
                  */
                function mul(uint32 a, uint32 b) internal pure returns (uint32) {
                  // 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-solidity/pull/522
                  if (a == 0) {
                    return 0;
                  }
              
                  uint32 c = a * b;
                  require(c / a == b, "SafeMath: multiplication overflow");
              
                  return c;
                }
              
                /**
                  * @dev Returns the integer division of two unsigned integers. Reverts on
                  * division by zero. The result is rounded towards zero.
                  *
                  * Counterpart to Solidity's `/` operator. Note: this function uses a
                  * `revert` opcode (which leaves remaining gas untouched) while Solidity
                  * uses an invalid opcode to revert (consuming all remaining gas).
                  *
                  * Requirements:
                  * - The divisor cannot be zero.
                  */
                function div(uint32 a, uint32 b) internal pure returns (uint32) {
                  // Solidity only automatically asserts when dividing by 0
                  require(b > 0, "SafeMath: division by zero");
                  uint32 c = a / b;
                  // assert(a == b * c + a % b); // There is no case in which this doesn't hold
              
                  return c;
                }
              
                /**
                  * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                  * Reverts when dividing by zero.
                  *
                  * Counterpart to Solidity's `%` operator. This function uses a `revert`
                  * opcode (which leaves remaining gas untouched) while Solidity uses an
                  * invalid opcode to revert (consuming all remaining gas).
                  *
                  * Requirements:
                  * - The divisor cannot be zero.
                  */
                function mod(uint32 a, uint32 b) internal pure returns (uint32) {
                  require(b != 0, "SafeMath: modulo by zero");
                  return a % b;
                }
              }
              
              /**
               * @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.
               *
               * This library is a version of Open Zeppelin's SafeMath, modified to support
               * unsigned 64 bit integers.
               */
              library SafeMath64 {
                /**
                  * @dev Returns the addition of two unsigned integers, reverting on
                  * overflow.
                  *
                  * Counterpart to Solidity's `+` operator.
                  *
                  * Requirements:
                  * - Addition cannot overflow.
                  */
                function add(uint64 a, uint64 b) internal pure returns (uint64) {
                  uint64 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(uint64 a, uint64 b) internal pure returns (uint64) {
                  require(b <= a, "SafeMath: subtraction overflow");
                  uint64 c = a - b;
              
                  return c;
                }
              
                /**
                  * @dev Returns the multiplication of two unsigned integers, reverting on
                  * overflow.
                  *
                  * Counterpart to Solidity's `*` operator.
                  *
                  * Requirements:
                  * - Multiplication cannot overflow.
                  */
                function mul(uint64 a, uint64 b) internal pure returns (uint64) {
                  // 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-solidity/pull/522
                  if (a == 0) {
                    return 0;
                  }
              
                  uint64 c = a * b;
                  require(c / a == b, "SafeMath: multiplication overflow");
              
                  return c;
                }
              
                /**
                  * @dev Returns the integer division of two unsigned integers. Reverts on
                  * division by zero. The result is rounded towards zero.
                  *
                  * Counterpart to Solidity's `/` operator. Note: this function uses a
                  * `revert` opcode (which leaves remaining gas untouched) while Solidity
                  * uses an invalid opcode to revert (consuming all remaining gas).
                  *
                  * Requirements:
                  * - The divisor cannot be zero.
                  */
                function div(uint64 a, uint64 b) internal pure returns (uint64) {
                  // Solidity only automatically asserts when dividing by 0
                  require(b > 0, "SafeMath: division by zero");
                  uint64 c = a / b;
                  // assert(a == b * c + a % b); // There is no case in which this doesn't hold
              
                  return c;
                }
              
                /**
                  * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                  * Reverts when dividing by zero.
                  *
                  * Counterpart to Solidity's `%` operator. This function uses a `revert`
                  * opcode (which leaves remaining gas untouched) while Solidity uses an
                  * invalid opcode to revert (consuming all remaining gas).
                  *
                  * Requirements:
                  * - The divisor cannot be zero.
                  */
                function mod(uint64 a, uint64 b) internal pure returns (uint64) {
                  require(b != 0, "SafeMath: modulo by zero");
                  return a % b;
                }
              }
              
              interface AggregatorInterface {
                function latestAnswer() external view returns (int256);
                function latestTimestamp() external view returns (uint256);
                function latestRound() external view returns (uint256);
                function getAnswer(uint256 roundId) external view returns (int256);
                function getTimestamp(uint256 roundId) external view returns (uint256);
              
                event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt);
                event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt);
              }
              
              interface AggregatorV3Interface {
              
                function decimals() external view returns (uint8);
                function description() external view returns (string memory);
                function version() external view returns (uint256);
              
                // getRoundData and latestRoundData should both raise "No data present"
                // if they do not have data to report, instead of returning unset values
                // which could be misinterpreted as actual reported values.
                function getRoundData(uint80 _roundId)
                  external
                  view
                  returns (
                    uint80 roundId,
                    int256 answer,
                    uint256 startedAt,
                    uint256 updatedAt,
                    uint80 answeredInRound
                  );
                function latestRoundData()
                  external
                  view
                  returns (
                    uint80 roundId,
                    int256 answer,
                    uint256 startedAt,
                    uint256 updatedAt,
                    uint80 answeredInRound
                  );
              
              }
              
              interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface
              {
              }
              
              interface AggregatorValidatorInterface {
                function validate(
                  uint256 previousRoundId,
                  int256 previousAnswer,
                  uint256 currentRoundId,
                  int256 currentAnswer
                ) external returns (bool);
              }
              
              interface LinkTokenInterface {
                function allowance(address owner, address spender) external view returns (uint256 remaining);
                function approve(address spender, uint256 value) external returns (bool success);
                function balanceOf(address owner) external view returns (uint256 balance);
                function decimals() external view returns (uint8 decimalPlaces);
                function decreaseApproval(address spender, uint256 addedValue) external returns (bool success);
                function increaseApproval(address spender, uint256 subtractedValue) external;
                function name() external view returns (string memory tokenName);
                function symbol() external view returns (string memory tokenSymbol);
                function totalSupply() external view returns (uint256 totalTokensIssued);
                function transfer(address to, uint256 value) external returns (bool success);
                function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool success);
                function transferFrom(address from, address to, uint256 value) external returns (bool success);
              }
              
              /**
               * @title The Prepaid Aggregator contract
               * @notice Handles aggregating data pushed in from off-chain, and unlocks
               * payment for oracles as they report. Oracles' submissions are gathered in
               * rounds, with each round aggregating the submissions for each oracle into a
               * single answer. The latest aggregated answer is exposed as well as historical
               * answers and their updated at timestamp.
               */
              contract FluxAggregator is AggregatorV2V3Interface, Owned {
                using SafeMath for uint256;
                using SafeMath128 for uint128;
                using SafeMath64 for uint64;
                using SafeMath32 for uint32;
              
                struct Round {
                  int256 answer;
                  uint64 startedAt;
                  uint64 updatedAt;
                  uint32 answeredInRound;
                }
              
                struct RoundDetails {
                  int256[] submissions;
                  uint32 maxSubmissions;
                  uint32 minSubmissions;
                  uint32 timeout;
                  uint128 paymentAmount;
                }
              
                struct OracleStatus {
                  uint128 withdrawable;
                  uint32 startingRound;
                  uint32 endingRound;
                  uint32 lastReportedRound;
                  uint32 lastStartedRound;
                  int256 latestSubmission;
                  uint16 index;
                  address admin;
                  address pendingAdmin;
                }
              
                struct Requester {
                  bool authorized;
                  uint32 delay;
                  uint32 lastStartedRound;
                }
              
                struct Funds {
                  uint128 available;
                  uint128 allocated;
                }
              
                LinkTokenInterface public linkToken;
                AggregatorValidatorInterface public validator;
              
                // Round related params
                uint128 public paymentAmount;
                uint32 public maxSubmissionCount;
                uint32 public minSubmissionCount;
                uint32 public restartDelay;
                uint32 public timeout;
                uint8 public override decimals;
                string public override description;
              
                int256 immutable public minSubmissionValue;
                int256 immutable public maxSubmissionValue;
              
                uint256 constant public override version = 3;
              
                /**
                 * @notice To ensure owner isn't withdrawing required funds as oracles are
                 * submitting updates, we enforce that the contract maintains a minimum
                 * reserve of RESERVE_ROUNDS * oracleCount() LINK earmarked for payment to
                 * oracles. (Of course, this doesn't prevent the contract from running out of
                 * funds without the owner's intervention.)
                 */
                uint256 constant private RESERVE_ROUNDS = 2;
                uint256 constant private MAX_ORACLE_COUNT = 77;
                uint32 constant private ROUND_MAX = 2**32-1;
                uint256 private constant VALIDATOR_GAS_LIMIT = 100000;
                // An error specific to the Aggregator V3 Interface, to prevent possible
                // confusion around accidentally reading unset values as reported values.
                string constant private V3_NO_DATA_ERROR = "No data present";
              
                uint32 private reportingRoundId;
                uint32 internal latestRoundId;
                mapping(address => OracleStatus) private oracles;
                mapping(uint32 => Round) internal rounds;
                mapping(uint32 => RoundDetails) internal details;
                mapping(address => Requester) internal requesters;
                address[] private oracleAddresses;
                Funds private recordedFunds;
              
                event AvailableFundsUpdated(
                  uint256 indexed amount
                );
                event RoundDetailsUpdated(
                  uint128 indexed paymentAmount,
                  uint32 indexed minSubmissionCount,
                  uint32 indexed maxSubmissionCount,
                  uint32 restartDelay,
                  uint32 timeout // measured in seconds
                );
                event OraclePermissionsUpdated(
                  address indexed oracle,
                  bool indexed whitelisted
                );
                event OracleAdminUpdated(
                  address indexed oracle,
                  address indexed newAdmin
                );
                event OracleAdminUpdateRequested(
                  address indexed oracle,
                  address admin,
                  address newAdmin
                );
                event SubmissionReceived(
                  int256 indexed submission,
                  uint32 indexed round,
                  address indexed oracle
                );
                event RequesterPermissionsSet(
                  address indexed requester,
                  bool authorized,
                  uint32 delay
                );
                event ValidatorUpdated(
                  address indexed previous,
                  address indexed current
                );
              
                /**
                 * @notice set up the aggregator with initial configuration
                 * @param _link The address of the LINK token
                 * @param _paymentAmount The amount paid of LINK paid to each oracle per submission, in wei (units of 10⁻¹⁸ LINK)
                 * @param _timeout is the number of seconds after the previous round that are
                 * allowed to lapse before allowing an oracle to skip an unfinished round
                 * @param _validator is an optional contract address for validating
                 * external validation of answers
                 * @param _minSubmissionValue is an immutable check for a lower bound of what
                 * submission values are accepted from an oracle
                 * @param _maxSubmissionValue is an immutable check for an upper bound of what
                 * submission values are accepted from an oracle
                 * @param _decimals represents the number of decimals to offset the answer by
                 * @param _description a short description of what is being reported
                 */
                constructor(
                  address _link,
                  uint128 _paymentAmount,
                  uint32 _timeout,
                  address _validator,
                  int256 _minSubmissionValue,
                  int256 _maxSubmissionValue,
                  uint8 _decimals,
                  string memory _description
                ) public {
                  linkToken = LinkTokenInterface(_link);
                  updateFutureRounds(_paymentAmount, 0, 0, 0, _timeout);
                  setValidator(_validator);
                  minSubmissionValue = _minSubmissionValue;
                  maxSubmissionValue = _maxSubmissionValue;
                  decimals = _decimals;
                  description = _description;
                  rounds[0].updatedAt = uint64(block.timestamp.sub(uint256(_timeout)));
                }
              
                /**
                 * @notice called by oracles when they have witnessed a need to update
                 * @param _roundId is the ID of the round this submission pertains to
                 * @param _submission is the updated data that the oracle is submitting
                 */
                function submit(uint256 _roundId, int256 _submission)
                  external
                {
                  bytes memory error = validateOracleRound(msg.sender, uint32(_roundId));
                  require(_submission >= minSubmissionValue, "value below minSubmissionValue");
                  require(_submission <= maxSubmissionValue, "value above maxSubmissionValue");
                  require(error.length == 0, string(error));
              
                  oracleInitializeNewRound(uint32(_roundId));
                  recordSubmission(_submission, uint32(_roundId));
                  (bool updated, int256 newAnswer) = updateRoundAnswer(uint32(_roundId));
                  payOracle(uint32(_roundId));
                  deleteRoundDetails(uint32(_roundId));
                  if (updated) {
                    validateAnswer(uint32(_roundId), newAnswer);
                  }
                }
              
                /**
                 * @notice called by the owner to remove and add new oracles as well as
                 * update the round related parameters that pertain to total oracle count
                 * @param _removed is the list of addresses for the new Oracles being removed
                 * @param _added is the list of addresses for the new Oracles being added
                 * @param _addedAdmins is the admin addresses for the new respective _added
                 * list. Only this address is allowed to access the respective oracle's funds
                 * @param _minSubmissions is the new minimum submission count for each round
                 * @param _maxSubmissions is the new maximum submission count for each round
                 * @param _restartDelay is the number of rounds an Oracle has to wait before
                 * they can initiate a round
                 */
                function changeOracles(
                  address[] calldata _removed,
                  address[] calldata _added,
                  address[] calldata _addedAdmins,
                  uint32 _minSubmissions,
                  uint32 _maxSubmissions,
                  uint32 _restartDelay
                )
                  external
                  onlyOwner()
                {
                  for (uint256 i = 0; i < _removed.length; i++) {
                    removeOracle(_removed[i]);
                  }
              
                  require(_added.length == _addedAdmins.length, "need same oracle and admin count");
                  require(uint256(oracleCount()).add(_added.length) <= MAX_ORACLE_COUNT, "max oracles allowed");
              
                  for (uint256 i = 0; i < _added.length; i++) {
                    addOracle(_added[i], _addedAdmins[i]);
                  }
              
                  updateFutureRounds(paymentAmount, _minSubmissions, _maxSubmissions, _restartDelay, timeout);
                }
              
                /**
                 * @notice update the round and payment related parameters for subsequent
                 * rounds
                 * @param _paymentAmount is the payment amount for subsequent rounds
                 * @param _minSubmissions is the new minimum submission count for each round
                 * @param _maxSubmissions is the new maximum submission count for each round
                 * @param _restartDelay is the number of rounds an Oracle has to wait before
                 * they can initiate a round
                 */
                function updateFutureRounds(
                  uint128 _paymentAmount,
                  uint32 _minSubmissions,
                  uint32 _maxSubmissions,
                  uint32 _restartDelay,
                  uint32 _timeout
                )
                  public
                  onlyOwner()
                {
                  uint32 oracleNum = oracleCount(); // Save on storage reads
                  require(_maxSubmissions >= _minSubmissions, "max must equal/exceed min");
                  require(oracleNum >= _maxSubmissions, "max cannot exceed total");
                  require(oracleNum == 0 || oracleNum > _restartDelay, "delay cannot exceed total");
                  require(recordedFunds.available >= requiredReserve(_paymentAmount), "insufficient funds for payment");
                  if (oracleCount() > 0) {
                    require(_minSubmissions > 0, "min must be greater than 0");
                  }
              
                  paymentAmount = _paymentAmount;
                  minSubmissionCount = _minSubmissions;
                  maxSubmissionCount = _maxSubmissions;
                  restartDelay = _restartDelay;
                  timeout = _timeout;
              
                  emit RoundDetailsUpdated(
                    paymentAmount,
                    _minSubmissions,
                    _maxSubmissions,
                    _restartDelay,
                    _timeout
                  );
                }
              
                /**
                 * @notice the amount of payment yet to be withdrawn by oracles
                 */
                function allocatedFunds()
                  external
                  view
                  returns (uint128)
                {
                  return recordedFunds.allocated;
                }
              
                /**
                 * @notice the amount of future funding available to oracles
                 */
                function availableFunds()
                  external
                  view
                  returns (uint128)
                {
                  return recordedFunds.available;
                }
              
                /**
                 * @notice recalculate the amount of LINK available for payouts
                 */
                function updateAvailableFunds()
                  public
                {
                  Funds memory funds = recordedFunds;
              
                  uint256 nowAvailable = linkToken.balanceOf(address(this)).sub(funds.allocated);
              
                  if (funds.available != nowAvailable) {
                    recordedFunds.available = uint128(nowAvailable);
                    emit AvailableFundsUpdated(nowAvailable);
                  }
                }
              
                /**
                 * @notice returns the number of oracles
                 */
                function oracleCount() public view returns (uint8) {
                  return uint8(oracleAddresses.length);
                }
              
                /**
                 * @notice returns an array of addresses containing the oracles on contract
                 */
                function getOracles() external view returns (address[] memory) {
                  return oracleAddresses;
                }
              
                /**
                 * @notice get the most recently reported answer
                 *
                 * @dev #[deprecated] Use latestRoundData instead. This does not error if no
                 * answer has been reached, it will simply return 0. Either wait to point to
                 * an already answered Aggregator or use the recommended latestRoundData
                 * instead which includes better verification information.
                 */
                function latestAnswer()
                  public
                  view
                  virtual
                  override
                  returns (int256)
                {
                  return rounds[latestRoundId].answer;
                }
              
                /**
                 * @notice get the most recent updated at timestamp
                 *
                 * @dev #[deprecated] Use latestRoundData instead. This does not error if no
                 * answer has been reached, it will simply return 0. Either wait to point to
                 * an already answered Aggregator or use the recommended latestRoundData
                 * instead which includes better verification information.
                 */
                function latestTimestamp()
                  public
                  view
                  virtual
                  override
                  returns (uint256)
                {
                  return rounds[latestRoundId].updatedAt;
                }
              
                /**
                 * @notice get the ID of the last updated round
                 *
                 * @dev #[deprecated] Use latestRoundData instead. This does not error if no
                 * answer has been reached, it will simply return 0. Either wait to point to
                 * an already answered Aggregator or use the recommended latestRoundData
                 * instead which includes better verification information.
                 */
                function latestRound()
                  public
                  view
                  virtual
                  override
                  returns (uint256)
                {
                  return latestRoundId;
                }
              
                /**
                 * @notice get past rounds answers
                 * @param _roundId the round number to retrieve the answer for
                 *
                 * @dev #[deprecated] Use getRoundData instead. This does not error if no
                 * answer has been reached, it will simply return 0. Either wait to point to
                 * an already answered Aggregator or use the recommended getRoundData
                 * instead which includes better verification information.
                 */
                function getAnswer(uint256 _roundId)
                  public
                  view
                  virtual
                  override
                  returns (int256)
                {
                  if (validRoundId(_roundId)) {
                    return rounds[uint32(_roundId)].answer;
                  }
                  return 0;
                }
              
                /**
                 * @notice get timestamp when an answer was last updated
                 * @param _roundId the round number to retrieve the updated timestamp for
                 *
                 * @dev #[deprecated] Use getRoundData instead. This does not error if no
                 * answer has been reached, it will simply return 0. Either wait to point to
                 * an already answered Aggregator or use the recommended getRoundData
                 * instead which includes better verification information.
                 */
                function getTimestamp(uint256 _roundId)
                  public
                  view
                  virtual
                  override
                  returns (uint256)
                {
                  if (validRoundId(_roundId)) {
                    return rounds[uint32(_roundId)].updatedAt;
                  }
                  return 0;
                }
              
                /**
                 * @notice get data about a round. Consumers are encouraged to check
                 * that they're receiving fresh data by inspecting the updatedAt and
                 * answeredInRound return values.
                 * @param _roundId the round ID to retrieve the round data for
                 * @return roundId is the round ID for which data was retrieved
                 * @return answer is the answer for the given round
                 * @return startedAt is the timestamp when the round was started. This is 0
                 * if the round hasn't been started yet.
                 * @return updatedAt is the timestamp when the round last was updated (i.e.
                 * answer was last computed)
                 * @return answeredInRound is the round ID of the round in which the answer
                 * was computed. answeredInRound may be smaller than roundId when the round
                 * timed out. answeredInRound is equal to roundId when the round didn't time out
                 * and was completed regularly.
                 * @dev Note that for in-progress rounds (i.e. rounds that haven't yet received
                 * maxSubmissions) answer and updatedAt may change between queries.
                 */
                function getRoundData(uint80 _roundId)
                  public
                  view
                  virtual
                  override
                  returns (
                    uint80 roundId,
                    int256 answer,
                    uint256 startedAt,
                    uint256 updatedAt,
                    uint80 answeredInRound
                  )
                {
                  Round memory r = rounds[uint32(_roundId)];
              
                  require(r.answeredInRound > 0 && validRoundId(_roundId), V3_NO_DATA_ERROR);
              
                  return (
                    _roundId,
                    r.answer,
                    r.startedAt,
                    r.updatedAt,
                    r.answeredInRound
                  );
                }
              
                /**
                 * @notice get data about the latest round. Consumers are encouraged to check
                 * that they're receiving fresh data by inspecting the updatedAt and
                 * answeredInRound return values. Consumers are encouraged to
                 * use this more fully featured method over the "legacy" latestRound/
                 * latestAnswer/latestTimestamp functions. Consumers are encouraged to check
                 * that they're receiving fresh data by inspecting the updatedAt and
                 * answeredInRound return values.
                 * @return roundId is the round ID for which data was retrieved
                 * @return answer is the answer for the given round
                 * @return startedAt is the timestamp when the round was started. This is 0
                 * if the round hasn't been started yet.
                 * @return updatedAt is the timestamp when the round last was updated (i.e.
                 * answer was last computed)
                 * @return answeredInRound is the round ID of the round in which the answer
                 * was computed. answeredInRound may be smaller than roundId when the round
                 * timed out. answeredInRound is equal to roundId when the round didn't time
                 * out and was completed regularly.
                 * @dev Note that for in-progress rounds (i.e. rounds that haven't yet
                 * received maxSubmissions) answer and updatedAt may change between queries.
                 */
                 function latestRoundData()
                  public
                  view
                  virtual
                  override
                  returns (
                    uint80 roundId,
                    int256 answer,
                    uint256 startedAt,
                    uint256 updatedAt,
                    uint80 answeredInRound
                  )
                {
                  return getRoundData(latestRoundId);
                }
              
              
                /**
                 * @notice query the available amount of LINK for an oracle to withdraw
                 */
                function withdrawablePayment(address _oracle)
                  external
                  view
                  returns (uint256)
                {
                  return oracles[_oracle].withdrawable;
                }
              
                /**
                 * @notice transfers the oracle's LINK to another address. Can only be called
                 * by the oracle's admin.
                 * @param _oracle is the oracle whose LINK is transferred
                 * @param _recipient is the address to send the LINK to
                 * @param _amount is the amount of LINK to send
                 */
                function withdrawPayment(address _oracle, address _recipient, uint256 _amount)
                  external
                {
                  require(oracles[_oracle].admin == msg.sender, "only callable by admin");
              
                  // Safe to downcast _amount because the total amount of LINK is less than 2^128.
                  uint128 amount = uint128(_amount);
                  uint128 available = oracles[_oracle].withdrawable;
                  require(available >= amount, "insufficient withdrawable funds");
              
                  oracles[_oracle].withdrawable = available.sub(amount);
                  recordedFunds.allocated = recordedFunds.allocated.sub(amount);
              
                  assert(linkToken.transfer(_recipient, uint256(amount)));
                }
              
                /**
                 * @notice transfers the owner's LINK to another address
                 * @param _recipient is the address to send the LINK to
                 * @param _amount is the amount of LINK to send
                 */
                function withdrawFunds(address _recipient, uint256 _amount)
                  external
                  onlyOwner()
                {
                  uint256 available = uint256(recordedFunds.available);
                  require(available.sub(requiredReserve(paymentAmount)) >= _amount, "insufficient reserve funds");
                  require(linkToken.transfer(_recipient, _amount), "token transfer failed");
                  updateAvailableFunds();
                }
              
                /**
                 * @notice get the admin address of an oracle
                 * @param _oracle is the address of the oracle whose admin is being queried
                 */
                function getAdmin(address _oracle)
                  external
                  view
                  returns (address)
                {
                  return oracles[_oracle].admin;
                }
              
                /**
                 * @notice transfer the admin address for an oracle
                 * @param _oracle is the address of the oracle whose admin is being transferred
                 * @param _newAdmin is the new admin address
                 */
                function transferAdmin(address _oracle, address _newAdmin)
                  external
                {
                  require(oracles[_oracle].admin == msg.sender, "only callable by admin");
                  oracles[_oracle].pendingAdmin = _newAdmin;
              
                  emit OracleAdminUpdateRequested(_oracle, msg.sender, _newAdmin);
                }
              
                /**
                 * @notice accept the admin address transfer for an oracle
                 * @param _oracle is the address of the oracle whose admin is being transferred
                 */
                function acceptAdmin(address _oracle)
                  external
                {
                  require(oracles[_oracle].pendingAdmin == msg.sender, "only callable by pending admin");
                  oracles[_oracle].pendingAdmin = address(0);
                  oracles[_oracle].admin = msg.sender;
              
                  emit OracleAdminUpdated(_oracle, msg.sender);
                }
              
                /**
                 * @notice allows non-oracles to request a new round
                 */
                function requestNewRound()
                  external
                  returns (uint80)
                {
                  require(requesters[msg.sender].authorized, "not authorized requester");
              
                  uint32 current = reportingRoundId;
                  require(rounds[current].updatedAt > 0 || timedOut(current), "prev round must be supersedable");
              
                  uint32 newRoundId = current.add(1);
                  requesterInitializeNewRound(newRoundId);
                  return newRoundId;
                }
              
                /**
                 * @notice allows the owner to specify new non-oracles to start new rounds
                 * @param _requester is the address to set permissions for
                 * @param _authorized is a boolean specifying whether they can start new rounds or not
                 * @param _delay is the number of rounds the requester must wait before starting another round
                 */
                function setRequesterPermissions(address _requester, bool _authorized, uint32 _delay)
                  external
                  onlyOwner()
                {
                  if (requesters[_requester].authorized == _authorized) return;
              
                  if (_authorized) {
                    requesters[_requester].authorized = _authorized;
                    requesters[_requester].delay = _delay;
                  } else {
                    delete requesters[_requester];
                  }
              
                  emit RequesterPermissionsSet(_requester, _authorized, _delay);
                }
              
                /**
                 * @notice called through LINK's transferAndCall to update available funds
                 * in the same transaction as the funds were transferred to the aggregator
                 * @param _data is mostly ignored. It is checked for length, to be sure
                 * nothing strange is passed in.
                 */
                function onTokenTransfer(address, uint256, bytes calldata _data)
                  external
                {
                  require(_data.length == 0, "transfer doesn't accept calldata");
                  updateAvailableFunds();
                }
              
                /**
                 * @notice a method to provide all current info oracles need. Intended only
                 * only to be callable by oracles. Not for use by contracts to read state.
                 * @param _oracle the address to look up information for.
                 */
                function oracleRoundState(address _oracle, uint32 _queriedRoundId)
                  external
                  view
                  returns (
                    bool _eligibleToSubmit,
                    uint32 _roundId,
                    int256 _latestSubmission,
                    uint64 _startedAt,
                    uint64 _timeout,
                    uint128 _availableFunds,
                    uint8 _oracleCount,
                    uint128 _paymentAmount
                  )
                {
                  require(msg.sender == tx.origin, "off-chain reading only");
              
                  if (_queriedRoundId > 0) {
                    Round storage round = rounds[_queriedRoundId];
                    RoundDetails storage details = details[_queriedRoundId];
                    return (
                      eligibleForSpecificRound(_oracle, _queriedRoundId),
                      _queriedRoundId,
                      oracles[_oracle].latestSubmission,
                      round.startedAt,
                      details.timeout,
                      recordedFunds.available,
                      oracleCount(),
                      (round.startedAt > 0 ? details.paymentAmount : paymentAmount)
                    );
                  } else {
                    return oracleRoundStateSuggestRound(_oracle);
                  }
                }
              
                /**
                 * @notice method to update the address which does external data validation.
                 * @param _newValidator designates the address of the new validation contract.
                 */
                function setValidator(address _newValidator)
                  public
                  onlyOwner()
                {
                  address previous = address(validator);
              
                  if (previous != _newValidator) {
                    validator = AggregatorValidatorInterface(_newValidator);
              
                    emit ValidatorUpdated(previous, _newValidator);
                  }
                }
              
              
                /**
                 * Private
                 */
              
                function initializeNewRound(uint32 _roundId)
                  private
                {
                  updateTimedOutRoundInfo(_roundId.sub(1));
              
                  reportingRoundId = _roundId;
                  RoundDetails memory nextDetails = RoundDetails(
                    new int256[](0),
                    maxSubmissionCount,
                    minSubmissionCount,
                    timeout,
                    paymentAmount
                  );
                  details[_roundId] = nextDetails;
                  rounds[_roundId].startedAt = uint64(block.timestamp);
              
                  emit NewRound(_roundId, msg.sender, rounds[_roundId].startedAt);
                }
              
                function oracleInitializeNewRound(uint32 _roundId)
                  private
                {
                  if (!newRound(_roundId)) return;
                  uint256 lastStarted = oracles[msg.sender].lastStartedRound; // cache storage reads
                  if (_roundId <= lastStarted + restartDelay && lastStarted != 0) return;
              
                  initializeNewRound(_roundId);
              
                  oracles[msg.sender].lastStartedRound = _roundId;
                }
              
                function requesterInitializeNewRound(uint32 _roundId)
                  private
                {
                  if (!newRound(_roundId)) return;
                  uint256 lastStarted = requesters[msg.sender].lastStartedRound; // cache storage reads
                  require(_roundId > lastStarted + requesters[msg.sender].delay || lastStarted == 0, "must delay requests");
              
                  initializeNewRound(_roundId);
              
                  requesters[msg.sender].lastStartedRound = _roundId;
                }
              
                function updateTimedOutRoundInfo(uint32 _roundId)
                  private
                {
                  if (!timedOut(_roundId)) return;
              
                  uint32 prevId = _roundId.sub(1);
                  rounds[_roundId].answer = rounds[prevId].answer;
                  rounds[_roundId].answeredInRound = rounds[prevId].answeredInRound;
                  rounds[_roundId].updatedAt = uint64(block.timestamp);
              
                  delete details[_roundId];
                }
              
                function eligibleForSpecificRound(address _oracle, uint32 _queriedRoundId)
                  private
                  view
                  returns (bool _eligible)
                {
                  if (rounds[_queriedRoundId].startedAt > 0) {
                    return acceptingSubmissions(_queriedRoundId) && validateOracleRound(_oracle, _queriedRoundId).length == 0;
                  } else {
                    return delayed(_oracle, _queriedRoundId) && validateOracleRound(_oracle, _queriedRoundId).length == 0;
                  }
                }
              
                function oracleRoundStateSuggestRound(address _oracle)
                  private
                  view
                  returns (
                    bool _eligibleToSubmit,
                    uint32 _roundId,
                    int256 _latestSubmission,
                    uint64 _startedAt,
                    uint64 _timeout,
                    uint128 _availableFunds,
                    uint8 _oracleCount,
                    uint128 _paymentAmount
                  )
                {
                  Round storage round = rounds[0];
                  OracleStatus storage oracle = oracles[_oracle];
              
                  bool shouldSupersede = oracle.lastReportedRound == reportingRoundId || !acceptingSubmissions(reportingRoundId);
                  // Instead of nudging oracles to submit to the next round, the inclusion of
                  // the shouldSupersede bool in the if condition pushes them towards
                  // submitting in a currently open round.
                  if (supersedable(reportingRoundId) && shouldSupersede) {
                    _roundId = reportingRoundId.add(1);
                    round = rounds[_roundId];
              
                    _paymentAmount = paymentAmount;
                    _eligibleToSubmit = delayed(_oracle, _roundId);
                  } else {
                    _roundId = reportingRoundId;
                    round = rounds[_roundId];
              
                    _paymentAmount = details[_roundId].paymentAmount;
                    _eligibleToSubmit = acceptingSubmissions(_roundId);
                  }
              
                  if (validateOracleRound(_oracle, _roundId).length != 0) {
                    _eligibleToSubmit = false;
                  }
              
                  return (
                    _eligibleToSubmit,
                    _roundId,
                    oracle.latestSubmission,
                    round.startedAt,
                    details[_roundId].timeout,
                    recordedFunds.available,
                    oracleCount(),
                    _paymentAmount
                  );
                }
              
                function updateRoundAnswer(uint32 _roundId)
                  internal
                  returns (bool, int256)
                {
                  if (details[_roundId].submissions.length < details[_roundId].minSubmissions) {
                    return (false, 0);
                  }
              
                  int256 newAnswer = Median.calculateInplace(details[_roundId].submissions);
                  rounds[_roundId].answer = newAnswer;
                  rounds[_roundId].updatedAt = uint64(block.timestamp);
                  rounds[_roundId].answeredInRound = _roundId;
                  latestRoundId = _roundId;
              
                  emit AnswerUpdated(newAnswer, _roundId, now);
              
                  return (true, newAnswer);
                }
              
                function validateAnswer(
                  uint32 _roundId,
                  int256 _newAnswer
                )
                  private
                {
                  AggregatorValidatorInterface av = validator; // cache storage reads
                  if (address(av) == address(0)) return;
              
                  uint32 prevRound = _roundId.sub(1);
                  uint32 prevAnswerRoundId = rounds[prevRound].answeredInRound;
                  int256 prevRoundAnswer = rounds[prevRound].answer;
                  // We do not want the validator to ever prevent reporting, so we limit its
                  // gas usage and catch any errors that may arise.
                  try av.validate{gas: VALIDATOR_GAS_LIMIT}(
                    prevAnswerRoundId,
                    prevRoundAnswer,
                    _roundId,
                    _newAnswer
                  ) {} catch {}
                }
              
                function payOracle(uint32 _roundId)
                  private
                {
                  uint128 payment = details[_roundId].paymentAmount;
                  Funds memory funds = recordedFunds;
                  funds.available = funds.available.sub(payment);
                  funds.allocated = funds.allocated.add(payment);
                  recordedFunds = funds;
                  oracles[msg.sender].withdrawable = oracles[msg.sender].withdrawable.add(payment);
              
                  emit AvailableFundsUpdated(funds.available);
                }
              
                function recordSubmission(int256 _submission, uint32 _roundId)
                  private
                {
                  require(acceptingSubmissions(_roundId), "round not accepting submissions");
              
                  details[_roundId].submissions.push(_submission);
                  oracles[msg.sender].lastReportedRound = _roundId;
                  oracles[msg.sender].latestSubmission = _submission;
              
                  emit SubmissionReceived(_submission, _roundId, msg.sender);
                }
              
                function deleteRoundDetails(uint32 _roundId)
                  private
                {
                  if (details[_roundId].submissions.length < details[_roundId].maxSubmissions) return;
              
                  delete details[_roundId];
                }
              
                function timedOut(uint32 _roundId)
                  private
                  view
                  returns (bool)
                {
                  uint64 startedAt = rounds[_roundId].startedAt;
                  uint32 roundTimeout = details[_roundId].timeout;
                  return startedAt > 0 && roundTimeout > 0 && startedAt.add(roundTimeout) < block.timestamp;
                }
              
                function getStartingRound(address _oracle)
                  private
                  view
                  returns (uint32)
                {
                  uint32 currentRound = reportingRoundId;
                  if (currentRound != 0 && currentRound == oracles[_oracle].endingRound) {
                    return currentRound;
                  }
                  return currentRound.add(1);
                }
              
                function previousAndCurrentUnanswered(uint32 _roundId, uint32 _rrId)
                  private
                  view
                  returns (bool)
                {
                  return _roundId.add(1) == _rrId && rounds[_rrId].updatedAt == 0;
                }
              
                function requiredReserve(uint256 payment)
                  private
                  view
                  returns (uint256)
                {
                  return payment.mul(oracleCount()).mul(RESERVE_ROUNDS);
                }
              
                function addOracle(
                  address _oracle,
                  address _admin
                )
                  private
                {
                  require(!oracleEnabled(_oracle), "oracle already enabled");
              
                  require(_admin != address(0), "cannot set admin to 0");
                  require(oracles[_oracle].admin == address(0) || oracles[_oracle].admin == _admin, "owner cannot overwrite admin");
              
                  oracles[_oracle].startingRound = getStartingRound(_oracle);
                  oracles[_oracle].endingRound = ROUND_MAX;
                  oracles[_oracle].index = uint16(oracleAddresses.length);
                  oracleAddresses.push(_oracle);
                  oracles[_oracle].admin = _admin;
              
                  emit OraclePermissionsUpdated(_oracle, true);
                  emit OracleAdminUpdated(_oracle, _admin);
                }
              
                function removeOracle(
                  address _oracle
                )
                  private
                {
                  require(oracleEnabled(_oracle), "oracle not enabled");
              
                  oracles[_oracle].endingRound = reportingRoundId.add(1);
                  address tail = oracleAddresses[uint256(oracleCount()).sub(1)];
                  uint16 index = oracles[_oracle].index;
                  oracles[tail].index = index;
                  delete oracles[_oracle].index;
                  oracleAddresses[index] = tail;
                  oracleAddresses.pop();
              
                  emit OraclePermissionsUpdated(_oracle, false);
                }
              
                function validateOracleRound(address _oracle, uint32 _roundId)
                  private
                  view
                  returns (bytes memory)
                {
                  // cache storage reads
                  uint32 startingRound = oracles[_oracle].startingRound;
                  uint32 rrId = reportingRoundId;
              
                  if (startingRound == 0) return "not enabled oracle";
                  if (startingRound > _roundId) return "not yet enabled oracle";
                  if (oracles[_oracle].endingRound < _roundId) return "no longer allowed oracle";
                  if (oracles[_oracle].lastReportedRound >= _roundId) return "cannot report on previous rounds";
                  if (_roundId != rrId && _roundId != rrId.add(1) && !previousAndCurrentUnanswered(_roundId, rrId)) return "invalid round to report";
                  if (_roundId != 1 && !supersedable(_roundId.sub(1))) return "previous round not supersedable";
                }
              
                function supersedable(uint32 _roundId)
                  private
                  view
                  returns (bool)
                {
                  return rounds[_roundId].updatedAt > 0 || timedOut(_roundId);
                }
              
                function oracleEnabled(address _oracle)
                  private
                  view
                  returns (bool)
                {
                  return oracles[_oracle].endingRound == ROUND_MAX;
                }
              
                function acceptingSubmissions(uint32 _roundId)
                  private
                  view
                  returns (bool)
                {
                  return details[_roundId].maxSubmissions != 0;
                }
              
                function delayed(address _oracle, uint32 _roundId)
                  private
                  view
                  returns (bool)
                {
                  uint256 lastStarted = oracles[_oracle].lastStartedRound;
                  return _roundId > lastStarted + restartDelay || lastStarted == 0;
                }
              
                function newRound(uint32 _roundId)
                  private
                  view
                  returns (bool)
                {
                  return _roundId == reportingRoundId.add(1);
                }
              
                function validRoundId(uint256 _roundId)
                  private
                  view
                  returns (bool)
                {
                  return _roundId <= ROUND_MAX;
                }
              
              }
              
              interface AccessControllerInterface {
                function hasAccess(address user, bytes calldata data) external view returns (bool);
              }
              
              /**
               * @title SimpleWriteAccessController
               * @notice Gives access to accounts explicitly added to an access list by the
               * controller's owner.
               * @dev does not make any special permissions for externally, see
               * SimpleReadAccessController for that.
               */
              contract SimpleWriteAccessController is AccessControllerInterface, Owned {
              
                bool public checkEnabled;
                mapping(address => bool) internal accessList;
              
                event AddedAccess(address user);
                event RemovedAccess(address user);
                event CheckAccessEnabled();
                event CheckAccessDisabled();
              
                constructor()
                  public
                {
                  checkEnabled = true;
                }
              
                /**
                 * @notice Returns the access of an address
                 * @param _user The address to query
                 */
                function hasAccess(
                  address _user,
                  bytes memory
                )
                  public
                  view
                  virtual
                  override
                  returns (bool)
                {
                  return accessList[_user] || !checkEnabled;
                }
              
                /**
                 * @notice Adds an address to the access list
                 * @param _user The address to add
                 */
                function addAccess(address _user)
                  external
                  onlyOwner()
                {
                  if (!accessList[_user]) {
                    accessList[_user] = true;
              
                    emit AddedAccess(_user);
                  }
                }
              
                /**
                 * @notice Removes an address from the access list
                 * @param _user The address to remove
                 */
                function removeAccess(address _user)
                  external
                  onlyOwner()
                {
                  if (accessList[_user]) {
                    accessList[_user] = false;
              
                    emit RemovedAccess(_user);
                  }
                }
              
                /**
                 * @notice makes the access check enforced
                 */
                function enableAccessCheck()
                  external
                  onlyOwner()
                {
                  if (!checkEnabled) {
                    checkEnabled = true;
              
                    emit CheckAccessEnabled();
                  }
                }
              
                /**
                 * @notice makes the access check unenforced
                 */
                function disableAccessCheck()
                  external
                  onlyOwner()
                {
                  if (checkEnabled) {
                    checkEnabled = false;
              
                    emit CheckAccessDisabled();
                  }
                }
              
                /**
                 * @dev reverts if the caller does not have access
                 */
                modifier checkAccess() {
                  require(hasAccess(msg.sender, msg.data), "No access");
                  _;
                }
              }
              
              /**
               * @title SimpleReadAccessController
               * @notice Gives access to:
               * - any externally owned account (note that offchain actors can always read
               * any contract storage regardless of onchain access control measures, so this
               * does not weaken the access control while improving usability)
               * - accounts explicitly added to an access list
               * @dev SimpleReadAccessController is not suitable for access controlling writes
               * since it grants any externally owned account access! See
               * SimpleWriteAccessController for that.
               */
              contract SimpleReadAccessController is SimpleWriteAccessController {
              
                /**
                 * @notice Returns the access of an address
                 * @param _user The address to query
                 */
                function hasAccess(
                  address _user,
                  bytes memory _calldata
                )
                  public
                  view
                  virtual
                  override
                  returns (bool)
                {
                  return super.hasAccess(_user, _calldata) || _user == tx.origin;
                }
              
              }
              
              /**
               * @title AccessControlled FluxAggregator contract
               * @notice This contract requires addresses to be added to a controller
               * in order to read the answers stored in the FluxAggregator contract
               */
              contract AccessControlledAggregator is FluxAggregator, SimpleReadAccessController {
              
                /**
                 * @notice set up the aggregator with initial configuration
                 * @param _link The address of the LINK token
                 * @param _paymentAmount The amount paid of LINK paid to each oracle per submission, in wei (units of 10⁻¹⁸ LINK)
                 * @param _timeout is the number of seconds after the previous round that are
                 * allowed to lapse before allowing an oracle to skip an unfinished round
                 * @param _validator is an optional contract address for validating
                 * external validation of answers
                 * @param _minSubmissionValue is an immutable check for a lower bound of what
                 * submission values are accepted from an oracle
                 * @param _maxSubmissionValue is an immutable check for an upper bound of what
                 * submission values are accepted from an oracle
                 * @param _decimals represents the number of decimals to offset the answer by
                 * @param _description a short description of what is being reported
                 */
                constructor(
                  address _link,
                  uint128 _paymentAmount,
                  uint32 _timeout,
                  address _validator,
                  int256 _minSubmissionValue,
                  int256 _maxSubmissionValue,
                  uint8 _decimals,
                  string memory _description
                ) public FluxAggregator(
                  _link,
                  _paymentAmount,
                  _timeout,
                  _validator,
                  _minSubmissionValue,
                  _maxSubmissionValue,
                  _decimals,
                  _description
                ){}
              
                /**
                 * @notice get data about a round. Consumers are encouraged to check
                 * that they're receiving fresh data by inspecting the updatedAt and
                 * answeredInRound return values.
                 * @param _roundId the round ID to retrieve the round data for
                 * @return roundId is the round ID for which data was retrieved
                 * @return answer is the answer for the given round
                 * @return startedAt is the timestamp when the round was started. This is 0
                 * if the round hasn't been started yet.
                 * @return updatedAt is the timestamp when the round last was updated (i.e.
                 * answer was last computed)
                 * @return answeredInRound is the round ID of the round in which the answer
                 * was computed. answeredInRound may be smaller than roundId when the round
                 * timed out. answerInRound is equal to roundId when the round didn't time out
                 * and was completed regularly.
                 * @dev overridden funcion to add the checkAccess() modifier
                 * @dev Note that for in-progress rounds (i.e. rounds that haven't yet
                 * received maxSubmissions) answer and updatedAt may change between queries.
                 */
                function getRoundData(uint80 _roundId)
                  public
                  view
                  override
                  checkAccess()
                  returns (
                    uint80 roundId,
                    int256 answer,
                    uint256 startedAt,
                    uint256 updatedAt,
                    uint80 answeredInRound
                  )
                {
                  return super.getRoundData(_roundId);
                }
              
                /**
                 * @notice get data about the latest round. Consumers are encouraged to check
                 * that they're receiving fresh data by inspecting the updatedAt and
                 * answeredInRound return values. Consumers are encouraged to
                 * use this more fully featured method over the "legacy" latestAnswer
                 * functions. Consumers are encouraged to check that they're receiving fresh
                 * data by inspecting the updatedAt and answeredInRound return values.
                 * @return roundId is the round ID for which data was retrieved
                 * @return answer is the answer for the given round
                 * @return startedAt is the timestamp when the round was started. This is 0
                 * if the round hasn't been started yet.
                 * @return updatedAt is the timestamp when the round last was updated (i.e.
                 * answer was last computed)
                 * @return answeredInRound is the round ID of the round in which the answer
                 * was computed. answeredInRound may be smaller than roundId when the round
                 * timed out. answerInRound is equal to roundId when the round didn't time out
                 * and was completed regularly.
                 * @dev overridden funcion to add the checkAccess() modifier
                 * @dev Note that for in-progress rounds (i.e. rounds that haven't yet
                 * received maxSubmissions) answer and updatedAt may change between queries.
                 */
                function latestRoundData()
                  public
                  view
                  override
                  checkAccess()
                  returns (
                    uint80 roundId,
                    int256 answer,
                    uint256 startedAt,
                    uint256 updatedAt,
                    uint80 answeredInRound
                  )
                {
                  return super.latestRoundData();
                }
              
                /**
                 * @notice get the most recently reported answer
                 * @dev overridden funcion to add the checkAccess() modifier
                 *
                 * @dev #[deprecated] Use latestRoundData instead. This does not error if no
                 * answer has been reached, it will simply return 0. Either wait to point to
                 * an already answered Aggregator or use the recommended latestRoundData
                 * instead which includes better verification information.
                 */
                function latestAnswer()
                  public
                  view
                  override
                  checkAccess()
                  returns (int256)
                {
                  return super.latestAnswer();
                }
              
                /**
                 * @notice get the most recently reported round ID
                 * @dev overridden funcion to add the checkAccess() modifier
                 *
                 * @dev #[deprecated] Use latestRoundData instead. This does not error if no
                 * answer has been reached, it will simply return 0. Either wait to point to
                 * an already answered Aggregator or use the recommended latestRoundData
                 * instead which includes better verification information.
                 */
                function latestRound()
                  public
                  view
                  override
                  checkAccess()
                  returns (uint256)
                {
                  return super.latestRound();
                }
              
                /**
                 * @notice get the most recent updated at timestamp
                 * @dev overridden funcion to add the checkAccess() modifier
                 *
                 * @dev #[deprecated] Use latestRoundData instead. This does not error if no
                 * answer has been reached, it will simply return 0. Either wait to point to
                 * an already answered Aggregator or use the recommended latestRoundData
                 * instead which includes better verification information.
                 */
                function latestTimestamp()
                  public
                  view
                  override
                  checkAccess()
                  returns (uint256)
                {
                  return super.latestTimestamp();
                }
              
                /**
                 * @notice get past rounds answers
                 * @dev overridden funcion to add the checkAccess() modifier
                 * @param _roundId the round number to retrieve the answer for
                 *
                 * @dev #[deprecated] Use getRoundData instead. This does not error if no
                 * answer has been reached, it will simply return 0. Either wait to point to
                 * an already answered Aggregator or use the recommended getRoundData
                 * instead which includes better verification information.
                 */
                function getAnswer(uint256 _roundId)
                  public
                  view
                  override
                  checkAccess()
                  returns (int256)
                {
                  return super.getAnswer(_roundId);
                }
              
                /**
                 * @notice get timestamp when an answer was last updated
                 * @dev overridden funcion to add the checkAccess() modifier
                 * @param _roundId the round number to retrieve the updated timestamp for
                 *
                 * @dev #[deprecated] Use getRoundData instead. This does not error if no
                 * answer has been reached, it will simply return 0. Either wait to point to
                 * an already answered Aggregator or use the recommended getRoundData
                 * instead which includes better verification information.
                 */
                function getTimestamp(uint256 _roundId)
                  public
                  view
                  override
                  checkAccess()
                  returns (uint256)
                {
                  return super.getTimestamp(_roundId);
                }
              
              }