ETH Price: $2,522.18 (-4.84%)

Transaction Decoder

Block:
16997785 at Apr-07-2023 04:26:59 PM +UTC
Transaction Fee:
0.009230138109121482 ETH $23.28
Gas Used:
296,949 Gas / 31.083243618 Gwei

Emitted Events:

464 BaseRegistrarImplementation.Transfer( from=0x00000000...000000000, to=ETHRegistrarController, tokenId=86438398272882906019343174234719865276647605804776702564804007897240476512351 )
465 ENSRegistryWithFallback.NewOwner( node=93CDEB708B7545DC668EB9280176169D1C33CFD8ED6F04690A0BCC88A93FC4AE, label=BF1A66514B205CE25B2C046570F5675E77578572FEB848015D2BC0A981DEF05F, owner=ETHRegistrarController )
466 BaseRegistrarImplementation.NameRegistered( id=86438398272882906019343174234719865276647605804776702564804007897240476512351, owner=ETHRegistrarController, expires=1712441771 )
467 ENSRegistryWithFallback.NewResolver( node=39FE090408EF0F1FB1B42E6DD644A3DD7F6D700EE86FF29D6D2389B14808A0DE, resolver=PublicResolver )
468 PublicResolver.AddressChanged( node=39FE090408EF0F1FB1B42E6DD644A3DD7F6D700EE86FF29D6D2389B14808A0DE, coinType=60, newAddress=[Sender] 0xf38965ff32f62b27a9b50e9b96e2a78cc9350feb )
469 PublicResolver.AddrChanged( node=39FE090408EF0F1FB1B42E6DD644A3DD7F6D700EE86FF29D6D2389B14808A0DE, a=[Sender] 0xf38965ff32f62b27a9b50e9b96e2a78cc9350feb )
470 ENSRegistryWithFallback.NewOwner( node=93CDEB708B7545DC668EB9280176169D1C33CFD8ED6F04690A0BCC88A93FC4AE, label=BF1A66514B205CE25B2C046570F5675E77578572FEB848015D2BC0A981DEF05F, owner=[Sender] 0xf38965ff32f62b27a9b50e9b96e2a78cc9350feb )
471 BaseRegistrarImplementation.Transfer( from=ETHRegistrarController, to=[Sender] 0xf38965ff32f62b27a9b50e9b96e2a78cc9350feb, tokenId=86438398272882906019343174234719865276647605804776702564804007897240476512351 )
472 ETHRegistrarController.NameRegistered( name=an8888, label=BF1A66514B205CE25B2C046570F5675E77578572FEB848015D2BC0A981DEF05F, owner=[Sender] 0xf38965ff32f62b27a9b50e9b96e2a78cc9350feb, cost=2688848190967726, expires=1712441771 )

Account State Difference:

  Address   Before After State Difference Code
0x00000000...A6C7d2e1e
(ENS: Registry with Fallback)
0x231b0Ee1...4EB5E8E63
(ENS: Public Resolver)
0x283Af0B2...A627EB7F5
(ENS: Old ETH Registrar Controller)
2,328.610872977243087379 Eth2,328.613561825434055105 Eth0.002688848190967726
0x57f1887a...Af147eA85
(Fee Recipient: 0x6d2...766)
43.602492353714403611 Eth43.602522048614403611 Eth0.0000296949
0xF38965fF...CC9350fEB
0.663184938154403752 Eth
Nonce: 356
0.651265951854314544 Eth
Nonce: 357
0.011918986300089208

Execution Trace

ETH 0.002957733010064498 ETHBulkRegistrarV1.registerWithConfig( name=an8888, owner=0xF38965fF32f62b27a9B50e9B96E2a78CC9350fEB, duration=31556952, secret=0A91BFD36FB8AA65DC54E70ACDBB908A29E2A74B0697BBA9C643F8AB79DC2D9F, resolver=0x231b0Ee14048e9dCcD1d247744d114a4EB5E8E63, addr=0xF38965fF32f62b27a9B50e9B96E2a78CC9350fEB )
  • ETHRegistrarController.rentPrice( name=an8888, duration=31556952 ) => ( 2688848190967726 )
    • BaseRegistrarImplementation.nameExpires( id=86438398272882906019343174234719865276647605804776702564804007897240476512351 ) => ( 0 )
    • ExponentialPremiumPriceOracle.price( name=an8888, expires=0, duration=31556952 ) => ( 2688848190967726 )
      • EACAggregatorProxy.STATICCALL( )
        • AccessControlledOffchainAggregator.STATICCALL( )
        • ETH 0.002688848190967726 ETHRegistrarController.registerWithConfig( name=an8888, owner=0xF38965fF32f62b27a9B50e9B96E2a78CC9350fEB, duration=31556952, secret=0A91BFD36FB8AA65DC54E70ACDBB908A29E2A74B0697BBA9C643F8AB79DC2D9F, resolver=0x231b0Ee14048e9dCcD1d247744d114a4EB5E8E63, addr=0xF38965fF32f62b27a9B50e9B96E2a78CC9350fEB )
          • BaseRegistrarImplementation.available( id=86438398272882906019343174234719865276647605804776702564804007897240476512351 ) => ( True )
          • BaseRegistrarImplementation.nameExpires( id=86438398272882906019343174234719865276647605804776702564804007897240476512351 ) => ( 0 )
          • ExponentialPremiumPriceOracle.price( name=an8888, expires=0, duration=31556952 ) => ( 2688848190967726 )
            • EACAggregatorProxy.STATICCALL( )
              • AccessControlledOffchainAggregator.STATICCALL( )
              • BaseRegistrarImplementation.register( id=86438398272882906019343174234719865276647605804776702564804007897240476512351, owner=0x283Af0B28c62C092C9727F1Ee09c02CA627EB7F5, duration=31556952 ) => ( 1712441771 )
                • ENSRegistryWithFallback.owner( node=93CDEB708B7545DC668EB9280176169D1C33CFD8ED6F04690A0BCC88A93FC4AE ) => ( 0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85 )
                • ENSRegistryWithFallback.setSubnodeOwner( node=93CDEB708B7545DC668EB9280176169D1C33CFD8ED6F04690A0BCC88A93FC4AE, label=BF1A66514B205CE25B2C046570F5675E77578572FEB848015D2BC0A981DEF05F, owner=0x283Af0B28c62C092C9727F1Ee09c02CA627EB7F5 ) => ( 39FE090408EF0F1FB1B42E6DD644A3DD7F6D700EE86FF29D6D2389B14808A0DE )
                • BaseRegistrarImplementation.STATICCALL( )
                • BaseRegistrarImplementation.STATICCALL( )
                • ENSRegistryWithFallback.setResolver( node=39FE090408EF0F1FB1B42E6DD644A3DD7F6D700EE86FF29D6D2389B14808A0DE, resolver=0x231b0Ee14048e9dCcD1d247744d114a4EB5E8E63 )
                • PublicResolver.setAddr( node=39FE090408EF0F1FB1B42E6DD644A3DD7F6D700EE86FF29D6D2389B14808A0DE, a=0xF38965fF32f62b27a9B50e9B96E2a78CC9350fEB )
                  • ENSRegistryWithFallback.owner( node=39FE090408EF0F1FB1B42E6DD644A3DD7F6D700EE86FF29D6D2389B14808A0DE ) => ( 0x283Af0B28c62C092C9727F1Ee09c02CA627EB7F5 )
                  • ENSRegistryWithFallback.owner( node=39FE090408EF0F1FB1B42E6DD644A3DD7F6D700EE86FF29D6D2389B14808A0DE ) => ( 0x283Af0B28c62C092C9727F1Ee09c02CA627EB7F5 )
                  • BaseRegistrarImplementation.reclaim( id=86438398272882906019343174234719865276647605804776702564804007897240476512351, owner=0xF38965fF32f62b27a9B50e9B96E2a78CC9350fEB )
                    • ENSRegistryWithFallback.owner( node=93CDEB708B7545DC668EB9280176169D1C33CFD8ED6F04690A0BCC88A93FC4AE ) => ( 0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85 )
                    • ENSRegistryWithFallback.setSubnodeOwner( node=93CDEB708B7545DC668EB9280176169D1C33CFD8ED6F04690A0BCC88A93FC4AE, label=BF1A66514B205CE25B2C046570F5675E77578572FEB848015D2BC0A981DEF05F, owner=0xF38965fF32f62b27a9B50e9B96E2a78CC9350fEB ) => ( 39FE090408EF0F1FB1B42E6DD644A3DD7F6D700EE86FF29D6D2389B14808A0DE )
                    • BaseRegistrarImplementation.transferFrom( from=0x283Af0B28c62C092C9727F1Ee09c02CA627EB7F5, to=0xF38965fF32f62b27a9B50e9B96E2a78CC9350fEB, tokenId=86438398272882906019343174234719865276647605804776702564804007897240476512351 )
                    • ETH 0.000268884819096772 0xf38965ff32f62b27a9b50e9b96e2a78cc9350feb.CALL( )
                      File 1 of 8: ETHBulkRegistrarV1
                      // SPDX-License-Identifier: MIT
                      pragma solidity >=0.8.4;
                      import "./IETHBulkRegistrar.sol";
                      import "./IETHRegistrarController.sol";
                      contract ETHBulkRegistrarV1 is IETHBulkRegistrar {
                          IETHRegistrarController public immutable registrarController;
                          constructor(IETHRegistrarController _registrarController) {
                              registrarController = _registrarController;
                          }
                          function bulkRentPrice(string[] calldata names, uint256 duration) external view override returns (uint256 total) {
                              for (uint256 i = 0; i < names.length; i++) {
                                  uint price = registrarController.rentPrice(names[i], duration);
                                  total += price;
                              }
                              return total;
                          }
                          function bulkMakeCommitment(string[] calldata name, address owner, bytes32 secret) external view override returns (bytes32[] memory commitmentList) {
                              commitmentList = new bytes32[](name.length);
                              for (uint256 i = 0; i < name.length; i++) {
                                  commitmentList[i] = registrarController.makeCommitmentWithConfig(name[i], owner, secret, address(0), address(0));
                              }
                              return commitmentList;
                          }
                          function commitments(bytes32 commit) external view override returns (uint256) {
                              return registrarController.commitments(commit);
                          }
                          function bulkCommit(bytes32[] calldata commitmentList) external override {
                              for (uint256 i = 0; i < commitmentList.length; i++) {
                                  registrarController.commit(commitmentList[i]);
                              }
                          }
                          function bulkRegister(string[] calldata names, address owner, uint duration, bytes32 secret) external payable override {
                              uint256 cost = 0;
                              for (uint256 i = 0; i < names.length; i++) {
                                  uint price = registrarController.rentPrice(names[i], duration);
                                  registrarController.register{value: (price)}(names[i], owner, duration, secret);
                                  cost = cost + price;
                              }
                              // Send any excess funds back
                              if (msg.value > cost) {
                                  (bool sent, ) = msg.sender.call{value: msg.value - cost}("");
                                  require(sent, "Failed to send Ether");
                              }
                          }
                          function registerWithConfig(string calldata name, address owner, uint duration, bytes32 secret, address resolver, address addr) external payable override {
                              uint cost = registrarController.rentPrice(name, duration);
                              registrarController.registerWithConfig{value: cost}(name, owner, duration, secret, resolver, addr);
                              // Send any excess funds back
                              if (msg.value > cost) {
                                  (bool sent, ) = msg.sender.call{value: msg.value - cost}("");
                                  require(sent, "Failed to send Ether");
                              }
                          }
                          function makeCommitmentWithConfig(string calldata name, address owner, bytes32 secret, address resolver, address addr) external view override returns (bytes32 commitment) {
                              commitment = registrarController.makeCommitmentWithConfig(name, owner, secret, resolver, addr);
                              return commitment;
                          }
                      }
                      pragma solidity >=0.8.4;
                      interface IETHRegistrarController {
                          function rentPrice(string memory, uint) external view returns (uint);
                          function available(string memory) external returns (bool);
                          function commit(bytes32) external;
                          function register(string calldata, address, uint256, bytes32) external payable;
                          function registerWithConfig(string memory, address, uint256, bytes32, address, address) external payable;
                          function makeCommitmentWithConfig(string memory, address, bytes32, address, address) external pure returns (bytes32);
                          function renew(string calldata, uint256) external payable;
                          function commitments(bytes32) external view returns (uint256);
                      }
                      pragma solidity >=0.8.4;
                      interface IETHBulkRegistrar {
                          function bulkRentPrice(string[] calldata names, uint256 duration) external view returns (uint256 total);
                          function bulkRegister(string[] calldata names, address owner, uint duration, bytes32 secret) external payable;
                          function bulkCommit(bytes32[] calldata commitments) external;
                          function bulkMakeCommitment(string[] calldata name, address owner, bytes32 secret) external view returns (bytes32[] memory commitments);
                          function commitments(bytes32 commit) external view returns (uint256);
                          function registerWithConfig(string calldata name, address owner, uint duration, bytes32 secret, address resolver, address addr) external payable;
                          function makeCommitmentWithConfig(string calldata name, address owner, bytes32 secret, address resolver, address addr) external view returns (bytes32);
                      }
                      

                      File 2 of 8: 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 8: 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 8: PublicResolver
                      // SPDX-License-Identifier: BSD-2-Clause
                      pragma solidity ^0.8.4;
                      /**
                      * @dev A library for working with mutable byte buffers in Solidity.
                      *
                      * Byte buffers are mutable and expandable, and provide a variety of primitives
                      * for appending 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)
                                  let fpm := add(32, add(ptr, capacity))
                                  if lt(fpm, ptr) {
                                      revert(0, 0)
                                  }
                                  mstore(0x40, fpm)
                              }
                              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);
                          }
                          /**
                          * @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 Appends len bytes of 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) {
                              require(len <= data.length);
                              uint off = buf.buf.length;
                              uint newCapacity = off + len;
                              if (newCapacity > buf.capacity) {
                                  resize(buf, newCapacity * 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(newCapacity, buflen) {
                                      mstore(bufptr, newCapacity)
                                  }
                                  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
                              unchecked {
                                  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.
                          * @return The original buffer, for chaining.
                          */
                          function append(buffer memory buf, bytes memory data) internal pure returns (buffer memory) {
                              return append(buf, data, data.length);
                          }
                          /**
                          * @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) {
                              uint off = buf.buf.length;
                              uint offPlusOne = off + 1;
                              if (off >= buf.capacity) {
                                  resize(buf, offPlusOne * 2);
                              }
                              assembly {
                                  // Memory address of the buffer data
                                  let bufptr := mload(buf)
                                  // 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 gt(offPlusOne, mload(bufptr)) {
                                      mstore(bufptr, offPlusOne)
                                  }
                              }
                              return buf;
                          }
                          /**
                          * @dev Appends len bytes of bytes32 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 write (left-aligned).
                          * @return The original buffer, for chaining.
                          */
                          function append(buffer memory buf, bytes32 data, uint len) private pure returns(buffer memory) {
                              uint off = buf.buf.length;
                              uint newCapacity = len + off;
                              if (newCapacity > buf.capacity) {
                                  resize(buf, newCapacity * 2);
                              }
                              unchecked {
                                  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) + newCapacity
                                      let dest := add(bufptr, newCapacity)
                                      mstore(dest, or(and(mload(dest), not(mask)), data))
                                      // Update buffer length if we extended it
                                      if gt(newCapacity, mload(bufptr)) {
                                          mstore(bufptr, newCapacity)
                                      }
                                  }
                              }
                              return buf;
                          }
                          /**
                          * @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 append(buf, 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 append(buf, data, 32);
                          }
                          /**
                           * @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.
                           * @param len The number of bytes to write (right-aligned).
                           * @return The original buffer.
                           */
                          function appendInt(buffer memory buf, uint data, uint len) internal pure returns(buffer memory) {
                              uint off = buf.buf.length;
                              uint newCapacity = len + off;
                              if (newCapacity > buf.capacity) {
                                  resize(buf, newCapacity * 2);
                              }
                              uint mask = (256 ** len) - 1;
                              assembly {
                                  // Memory address of the buffer data
                                  let bufptr := mload(buf)
                                  // Address = buffer address + sizeof(buffer length) + newCapacity
                                  let dest := add(bufptr, newCapacity)
                                  mstore(dest, or(and(mload(dest), not(mask)), data))
                                  // Update buffer length if we extended it
                                  if gt(newCapacity, mload(bufptr)) {
                                      mstore(bufptr, newCapacity)
                                  }
                              }
                              return buf;
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      // OpenZeppelin Contracts (last updated v4.7.0) (token/ERC1155/IERC1155.sol)
                      pragma solidity ^0.8.0;
                      import "../../utils/introspection/IERC165.sol";
                      /**
                       * @dev Required interface of an ERC1155 compliant contract, as defined in the
                       * https://eips.ethereum.org/EIPS/eip-1155[EIP].
                       *
                       * _Available since v3.1._
                       */
                      interface IERC1155 is IERC165 {
                          /**
                           * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
                           */
                          event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
                          /**
                           * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
                           * transfers.
                           */
                          event TransferBatch(
                              address indexed operator,
                              address indexed from,
                              address indexed to,
                              uint256[] ids,
                              uint256[] values
                          );
                          /**
                           * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
                           * `approved`.
                           */
                          event ApprovalForAll(address indexed account, address indexed operator, bool approved);
                          /**
                           * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
                           *
                           * If an {URI} event was emitted for `id`, the standard
                           * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
                           * returned by {IERC1155MetadataURI-uri}.
                           */
                          event URI(string value, uint256 indexed id);
                          /**
                           * @dev Returns the amount of tokens of token type `id` owned by `account`.
                           *
                           * Requirements:
                           *
                           * - `account` cannot be the zero address.
                           */
                          function balanceOf(address account, uint256 id) external view returns (uint256);
                          /**
                           * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
                           *
                           * Requirements:
                           *
                           * - `accounts` and `ids` must have the same length.
                           */
                          function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
                              external
                              view
                              returns (uint256[] memory);
                          /**
                           * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
                           *
                           * Emits an {ApprovalForAll} event.
                           *
                           * Requirements:
                           *
                           * - `operator` cannot be the caller.
                           */
                          function setApprovalForAll(address operator, bool approved) external;
                          /**
                           * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
                           *
                           * See {setApprovalForAll}.
                           */
                          function isApprovedForAll(address account, address operator) external view returns (bool);
                          /**
                           * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
                           *
                           * Emits a {TransferSingle} event.
                           *
                           * Requirements:
                           *
                           * - `to` cannot be the zero address.
                           * - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}.
                           * - `from` must have a balance of tokens of type `id` of at least `amount`.
                           * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
                           * acceptance magic value.
                           */
                          function safeTransferFrom(
                              address from,
                              address to,
                              uint256 id,
                              uint256 amount,
                              bytes calldata data
                          ) external;
                          /**
                           * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
                           *
                           * Emits a {TransferBatch} event.
                           *
                           * Requirements:
                           *
                           * - `ids` and `amounts` must have the same length.
                           * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
                           * acceptance magic value.
                           */
                          function safeBatchTransferFrom(
                              address from,
                              address to,
                              uint256[] calldata ids,
                              uint256[] calldata amounts,
                              bytes calldata data
                          ) external;
                      }
                      // SPDX-License-Identifier: MIT
                      // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/IERC721.sol)
                      pragma solidity ^0.8.0;
                      import "../../utils/introspection/IERC165.sol";
                      /**
                       * @dev Required interface of an ERC721 compliant contract.
                       */
                      interface IERC721 is IERC165 {
                          /**
                           * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
                           */
                          event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
                          /**
                           * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
                           */
                          event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
                          /**
                           * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
                           */
                          event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
                          /**
                           * @dev Returns the number of tokens in ``owner``'s account.
                           */
                          function balanceOf(address owner) external view returns (uint256 balance);
                          /**
                           * @dev Returns the owner of the `tokenId` token.
                           *
                           * Requirements:
                           *
                           * - `tokenId` must exist.
                           */
                          function ownerOf(uint256 tokenId) external view returns (address owner);
                          /**
                           * @dev Safely transfers `tokenId` token from `from` to `to`.
                           *
                           * Requirements:
                           *
                           * - `from` cannot be the zero address.
                           * - `to` cannot be the zero address.
                           * - `tokenId` token must exist and be owned by `from`.
                           * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
                           * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                           *
                           * Emits a {Transfer} event.
                           */
                          function safeTransferFrom(
                              address from,
                              address to,
                              uint256 tokenId,
                              bytes calldata data
                          ) external;
                          /**
                           * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
                           * are aware of the ERC721 protocol to prevent tokens from being forever locked.
                           *
                           * Requirements:
                           *
                           * - `from` cannot be the zero address.
                           * - `to` cannot be the zero address.
                           * - `tokenId` token must exist and be owned by `from`.
                           * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
                           * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                           *
                           * Emits a {Transfer} event.
                           */
                          function safeTransferFrom(
                              address from,
                              address to,
                              uint256 tokenId
                          ) external;
                          /**
                           * @dev Transfers `tokenId` token from `from` to `to`.
                           *
                           * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
                           * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
                           * understand this adds an external call which potentially creates a reentrancy vulnerability.
                           *
                           * Requirements:
                           *
                           * - `from` cannot be the zero address.
                           * - `to` cannot be the zero address.
                           * - `tokenId` token must be owned by `from`.
                           * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
                           *
                           * Emits a {Transfer} event.
                           */
                          function transferFrom(
                              address from,
                              address to,
                              uint256 tokenId
                          ) external;
                          /**
                           * @dev Gives permission to `to` to transfer `tokenId` token to another account.
                           * The approval is cleared when the token is transferred.
                           *
                           * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
                           *
                           * Requirements:
                           *
                           * - The caller must own the token or be an approved operator.
                           * - `tokenId` must exist.
                           *
                           * Emits an {Approval} event.
                           */
                          function approve(address to, uint256 tokenId) external;
                          /**
                           * @dev Approve or remove `operator` as an operator for the caller.
                           * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
                           *
                           * Requirements:
                           *
                           * - The `operator` cannot be the caller.
                           *
                           * Emits an {ApprovalForAll} event.
                           */
                          function setApprovalForAll(address operator, bool _approved) external;
                          /**
                           * @dev Returns the account approved for `tokenId` token.
                           *
                           * Requirements:
                           *
                           * - `tokenId` must exist.
                           */
                          function getApproved(uint256 tokenId) external view returns (address operator);
                          /**
                           * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
                           *
                           * See {setApprovalForAll}
                           */
                          function isApprovedForAll(address owner, address operator) external view returns (bool);
                      }
                      // SPDX-License-Identifier: MIT
                      // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
                      pragma solidity ^0.8.0;
                      import "./IERC165.sol";
                      /**
                       * @dev Implementation of the {IERC165} interface.
                       *
                       * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
                       * for the additional interface id that will be supported. For example:
                       *
                       * ```solidity
                       * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                       *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
                       * }
                       * ```
                       *
                       * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
                       */
                      abstract contract ERC165 is IERC165 {
                          /**
                           * @dev See {IERC165-supportsInterface}.
                           */
                          function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                              return interfaceId == type(IERC165).interfaceId;
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
                      pragma solidity ^0.8.0;
                      /**
                       * @dev Interface of the ERC165 standard, as defined in the
                       * https://eips.ethereum.org/EIPS/eip-165[EIP].
                       *
                       * Implementers can declare support of contract interfaces, which can then be
                       * queried by others ({ERC165Checker}).
                       *
                       * For an implementation, see {ERC165}.
                       */
                      interface IERC165 {
                          /**
                           * @dev Returns true if this contract implements the interface defined by
                           * `interfaceId`. See the corresponding
                           * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
                           * to learn more about how these ids are created.
                           *
                           * This function call must use less than 30 000 gas.
                           */
                          function supportsInterface(bytes4 interfaceId) external view returns (bool);
                      }
                      pragma solidity ^0.8.4;
                      library BytesUtils {
                          error OffsetOutOfBoundsError(uint256 offset, uint256 length);
                          /*
                           * @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,
                              uint256 offset,
                              uint256 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 (int256) {
                              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,
                              uint256 offset,
                              uint256 len,
                              bytes memory other,
                              uint256 otheroffset,
                              uint256 otherlen
                          ) internal pure returns (int256) {
                              if (offset + len > self.length) {
                                  revert OffsetOutOfBoundsError(offset + len, self.length);
                              }
                              if (otheroffset + otherlen > other.length) {
                                  revert OffsetOutOfBoundsError(otheroffset + otherlen, other.length);
                              }
                              uint256 shortest = len;
                              if (otherlen < len) shortest = otherlen;
                              uint256 selfptr;
                              uint256 otherptr;
                              assembly {
                                  selfptr := add(self, add(offset, 32))
                                  otherptr := add(other, add(otheroffset, 32))
                              }
                              for (uint256 idx = 0; idx < shortest; idx += 32) {
                                  uint256 a;
                                  uint256 b;
                                  assembly {
                                      a := mload(selfptr)
                                      b := mload(otherptr)
                                  }
                                  if (a != b) {
                                      // Mask out irrelevant bytes and check again
                                      uint256 mask;
                                      if (shortest - idx >= 32) {
                                          mask = type(uint256).max;
                                      } else {
                                          mask = ~(2 ** (8 * (idx + 32 - shortest)) - 1);
                                      }
                                      int256 diff = int256(a & mask) - int256(b & mask);
                                      if (diff != 0) return diff;
                                  }
                                  selfptr += 32;
                                  otherptr += 32;
                              }
                              return int256(len) - int256(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,
                              uint256 offset,
                              bytes memory other,
                              uint256 otherOffset,
                              uint256 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,
                              uint256 offset,
                              bytes memory other,
                              uint256 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,
                              uint256 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,
                              uint256 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,
                              uint256 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,
                              uint256 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,
                              uint256 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,
                              uint256 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,
                              uint256 idx,
                              uint256 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(uint256 dest, uint256 src, uint256 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
                              unchecked {
                                  uint256 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,
                              uint256 offset,
                              uint256 len
                          ) internal pure returns (bytes memory) {
                              require(offset + len <= self.length);
                              bytes memory ret = new bytes(len);
                              uint256 dest;
                              uint256 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,
                              uint256 off,
                              uint256 len
                          ) internal pure returns (bytes32) {
                              require(len <= 52);
                              uint256 ret = 0;
                              uint8 decoded;
                              for (uint256 i = 0; i < len; i++) {
                                  bytes1 char = self[off + i];
                                  require(char >= 0x30 && char <= 0x7A);
                                  decoded = uint8(base32HexTable[uint256(uint8(char)) - 0x30]);
                                  require(decoded <= 0x20);
                                  if (i == len - 1) {
                                      break;
                                  }
                                  ret = (ret << 5) | decoded;
                              }
                              uint256 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));
                          }
                          /**
                           * @dev Finds the first occurrence of the byte `needle` in `self`.
                           * @param self The string to search
                           * @param off The offset to start searching at
                           * @param len The number of bytes to search
                           * @param needle The byte to search for
                           * @return The offset of `needle` in `self`, or 2**256-1 if it was not found.
                           */
                          function find(
                              bytes memory self,
                              uint256 off,
                              uint256 len,
                              bytes1 needle
                          ) internal pure returns (uint256) {
                              for (uint256 idx = off; idx < off + len; idx++) {
                                  if (self[idx] == needle) {
                                      return idx;
                                  }
                              }
                              return type(uint256).max;
                          }
                      }
                      pragma solidity ^0.8.4;
                      import "./BytesUtils.sol";
                      import "@ensdomains/buffer/contracts/Buffer.sol";
                      /**
                       * @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,
                              uint256 offset
                          ) internal pure returns (uint256) {
                              uint256 idx = offset;
                              while (true) {
                                  assert(idx < self.length);
                                  uint256 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 ret The name.
                           */
                          function readName(
                              bytes memory self,
                              uint256 offset
                          ) internal pure returns (bytes memory ret) {
                              uint256 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,
                              uint256 offset
                          ) internal pure returns (uint256) {
                              uint256 count = 0;
                              while (true) {
                                  assert(offset < self.length);
                                  uint256 labelLen = self.readUint8(offset);
                                  offset += labelLen + 1;
                                  if (labelLen == 0) {
                                      break;
                                  }
                                  count += 1;
                              }
                              return count;
                          }
                          uint256 constant RRSIG_TYPE = 0;
                          uint256 constant RRSIG_ALGORITHM = 2;
                          uint256 constant RRSIG_LABELS = 3;
                          uint256 constant RRSIG_TTL = 4;
                          uint256 constant RRSIG_EXPIRATION = 8;
                          uint256 constant RRSIG_INCEPTION = 12;
                          uint256 constant RRSIG_KEY_TAG = 16;
                          uint256 constant RRSIG_SIGNER_NAME = 18;
                          struct SignedSet {
                              uint16 typeCovered;
                              uint8 algorithm;
                              uint8 labels;
                              uint32 ttl;
                              uint32 expiration;
                              uint32 inception;
                              uint16 keytag;
                              bytes signerName;
                              bytes data;
                              bytes name;
                          }
                          function readSignedSet(
                              bytes memory data
                          ) internal pure returns (SignedSet memory self) {
                              self.typeCovered = data.readUint16(RRSIG_TYPE);
                              self.algorithm = data.readUint8(RRSIG_ALGORITHM);
                              self.labels = data.readUint8(RRSIG_LABELS);
                              self.ttl = data.readUint32(RRSIG_TTL);
                              self.expiration = data.readUint32(RRSIG_EXPIRATION);
                              self.inception = data.readUint32(RRSIG_INCEPTION);
                              self.keytag = data.readUint16(RRSIG_KEY_TAG);
                              self.signerName = readName(data, RRSIG_SIGNER_NAME);
                              self.data = data.substring(
                                  RRSIG_SIGNER_NAME + self.signerName.length,
                                  data.length - RRSIG_SIGNER_NAME - self.signerName.length
                              );
                          }
                          function rrs(
                              SignedSet memory rrset
                          ) internal pure returns (RRIterator memory) {
                              return iterateRRs(rrset.data, 0);
                          }
                          /**
                           * @dev An iterator over resource records.
                           */
                          struct RRIterator {
                              bytes data;
                              uint256 offset;
                              uint16 dnstype;
                              uint16 class;
                              uint32 ttl;
                              uint256 rdataOffset;
                              uint256 nextOffset;
                          }
                          /**
                           * @dev Begins iterating over resource records.
                           * @param self The byte string to read from.
                           * @param offset The offset to start reading at.
                           * @return ret An iterator object.
                           */
                          function iterateRRs(
                              bytes memory self,
                              uint256 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
                              uint256 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
                              uint256 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
                                  );
                          }
                          uint256 constant DNSKEY_FLAGS = 0;
                          uint256 constant DNSKEY_PROTOCOL = 2;
                          uint256 constant DNSKEY_ALGORITHM = 3;
                          uint256 constant DNSKEY_PUBKEY = 4;
                          struct DNSKEY {
                              uint16 flags;
                              uint8 protocol;
                              uint8 algorithm;
                              bytes publicKey;
                          }
                          function readDNSKEY(
                              bytes memory data,
                              uint256 offset,
                              uint256 length
                          ) internal pure returns (DNSKEY memory self) {
                              self.flags = data.readUint16(offset + DNSKEY_FLAGS);
                              self.protocol = data.readUint8(offset + DNSKEY_PROTOCOL);
                              self.algorithm = data.readUint8(offset + DNSKEY_ALGORITHM);
                              self.publicKey = data.substring(
                                  offset + DNSKEY_PUBKEY,
                                  length - DNSKEY_PUBKEY
                              );
                          }
                          uint256 constant DS_KEY_TAG = 0;
                          uint256 constant DS_ALGORITHM = 2;
                          uint256 constant DS_DIGEST_TYPE = 3;
                          uint256 constant DS_DIGEST = 4;
                          struct DS {
                              uint16 keytag;
                              uint8 algorithm;
                              uint8 digestType;
                              bytes digest;
                          }
                          function readDS(
                              bytes memory data,
                              uint256 offset,
                              uint256 length
                          ) internal pure returns (DS memory self) {
                              self.keytag = data.readUint16(offset + DS_KEY_TAG);
                              self.algorithm = data.readUint8(offset + DS_ALGORITHM);
                              self.digestType = data.readUint8(offset + DS_DIGEST_TYPE);
                              self.digest = data.substring(offset + DS_DIGEST, length - DS_DIGEST);
                          }
                          function isSubdomainOf(
                              bytes memory self,
                              bytes memory other
                          ) internal pure returns (bool) {
                              uint256 off = 0;
                              uint256 counts = labelCount(self, 0);
                              uint256 othercounts = labelCount(other, 0);
                              while (counts > othercounts) {
                                  off = progress(self, off);
                                  counts--;
                              }
                              return self.equals(off, other, 0);
                          }
                          function compareNames(
                              bytes memory self,
                              bytes memory other
                          ) internal pure returns (int256) {
                              if (self.equals(other)) {
                                  return 0;
                              }
                              uint256 off;
                              uint256 otheroff;
                              uint256 prevoff;
                              uint256 otherprevoff;
                              uint256 counts = labelCount(self, 0);
                              uint256 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)
                                  );
                          }
                          /**
                           * @dev Compares two serial numbers using RFC1982 serial number math.
                           */
                          function serialNumberGte(
                              uint32 i1,
                              uint32 i2
                          ) internal pure returns (bool) {
                              unchecked {
                                  return int32(i1) - int32(i2) >= 0;
                              }
                          }
                          function progress(
                              bytes memory body,
                              uint256 off
                          ) internal pure returns (uint256) {
                              return off + 1 + body.readUint8(off);
                          }
                          /**
                           * @dev Computes the keytag for a chunk of data.
                           * @param data The data to compute a keytag for.
                           * @return The computed key tag.
                           */
                          function computeKeytag(bytes memory data) internal pure returns (uint16) {
                              /* This function probably deserves some explanation.
                               * The DNSSEC keytag function is a checksum that relies on summing up individual bytes
                               * from the input string, with some mild bitshifting. Here's a Naive solidity implementation:
                               *
                               *     function computeKeytag(bytes memory data) internal pure returns (uint16) {
                               *         uint ac;
                               *         for (uint i = 0; i < data.length; i++) {
                               *             ac += i & 1 == 0 ? uint16(data.readUint8(i)) << 8 : data.readUint8(i);
                               *         }
                               *         return uint16(ac + (ac >> 16));
                               *     }
                               *
                               * The EVM, with its 256 bit words, is exceedingly inefficient at doing byte-by-byte operations;
                               * the code above, on reasonable length inputs, consumes over 100k gas. But we can make the EVM's
                               * large words work in our favour.
                               *
                               * The code below works by treating the input as a series of 256 bit words. It first masks out
                               * even and odd bytes from each input word, adding them to two separate accumulators `ac1` and `ac2`.
                               * The bytes are separated by empty bytes, so as long as no individual sum exceeds 2^16-1, we're
                               * effectively summing 16 different numbers with each EVM ADD opcode.
                               *
                               * Once it's added up all the inputs, it has to add all the 16 bit values in `ac1` and `ac2` together.
                               * It does this using the same trick - mask out every other value, shift to align them, add them together.
                               * After the first addition on both accumulators, there's enough room to add the two accumulators together,
                               * and the remaining sums can be done just on ac1.
                               */
                              unchecked {
                                  require(data.length <= 8192, "Long keys not permitted");
                                  uint256 ac1;
                                  uint256 ac2;
                                  for (uint256 i = 0; i < data.length + 31; i += 32) {
                                      uint256 word;
                                      assembly {
                                          word := mload(add(add(data, 32), i))
                                      }
                                      if (i + 32 > data.length) {
                                          uint256 unused = 256 - (data.length - i) * 8;
                                          word = (word >> unused) << unused;
                                      }
                                      ac1 +=
                                          (word &
                                              0xFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00) >>
                                          8;
                                      ac2 += (word &
                                          0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF);
                                  }
                                  ac1 =
                                      (ac1 &
                                          0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF) +
                                      ((ac1 &
                                          0xFFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000) >>
                                          16);
                                  ac2 =
                                      (ac2 &
                                          0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF) +
                                      ((ac2 &
                                          0xFFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000) >>
                                          16);
                                  ac1 = (ac1 << 8) + ac2;
                                  ac1 =
                                      (ac1 &
                                          0x00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF) +
                                      ((ac1 &
                                          0xFFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000) >>
                                          32);
                                  ac1 =
                                      (ac1 &
                                          0x0000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF) +
                                      ((ac1 &
                                          0xFFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF0000000000000000) >>
                                          64);
                                  ac1 =
                                      (ac1 &
                                          0x00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) +
                                      (ac1 >> 128);
                                  ac1 += (ac1 >> 16) & 0xFFFF;
                                  return uint16(ac1);
                              }
                          }
                      }
                      import "../registry/ENS.sol";
                      import "./IBaseRegistrar.sol";
                      import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
                      interface IBaseRegistrar is IERC721 {
                          event ControllerAdded(address indexed controller);
                          event ControllerRemoved(address indexed controller);
                          event NameMigrated(
                              uint256 indexed id,
                              address indexed owner,
                              uint256 expires
                          );
                          event NameRegistered(
                              uint256 indexed id,
                              address indexed owner,
                              uint256 expires
                          );
                          event NameRenewed(uint256 indexed id, uint256 expires);
                          // 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 (uint256);
                          // Returns true iff the specified name is available for registration.
                          function available(uint256 id) external view returns (bool);
                          /**
                           * @dev Register a name.
                           */
                          function register(
                              uint256 id,
                              address owner,
                              uint256 duration
                          ) external returns (uint256);
                          function renew(uint256 id, uint256 duration) external returns (uint256);
                          /**
                           * @dev Reclaim ownership of a name in ENS, if you own it in the registrar.
                           */
                          function reclaim(uint256 id, address owner) external;
                      }
                      pragma solidity >=0.8.4;
                      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);
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.8.4;
                      interface IMulticallable {
                          function multicall(
                              bytes[] calldata data
                          ) external returns (bytes[] memory results);
                          function multicallWithNodeCheck(
                              bytes32,
                              bytes[] calldata data
                          ) external returns (bytes[] memory results);
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.8.4;
                      import "./IMulticallable.sol";
                      import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
                      abstract contract Multicallable is IMulticallable, ERC165 {
                          function _multicall(
                              bytes32 nodehash,
                              bytes[] calldata data
                          ) internal returns (bytes[] memory results) {
                              results = new bytes[](data.length);
                              for (uint256 i = 0; i < data.length; i++) {
                                  if (nodehash != bytes32(0)) {
                                      bytes32 txNamehash = bytes32(data[i][4:36]);
                                      require(
                                          txNamehash == nodehash,
                                          "multicall: All records must have a matching namehash"
                                      );
                                  }
                                  (bool success, bytes memory result) = address(this).delegatecall(
                                      data[i]
                                  );
                                  require(success);
                                  results[i] = result;
                              }
                              return results;
                          }
                          // This function provides an extra security check when called
                          // from priviledged contracts (such as EthRegistrarController)
                          // that can set records on behalf of the node owners
                          function multicallWithNodeCheck(
                              bytes32 nodehash,
                              bytes[] calldata data
                          ) external returns (bytes[] memory results) {
                              return _multicall(nodehash, data);
                          }
                          function multicall(
                              bytes[] calldata data
                          ) public override returns (bytes[] memory results) {
                              return _multicall(bytes32(0), data);
                          }
                          function supportsInterface(
                              bytes4 interfaceID
                          ) public view virtual override returns (bool) {
                              return
                                  interfaceID == type(IMulticallable).interfaceId ||
                                  super.supportsInterface(interfaceID);
                          }
                      }
                      //SPDX-License-Identifier: MIT
                      pragma solidity >=0.8.17 <0.9.0;
                      import "../registry/ENS.sol";
                      import "./profiles/ABIResolver.sol";
                      import "./profiles/AddrResolver.sol";
                      import "./profiles/ContentHashResolver.sol";
                      import "./profiles/DNSResolver.sol";
                      import "./profiles/InterfaceResolver.sol";
                      import "./profiles/NameResolver.sol";
                      import "./profiles/PubkeyResolver.sol";
                      import "./profiles/TextResolver.sol";
                      import "./Multicallable.sol";
                      import {ReverseClaimer} from "../reverseRegistrar/ReverseClaimer.sol";
                      import {INameWrapper} from "../wrapper/INameWrapper.sol";
                      /**
                       * A simple resolver anyone can use; only allows the owner of a node to set its
                       * address.
                       */
                      contract PublicResolver is
                          Multicallable,
                          ABIResolver,
                          AddrResolver,
                          ContentHashResolver,
                          DNSResolver,
                          InterfaceResolver,
                          NameResolver,
                          PubkeyResolver,
                          TextResolver,
                          ReverseClaimer
                      {
                          ENS immutable ens;
                          INameWrapper immutable nameWrapper;
                          address immutable trustedETHController;
                          address immutable trustedReverseRegistrar;
                          /**
                           * A mapping of operators. An address that is authorised for an address
                           * may make any changes to the name that the owner could, but may not update
                           * the set of authorisations.
                           * (owner, operator) => approved
                           */
                          mapping(address => mapping(address => bool)) private _operatorApprovals;
                          /**
                           * A mapping of delegates. A delegate that is authorised by an owner
                           * for a name may make changes to the name's resolver, but may not update
                           * the set of token approvals.
                           * (owner, name, delegate) => approved
                           */
                          mapping(address => mapping(bytes32 => mapping(address => bool)))
                              private _tokenApprovals;
                          // Logged when an operator is added or removed.
                          event ApprovalForAll(
                              address indexed owner,
                              address indexed operator,
                              bool approved
                          );
                          // Logged when a delegate is approved or  an approval is revoked.
                          event Approved(
                              address owner,
                              bytes32 indexed node,
                              address indexed delegate,
                              bool indexed approved
                          );
                          constructor(
                              ENS _ens,
                              INameWrapper wrapperAddress,
                              address _trustedETHController,
                              address _trustedReverseRegistrar
                          ) ReverseClaimer(_ens, msg.sender) {
                              ens = _ens;
                              nameWrapper = wrapperAddress;
                              trustedETHController = _trustedETHController;
                              trustedReverseRegistrar = _trustedReverseRegistrar;
                          }
                          /**
                           * @dev See {IERC1155-setApprovalForAll}.
                           */
                          function setApprovalForAll(address operator, bool approved) external {
                              require(
                                  msg.sender != operator,
                                  "ERC1155: setting approval status for self"
                              );
                              _operatorApprovals[msg.sender][operator] = approved;
                              emit ApprovalForAll(msg.sender, operator, approved);
                          }
                          /**
                           * @dev See {IERC1155-isApprovedForAll}.
                           */
                          function isApprovedForAll(
                              address account,
                              address operator
                          ) public view returns (bool) {
                              return _operatorApprovals[account][operator];
                          }
                          /**
                           * @dev Approve a delegate to be able to updated records on a node.
                           */
                          function approve(bytes32 node, address delegate, bool approved) external {
                              require(msg.sender != delegate, "Setting delegate status for self");
                              _tokenApprovals[msg.sender][node][delegate] = approved;
                              emit Approved(msg.sender, node, delegate, approved);
                          }
                          /**
                           * @dev Check to see if the delegate has been approved by the owner for the node.
                           */
                          function isApprovedFor(
                              address owner,
                              bytes32 node,
                              address delegate
                          ) public view returns (bool) {
                              return _tokenApprovals[owner][node][delegate];
                          }
                          function isAuthorised(bytes32 node) internal view override returns (bool) {
                              if (
                                  msg.sender == trustedETHController ||
                                  msg.sender == trustedReverseRegistrar
                              ) {
                                  return true;
                              }
                              address owner = ens.owner(node);
                              if (owner == address(nameWrapper)) {
                                  owner = nameWrapper.ownerOf(uint256(node));
                              }
                              return
                                  owner == msg.sender ||
                                  isApprovedForAll(owner, msg.sender) ||
                                  isApprovedFor(owner, node, msg.sender);
                          }
                          function supportsInterface(
                              bytes4 interfaceID
                          )
                              public
                              view
                              override(
                                  Multicallable,
                                  ABIResolver,
                                  AddrResolver,
                                  ContentHashResolver,
                                  DNSResolver,
                                  InterfaceResolver,
                                  NameResolver,
                                  PubkeyResolver,
                                  TextResolver
                              )
                              returns (bool)
                          {
                              return super.supportsInterface(interfaceID);
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity >=0.8.4;
                      import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
                      import "./profiles/IVersionableResolver.sol";
                      abstract contract ResolverBase is ERC165, IVersionableResolver {
                          mapping(bytes32 => uint64) public recordVersions;
                          function isAuthorised(bytes32 node) internal view virtual returns (bool);
                          modifier authorised(bytes32 node) {
                              require(isAuthorised(node));
                              _;
                          }
                          /**
                           * Increments the record version 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.
                           */
                          function clearRecords(bytes32 node) public virtual authorised(node) {
                              recordVersions[node]++;
                              emit VersionChanged(node, recordVersions[node]);
                          }
                          function supportsInterface(
                              bytes4 interfaceID
                          ) public view virtual override returns (bool) {
                              return
                                  interfaceID == type(IVersionableResolver).interfaceId ||
                                  super.supportsInterface(interfaceID);
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity >=0.8.4;
                      import "./IABIResolver.sol";
                      import "../ResolverBase.sol";
                      abstract contract ABIResolver is IABIResolver, ResolverBase {
                          mapping(uint64 => mapping(bytes32 => mapping(uint256 => bytes))) versionable_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 virtual authorised(node) {
                              // Content types must be powers of 2
                              require(((contentType - 1) & contentType) == 0);
                              versionable_abis[recordVersions[node]][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 virtual override returns (uint256, bytes memory) {
                              mapping(uint256 => bytes) storage abiset = versionable_abis[
                                  recordVersions[node]
                              ][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 view virtual override returns (bool) {
                              return
                                  interfaceID == type(IABIResolver).interfaceId ||
                                  super.supportsInterface(interfaceID);
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity >=0.8.4;
                      import "../ResolverBase.sol";
                      import "./IAddrResolver.sol";
                      import "./IAddressResolver.sol";
                      abstract contract AddrResolver is
                          IAddrResolver,
                          IAddressResolver,
                          ResolverBase
                      {
                          uint256 private constant COIN_TYPE_ETH = 60;
                          mapping(uint64 => mapping(bytes32 => mapping(uint256 => bytes))) versionable_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 virtual 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 virtual override returns (address payable) {
                              bytes memory a = addr(node, COIN_TYPE_ETH);
                              if (a.length == 0) {
                                  return payable(0);
                              }
                              return bytesToAddress(a);
                          }
                          function setAddr(
                              bytes32 node,
                              uint256 coinType,
                              bytes memory a
                          ) public virtual authorised(node) {
                              emit AddressChanged(node, coinType, a);
                              if (coinType == COIN_TYPE_ETH) {
                                  emit AddrChanged(node, bytesToAddress(a));
                              }
                              versionable_addresses[recordVersions[node]][node][coinType] = a;
                          }
                          function addr(
                              bytes32 node,
                              uint256 coinType
                          ) public view virtual override returns (bytes memory) {
                              return versionable_addresses[recordVersions[node]][node][coinType];
                          }
                          function supportsInterface(
                              bytes4 interfaceID
                          ) public view virtual override returns (bool) {
                              return
                                  interfaceID == type(IAddrResolver).interfaceId ||
                                  interfaceID == type(IAddressResolver).interfaceId ||
                                  super.supportsInterface(interfaceID);
                          }
                          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)))
                              }
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity >=0.8.4;
                      import "../ResolverBase.sol";
                      import "./IContentHashResolver.sol";
                      abstract contract ContentHashResolver is IContentHashResolver, ResolverBase {
                          mapping(uint64 => mapping(bytes32 => bytes)) versionable_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 virtual authorised(node) {
                              versionable_hashes[recordVersions[node]][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 virtual override returns (bytes memory) {
                              return versionable_hashes[recordVersions[node]][node];
                          }
                          function supportsInterface(
                              bytes4 interfaceID
                          ) public view virtual override returns (bool) {
                              return
                                  interfaceID == type(IContentHashResolver).interfaceId ||
                                  super.supportsInterface(interfaceID);
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity >=0.8.4;
                      import "../ResolverBase.sol";
                      import "../../dnssec-oracle/RRUtils.sol";
                      import "./IDNSRecordResolver.sol";
                      import "./IDNSZoneResolver.sol";
                      abstract contract DNSResolver is
                          IDNSRecordResolver,
                          IDNSZoneResolver,
                          ResolverBase
                      {
                          using RRUtils for *;
                          using BytesUtils for bytes;
                          // Zone hashes for the domains.
                          // A zone hash is an EIP-1577 content hash in binary format that should point to a
                          // resource containing a single zonefile.
                          // node => contenthash
                          mapping(uint64 => mapping(bytes32 => bytes)) private versionable_zonehashes;
                          // The records themselves.  Stored as binary RRSETs
                          // node => version => name => resource => data
                          mapping(uint64 => mapping(bytes32 => mapping(bytes32 => mapping(uint16 => bytes))))
                              private versionable_records;
                          // Count of number of entries for a given name.  Required for DNS resolvers
                          // when resolving wildcards.
                          // node => version => name => number of records
                          mapping(uint64 => mapping(bytes32 => mapping(bytes32 => uint16)))
                              private versionable_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 virtual authorised(node) {
                              uint16 resource = 0;
                              uint256 offset = 0;
                              bytes memory name;
                              bytes memory value;
                              bytes32 nameHash;
                              uint64 version = recordVersions[node];
                              // 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,
                                              version
                                          );
                                          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,
                                      version
                                  );
                              }
                          }
                          /**
                           * 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 virtual override returns (bytes memory) {
                              return versionable_records[recordVersions[node]][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 virtual returns (bool) {
                              return (versionable_nameEntriesCount[recordVersions[node]][node][
                                  name
                              ] != 0);
                          }
                          /**
                           * setZonehash sets the hash for the zone.
                           * May only be called by the owner of that node in the ENS registry.
                           * @param node The node to update.
                           * @param hash The zonehash to set
                           */
                          function setZonehash(
                              bytes32 node,
                              bytes calldata hash
                          ) external virtual authorised(node) {
                              uint64 currentRecordVersion = recordVersions[node];
                              bytes memory oldhash = versionable_zonehashes[currentRecordVersion][
                                  node
                              ];
                              versionable_zonehashes[currentRecordVersion][node] = hash;
                              emit DNSZonehashChanged(node, oldhash, hash);
                          }
                          /**
                           * zonehash obtains the hash for the zone.
                           * @param node The ENS node to query.
                           * @return The associated contenthash.
                           */
                          function zonehash(
                              bytes32 node
                          ) external view virtual override returns (bytes memory) {
                              return versionable_zonehashes[recordVersions[node]][node];
                          }
                          function supportsInterface(
                              bytes4 interfaceID
                          ) public view virtual override returns (bool) {
                              return
                                  interfaceID == type(IDNSRecordResolver).interfaceId ||
                                  interfaceID == type(IDNSZoneResolver).interfaceId ||
                                  super.supportsInterface(interfaceID);
                          }
                          function setDNSRRSet(
                              bytes32 node,
                              bytes memory name,
                              uint16 resource,
                              bytes memory data,
                              uint256 offset,
                              uint256 size,
                              bool deleteRecord,
                              uint64 version
                          ) private {
                              bytes32 nameHash = keccak256(name);
                              bytes memory rrData = data.substring(offset, size);
                              if (deleteRecord) {
                                  if (
                                      versionable_records[version][node][nameHash][resource].length !=
                                      0
                                  ) {
                                      versionable_nameEntriesCount[version][node][nameHash]--;
                                  }
                                  delete (versionable_records[version][node][nameHash][resource]);
                                  emit DNSRecordDeleted(node, name, resource);
                              } else {
                                  if (
                                      versionable_records[version][node][nameHash][resource].length ==
                                      0
                                  ) {
                                      versionable_nameEntriesCount[version][node][nameHash]++;
                                  }
                                  versionable_records[version][node][nameHash][resource] = rrData;
                                  emit DNSRecordChanged(node, name, resource, rrData);
                              }
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity >=0.8.4;
                      interface IABIResolver {
                          event ABIChanged(bytes32 indexed node, uint256 indexed 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);
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity >=0.8.4;
                      /**
                       * Interface for the legacy (ETH-only) addr function.
                       */
                      interface IAddrResolver {
                          event AddrChanged(bytes32 indexed node, address a);
                          /**
                           * Returns the address associated with an ENS node.
                           * @param node The ENS node to query.
                           * @return The associated address.
                           */
                          function addr(bytes32 node) external view returns (address payable);
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity >=0.8.4;
                      /**
                       * Interface for the new (multicoin) addr function.
                       */
                      interface IAddressResolver {
                          event AddressChanged(
                              bytes32 indexed node,
                              uint256 coinType,
                              bytes newAddress
                          );
                          function addr(
                              bytes32 node,
                              uint256 coinType
                          ) external view returns (bytes memory);
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity >=0.8.4;
                      interface IContentHashResolver {
                          event ContenthashChanged(bytes32 indexed node, bytes 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);
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity >=0.8.4;
                      interface IDNSRecordResolver {
                          // 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);
                          /**
                           * 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
                          ) external view returns (bytes memory);
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity >=0.8.4;
                      interface IDNSZoneResolver {
                          // DNSZonehashChanged is emitted whenever a given node's zone hash is updated.
                          event DNSZonehashChanged(
                              bytes32 indexed node,
                              bytes lastzonehash,
                              bytes zonehash
                          );
                          /**
                           * zonehash obtains the hash for the zone.
                           * @param node The ENS node to query.
                           * @return The associated contenthash.
                           */
                          function zonehash(bytes32 node) external view returns (bytes memory);
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity >=0.8.4;
                      interface IInterfaceResolver {
                          event InterfaceChanged(
                              bytes32 indexed node,
                              bytes4 indexed interfaceID,
                              address 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 EIP165 and returns `true` for the specified interfaceID, its address
                           * will be returned.
                           * @param node The ENS node to query.
                           * @param interfaceID The EIP 165 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);
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity >=0.8.4;
                      interface INameResolver {
                          event NameChanged(bytes32 indexed node, string 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);
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity >=0.8.4;
                      interface IPubkeyResolver {
                          event PubkeyChanged(bytes32 indexed node, bytes32 x, bytes32 y);
                          /**
                           * Returns the SECP256k1 public key associated with an ENS node.
                           * Defined in EIP 619.
                           * @param node The ENS node to query
                           * @return x The X coordinate of the curve point for the public key.
                           * @return y The Y coordinate of the curve point for the public key.
                           */
                          function pubkey(bytes32 node) external view returns (bytes32 x, bytes32 y);
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity >=0.8.4;
                      interface ITextResolver {
                          event TextChanged(
                              bytes32 indexed node,
                              string indexed indexedKey,
                              string key,
                              string value
                          );
                          /**
                           * 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);
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity >=0.8.4;
                      interface IVersionableResolver {
                          event VersionChanged(bytes32 indexed node, uint64 newVersion);
                          function recordVersions(bytes32 node) external view returns (uint64);
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity >=0.8.4;
                      import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
                      import "../ResolverBase.sol";
                      import "./AddrResolver.sol";
                      import "./IInterfaceResolver.sol";
                      abstract contract InterfaceResolver is IInterfaceResolver, AddrResolver {
                          mapping(uint64 => mapping(bytes32 => mapping(bytes4 => address))) versionable_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 165 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 virtual authorised(node) {
                              versionable_interfaces[recordVersions[node]][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 EIP165 and returns `true` for the specified interfaceID, its address
                           * will be returned.
                           * @param node The ENS node to query.
                           * @param interfaceID The EIP 165 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 virtual override returns (address) {
                              address implementer = versionable_interfaces[recordVersions[node]][
                                  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)",
                                      type(IERC165).interfaceId
                                  )
                              );
                              if (!success || returnData.length < 32 || returnData[31] == 0) {
                                  // EIP 165 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 view virtual override returns (bool) {
                              return
                                  interfaceID == type(IInterfaceResolver).interfaceId ||
                                  super.supportsInterface(interfaceID);
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity >=0.8.4;
                      import "../ResolverBase.sol";
                      import "./INameResolver.sol";
                      abstract contract NameResolver is INameResolver, ResolverBase {
                          mapping(uint64 => mapping(bytes32 => string)) versionable_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.
                           */
                          function setName(
                              bytes32 node,
                              string calldata newName
                          ) external virtual authorised(node) {
                              versionable_names[recordVersions[node]][node] = newName;
                              emit NameChanged(node, newName);
                          }
                          /**
                           * 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 virtual override returns (string memory) {
                              return versionable_names[recordVersions[node]][node];
                          }
                          function supportsInterface(
                              bytes4 interfaceID
                          ) public view virtual override returns (bool) {
                              return
                                  interfaceID == type(INameResolver).interfaceId ||
                                  super.supportsInterface(interfaceID);
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity >=0.8.4;
                      import "../ResolverBase.sol";
                      import "./IPubkeyResolver.sol";
                      abstract contract PubkeyResolver is IPubkeyResolver, ResolverBase {
                          struct PublicKey {
                              bytes32 x;
                              bytes32 y;
                          }
                          mapping(uint64 => mapping(bytes32 => PublicKey)) versionable_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 virtual authorised(node) {
                              versionable_pubkeys[recordVersions[node]][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 The X coordinate of the curve point for the public key.
                           * @return y The Y coordinate of the curve point for the public key.
                           */
                          function pubkey(
                              bytes32 node
                          ) external view virtual override returns (bytes32 x, bytes32 y) {
                              uint64 currentRecordVersion = recordVersions[node];
                              return (
                                  versionable_pubkeys[currentRecordVersion][node].x,
                                  versionable_pubkeys[currentRecordVersion][node].y
                              );
                          }
                          function supportsInterface(
                              bytes4 interfaceID
                          ) public view virtual override returns (bool) {
                              return
                                  interfaceID == type(IPubkeyResolver).interfaceId ||
                                  super.supportsInterface(interfaceID);
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity >=0.8.4;
                      import "../ResolverBase.sol";
                      import "./ITextResolver.sol";
                      abstract contract TextResolver is ITextResolver, ResolverBase {
                          mapping(uint64 => mapping(bytes32 => mapping(string => string))) versionable_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 virtual authorised(node) {
                              versionable_texts[recordVersions[node]][node][key] = value;
                              emit TextChanged(node, key, key, value);
                          }
                          /**
                           * 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 virtual override returns (string memory) {
                              return versionable_texts[recordVersions[node]][node][key];
                          }
                          function supportsInterface(
                              bytes4 interfaceID
                          ) public view virtual override returns (bool) {
                              return
                                  interfaceID == type(ITextResolver).interfaceId ||
                                  super.supportsInterface(interfaceID);
                          }
                      }
                      pragma solidity >=0.8.4;
                      interface IReverseRegistrar {
                          function setDefaultResolver(address resolver) external;
                          function claim(address owner) external returns (bytes32);
                          function claimForAddr(
                              address addr,
                              address owner,
                              address resolver
                          ) external returns (bytes32);
                          function claimWithResolver(
                              address owner,
                              address resolver
                          ) external returns (bytes32);
                          function setName(string memory name) external returns (bytes32);
                          function setNameForAddr(
                              address addr,
                              address owner,
                              address resolver,
                              string memory name
                          ) external returns (bytes32);
                          function node(address addr) external pure returns (bytes32);
                      }
                      //SPDX-License-Identifier: MIT
                      pragma solidity >=0.8.17 <0.9.0;
                      import {ENS} from "../registry/ENS.sol";
                      import {IReverseRegistrar} from "../reverseRegistrar/IReverseRegistrar.sol";
                      contract ReverseClaimer {
                          bytes32 constant ADDR_REVERSE_NODE =
                              0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2;
                          constructor(ENS ens, address claimant) {
                              IReverseRegistrar reverseRegistrar = IReverseRegistrar(
                                  ens.owner(ADDR_REVERSE_NODE)
                              );
                              reverseRegistrar.claim(claimant);
                          }
                      }
                      //SPDX-License-Identifier: MIT
                      pragma solidity ~0.8.17;
                      interface IMetadataService {
                          function uri(uint256) external view returns (string memory);
                      }
                      //SPDX-License-Identifier: MIT
                      pragma solidity ~0.8.17;
                      import "../registry/ENS.sol";
                      import "../ethregistrar/IBaseRegistrar.sol";
                      import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
                      import "./IMetadataService.sol";
                      import "./INameWrapperUpgrade.sol";
                      uint32 constant CANNOT_UNWRAP = 1;
                      uint32 constant CANNOT_BURN_FUSES = 2;
                      uint32 constant CANNOT_TRANSFER = 4;
                      uint32 constant CANNOT_SET_RESOLVER = 8;
                      uint32 constant CANNOT_SET_TTL = 16;
                      uint32 constant CANNOT_CREATE_SUBDOMAIN = 32;
                      uint32 constant CANNOT_APPROVE = 64;
                      //uint16 reserved for parent controlled fuses from bit 17 to bit 32
                      uint32 constant PARENT_CANNOT_CONTROL = 1 << 16;
                      uint32 constant IS_DOT_ETH = 1 << 17;
                      uint32 constant CAN_EXTEND_EXPIRY = 1 << 18;
                      uint32 constant CAN_DO_EVERYTHING = 0;
                      uint32 constant PARENT_CONTROLLED_FUSES = 0xFFFF0000;
                      // all fuses apart from IS_DOT_ETH
                      uint32 constant USER_SETTABLE_FUSES = 0xFFFDFFFF;
                      interface INameWrapper is IERC1155 {
                          event NameWrapped(
                              bytes32 indexed node,
                              bytes name,
                              address owner,
                              uint32 fuses,
                              uint64 expiry
                          );
                          event NameUnwrapped(bytes32 indexed node, address owner);
                          event FusesSet(bytes32 indexed node, uint32 fuses);
                          event ExpiryExtended(bytes32 indexed node, uint64 expiry);
                          function ens() external view returns (ENS);
                          function registrar() external view returns (IBaseRegistrar);
                          function metadataService() external view returns (IMetadataService);
                          function names(bytes32) external view returns (bytes memory);
                          function name() external view returns (string memory);
                          function upgradeContract() external view returns (INameWrapperUpgrade);
                          function supportsInterface(bytes4 interfaceID) external view returns (bool);
                          function wrap(
                              bytes calldata name,
                              address wrappedOwner,
                              address resolver
                          ) external;
                          function wrapETH2LD(
                              string calldata label,
                              address wrappedOwner,
                              uint16 ownerControlledFuses,
                              address resolver
                          ) external returns (uint64 expires);
                          function registerAndWrapETH2LD(
                              string calldata label,
                              address wrappedOwner,
                              uint256 duration,
                              address resolver,
                              uint16 ownerControlledFuses
                          ) external returns (uint256 registrarExpiry);
                          function renew(
                              uint256 labelHash,
                              uint256 duration
                          ) external returns (uint256 expires);
                          function unwrap(bytes32 node, bytes32 label, address owner) external;
                          function unwrapETH2LD(
                              bytes32 label,
                              address newRegistrant,
                              address newController
                          ) external;
                          function upgrade(bytes calldata name, bytes calldata extraData) external;
                          function setFuses(
                              bytes32 node,
                              uint16 ownerControlledFuses
                          ) external returns (uint32 newFuses);
                          function setChildFuses(
                              bytes32 parentNode,
                              bytes32 labelhash,
                              uint32 fuses,
                              uint64 expiry
                          ) external;
                          function setSubnodeRecord(
                              bytes32 node,
                              string calldata label,
                              address owner,
                              address resolver,
                              uint64 ttl,
                              uint32 fuses,
                              uint64 expiry
                          ) external returns (bytes32);
                          function setRecord(
                              bytes32 node,
                              address owner,
                              address resolver,
                              uint64 ttl
                          ) external;
                          function setSubnodeOwner(
                              bytes32 node,
                              string calldata label,
                              address newOwner,
                              uint32 fuses,
                              uint64 expiry
                          ) external returns (bytes32);
                          function extendExpiry(
                              bytes32 node,
                              bytes32 labelhash,
                              uint64 expiry
                          ) external returns (uint64);
                          function canModifyName(
                              bytes32 node,
                              address addr
                          ) external view returns (bool);
                          function setResolver(bytes32 node, address resolver) external;
                          function setTTL(bytes32 node, uint64 ttl) external;
                          function ownerOf(uint256 id) external view returns (address owner);
                          function approve(address to, uint256 tokenId) external;
                          function getApproved(uint256 tokenId) external view returns (address);
                          function getData(
                              uint256 id
                          ) external view returns (address, uint32, uint64);
                          function setMetadataService(IMetadataService _metadataService) external;
                          function uri(uint256 tokenId) external view returns (string memory);
                          function setUpgradeContract(INameWrapperUpgrade _upgradeAddress) external;
                          function allFusesBurned(
                              bytes32 node,
                              uint32 fuseMask
                          ) external view returns (bool);
                          function isWrapped(bytes32) external view returns (bool);
                          function isWrapped(bytes32, bytes32) external view returns (bool);
                      }
                      //SPDX-License-Identifier: MIT
                      pragma solidity ~0.8.17;
                      interface INameWrapperUpgrade {
                          function wrapFromUpgrade(
                              bytes calldata name,
                              address wrappedOwner,
                              uint32 fuses,
                              uint64 expiry,
                              address approved,
                              bytes calldata extraData
                          ) external;
                      }
                      

                      File 5 of 8: 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 6 of 8: ExponentialPremiumPriceOracle
                      // SPDX-License-Identifier: MIT
                      // OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)
                      pragma solidity ^0.8.0;
                      import "../utils/Context.sol";
                      /**
                       * @dev Contract module which provides a basic access control mechanism, where
                       * there is an account (an owner) that can be granted exclusive access to
                       * specific functions.
                       *
                       * By default, the owner account will be the one that deploys the contract. This
                       * can later be changed with {transferOwnership}.
                       *
                       * This module is used through inheritance. It will make available the modifier
                       * `onlyOwner`, which can be applied to your functions to restrict their use to
                       * the owner.
                       */
                      abstract contract Ownable is Context {
                          address private _owner;
                          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                          /**
                           * @dev Initializes the contract setting the deployer as the initial owner.
                           */
                          constructor() {
                              _transferOwnership(_msgSender());
                          }
                          /**
                           * @dev Returns the address of the current owner.
                           */
                          function owner() public view virtual returns (address) {
                              return _owner;
                          }
                          /**
                           * @dev Throws if called by any account other than the owner.
                           */
                          modifier onlyOwner() {
                              require(owner() == _msgSender(), "Ownable: caller is not the owner");
                              _;
                          }
                          /**
                           * @dev Leaves the contract without owner. It will not be possible to call
                           * `onlyOwner` functions anymore. Can only be called by the current owner.
                           *
                           * NOTE: Renouncing ownership will leave the contract without an owner,
                           * thereby removing any functionality that is only available to the owner.
                           */
                          function renounceOwnership() public virtual onlyOwner {
                              _transferOwnership(address(0));
                          }
                          /**
                           * @dev Transfers ownership of the contract to a new account (`newOwner`).
                           * Can only be called by the current owner.
                           */
                          function transferOwnership(address newOwner) public virtual onlyOwner {
                              require(newOwner != address(0), "Ownable: new owner is the zero address");
                              _transferOwnership(newOwner);
                          }
                          /**
                           * @dev Transfers ownership of the contract to a new account (`newOwner`).
                           * Internal function without access restriction.
                           */
                          function _transferOwnership(address newOwner) internal virtual {
                              address oldOwner = _owner;
                              _owner = newOwner;
                              emit OwnershipTransferred(oldOwner, newOwner);
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
                      pragma solidity ^0.8.0;
                      /**
                       * @dev Provides information about the current execution context, including the
                       * sender of the transaction and its data. While these are generally available
                       * via msg.sender and msg.data, they should not be accessed in such a direct
                       * manner, since when dealing with meta-transactions the account sending and
                       * paying for execution may not be the actual sender (as far as an application
                       * is concerned).
                       *
                       * This contract is only required for intermediate, library-like contracts.
                       */
                      abstract contract Context {
                          function _msgSender() internal view virtual returns (address) {
                              return msg.sender;
                          }
                          function _msgData() internal view virtual returns (bytes calldata) {
                              return msg.data;
                          }
                      }
                      pragma solidity >=0.8.4;
                      import "./SafeMath.sol";
                      import "./StablePriceOracle.sol";
                      contract ExponentialPremiumPriceOracle is StablePriceOracle {
                          uint256 constant GRACE_PERIOD = 90 days;
                          uint256 immutable startPremium;
                          uint256 immutable endValue;
                          constructor(
                              AggregatorInterface _usdOracle,
                              uint256[] memory _rentPrices,
                              uint256 _startPremium,
                              uint256 totalDays
                          ) StablePriceOracle(_usdOracle, _rentPrices) {
                              startPremium = _startPremium;
                              endValue = _startPremium >> totalDays;
                          }
                          uint256 constant PRECISION = 1e18;
                          uint256 constant bit1 = 999989423469314432; // 0.5 ^ 1/65536 * (10 ** 18)
                          uint256 constant bit2 = 999978847050491904; // 0.5 ^ 2/65536 * (10 ** 18)
                          uint256 constant bit3 = 999957694548431104;
                          uint256 constant bit4 = 999915390886613504;
                          uint256 constant bit5 = 999830788931929088;
                          uint256 constant bit6 = 999661606496243712;
                          uint256 constant bit7 = 999323327502650752;
                          uint256 constant bit8 = 998647112890970240;
                          uint256 constant bit9 = 997296056085470080;
                          uint256 constant bit10 = 994599423483633152;
                          uint256 constant bit11 = 989228013193975424;
                          uint256 constant bit12 = 978572062087700096;
                          uint256 constant bit13 = 957603280698573696;
                          uint256 constant bit14 = 917004043204671232;
                          uint256 constant bit15 = 840896415253714560;
                          uint256 constant bit16 = 707106781186547584;
                          /**
                           * @dev Returns the pricing premium in internal base units.
                           */
                          function _premium(
                              string memory,
                              uint256 expires,
                              uint256
                          ) internal view override returns (uint256) {
                              expires = expires + GRACE_PERIOD;
                              if (expires > block.timestamp) {
                                  return 0;
                              }
                              uint256 elapsed = block.timestamp - expires;
                              uint256 premium = decayedPremium(startPremium, elapsed);
                              if (premium >= endValue) {
                                  return premium - endValue;
                              }
                              return 0;
                          }
                          /**
                           * @dev Returns the premium price at current time elapsed
                           * @param startPremium starting price
                           * @param elapsed time past since expiry
                           */
                          function decayedPremium(uint256 startPremium, uint256 elapsed)
                              public
                              pure
                              returns (uint256)
                          {
                              uint256 daysPast = (elapsed * PRECISION) / 1 days;
                              uint256 intDays = daysPast / PRECISION;
                              uint256 premium = startPremium >> intDays;
                              uint256 partDay = (daysPast - intDays * PRECISION);
                              uint256 fraction = (partDay * (2**16)) / PRECISION;
                              uint256 totalPremium = addFractionalPremium(fraction, premium);
                              return totalPremium;
                          }
                          function addFractionalPremium(uint256 fraction, uint256 premium)
                              internal
                              pure
                              returns (uint256)
                          {
                              if (fraction & (1 << 0) != 0) {
                                  premium = (premium * bit1) / PRECISION;
                              }
                              if (fraction & (1 << 1) != 0) {
                                  premium = (premium * bit2) / PRECISION;
                              }
                              if (fraction & (1 << 2) != 0) {
                                  premium = (premium * bit3) / PRECISION;
                              }
                              if (fraction & (1 << 3) != 0) {
                                  premium = (premium * bit4) / PRECISION;
                              }
                              if (fraction & (1 << 4) != 0) {
                                  premium = (premium * bit5) / PRECISION;
                              }
                              if (fraction & (1 << 5) != 0) {
                                  premium = (premium * bit6) / PRECISION;
                              }
                              if (fraction & (1 << 6) != 0) {
                                  premium = (premium * bit7) / PRECISION;
                              }
                              if (fraction & (1 << 7) != 0) {
                                  premium = (premium * bit8) / PRECISION;
                              }
                              if (fraction & (1 << 8) != 0) {
                                  premium = (premium * bit9) / PRECISION;
                              }
                              if (fraction & (1 << 9) != 0) {
                                  premium = (premium * bit10) / PRECISION;
                              }
                              if (fraction & (1 << 10) != 0) {
                                  premium = (premium * bit11) / PRECISION;
                              }
                              if (fraction & (1 << 11) != 0) {
                                  premium = (premium * bit12) / PRECISION;
                              }
                              if (fraction & (1 << 12) != 0) {
                                  premium = (premium * bit13) / PRECISION;
                              }
                              if (fraction & (1 << 13) != 0) {
                                  premium = (premium * bit14) / PRECISION;
                              }
                              if (fraction & (1 << 14) != 0) {
                                  premium = (premium * bit15) / PRECISION;
                              }
                              if (fraction & (1 << 15) != 0) {
                                  premium = (premium * bit16) / PRECISION;
                              }
                              return premium;
                          }
                          function supportsInterface(bytes4 interfaceID)
                              public
                              view
                              virtual
                              override
                              returns (bool)
                          {
                              return super.supportsInterface(interfaceID);
                          }
                      }
                      pragma solidity >=0.8.4;
                      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);
                      }
                      pragma solidity >=0.8.4;
                      /**
                       * @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;
                          }
                      }
                      pragma solidity >=0.8.4;
                      import "./PriceOracle.sol";
                      import "./SafeMath.sol";
                      import "./StringUtils.sol";
                      import "@openzeppelin/contracts/access/Ownable.sol";
                      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.
                          uint256[] public rentPrices;
                          // Oracle address
                          AggregatorInterface public immutable usdOracle;
                          event OracleChanged(address oracle);
                          event RentPriceChanged(uint256[] prices);
                          bytes4 private constant INTERFACE_META_ID =
                              bytes4(keccak256("supportsInterface(bytes4)"));
                          bytes4 private constant ORACLE_ID =
                              bytes4(
                                  keccak256("price(string,uint256,uint256)") ^
                                      keccak256("premium(string,uint256,uint256)")
                              );
                          constructor(AggregatorInterface _usdOracle, uint256[] memory _rentPrices)
                              public
                          {
                              usdOracle = _usdOracle;
                              setPrices(_rentPrices);
                          }
                          function price(
                              string calldata name,
                              uint256 expires,
                              uint256 duration
                          ) external view override returns (uint256) {
                              uint256 len = name.strlen();
                              if (len > rentPrices.length) {
                                  len = rentPrices.length;
                              }
                              require(len > 0);
                              uint256 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(uint256[] memory _rentPrices) public onlyOwner {
                              rentPrices = _rentPrices;
                              emit RentPriceChanged(_rentPrices);
                          }
                          /**
                           * @dev Returns the pricing premium in wei.
                           */
                          function premium(
                              string calldata name,
                              uint256 expires,
                              uint256 duration
                          ) external view returns (uint256) {
                              uint256 weiPrice = attoUSDToWei(_premium(name, expires, duration));
                              return weiPrice;
                          }
                          /**
                           * @dev Returns the pricing premium in internal base units.
                           */
                          function _premium(
                              string memory name,
                              uint256 expires,
                              uint256 duration
                          ) internal view virtual returns (uint256) {
                              return 0;
                          }
                          function attoUSDToWei(uint256 amount) internal view returns (uint256) {
                              uint256 ethPrice = uint256(usdOracle.latestAnswer()); //2
                              return amount.mul(1e8).div(ethPrice);
                          }
                          function weiToAttoUSD(uint256 amount) internal view returns (uint256) {
                              uint256 ethPrice = uint256(usdOracle.latestAnswer());
                              return amount.mul(ethPrice).div(1e8);
                          }
                          function supportsInterface(bytes4 interfaceID)
                              public
                              view
                              virtual
                              returns (bool)
                          {
                              return interfaceID == INTERFACE_META_ID || interfaceID == ORACLE_ID;
                          }
                      }
                      pragma solidity >=0.8.4;
                      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++) {
                                  bytes1 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 7 of 8: 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 8 of 8: AccessControlledOffchainAggregator
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.7.1;
                      import "./OffchainAggregator.sol";
                      import "./SimpleReadAccessController.sol";
                      /**
                       * @notice Wrapper of OffchainAggregator which checks read access on Aggregator-interface methods
                       */
                      contract AccessControlledOffchainAggregator is OffchainAggregator, SimpleReadAccessController {
                        constructor(
                          uint32 _maximumGasPrice,
                          uint32 _reasonableGasPrice,
                          uint32 _microLinkPerEth,
                          uint32 _linkGweiPerObservation,
                          uint32 _linkGweiPerTransmission,
                          address _link,
                          int192 _minAnswer,
                          int192 _maxAnswer,
                          AccessControllerInterface _billingAccessController,
                          AccessControllerInterface _requesterAccessController,
                          uint8 _decimals,
                          string memory description
                        )
                          OffchainAggregator(
                            _maximumGasPrice,
                            _reasonableGasPrice,
                            _microLinkPerEth,
                            _linkGweiPerObservation,
                            _linkGweiPerTransmission,
                            _link,
                            _minAnswer,
                            _maxAnswer,
                            _billingAccessController,
                            _requesterAccessController,
                            _decimals,
                            description
                          ) {
                          }
                        /*
                         * Versioning
                         */
                        function typeAndVersion()
                          external
                          override
                          pure
                          virtual
                          returns (string memory)
                        {
                          return "AccessControlledOffchainAggregator 2.0.0";
                        }
                        /*
                         * v2 Aggregator interface
                         */
                        /// @inheritdoc OffchainAggregator
                        function latestAnswer()
                          public
                          override
                          view
                          checkAccess()
                          returns (int256)
                        {
                          return super.latestAnswer();
                        }
                        /// @inheritdoc OffchainAggregator
                        function latestTimestamp()
                          public
                          override
                          view
                          checkAccess()
                          returns (uint256)
                        {
                          return super.latestTimestamp();
                        }
                        /// @inheritdoc OffchainAggregator
                        function latestRound()
                          public
                          override
                          view
                          checkAccess()
                          returns (uint256)
                        {
                          return super.latestRound();
                        }
                        /// @inheritdoc OffchainAggregator
                        function getAnswer(uint256 _roundId)
                          public
                          override
                          view
                          checkAccess()
                          returns (int256)
                        {
                          return super.getAnswer(_roundId);
                        }
                        /// @inheritdoc OffchainAggregator
                        function getTimestamp(uint256 _roundId)
                          public
                          override
                          view
                          checkAccess()
                          returns (uint256)
                        {
                          return super.getTimestamp(_roundId);
                        }
                        /*
                         * v3 Aggregator interface
                         */
                        /// @inheritdoc OffchainAggregator
                        function description()
                          public
                          override
                          view
                          checkAccess()
                          returns (string memory)
                        {
                          return super.description();
                        }
                        /// @inheritdoc OffchainAggregator
                        function getRoundData(uint80 _roundId)
                          public
                          override
                          view
                          checkAccess()
                          returns (
                            uint80 roundId,
                            int256 answer,
                            uint256 startedAt,
                            uint256 updatedAt,
                            uint80 answeredInRound
                          )
                        {
                          return super.getRoundData(_roundId);
                        }
                        /// @inheritdoc OffchainAggregator
                        function latestRoundData()
                          public
                          override
                          view
                          checkAccess()
                          returns (
                            uint80 roundId,
                            int256 answer,
                            uint256 startedAt,
                            uint256 updatedAt,
                            uint80 answeredInRound
                          )
                        {
                          return super.latestRoundData();
                        }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.7.0;
                      import "./AccessControllerInterface.sol";
                      import "./AggregatorV2V3Interface.sol";
                      import "./AggregatorValidatorInterface.sol";
                      import "./LinkTokenInterface.sol";
                      import "./Owned.sol";
                      import "./OffchainAggregatorBilling.sol";
                      import "./TypeAndVersionInterface.sol";
                      /**
                        * @notice Onchain verification of reports from the offchain reporting protocol
                        * @dev For details on its operation, see the offchain reporting protocol design
                        * @dev doc, which refers to this contract as simply the "contract".
                      */
                      contract OffchainAggregator is Owned, OffchainAggregatorBilling, AggregatorV2V3Interface, TypeAndVersionInterface {
                        uint256 constant private maxUint32 = (1 << 32) - 1;
                        // Storing these fields used on the hot path in a HotVars variable reduces the
                        // retrieval of all of them to a single SLOAD. If any further fields are
                        // added, make sure that storage of the struct still takes at most 32 bytes.
                        struct HotVars {
                          // Provides 128 bits of security against 2nd pre-image attacks, but only
                          // 64 bits against collisions. This is acceptable, since a malicious owner has
                          // easier way of messing up the protocol than to find hash collisions.
                          bytes16 latestConfigDigest;
                          uint40 latestEpochAndRound; // 32 most sig bits for epoch, 8 least sig bits for round
                          // Current bound assumed on number of faulty/dishonest oracles participating
                          // in the protocol, this value is referred to as f in the design
                          uint8 threshold;
                          // Chainlink Aggregators expose a roundId to consumers. The offchain reporting
                          // protocol does not use this id anywhere. We increment it whenever a new
                          // transmission is made to provide callers with contiguous ids for successive
                          // reports.
                          uint32 latestAggregatorRoundId;
                        }
                        HotVars internal s_hotVars;
                        // Transmission records the median answer from the transmit transaction at
                        // time timestamp
                        struct Transmission {
                          int192 answer; // 192 bits ought to be enough for anyone
                          uint64 timestamp;
                        }
                        mapping(uint32 /* aggregator round ID */ => Transmission) internal s_transmissions;
                        // incremented each time a new config is posted. This count is incorporated
                        // into the config digest, to prevent replay attacks.
                        uint32 internal s_configCount;
                        uint32 internal s_latestConfigBlockNumber; // makes it easier for offchain systems
                                                                   // to extract config from logs.
                        // Lowest answer the system is allowed to report in response to transmissions
                        int192 immutable public minAnswer;
                        // Highest answer the system is allowed to report in response to transmissions
                        int192 immutable public maxAnswer;
                        /*
                         * @param _maximumGasPrice highest gas price for which transmitter will be compensated
                         * @param _reasonableGasPrice transmitter will receive reward for gas prices under this value
                         * @param _microLinkPerEth reimbursement per ETH of gas cost, in 1e-6LINK units
                         * @param _linkGweiPerObservation reward to oracle for contributing an observation to a successfully transmitted report, in 1e-9LINK units
                         * @param _linkGweiPerTransmission reward to transmitter of a successful report, in 1e-9LINK units
                         * @param _link address of the LINK contract
                         * @param _minAnswer lowest answer the median of a report is allowed to be
                         * @param _maxAnswer highest answer the median of a report is allowed to be
                         * @param _billingAccessController access controller for billing admin functions
                         * @param _requesterAccessController access controller for requesting new rounds
                         * @param _decimals answers are stored in fixed-point format, with this many digits of precision
                         * @param _description short human-readable description of observable this contract's answers pertain to
                         */
                        constructor(
                          uint32 _maximumGasPrice,
                          uint32 _reasonableGasPrice,
                          uint32 _microLinkPerEth,
                          uint32 _linkGweiPerObservation,
                          uint32 _linkGweiPerTransmission,
                          address _link,
                          int192 _minAnswer,
                          int192 _maxAnswer,
                          AccessControllerInterface _billingAccessController,
                          AccessControllerInterface _requesterAccessController,
                          uint8 _decimals,
                          string memory _description
                        )
                          OffchainAggregatorBilling(_maximumGasPrice, _reasonableGasPrice, _microLinkPerEth,
                            _linkGweiPerObservation, _linkGweiPerTransmission, _link,
                            _billingAccessController
                          )
                        {
                          decimals = _decimals;
                          s_description = _description;
                          setRequesterAccessController(_requesterAccessController);
                          setValidatorConfig(AggregatorValidatorInterface(0x0), 0);
                          minAnswer = _minAnswer;
                          maxAnswer = _maxAnswer;
                        }
                        /*
                         * Versioning
                         */
                        function typeAndVersion()
                          external
                          override
                          pure
                          virtual
                          returns (string memory)
                        {
                          return "OffchainAggregator 2.0.0";
                        }
                        /*
                         * Config logic
                         */
                        /**
                         * @notice triggers a new run of the offchain reporting protocol
                         * @param previousConfigBlockNumber block in which the previous config was set, to simplify historic analysis
                         * @param configCount ordinal number of this config setting among all config settings over the life of this contract
                         * @param signers ith element is address ith oracle uses to sign a report
                         * @param transmitters ith element is address ith oracle uses to transmit a report via the transmit method
                         * @param threshold maximum number of faulty/dishonest oracles the protocol can tolerate while still working correctly
                         * @param encodedConfigVersion version of the serialization format used for "encoded" parameter
                         * @param encoded serialized data used by oracles to configure their offchain operation
                         */
                        event ConfigSet(
                          uint32 previousConfigBlockNumber,
                          uint64 configCount,
                          address[] signers,
                          address[] transmitters,
                          uint8 threshold,
                          uint64 encodedConfigVersion,
                          bytes encoded
                        );
                        // Reverts transaction if config args are invalid
                        modifier checkConfigValid (
                          uint256 _numSigners, uint256 _numTransmitters, uint256 _threshold
                        ) {
                          require(_numSigners <= maxNumOracles, "too many signers");
                          require(_threshold > 0, "threshold must be positive");
                          require(
                            _numSigners == _numTransmitters,
                            "oracle addresses out of registration"
                          );
                          require(_numSigners > 3*_threshold, "faulty-oracle threshold too high");
                          _;
                        }
                        /**
                         * @notice sets offchain reporting protocol configuration incl. participating oracles
                         * @param _signers addresses with which oracles sign the reports
                         * @param _transmitters addresses oracles use to transmit the reports
                         * @param _threshold number of faulty oracles the system can tolerate
                         * @param _encodedConfigVersion version number for offchainEncoding schema
                         * @param _encoded encoded off-chain oracle configuration
                         */
                        function setConfig(
                          address[] calldata _signers,
                          address[] calldata _transmitters,
                          uint8 _threshold,
                          uint64 _encodedConfigVersion,
                          bytes calldata _encoded
                        )
                          external
                          checkConfigValid(_signers.length, _transmitters.length, _threshold)
                          onlyOwner()
                        {
                          while (s_signers.length != 0) { // remove any old signer/transmitter addresses
                            uint lastIdx = s_signers.length - 1;
                            address signer = s_signers[lastIdx];
                            address transmitter = s_transmitters[lastIdx];
                            payOracle(transmitter);
                            delete s_oracles[signer];
                            delete s_oracles[transmitter];
                            s_signers.pop();
                            s_transmitters.pop();
                          }
                          for (uint i = 0; i < _signers.length; i++) { // add new signer/transmitter addresses
                            require(
                              s_oracles[_signers[i]].role == Role.Unset,
                              "repeated signer address"
                            );
                            s_oracles[_signers[i]] = Oracle(uint8(i), Role.Signer);
                            require(s_payees[_transmitters[i]] != address(0), "payee must be set");
                            require(
                              s_oracles[_transmitters[i]].role == Role.Unset,
                              "repeated transmitter address"
                            );
                            s_oracles[_transmitters[i]] = Oracle(uint8(i), Role.Transmitter);
                            s_signers.push(_signers[i]);
                            s_transmitters.push(_transmitters[i]);
                          }
                          s_hotVars.threshold = _threshold;
                          uint32 previousConfigBlockNumber = s_latestConfigBlockNumber;
                          s_latestConfigBlockNumber = uint32(block.number);
                          s_configCount += 1;
                          uint64 configCount = s_configCount;
                          {
                            s_hotVars.latestConfigDigest = configDigestFromConfigData(
                              address(this),
                              configCount,
                              _signers,
                              _transmitters,
                              _threshold,
                              _encodedConfigVersion,
                              _encoded
                            );
                            s_hotVars.latestEpochAndRound = 0;
                          }
                          emit ConfigSet(
                            previousConfigBlockNumber,
                            configCount,
                            _signers,
                            _transmitters,
                            _threshold,
                            _encodedConfigVersion,
                            _encoded
                          );
                        }
                        function configDigestFromConfigData(
                          address _contractAddress,
                          uint64 _configCount,
                          address[] calldata _signers,
                          address[] calldata _transmitters,
                          uint8 _threshold,
                          uint64 _encodedConfigVersion,
                          bytes calldata _encodedConfig
                        ) internal pure returns (bytes16) {
                          return bytes16(keccak256(abi.encode(_contractAddress, _configCount,
                            _signers, _transmitters, _threshold, _encodedConfigVersion, _encodedConfig
                          )));
                        }
                        /**
                         * @notice information about current offchain reporting protocol configuration
                         * @return configCount ordinal number of current config, out of all configs applied to this contract so far
                         * @return blockNumber block at which this config was set
                         * @return configDigest domain-separation tag for current config (see configDigestFromConfigData)
                         */
                        function latestConfigDetails()
                          external
                          view
                          returns (
                            uint32 configCount,
                            uint32 blockNumber,
                            bytes16 configDigest
                          )
                        {
                          return (s_configCount, s_latestConfigBlockNumber, s_hotVars.latestConfigDigest);
                        }
                        /**
                         * @return list of addresses permitted to transmit reports to this contract
                         * @dev The list will match the order used to specify the transmitter during setConfig
                         */
                        function transmitters()
                          external
                          view
                          returns(address[] memory)
                        {
                            return s_transmitters;
                        }
                        /*
                         * On-chain validation logc
                         */
                        // Configuration for validator
                        struct ValidatorConfig {
                          AggregatorValidatorInterface validator;
                          uint32 gasLimit;
                        }
                        ValidatorConfig private s_validatorConfig;
                        /**
                         * @notice indicates that the validator configuration has been set
                         * @param previousValidator previous validator contract
                         * @param previousGasLimit previous gas limit for validate calls
                         * @param currentValidator current validator contract
                         * @param currentGasLimit current gas limit for validate calls
                         */
                        event ValidatorConfigSet(
                          AggregatorValidatorInterface indexed previousValidator,
                          uint32 previousGasLimit,
                          AggregatorValidatorInterface indexed currentValidator,
                          uint32 currentGasLimit
                        );
                        /**
                         * @notice validator configuration
                         * @return validator validator contract
                         * @return gasLimit gas limit for validate calls
                         */
                        function validatorConfig()
                          external
                          view
                          returns (AggregatorValidatorInterface validator, uint32 gasLimit)
                        {
                          ValidatorConfig memory vc = s_validatorConfig;
                          return (vc.validator, vc.gasLimit);
                        }
                        /**
                         * @notice sets validator configuration
                         * @dev set _newValidator to 0x0 to disable validate calls
                         * @param _newValidator address of the new validator contract
                         * @param _newGasLimit new gas limit for validate calls
                         */
                        function setValidatorConfig(AggregatorValidatorInterface _newValidator, uint32 _newGasLimit)
                          public
                          onlyOwner()
                        {
                          ValidatorConfig memory previous = s_validatorConfig;
                          if (previous.validator != _newValidator || previous.gasLimit != _newGasLimit) {
                            s_validatorConfig = ValidatorConfig({
                              validator: _newValidator,
                              gasLimit: _newGasLimit
                            });
                            emit ValidatorConfigSet(previous.validator, previous.gasLimit, _newValidator, _newGasLimit);
                          }
                        }
                        function validateAnswer(
                          uint32 _aggregatorRoundId,
                          int256 _answer
                        )
                          private
                        {
                          ValidatorConfig memory vc = s_validatorConfig;
                          if (address(vc.validator) == address(0)) {
                            return;
                          }
                          uint32 prevAggregatorRoundId = _aggregatorRoundId - 1;
                          int256 prevAggregatorRoundAnswer = s_transmissions[prevAggregatorRoundId].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 vc.validator.validate{gas: vc.gasLimit}(
                            prevAggregatorRoundId,
                            prevAggregatorRoundAnswer,
                            _aggregatorRoundId,
                            _answer
                          ) {} catch {}
                        }
                        /*
                         * requestNewRound logic
                         */
                        AccessControllerInterface internal s_requesterAccessController;
                        /**
                         * @notice emitted when a new requester access controller contract is set
                         * @param old the address prior to the current setting
                         * @param current the address of the new access controller contract
                         */
                        event RequesterAccessControllerSet(AccessControllerInterface old, AccessControllerInterface current);
                        /**
                         * @notice emitted to immediately request a new round
                         * @param requester the address of the requester
                         * @param configDigest the latest transmission's configDigest
                         * @param epoch the latest transmission's epoch
                         * @param round the latest transmission's round
                         */
                        event RoundRequested(address indexed requester, bytes16 configDigest, uint32 epoch, uint8 round);
                        /**
                         * @notice address of the requester access controller contract
                         * @return requester access controller address
                         */
                        function requesterAccessController()
                          external
                          view
                          returns (AccessControllerInterface)
                        {
                          return s_requesterAccessController;
                        }
                        /**
                         * @notice sets the requester access controller
                         * @param _requesterAccessController designates the address of the new requester access controller
                         */
                        function setRequesterAccessController(AccessControllerInterface _requesterAccessController)
                          public
                          onlyOwner()
                        {
                          AccessControllerInterface oldController = s_requesterAccessController;
                          if (_requesterAccessController != oldController) {
                            s_requesterAccessController = AccessControllerInterface(_requesterAccessController);
                            emit RequesterAccessControllerSet(oldController, _requesterAccessController);
                          }
                        }
                        /**
                         * @notice immediately requests a new round
                         * @return the aggregatorRoundId of the next round. Note: The report for this round may have been
                         * transmitted (but not yet mined) *before* requestNewRound() was even called. There is *no*
                         * guarantee of causality between the request and the report at aggregatorRoundId.
                         */
                        function requestNewRound() external returns (uint80) {
                          require(msg.sender == owner || s_requesterAccessController.hasAccess(msg.sender, msg.data),
                            "Only owner&requester can call");
                          HotVars memory hotVars = s_hotVars;
                          emit RoundRequested(
                            msg.sender,
                            hotVars.latestConfigDigest,
                            uint32(s_hotVars.latestEpochAndRound >> 8),
                            uint8(s_hotVars.latestEpochAndRound)
                          );
                          return hotVars.latestAggregatorRoundId + 1;
                        }
                        /*
                         * Transmission logic
                         */
                        /**
                         * @notice indicates that a new report was transmitted
                         * @param aggregatorRoundId the round to which this report was assigned
                         * @param answer median of the observations attached this report
                         * @param transmitter address from which the report was transmitted
                         * @param observations observations transmitted with this report
                         * @param rawReportContext signature-replay-prevention domain-separation tag
                         */
                        event NewTransmission(
                          uint32 indexed aggregatorRoundId,
                          int192 answer,
                          address transmitter,
                          int192[] observations,
                          bytes observers,
                          bytes32 rawReportContext
                        );
                        // decodeReport is used to check that the solidity and go code are using the
                        // same format. See TestOffchainAggregator.testDecodeReport and TestReportParsing
                        function decodeReport(bytes memory _report)
                          internal
                          pure
                          returns (
                            bytes32 rawReportContext,
                            bytes32 rawObservers,
                            int192[] memory observations
                          )
                        {
                          (rawReportContext, rawObservers, observations) = abi.decode(_report,
                            (bytes32, bytes32, int192[]));
                        }
                        // Used to relieve stack pressure in transmit
                        struct ReportData {
                          HotVars hotVars; // Only read from storage once
                          bytes observers; // ith element is the index of the ith observer
                          int192[] observations; // ith element is the ith observation
                          bytes vs; // jth element is the v component of the jth signature
                          bytes32 rawReportContext;
                        }
                        /*
                         * @notice details about the most recent report
                         * @return configDigest domain separation tag for the latest report
                         * @return epoch epoch in which the latest report was generated
                         * @return round OCR round in which the latest report was generated
                         * @return latestAnswer median value from latest report
                         * @return latestTimestamp when the latest report was transmitted
                         */
                        function latestTransmissionDetails()
                          external
                          view
                          returns (
                            bytes16 configDigest,
                            uint32 epoch,
                            uint8 round,
                            int192 latestAnswer,
                            uint64 latestTimestamp
                          )
                        {
                          require(msg.sender == tx.origin, "Only callable by EOA");
                          return (
                            s_hotVars.latestConfigDigest,
                            uint32(s_hotVars.latestEpochAndRound >> 8),
                            uint8(s_hotVars.latestEpochAndRound),
                            s_transmissions[s_hotVars.latestAggregatorRoundId].answer,
                            s_transmissions[s_hotVars.latestAggregatorRoundId].timestamp
                          );
                        }
                        // The constant-length components of the msg.data sent to transmit.
                        // See the "If we wanted to call sam" example on for example reasoning
                        // https://solidity.readthedocs.io/en/v0.7.2/abi-spec.html
                        uint16 private constant TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT =
                          4 + // function selector
                          32 + // word containing start location of abiencoded _report value
                          32 + // word containing location start of abiencoded  _rs value
                          32 + // word containing start location of abiencoded _ss value
                          32 + // _rawVs value
                          32 + // word containing length of _report
                          32 + // word containing length _rs
                          32 + // word containing length of _ss
                          0; // placeholder
                        function expectedMsgDataLength(
                          bytes calldata _report, bytes32[] calldata _rs, bytes32[] calldata _ss
                        ) private pure returns (uint256 length)
                        {
                          // calldata will never be big enough to make this overflow
                          return uint256(TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT) +
                            _report.length + // one byte pure entry in _report
                            _rs.length * 32 + // 32 bytes per entry in _rs
                            _ss.length * 32 + // 32 bytes per entry in _ss
                            0; // placeholder
                        }
                        /**
                         * @notice transmit is called to post a new report to the contract
                         * @param _report serialized report, which the signatures are signing. See parsing code below for format. The ith element of the observers component must be the index in s_signers of the address for the ith signature
                         * @param _rs ith element is the R components of the ith signature on report. Must have at most maxNumOracles entries
                         * @param _ss ith element is the S components of the ith signature on report. Must have at most maxNumOracles entries
                         * @param _rawVs ith element is the the V component of the ith signature
                         */
                        function transmit(
                          // NOTE: If these parameters are changed, expectedMsgDataLength and/or
                          // TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT need to be changed accordingly
                          bytes calldata _report,
                          bytes32[] calldata _rs, bytes32[] calldata _ss, bytes32 _rawVs // signatures
                        )
                          external
                        {
                          uint256 initialGas = gasleft(); // This line must come first
                          // Make sure the transmit message-length matches the inputs. Otherwise, the
                          // transmitter could append an arbitrarily long (up to gas-block limit)
                          // string of 0 bytes, which we would reimburse at a rate of 16 gas/byte, but
                          // which would only cost the transmitter 4 gas/byte. (Appendix G of the
                          // yellow paper, p. 25, for G_txdatazero and EIP 2028 for G_txdatanonzero.)
                          // This could amount to reimbursement profit of 36 million gas, given a 3MB
                          // zero tail.
                          require(msg.data.length == expectedMsgDataLength(_report, _rs, _ss),
                            "transmit message too long");
                          ReportData memory r; // Relieves stack pressure
                          {
                            r.hotVars = s_hotVars; // cache read from storage
                            bytes32 rawObservers;
                            (r.rawReportContext, rawObservers, r.observations) = abi.decode(
                              _report, (bytes32, bytes32, int192[])
                            );
                            // rawReportContext consists of:
                            // 11-byte zero padding
                            // 16-byte configDigest
                            // 4-byte epoch
                            // 1-byte round
                            bytes16 configDigest = bytes16(r.rawReportContext << 88);
                            require(
                              r.hotVars.latestConfigDigest == configDigest,
                              "configDigest mismatch"
                            );
                            uint40 epochAndRound = uint40(uint256(r.rawReportContext));
                            // direct numerical comparison works here, because
                            //
                            //   ((e,r) <= (e',r')) implies (epochAndRound <= epochAndRound')
                            //
                            // because alphabetic ordering implies e <= e', and if e = e', then r<=r',
                            // so e*256+r <= e'*256+r', because r, r' < 256
                            require(r.hotVars.latestEpochAndRound < epochAndRound, "stale report");
                            require(_rs.length > r.hotVars.threshold, "not enough signatures");
                            require(_rs.length <= maxNumOracles, "too many signatures");
                            require(_ss.length == _rs.length, "signatures out of registration");
                            require(r.observations.length <= maxNumOracles,
                                    "num observations out of bounds");
                            require(r.observations.length > 2 * r.hotVars.threshold,
                                    "too few values to trust median");
                            // Copy signature parities in bytes32 _rawVs to bytes r.v
                            r.vs = new bytes(_rs.length);
                            for (uint8 i = 0; i < _rs.length; i++) {
                              r.vs[i] = _rawVs[i];
                            }
                            // Copy observer identities in bytes32 rawObservers to bytes r.observers
                            r.observers = new bytes(r.observations.length);
                            bool[maxNumOracles] memory seen;
                            for (uint8 i = 0; i < r.observations.length; i++) {
                              uint8 observerIdx = uint8(rawObservers[i]);
                              require(!seen[observerIdx], "observer index repeated");
                              seen[observerIdx] = true;
                              r.observers[i] = rawObservers[i];
                            }
                            Oracle memory transmitter = s_oracles[msg.sender];
                            require( // Check that sender is authorized to report
                              transmitter.role == Role.Transmitter &&
                              msg.sender == s_transmitters[transmitter.index],
                              "unauthorized transmitter"
                            );
                            // record epochAndRound here, so that we don't have to carry the local
                            // variable in transmit. The change is reverted if something fails later.
                            r.hotVars.latestEpochAndRound = epochAndRound;
                          }
                          { // Verify signatures attached to report
                            bytes32 h = keccak256(_report);
                            bool[maxNumOracles] memory signed;
                            Oracle memory o;
                            for (uint i = 0; i < _rs.length; i++) {
                              address signer = ecrecover(h, uint8(r.vs[i])+27, _rs[i], _ss[i]);
                              o = s_oracles[signer];
                              require(o.role == Role.Signer, "address not authorized to sign");
                              require(!signed[o.index], "non-unique signature");
                              signed[o.index] = true;
                            }
                          }
                          { // Check the report contents, and record the result
                            for (uint i = 0; i < r.observations.length - 1; i++) {
                              bool inOrder = r.observations[i] <= r.observations[i+1];
                              require(inOrder, "observations not sorted");
                            }
                            int192 median = r.observations[r.observations.length/2];
                            require(minAnswer <= median && median <= maxAnswer, "median is out of min-max range");
                            r.hotVars.latestAggregatorRoundId++;
                            s_transmissions[r.hotVars.latestAggregatorRoundId] =
                              Transmission(median, uint64(block.timestamp));
                            emit NewTransmission(
                              r.hotVars.latestAggregatorRoundId,
                              median,
                              msg.sender,
                              r.observations,
                              r.observers,
                              r.rawReportContext
                            );
                            // Emit these for backwards compatability with offchain consumers
                            // that only support legacy events
                            emit NewRound(
                              r.hotVars.latestAggregatorRoundId,
                              address(0x0), // use zero address since we don't have anybody "starting" the round here
                              block.timestamp
                            );
                            emit AnswerUpdated(
                              median,
                              r.hotVars.latestAggregatorRoundId,
                              block.timestamp
                            );
                            validateAnswer(r.hotVars.latestAggregatorRoundId, median);
                          }
                          s_hotVars = r.hotVars;
                          assert(initialGas < maxUint32);
                          reimburseAndRewardOracles(uint32(initialGas), r.observers);
                        }
                        /*
                         * v2 Aggregator interface
                         */
                        /**
                         * @notice median from the most recent report
                         */
                        function latestAnswer()
                          public
                          override
                          view
                          virtual
                          returns (int256)
                        {
                          return s_transmissions[s_hotVars.latestAggregatorRoundId].answer;
                        }
                        /**
                         * @notice timestamp of block in which last report was transmitted
                         */
                        function latestTimestamp()
                          public
                          override
                          view
                          virtual
                          returns (uint256)
                        {
                          return s_transmissions[s_hotVars.latestAggregatorRoundId].timestamp;
                        }
                        /**
                         * @notice Aggregator round (NOT OCR round) in which last report was transmitted
                         */
                        function latestRound()
                          public
                          override
                          view
                          virtual
                          returns (uint256)
                        {
                          return s_hotVars.latestAggregatorRoundId;
                        }
                        /**
                         * @notice median of report from given aggregator round (NOT OCR round)
                         * @param _roundId the aggregator round of the target report
                         */
                        function getAnswer(uint256 _roundId)
                          public
                          override
                          view
                          virtual
                          returns (int256)
                        {
                          if (_roundId > 0xFFFFFFFF) { return 0; }
                          return s_transmissions[uint32(_roundId)].answer;
                        }
                        /**
                         * @notice timestamp of block in which report from given aggregator round was transmitted
                         * @param _roundId aggregator round (NOT OCR round) of target report
                         */
                        function getTimestamp(uint256 _roundId)
                          public
                          override
                          view
                          virtual
                          returns (uint256)
                        {
                          if (_roundId > 0xFFFFFFFF) { return 0; }
                          return s_transmissions[uint32(_roundId)].timestamp;
                        }
                        /*
                         * v3 Aggregator interface
                         */
                        string constant private V3_NO_DATA_ERROR = "No data present";
                        /**
                         * @return answers are stored in fixed-point format, with this many digits of precision
                         */
                        uint8 immutable public override decimals;
                        /**
                         * @notice aggregator contract version
                         */
                        uint256 constant public override version = 4;
                        string internal s_description;
                        /**
                         * @notice human-readable description of observable this contract is reporting on
                         */
                        function description()
                          public
                          override
                          view
                          virtual
                          returns (string memory)
                        {
                          return s_description;
                        }
                        /**
                         * @notice details for the given aggregator round
                         * @param _roundId target aggregator round (NOT OCR round). Must fit in uint32
                         * @return roundId _roundId
                         * @return answer median of report from given _roundId
                         * @return startedAt timestamp of block in which report from given _roundId was transmitted
                         * @return updatedAt timestamp of block in which report from given _roundId was transmitted
                         * @return answeredInRound _roundId
                         */
                        function getRoundData(uint80 _roundId)
                          public
                          override
                          view
                          virtual
                          returns (
                            uint80 roundId,
                            int256 answer,
                            uint256 startedAt,
                            uint256 updatedAt,
                            uint80 answeredInRound
                          )
                        {
                          require(_roundId <= 0xFFFFFFFF, V3_NO_DATA_ERROR);
                          Transmission memory transmission = s_transmissions[uint32(_roundId)];
                          return (
                            _roundId,
                            transmission.answer,
                            transmission.timestamp,
                            transmission.timestamp,
                            _roundId
                          );
                        }
                        /**
                         * @notice aggregator details for the most recently transmitted report
                         * @return roundId aggregator round of latest report (NOT OCR round)
                         * @return answer median of latest report
                         * @return startedAt timestamp of block containing latest report
                         * @return updatedAt timestamp of block containing latest report
                         * @return answeredInRound aggregator round of latest report
                         */
                        function latestRoundData()
                          public
                          override
                          view
                          virtual
                          returns (
                            uint80 roundId,
                            int256 answer,
                            uint256 startedAt,
                            uint256 updatedAt,
                            uint80 answeredInRound
                          )
                        {
                          roundId = s_hotVars.latestAggregatorRoundId;
                          // Skipped for compatability with existing FluxAggregator in which latestRoundData never reverts.
                          // require(roundId != 0, V3_NO_DATA_ERROR);
                          Transmission memory transmission = s_transmissions[uint32(roundId)];
                          return (
                            roundId,
                            transmission.answer,
                            transmission.timestamp,
                            transmission.timestamp,
                            roundId
                          );
                        }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.7.1;
                      import "./SimpleWriteAccessController.sol";
                      /**
                       * @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;
                        }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.7.0;
                      interface AccessControllerInterface {
                        function hasAccess(address user, bytes calldata data) external view returns (bool);
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.7.0;
                      import "./AggregatorInterface.sol";
                      import "./AggregatorV3Interface.sol";
                      interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface
                      {
                      }// SPDX-License-Identifier: MIT
                      pragma solidity ^0.7.0;
                      interface AggregatorValidatorInterface {
                        function validate(
                          uint256 previousRoundId,
                          int256 previousAnswer,
                          uint256 currentRoundId,
                          int256 currentAnswer
                        ) external returns (bool);
                      }// SPDX-License-Identifier: MIT
                      pragma solidity ^0.7.1;
                      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);
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.7.0;
                      /**
                       * @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() {
                          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");
                          _;
                        }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.7.0;
                      import "./AccessControllerInterface.sol";
                      import "./LinkTokenInterface.sol";
                      import "./Owned.sol";
                      /**
                       * @notice tracks administration of oracle-reward and gas-reimbursement parameters.
                       * @dev
                       * If you read or change this, be sure to read or adjust the comments. They
                       * track the units of the values under consideration, and are crucial to
                       * the readability of the operations it specifies.
                       * @notice
                       * Trust Model:
                       * Nothing in this contract prevents a billing admin from setting insane
                       * values for the billing parameters in setBilling. Oracles
                       * participating in this contract should regularly check that the
                       * parameters make sense. Similarly, the outstanding obligations of this
                       * contract to the oracles can exceed the funds held by the contract.
                       * Oracles participating in this contract should regularly check that it
                       * holds sufficient funds and stop interacting with it if funding runs
                       * out.
                       * This still leaves oracles with some risk due to TOCTOU issues.
                       * However, since the sums involved are pretty small (Ethereum
                       * transactions aren't that expensive in the end) and an oracle would
                       * likely stop participating in a contract it repeatedly lost money on,
                       * this risk is deemed acceptable. Oracles should also regularly
                       * withdraw any funds in the contract to prevent issues where the
                       * contract becomes underfunded at a later time, and different oracles
                       * are competing for the left-over funds.
                       * Finally, note that any change to the set of oracles or to the billing
                       * parameters will trigger payout of all oracles first (using the old
                       * parameters), a billing admin cannot take away funds that are already
                       * marked for payment.
                      */
                      contract OffchainAggregatorBilling is Owned {
                        // Maximum number of oracles the offchain reporting protocol is designed for
                        uint256 constant internal maxNumOracles = 31;
                        // Parameters for oracle payments
                        struct Billing {
                          // Highest compensated gas price, in ETH-gwei uints
                          uint32 maximumGasPrice;
                          // If gas price is less (in ETH-gwei units), transmitter gets half the savings
                          uint32 reasonableGasPrice;
                          // Pay transmitter back this much LINK per unit eth spent on gas
                          // (1e-6LINK/ETH units)
                          uint32 microLinkPerEth;
                          // Fixed LINK reward for each observer, in LINK-gwei units
                          uint32 linkGweiPerObservation;
                          // Fixed reward for transmitter, in linkGweiPerObservation units
                          uint32 linkGweiPerTransmission;
                        }
                        Billing internal s_billing;
                        /**
                        * @return LINK token contract used for billing
                        */
                        LinkTokenInterface immutable public LINK;
                        AccessControllerInterface internal s_billingAccessController;
                        // ith element is number of observation rewards due to ith process, plus one.
                        // This is expected to saturate after an oracle has submitted 65,535
                        // observations, or about 65535/(3*24*20) = 45 days, given a transmission
                        // every 3 minutes.
                        //
                        // This is always one greater than the actual value, so that when the value is
                        // reset to zero, we don't end up with a zero value in storage (which would
                        // result in a higher gas cost, the next time the value is incremented.)
                        // Calculations using this variable need to take that offset into account.
                        uint16[maxNumOracles] internal s_oracleObservationsCounts;
                        // Addresses at which oracles want to receive payments, by transmitter address
                        mapping (address /* transmitter */ => address /* payment address */)
                          internal
                          s_payees;
                        // Payee addresses which must be approved by the owner
                        mapping (address /* transmitter */ => address /* payment address */)
                          internal
                          s_proposedPayees;
                        // LINK-wei-denominated reimbursements for gas used by transmitters.
                        //
                        // This is always one greater than the actual value, so that when the value is
                        // reset to zero, we don't end up with a zero value in storage (which would
                        // result in a higher gas cost, the next time the value is incremented.)
                        // Calculations using this variable need to take that offset into account.
                        //
                        // Argument for overflow safety:
                        // We have the following maximum intermediate values:
                        // - 2**40 additions to this variable (epochAndRound is a uint40)
                        // - 2**32 gas price in ethgwei/gas
                        // - 1e9 ethwei/ethgwei
                        // - 2**32 gas since the block gas limit is at ~20 million
                        // - 2**32 (microlink/eth)
                        // And we have 2**40 * 2**32 * 1e9 * 2**32 * 2**32 < 2**166
                        // (we also divide in some places, but that only makes the value smaller)
                        // We can thus safely use uint256 intermediate values for the computation
                        // updating this variable.
                        uint256[maxNumOracles] internal s_gasReimbursementsLinkWei;
                        // Used for s_oracles[a].role, where a is an address, to track the purpose
                        // of the address, or to indicate that the address is unset.
                        enum Role {
                          // No oracle role has been set for address a
                          Unset,
                          // Signing address for the s_oracles[a].index'th oracle. I.e., report
                          // signatures from this oracle should ecrecover back to address a.
                          Signer,
                          // Transmission address for the s_oracles[a].index'th oracle. I.e., if a
                          // report is received by OffchainAggregator.transmit in which msg.sender is
                          // a, it is attributed to the s_oracles[a].index'th oracle.
                          Transmitter
                        }
                        struct Oracle {
                          uint8 index; // Index of oracle in s_signers/s_transmitters
                          Role role;   // Role of the address which mapped to this struct
                        }
                        mapping (address /* signer OR transmitter address */ => Oracle)
                          internal s_oracles;
                        // s_signers contains the signing address of each oracle
                        address[] internal s_signers;
                        // s_transmitters contains the transmission address of each oracle,
                        // i.e. the address the oracle actually sends transactions to the contract from
                        address[] internal s_transmitters;
                        uint256 constant private  maxUint16 = (1 << 16) - 1;
                        uint256 constant internal maxUint128 = (1 << 128) - 1;
                        constructor(
                          uint32 _maximumGasPrice,
                          uint32 _reasonableGasPrice,
                          uint32 _microLinkPerEth,
                          uint32 _linkGweiPerObservation,
                          uint32 _linkGweiPerTransmission,
                          address _link,
                          AccessControllerInterface _billingAccessController
                        )
                        {
                          setBillingInternal(_maximumGasPrice, _reasonableGasPrice, _microLinkPerEth,
                            _linkGweiPerObservation, _linkGweiPerTransmission);
                          setBillingAccessControllerInternal(_billingAccessController);
                          LINK = LinkTokenInterface(_link);
                          uint16[maxNumOracles] memory counts; // See s_oracleObservationsCounts docstring
                          uint256[maxNumOracles] memory gas; // see s_gasReimbursementsLinkWei docstring
                          for (uint8 i = 0; i < maxNumOracles; i++) {
                            counts[i] = 1;
                            gas[i] = 1;
                          }
                          s_oracleObservationsCounts = counts;
                          s_gasReimbursementsLinkWei = gas;
                        }
                        /**
                         * @notice emitted when billing parameters are set
                         * @param maximumGasPrice highest gas price for which transmitter will be compensated
                         * @param reasonableGasPrice transmitter will receive reward for gas prices under this value
                         * @param microLinkPerEth reimbursement per ETH of gas cost, in 1e-6LINK units
                         * @param linkGweiPerObservation reward to oracle for contributing an observation to a successfully transmitted report, in 1e-9LINK units
                         * @param linkGweiPerTransmission reward to transmitter of a successful report, in 1e-9LINK units
                         */
                        event BillingSet(
                          uint32 maximumGasPrice,
                          uint32 reasonableGasPrice,
                          uint32 microLinkPerEth,
                          uint32 linkGweiPerObservation,
                          uint32 linkGweiPerTransmission
                        );
                        function setBillingInternal(
                          uint32 _maximumGasPrice,
                          uint32 _reasonableGasPrice,
                          uint32 _microLinkPerEth,
                          uint32 _linkGweiPerObservation,
                          uint32 _linkGweiPerTransmission
                        )
                          internal
                        {
                          s_billing = Billing(_maximumGasPrice, _reasonableGasPrice, _microLinkPerEth,
                            _linkGweiPerObservation, _linkGweiPerTransmission);
                          emit BillingSet(_maximumGasPrice, _reasonableGasPrice, _microLinkPerEth,
                            _linkGweiPerObservation, _linkGweiPerTransmission);
                        }
                        /**
                         * @notice sets billing parameters
                         * @param _maximumGasPrice highest gas price for which transmitter will be compensated
                         * @param _reasonableGasPrice transmitter will receive reward for gas prices under this value
                         * @param _microLinkPerEth reimbursement per ETH of gas cost, in 1e-6LINK units
                         * @param _linkGweiPerObservation reward to oracle for contributing an observation to a successfully transmitted report, in 1e-9LINK units
                         * @param _linkGweiPerTransmission reward to transmitter of a successful report, in 1e-9LINK units
                         * @dev access control provided by billingAccessController
                         */
                        function setBilling(
                          uint32 _maximumGasPrice,
                          uint32 _reasonableGasPrice,
                          uint32 _microLinkPerEth,
                          uint32 _linkGweiPerObservation,
                          uint32 _linkGweiPerTransmission
                        )
                          external
                        {
                          AccessControllerInterface access = s_billingAccessController;
                          require(msg.sender == owner || access.hasAccess(msg.sender, msg.data),
                            "Only owner&billingAdmin can call");
                          payOracles();
                          setBillingInternal(_maximumGasPrice, _reasonableGasPrice, _microLinkPerEth,
                            _linkGweiPerObservation, _linkGweiPerTransmission);
                        }
                        /**
                         * @notice gets billing parameters
                         * @param maximumGasPrice highest gas price for which transmitter will be compensated
                         * @param reasonableGasPrice transmitter will receive reward for gas prices under this value
                         * @param microLinkPerEth reimbursement per ETH of gas cost, in 1e-6LINK units
                         * @param linkGweiPerObservation reward to oracle for contributing an observation to a successfully transmitted report, in 1e-9LINK units
                         * @param linkGweiPerTransmission reward to transmitter of a successful report, in 1e-9LINK units
                         */
                        function getBilling()
                          external
                          view
                          returns (
                            uint32 maximumGasPrice,
                            uint32 reasonableGasPrice,
                            uint32 microLinkPerEth,
                            uint32 linkGweiPerObservation,
                            uint32 linkGweiPerTransmission
                          )
                        {
                          Billing memory billing = s_billing;
                          return (
                            billing.maximumGasPrice,
                            billing.reasonableGasPrice,
                            billing.microLinkPerEth,
                            billing.linkGweiPerObservation,
                            billing.linkGweiPerTransmission
                          );
                        }
                        /**
                         * @notice emitted when a new access-control contract is set
                         * @param old the address prior to the current setting
                         * @param current the address of the new access-control contract
                         */
                        event BillingAccessControllerSet(AccessControllerInterface old, AccessControllerInterface current);
                        function setBillingAccessControllerInternal(AccessControllerInterface _billingAccessController)
                          internal
                        {
                          AccessControllerInterface oldController = s_billingAccessController;
                          if (_billingAccessController != oldController) {
                            s_billingAccessController = _billingAccessController;
                            emit BillingAccessControllerSet(
                              oldController,
                              _billingAccessController
                            );
                          }
                        }
                        /**
                         * @notice sets billingAccessController
                         * @param _billingAccessController new billingAccessController contract address
                         * @dev only owner can call this
                         */
                        function setBillingAccessController(AccessControllerInterface _billingAccessController)
                          external
                          onlyOwner
                        {
                          setBillingAccessControllerInternal(_billingAccessController);
                        }
                        /**
                         * @notice gets billingAccessController
                         * @return address of billingAccessController contract
                         */
                        function billingAccessController()
                          external
                          view
                          returns (AccessControllerInterface)
                        {
                          return s_billingAccessController;
                        }
                        /**
                         * @notice withdraws an oracle's payment from the contract
                         * @param _transmitter the transmitter address of the oracle
                         * @dev must be called by oracle's payee address
                         */
                        function withdrawPayment(address _transmitter)
                          external
                        {
                          require(msg.sender == s_payees[_transmitter], "Only payee can withdraw");
                          payOracle(_transmitter);
                        }
                        /**
                         * @notice query an oracle's payment amount
                         * @param _transmitter the transmitter address of the oracle
                         */
                        function owedPayment(address _transmitter)
                          public
                          view
                          returns (uint256)
                        {
                          Oracle memory oracle = s_oracles[_transmitter];
                          if (oracle.role == Role.Unset) { return 0; }
                          Billing memory billing = s_billing;
                          uint256 linkWeiAmount =
                            uint256(s_oracleObservationsCounts[oracle.index] - 1) *
                            uint256(billing.linkGweiPerObservation) *
                            (1 gwei);
                          linkWeiAmount += s_gasReimbursementsLinkWei[oracle.index] - 1;
                          return linkWeiAmount;
                        }
                        /**
                         * @notice emitted when an oracle has been paid LINK
                         * @param transmitter address from which the oracle sends reports to the transmit method
                         * @param payee address to which the payment is sent
                         * @param amount amount of LINK sent
                         */
                        event OraclePaid(address transmitter, address payee, uint256 amount);
                        // payOracle pays out _transmitter's balance to the corresponding payee, and zeros it out
                        function payOracle(address _transmitter)
                          internal
                        {
                          Oracle memory oracle = s_oracles[_transmitter];
                          uint256 linkWeiAmount = owedPayment(_transmitter);
                          if (linkWeiAmount > 0) {
                            address payee = s_payees[_transmitter];
                            // Poses no re-entrancy issues, because LINK.transfer does not yield
                            // control flow.
                            require(LINK.transfer(payee, linkWeiAmount), "insufficient funds");
                            s_oracleObservationsCounts[oracle.index] = 1; // "zero" the counts. see var's docstring
                            s_gasReimbursementsLinkWei[oracle.index] = 1; // "zero" the counts. see var's docstring
                            emit OraclePaid(_transmitter, payee, linkWeiAmount);
                          }
                        }
                        // payOracles pays out all transmitters, and zeros out their balances.
                        //
                        // It's much more gas-efficient to do this as a single operation, to avoid
                        // hitting storage too much.
                        function payOracles()
                          internal
                        {
                          Billing memory billing = s_billing;
                          uint16[maxNumOracles] memory observationsCounts = s_oracleObservationsCounts;
                          uint256[maxNumOracles] memory gasReimbursementsLinkWei =
                            s_gasReimbursementsLinkWei;
                          address[] memory transmitters = s_transmitters;
                          for (uint transmitteridx = 0; transmitteridx < transmitters.length; transmitteridx++) {
                            uint256 reimbursementAmountLinkWei = gasReimbursementsLinkWei[transmitteridx] - 1;
                            uint256 obsCount = observationsCounts[transmitteridx] - 1;
                            uint256 linkWeiAmount =
                              obsCount * uint256(billing.linkGweiPerObservation) * (1 gwei) + reimbursementAmountLinkWei;
                            if (linkWeiAmount > 0) {
                                address payee = s_payees[transmitters[transmitteridx]];
                                // Poses no re-entrancy issues, because LINK.transfer does not yield
                                // control flow.
                                require(LINK.transfer(payee, linkWeiAmount), "insufficient funds");
                                observationsCounts[transmitteridx] = 1;       // "zero" the counts.
                                gasReimbursementsLinkWei[transmitteridx] = 1; // "zero" the counts.
                                emit OraclePaid(transmitters[transmitteridx], payee, linkWeiAmount);
                              }
                          }
                          // "Zero" the accounting storage variables
                          s_oracleObservationsCounts = observationsCounts;
                          s_gasReimbursementsLinkWei = gasReimbursementsLinkWei;
                        }
                        function oracleRewards(
                          bytes memory observers,
                          uint16[maxNumOracles] memory observations
                        )
                          internal
                          pure
                          returns (uint16[maxNumOracles] memory)
                        {
                          // reward each observer-participant with the observer reward
                          for (uint obsIdx = 0; obsIdx < observers.length; obsIdx++) {
                            uint8 observer = uint8(observers[obsIdx]);
                            observations[observer] = saturatingAddUint16(observations[observer], 1);
                          }
                          return observations;
                        }
                        // This value needs to change if maxNumOracles is increased, or the accounting
                        // calculations at the bottom of reimburseAndRewardOracles change.
                        //
                        // To recalculate it, run the profiler as described in
                        // ../../profile/README.md, and add up the gas-usage values reported for the
                        // lines in reimburseAndRewardOracles following the "gasLeft = gasleft()"
                        // line. E.g., you will see output like this:
                        //
                        //      7        uint256 gasLeft = gasleft();
                        //     29        uint256 gasCostEthWei = transmitterGasCostEthWei(
                        //      9          uint256(initialGas),
                        //      3          gasPrice,
                        //      3          callDataGasCost,
                        //      3          gasLeft
                        //      .
                        //      .
                        //      .
                        //     59        uint256 gasCostLinkWei = (gasCostEthWei * billing.microLinkPerEth)/ 1e6;
                        //      .
                        //      .
                        //      .
                        //   5047        s_gasReimbursementsLinkWei[txOracle.index] =
                        //    856          s_gasReimbursementsLinkWei[txOracle.index] + gasCostLinkWei +
                        //     26          uint256(billing.linkGweiPerTransmission) * (1 gwei);
                        //
                        // If those were the only lines to be accounted for, you would add up
                        // 29+9+3+3+3+59+5047+856+26=6035.
                        uint256 internal constant accountingGasCost = 6035;
                        // Uncomment the following declaration to compute the remaining gas cost after
                        // above gasleft(). (This must exist in a base class to OffchainAggregator, so
                        // it can't go in TestOffchainAggregator.)
                        //
                        // uint256 public gasUsedInAccounting;
                        // Gas price at which the transmitter should be reimbursed, in ETH-gwei/gas
                        function impliedGasPrice(
                          uint256 txGasPrice,         // ETH-gwei/gas units
                          uint256 reasonableGasPrice, // ETH-gwei/gas units
                          uint256 maximumGasPrice     // ETH-gwei/gas units
                        )
                          internal
                          pure
                          returns (uint256)
                        {
                          // Reward the transmitter for choosing an efficient gas price: if they manage
                          // to come in lower than considered reasonable, give them half the savings.
                          //
                          // The following calculations are all in units of gwei/gas, i.e. 1e-9ETH/gas
                          uint256 gasPrice = txGasPrice;
                          if (txGasPrice < reasonableGasPrice) {
                            // Give transmitter half the savings for coming in under the reasonable gas price
                            gasPrice += (reasonableGasPrice - txGasPrice) / 2;
                          }
                          // Don't reimburse a gas price higher than maximumGasPrice
                          return min(gasPrice, maximumGasPrice);
                        }
                        // gas reimbursement due the transmitter, in ETH-wei
                        //
                        // If this function is changed, accountingGasCost needs to change, too. See
                        // its docstring
                        function transmitterGasCostEthWei(
                          uint256 initialGas,
                          uint256 gasPrice, // ETH-gwei/gas units
                          uint256 callDataCost, // gas units
                          uint256 gasLeft
                        )
                          internal
                          pure
                          returns (uint128 gasCostEthWei)
                        {
                          require(initialGas >= gasLeft, "gasLeft cannot exceed initialGas");
                          uint256 gasUsed = // gas units
                            initialGas - gasLeft + // observed gas usage
                            callDataCost + accountingGasCost; // estimated gas usage
                          // gasUsed is in gas units, gasPrice is in ETH-gwei/gas units; convert to ETH-wei
                          uint256 fullGasCostEthWei = gasUsed * gasPrice * (1 gwei);
                          assert(fullGasCostEthWei < maxUint128); // the entire ETH supply fits in a uint128...
                          return uint128(fullGasCostEthWei);
                        }
                        /**
                         * @notice withdraw any available funds left in the contract, up to _amount, after accounting for the funds due to participants in past reports
                         * @param _recipient address to send funds to
                         * @param _amount maximum amount to withdraw, denominated in LINK-wei.
                         * @dev access control provided by billingAccessController
                         */
                        function withdrawFunds(address _recipient, uint256 _amount)
                          external
                        {
                          require(msg.sender == owner || s_billingAccessController.hasAccess(msg.sender, msg.data),
                            "Only owner&billingAdmin can call");
                          uint256 linkDue = totalLINKDue();
                          uint256 linkBalance = LINK.balanceOf(address(this));
                          require(linkBalance >= linkDue, "insufficient balance");
                          require(LINK.transfer(_recipient, min(linkBalance - linkDue, _amount)), "insufficient funds");
                        }
                        // Total LINK due to participants in past reports.
                        function totalLINKDue()
                          internal
                          view
                          returns (uint256 linkDue)
                        {
                          // Argument for overflow safety: We do all computations in
                          // uint256s. The inputs to linkDue are:
                          // - the <= 31 observation rewards each of which has less than
                          //   64 bits (32 bits for billing.linkGweiPerObservation, 32 bits
                          //   for wei/gwei conversion). Hence 69 bits are sufficient for this part.
                          // - the <= 31 gas reimbursements, each of which consists of at most 166
                          //   bits (see s_gasReimbursementsLinkWei docstring). Hence 171 bits are
                          //   sufficient for this part
                          // In total, 172 bits are enough.
                          uint16[maxNumOracles] memory observationCounts = s_oracleObservationsCounts;
                          for (uint i = 0; i < maxNumOracles; i++) {
                            linkDue += observationCounts[i] - 1; // Stored value is one greater than actual value
                          }
                          Billing memory billing = s_billing;
                          // Convert linkGweiPerObservation to uint256, or this overflows!
                          linkDue *= uint256(billing.linkGweiPerObservation) * (1 gwei);
                          address[] memory transmitters = s_transmitters;
                          uint256[maxNumOracles] memory gasReimbursementsLinkWei =
                            s_gasReimbursementsLinkWei;
                          for (uint i = 0; i < transmitters.length; i++) {
                            linkDue += uint256(gasReimbursementsLinkWei[i]-1); // Stored value is one greater than actual value
                          }
                        }
                        /**
                         * @notice allows oracles to check that sufficient LINK balance is available
                         * @return availableBalance LINK available on this contract, after accounting for outstanding obligations. can become negative
                         */
                        function linkAvailableForPayment()
                          external
                          view
                          returns (int256 availableBalance)
                        {
                          // there are at most one billion LINK, so this cast is safe
                          int256 balance = int256(LINK.balanceOf(address(this)));
                          // according to the argument in the definition of totalLINKDue,
                          // totalLINKDue is never greater than 2**172, so this cast is safe
                          int256 due = int256(totalLINKDue());
                          // safe from overflow according to above sizes
                          return int256(balance) - int256(due);
                        }
                        /**
                         * @notice number of observations oracle is due to be reimbursed for
                         * @param _signerOrTransmitter address used by oracle for signing or transmitting reports
                         */
                        function oracleObservationCount(address _signerOrTransmitter)
                          external
                          view
                          returns (uint16)
                        {
                          Oracle memory oracle = s_oracles[_signerOrTransmitter];
                          if (oracle.role == Role.Unset) { return 0; }
                          return s_oracleObservationsCounts[oracle.index] - 1;
                        }
                        function reimburseAndRewardOracles(
                          uint32 initialGas,
                          bytes memory observers
                        )
                          internal
                        {
                          Oracle memory txOracle = s_oracles[msg.sender];
                          Billing memory billing = s_billing;
                          // Reward oracles for providing observations. Oracles are not rewarded
                          // for providing signatures, because signing is essentially free.
                          s_oracleObservationsCounts =
                            oracleRewards(observers, s_oracleObservationsCounts);
                          // Reimburse transmitter of the report for gas usage
                          require(txOracle.role == Role.Transmitter,
                            "sent by undesignated transmitter"
                          );
                          uint256 gasPrice = impliedGasPrice(
                            tx.gasprice / (1 gwei), // convert to ETH-gwei units
                            billing.reasonableGasPrice,
                            billing.maximumGasPrice
                          );
                          // The following is only an upper bound, as it ignores the cheaper cost for
                          // 0 bytes. Safe from overflow, because calldata just isn't that long.
                          uint256 callDataGasCost = 16 * msg.data.length;
                          // If any changes are made to subsequent calculations, accountingGasCost
                          // needs to change, too.
                          uint256 gasLeft = gasleft();
                          uint256 gasCostEthWei = transmitterGasCostEthWei(
                            uint256(initialGas),
                            gasPrice,
                            callDataGasCost,
                            gasLeft
                          );
                          // microLinkPerEth is 1e-6LINK/ETH units, gasCostEthWei is 1e-18ETH units
                          // (ETH-wei), product is 1e-24LINK-wei units, dividing by 1e6 gives
                          // 1e-18LINK units, i.e. LINK-wei units
                          // Safe from over/underflow, since all components are non-negative,
                          // gasCostEthWei will always fit into uint128 and microLinkPerEth is a
                          // uint32 (128+32 < 256!).
                          uint256 gasCostLinkWei = (gasCostEthWei * billing.microLinkPerEth)/ 1e6;
                          // Safe from overflow, because gasCostLinkWei < 2**160 and
                          // billing.linkGweiPerTransmission * (1 gwei) < 2**64 and we increment
                          // s_gasReimbursementsLinkWei[txOracle.index] at most 2**40 times.
                          s_gasReimbursementsLinkWei[txOracle.index] =
                            s_gasReimbursementsLinkWei[txOracle.index] + gasCostLinkWei +
                            uint256(billing.linkGweiPerTransmission) * (1 gwei); // convert from linkGwei to linkWei
                          // Uncomment next line to compute the remaining gas cost after above gasleft().
                          // See OffchainAggregatorBilling.accountingGasCost docstring for more information.
                          //
                          // gasUsedInAccounting = gasLeft - gasleft();
                        }
                        /*
                         * Payee management
                         */
                        /**
                         * @notice emitted when a transfer of an oracle's payee address has been initiated
                         * @param transmitter address from which the oracle sends reports to the transmit method
                         * @param current the payeee address for the oracle, prior to this setting
                         * @param proposed the proposed new payee address for the oracle
                         */
                        event PayeeshipTransferRequested(
                          address indexed transmitter,
                          address indexed current,
                          address indexed proposed
                        );
                        /**
                         * @notice emitted when a transfer of an oracle's payee address has been completed
                         * @param transmitter address from which the oracle sends reports to the transmit method
                         * @param current the payeee address for the oracle, prior to this setting
                         */
                        event PayeeshipTransferred(
                          address indexed transmitter,
                          address indexed previous,
                          address indexed current
                        );
                        /**
                         * @notice sets the payees for transmitting addresses
                         * @param _transmitters addresses oracles use to transmit the reports
                         * @param _payees addresses of payees corresponding to list of transmitters
                         * @dev must be called by owner
                         * @dev cannot be used to change payee addresses, only to initially populate them
                         */
                        function setPayees(
                          address[] calldata _transmitters,
                          address[] calldata _payees
                        )
                          external
                          onlyOwner()
                        {
                          require(_transmitters.length == _payees.length, "transmitters.size != payees.size");
                          for (uint i = 0; i < _transmitters.length; i++) {
                            address transmitter = _transmitters[i];
                            address payee = _payees[i];
                            address currentPayee = s_payees[transmitter];
                            bool zeroedOut = currentPayee == address(0);
                            require(zeroedOut || currentPayee == payee, "payee already set");
                            s_payees[transmitter] = payee;
                            if (currentPayee != payee) {
                              emit PayeeshipTransferred(transmitter, currentPayee, payee);
                            }
                          }
                        }
                        /**
                         * @notice first step of payeeship transfer (safe transfer pattern)
                         * @param _transmitter transmitter address of oracle whose payee is changing
                         * @param _proposed new payee address
                         * @dev can only be called by payee address
                         */
                        function transferPayeeship(
                          address _transmitter,
                          address _proposed
                        )
                          external
                        {
                            require(msg.sender == s_payees[_transmitter], "only current payee can update");
                            require(msg.sender != _proposed, "cannot transfer to self");
                            address previousProposed = s_proposedPayees[_transmitter];
                            s_proposedPayees[_transmitter] = _proposed;
                            if (previousProposed != _proposed) {
                              emit PayeeshipTransferRequested(_transmitter, msg.sender, _proposed);
                            }
                        }
                        /**
                         * @notice second step of payeeship transfer (safe transfer pattern)
                         * @param _transmitter transmitter address of oracle whose payee is changing
                         * @dev can only be called by proposed new payee address
                         */
                        function acceptPayeeship(
                          address _transmitter
                        )
                          external
                        {
                          require(msg.sender == s_proposedPayees[_transmitter], "only proposed payees can accept");
                          address currentPayee = s_payees[_transmitter];
                          s_payees[_transmitter] = msg.sender;
                          s_proposedPayees[_transmitter] = address(0);
                          emit PayeeshipTransferred(_transmitter, currentPayee, msg.sender);
                        }
                        /*
                         * Helper functions
                         */
                        function saturatingAddUint16(uint16 _x, uint16 _y)
                          internal
                          pure
                          returns (uint16)
                        {
                          return uint16(min(uint256(_x)+uint256(_y), maxUint16));
                        }
                        function min(uint256 a, uint256 b)
                          internal
                          pure
                          returns (uint256)
                        {
                          if (a < b) { return a; }
                          return b;
                        }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.7.0;
                      abstract contract TypeAndVersionInterface{
                        function typeAndVersion()
                          external
                          pure
                          virtual
                          returns (string memory);
                      }// SPDX-License-Identifier: MIT
                      pragma solidity ^0.7.0;
                      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);
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.7.0;
                      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
                          );
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.7.0;
                      import "./Owned.sol";
                      import "./AccessControllerInterface.sol";
                      /**
                       * @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()
                        {
                          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() {
                          addAccessInternal(_user);
                        }
                        function addAccessInternal(address _user) internal {
                          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");
                          _;
                        }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.7.0;
                      import "./AccessControlledOffchainAggregator.sol";
                      import "./AccessControlTestHelper.sol";
                      contract TestOffchainAggregator is AccessControlledOffchainAggregator {
                        function testDecodeReport(
                          bytes memory report
                        ) public pure returns (bytes32, bytes32, int192[] memory)
                        {
                          return decodeReport(report);
                        }
                        constructor(
                          uint32 _maximumGasPrice,
                          uint32 _reasonableGasPrice,
                          uint32 _microLinkPerEth,
                          uint32 _linkGweiPerObservation,
                          uint32 _linkGweiPerTransmission,
                          address _link,
                          int192 _minAnswer, int192 _maxAnswer,
                          AccessControllerInterface _billingAccessController,
                          AccessControllerInterface _requesterAdminAccessController
                        )
                          AccessControlledOffchainAggregator(_maximumGasPrice, _reasonableGasPrice, _microLinkPerEth,
                            _linkGweiPerObservation, _linkGweiPerTransmission, _link,
                            _minAnswer, _maxAnswer, _billingAccessController, _requesterAdminAccessController, 0, "TEST"
                          )
                        {}
                        function testPayee(
                          address _transmitter
                        )
                          external
                          view
                          returns (address)
                        {
                          return s_payees[_transmitter];
                        }
                        function getConfigDigest() public view returns (bytes16) {
                          return s_hotVars.latestConfigDigest;
                        }
                        function testSaturatingAddUint16(uint16 _x, uint16 _y)
                          external pure returns (uint16)
                        {
                          return saturatingAddUint16(_x, _y);
                        }
                        function testImpliedGasPrice(uint256 txGasPrice, uint256 reasonableGasPrice,
                          uint256 maximumGasPrice
                        ) external pure returns (uint256) {
                          return impliedGasPrice(txGasPrice, reasonableGasPrice, maximumGasPrice);
                        }
                        function testTransmitterGasCostEthWei(uint256 initialGas, uint256 gasPrice,
                          uint256 callDataCost, uint256 gasLeft
                        ) external pure returns (uint128) {
                          return transmitterGasCostEthWei(
                            initialGas, gasPrice, callDataCost, gasLeft
                          );
                        }
                        function testSetOracleObservationCount(address _oracle, uint16 _amount) external {
                          s_oracleObservationsCounts[s_oracles[_oracle].index] = _amount + 1;
                        }
                        function testTotalLinkDue()
                          external view returns (uint256 linkDue)
                        {
                          return totalLINKDue();
                        }
                        function billingData() external view returns (
                          uint16[maxNumOracles] memory observationsCounts,
                          uint256[maxNumOracles] memory gasReimbursements,
                          uint32 maximumGasPrice,
                          uint32 reasonableGasPrice,
                          uint32 microLinkPerEth,
                          uint32 linkGweiPerObservation,
                          uint32 linkGweiPerTransmission
                        ) {
                          Billing memory b = s_billing;
                          return (s_oracleObservationsCounts, s_gasReimbursementsLinkWei,
                            b.maximumGasPrice, b.reasonableGasPrice, b.microLinkPerEth,
                            b.linkGweiPerObservation, b.linkGweiPerTransmission);
                        }
                        function testSetGasReimbursements(address _transmitterOrSigner, uint256 _amountLinkWei)
                          external
                        {
                          require(s_oracles[_transmitterOrSigner].role != Role.Unset, "address unknown");
                          s_gasReimbursementsLinkWei[s_oracles[_transmitterOrSigner].index] = _amountLinkWei + 1;
                        }
                        function testAccountingGasCost() public pure returns (uint256) {
                          return accountingGasCost;
                        }
                        function testBurnLINK(uint256 amount) public {
                            LINK.transfer(address(1), amount);
                        }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.7.1;
                      import "./AccessControlledOffchainAggregator.sol";
                      contract AccessControlTestHelper {
                        event Dummy(); // Used to silence warning that these methods are pure
                        function readGetRoundData(address _aggregator, uint80 _roundID)
                          external
                        {
                          AccessControlledOffchainAggregator(_aggregator).getRoundData(_roundID);
                          emit Dummy();
                        }
                        function readLatestRoundData(address _aggregator)
                          external
                        {
                          AccessControlledOffchainAggregator(_aggregator).latestRoundData();
                          emit Dummy();
                        }
                        function readLatestAnswer(address _aggregator)
                          external
                        {
                          AccessControlledOffchainAggregator(_aggregator).latestAnswer();
                          emit Dummy();
                        }
                        function readLatestTimestamp(address _aggregator)
                          external
                        {
                          AccessControlledOffchainAggregator(_aggregator).latestTimestamp();
                          emit Dummy();
                        }
                        function readLatestRound(address _aggregator)
                          external
                        {
                          AccessControlledOffchainAggregator(_aggregator).latestRound();
                          emit Dummy();
                        }
                        function readGetAnswer(address _aggregator, uint256 _roundID)
                          external
                        {
                          AccessControlledOffchainAggregator(_aggregator).getAnswer(_roundID);
                          emit Dummy();
                        }
                        function readGetTimestamp(address _aggregator, uint256 _roundID)
                          external
                        {
                          AccessControlledOffchainAggregator(_aggregator).getTimestamp(_roundID);
                          emit Dummy();
                        }
                        function testLatestTransmissionDetails(address _aggregator) external view {
                            OffchainAggregator(_aggregator).latestTransmissionDetails();
                        }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.7.0;
                      import "./OffchainAggregator.sol";
                      // ExposedOffchainAggregator exposes certain internal OffchainAggregator
                      // methods/structures so that golang code can access them, and we get
                      // reliable type checking on their usage
                      contract ExposedOffchainAggregator is OffchainAggregator {
                        constructor()
                          OffchainAggregator(
                            0, 0, 0, 0, 0, address(0), 0, 0, AccessControllerInterface(address(0)), AccessControllerInterface(address(0)), 0, ""
                          )
                          {}
                        function exposedConfigDigestFromConfigData(
                          address _contractAddress,
                          uint64 _configCount,
                          address[] calldata _signers,
                          address[] calldata _transmitters,
                          uint8 _threshold,
                          uint64 _encodedConfigVersion,
                          bytes calldata _encodedConfig
                        ) external pure returns (bytes16) {
                          return configDigestFromConfigData(_contractAddress, _configCount,
                            _signers, _transmitters, _threshold, _encodedConfigVersion,
                            _encodedConfig);
                        }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.7.0;
                      import "./AggregatorValidatorInterface.sol";
                      contract TestValidator is AggregatorValidatorInterface {
                        uint32 s_minGasUse;
                        uint256 s_latestRoundId;
                        event Validated(
                          uint256 previousRoundId,
                          int256 previousAnswer,
                          uint256 currentRoundId,
                          int256 currentAnswer,
                          uint256 initialGas
                        );
                        function validate(
                          uint256 previousRoundId,
                          int256 previousAnswer,
                          uint256 currentRoundId,
                          int256 currentAnswer
                        ) external override returns (bool) {
                          uint256 initialGas = gasleft();
                          emit Validated(
                            previousRoundId,
                            previousAnswer,
                            currentRoundId,
                            currentAnswer,
                            initialGas
                          );
                          s_latestRoundId = currentRoundId;
                          uint256 minGasUse = s_minGasUse;
                          while (initialGas - gasleft() < minGasUse) {}
                          return true;
                        }
                        function setMinGasUse(uint32 minGasUse) external {
                          s_minGasUse = minGasUse;
                        }
                        function latestRoundId() external view returns (uint256) {
                          return s_latestRoundId;
                        }
                      }