ETH Price: $3,115.67 (-0.04%)

Transaction Decoder

Block:
23989216 at Dec-11-2025 11:44:11 AM +UTC
Transaction Fee:
0.000022886603005763 ETH $0.07
Gas Used:
188,027 Gas / 0.121719769 Gwei

Emitted Events:

315 BaseRegistrarImplementation.Transfer( from=0x3c883d31...0Ae3612C7, to=0x00000000...000000000, tokenId=25308102514041779625298004784373259821162920423098545848443775620331382158103 )
316 BaseRegistrarImplementation.Transfer( from=0x00000000...000000000, to=[Receiver] 0x59e16fccd424cc24e280be16e11bcd56fb0ce547, tokenId=25308102514041779625298004784373259821162920423098545848443775620331382158103 )
317 ENSRegistryWithFallback.NewOwner( node=93CDEB708B7545DC668EB9280176169D1C33CFD8ED6F04690A0BCC88A93FC4AE, label=37F3E0DF12B7B46C3CB953AEA88603315A616E0FD5A3636C89C4DABD69A6B317, owner=[Receiver] 0x59e16fccd424cc24e280be16e11bcd56fb0ce547 )
318 BaseRegistrarImplementation.NameRegistered( id=25308102514041779625298004784373259821162920423098545848443775620331382158103, owner=[Receiver] 0x59e16fccd424cc24e280be16e11bcd56fb0ce547, expires=1768131851 )
319 ENSRegistryWithFallback.Transfer( node=9549D45B8DE35ED985D3C4AE30E2FFC5F0E1A2027A518D98BCA49CF708C0A7A6, owner=[Sender] 0xc4a74d45afb04faed602c20a5c4c3eacf43f4cad )
320 ENSRegistryWithFallback.NewResolver( node=9549D45B8DE35ED985D3C4AE30E2FFC5F0E1A2027A518D98BCA49CF708C0A7A6, resolver=PublicResolver )
321 PublicResolver.AddressChanged( node=9549D45B8DE35ED985D3C4AE30E2FFC5F0E1A2027A518D98BCA49CF708C0A7A6, coinType=60, newAddress=[Sender] 0xc4a74d45afb04faed602c20a5c4c3eacf43f4cad )
322 PublicResolver.AddrChanged( node=9549D45B8DE35ED985D3C4AE30E2FFC5F0E1A2027A518D98BCA49CF708C0A7A6, a=[Sender] 0xc4a74d45afb04faed602c20a5c4c3eacf43f4cad )
323 BaseRegistrarImplementation.Transfer( from=[Receiver] 0x59e16fccd424cc24e280be16e11bcd56fb0ce547, to=[Sender] 0xc4a74d45afb04faed602c20a5c4c3eacf43f4cad, tokenId=25308102514041779625298004784373259821162920423098545848443775620331382158103 )
324 0x59e16fccd424cc24e280be16e11bcd56fb0ce547.0xc2240194853531f1ae318dcef227de79c6ad0fd9d1b0e4fe08568415be2e08a5( 0xc2240194853531f1ae318dcef227de79c6ad0fd9d1b0e4fe08568415be2e08a5, 0x37f3e0df12b7b46c3cb953aea88603315a616e0fd5a3636c89c4dabd69a6b317, 0x000000000000000000000000c4a74d45afb04faed602c20a5c4c3eacf43f4cad, 00000000000000000000000000000000000000000000000000000000000000a0, 000000000000000000000000000000000000000000000000000078bf726cebdb, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000069638d0b, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000006, 3078393939310000000000000000000000000000000000000000000000000000 )

Account State Difference:

  Address   Before After State Difference Code
0x00000000...A6C7d2e1e
(ENS: Registry with Fallback)
(Titan Builder)
18.627292883789613077 Eth18.627292902592313077 Eth0.0000000188027
0x57f1887a...Af147eA85
0x59E16fcC...6fb0CE547 35.874027619635040856 Eth35.874160383288868403 Eth0.000132763653827547
0xc4A74D45...cF43f4cad
0.001519515032731242 Eth
Nonce: 892
0.001363864775897932 Eth
Nonce: 893
0.00015565025683331
0xF2910098...7B804AC15

Execution Trace

ETH 0.000135418926904097 0x59e16fccd424cc24e280be16e11bcd56fb0ce547.ef9c8805( )
  • BaseRegistrarImplementation.nameExpires( id=25308102514041779625298004784373259821162920423098545848443775620331382158103 ) => ( 1714430670 )
  • ExponentialPremiumPriceOracle.price( name=0x9991, expires=1714430670, duration=2678400 ) => ( [{name:base, type:uint256, order:1, indexed:false, value:132763653827547, valueString:132763653827547}, {name:premium, type:uint256, order:2, indexed:false, value:0, valueString:0}] )
    • EACAggregatorProxy.STATICCALL( )
      • 0x7d4e742018fb52e48b08be73d041c18b21de6fb5.STATICCALL( )
      • EACAggregatorProxy.STATICCALL( )
        • 0x7d4e742018fb52e48b08be73d041c18b21de6fb5.STATICCALL( )
        • BaseRegistrarImplementation.available( id=25308102514041779625298004784373259821162920423098545848443775620331382158103 ) => ( True )
        • BaseRegistrarImplementation.register( id=25308102514041779625298004784373259821162920423098545848443775620331382158103, owner=0x59E16fcCd424Cc24e280Be16E11Bcd56fb0CE547, duration=2678400 ) => ( 1768131851 )
          • ENSRegistryWithFallback.owner( node=93CDEB708B7545DC668EB9280176169D1C33CFD8ED6F04690A0BCC88A93FC4AE ) => ( 0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85 )
          • ENSRegistryWithFallback.setSubnodeOwner( node=93CDEB708B7545DC668EB9280176169D1C33CFD8ED6F04690A0BCC88A93FC4AE, label=37F3E0DF12B7B46C3CB953AEA88603315A616E0FD5A3636C89C4DABD69A6B317, owner=0x59E16fcCd424Cc24e280Be16E11Bcd56fb0CE547 ) => ( 9549D45B8DE35ED985D3C4AE30E2FFC5F0E1A2027A518D98BCA49CF708C0A7A6 )
          • ENSRegistryWithFallback.setRecord( node=9549D45B8DE35ED985D3C4AE30E2FFC5F0E1A2027A518D98BCA49CF708C0A7A6, owner=0xc4A74D45AFB04FAEd602C20a5c4c3eAcF43f4cad, resolver=0xF29100983E058B709F3D539b0c765937B804AC15, ttl=0 )
          • PublicResolver.multicallWithNodeCheck( nodehash=9549D45B8DE35ED985D3C4AE30E2FFC5F0E1A2027A518D98BCA49CF708C0A7A6, data=[i5XdcZVJ1FuN417ZhdPErjDi/8Xw4aICelGNmLyknPcIwKemAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUxKdNRa+wT67WAsIKXEw+rPQ/TK0AAAAAAAAAAAAAAAA=] ) => ( results=[] )
            • PublicResolver.setAddr( node=9549D45B8DE35ED985D3C4AE30E2FFC5F0E1A2027A518D98BCA49CF708C0A7A6, coinType=60, addressBytes=0xC4A74D45AFB04FAED602C20A5C4C3EACF43F4CAD )
            • BaseRegistrarImplementation.transferFrom( from=0x59E16fcCd424Cc24e280Be16E11Bcd56fb0CE547, to=0xc4A74D45AFB04FAEd602C20a5c4c3eAcF43f4cad, tokenId=25308102514041779625298004784373259821162920423098545848443775620331382158103 )
            • ETH 0.00000265527307655 0xc4a74d45afb04faed602c20a5c4c3eacf43f4cad.CALL( )
              File 1 of 5: 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 2 of 5: 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 3 of 5: 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.9.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.9.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);
              }
              //SPDX-License-Identifier: MIT
              pragma solidity ^0.8.4;
              import "../utils/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);
                      }
                  }
              }
              //SPDX-License-Identifier: MIT
              pragma solidity ^0.8.4;
              import "../registry/ENS.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 if 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;
              }
              //SPDX-License-Identifier: MIT
              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 > 0 && 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, IERC165} from "../ResolverBase.sol";
              import {IAddrResolver} from "./IAddrResolver.sol";
              import {IAddressResolver} from "./IAddressResolver.sol";
              import {IHasAddressResolver} from "./IHasAddressResolver.sol";
              import {ENSIP19, COIN_TYPE_ETH, COIN_TYPE_DEFAULT} from "../../utils/ENSIP19.sol";
              abstract contract AddrResolver is
                  IAddrResolver,
                  IAddressResolver,
                  IHasAddressResolver,
                  ResolverBase
              {
                  mapping(uint64 => mapping(bytes32 => mapping(uint256 => bytes))) versionable_addresses;
                  /// @notice The supplied address could not be converted to `address`.
                  /// @dev Error selector: `0x8d666f60`
                  error InvalidEVMAddress(bytes addressBytes);
                  /// @notice Set `addr(60)` of the associated ENS node.
                  ///         `address(0)` is stored as `new bytes(20)`.
                  /// @param node The node to update.
                  /// @param _addr The address to set.
                  function setAddr(
                      bytes32 node,
                      address _addr
                  ) external virtual authorised(node) {
                      setAddr(node, COIN_TYPE_ETH, abi.encodePacked(_addr));
                  }
                  /// @notice Get `addr(60)` as `address` of the associated ENS node.
                  /// @param node The node to query.
                  /// @return The associated address.
                  function addr(
                      bytes32 node
                  ) public view virtual override returns (address payable) {
                      return payable(address(bytes20(addr(node, COIN_TYPE_ETH))));
                  }
                  /// @notice Set the address for coin type of the associated ENS node.
                  ///         Reverts `InvalidEVMAddress` if coin type is EVM and not 0 or 20 bytes.
                  /// @param node The node to update.
                  /// @param coinType The coin type.
                  /// @param addressBytes The address to set.
                  function setAddr(
                      bytes32 node,
                      uint256 coinType,
                      bytes memory addressBytes
                  ) public virtual authorised(node) {
                      if (
                          addressBytes.length != 0 &&
                          addressBytes.length != 20 &&
                          ENSIP19.isEVMCoinType(coinType)
                      ) {
                          revert InvalidEVMAddress(addressBytes);
                      }
                      emit AddressChanged(node, coinType, addressBytes);
                      if (coinType == COIN_TYPE_ETH) {
                          emit AddrChanged(node, address(bytes20(addressBytes)));
                      }
                      versionable_addresses[recordVersions[node]][node][
                          coinType
                      ] = addressBytes;
                  }
                  /// @notice Get the address for coin type of the associated ENS node.
                  ///         If coin type is EVM and empty, defaults to `addr(COIN_TYPE_DEFAULT)`.
                  /// @param node The node to query.
                  /// @param coinType The coin type.
                  /// @return addressBytes The assocated address.
                  function addr(
                      bytes32 node,
                      uint256 coinType
                  ) public view virtual override returns (bytes memory addressBytes) {
                      mapping(uint256 => bytes) storage addrs = versionable_addresses[
                          recordVersions[node]
                      ][node];
                      addressBytes = addrs[coinType];
                      if (
                          addressBytes.length == 0 && ENSIP19.chainFromCoinType(coinType) > 0
                      ) {
                          addressBytes = addrs[COIN_TYPE_DEFAULT];
                      }
                  }
                  /// @inheritdoc IHasAddressResolver
                  function hasAddr(
                      bytes32 node,
                      uint256 coinType
                  ) external view returns (bool) {
                      return
                          versionable_addresses[recordVersions[node]][node][coinType].length >
                          0;
                  }
                  /// @inheritdoc IERC165
                  function supportsInterface(
                      bytes4 interfaceId
                  ) public view virtual override returns (bool) {
                      return
                          type(IAddrResolver).interfaceId == interfaceId ||
                          type(IAddressResolver).interfaceId == interfaceId ||
                          type(IHasAddressResolver).interfaceId == interfaceId ||
                          super.supportsInterface(interfaceId);
                  }
              }
              // 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 IHasAddressResolver {
                  /// @notice Determine if an addresss is stored for the coin type of the associated ENS node.
                  /// @param node The node to query.
                  /// @param coinType The coin type.
                  /// @return True if the associated address is not empty.
                  function hasAddr(
                      bytes32 node,
                      uint256 coinType
                  ) external view returns (bool);
              }
              // 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.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 ret 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) {
                              uint256 rest = shortest - idx;
                              if (rest < 32) {
                                  // shift out the irrelevant bits
                                  rest = (32 - rest) << 3; // bits to drop
                                  a >>= rest;
                                  b >>= rest;
                              }
                              if (a < b) {
                                  return -1;
                              } else if (a > b) {
                                  return 1;
                              }
                          }
                          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 ret 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 ret 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 ret 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 ret 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 ret 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 ret 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;
                  }
              }
              //SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import {HexUtils} from "../utils/HexUtils.sol";
              import {NameCoder} from "../utils/NameCoder.sol";
              uint32 constant CHAIN_ID_ETH = 1;
              uint256 constant COIN_TYPE_ETH = 60;
              uint256 constant COIN_TYPE_DEFAULT = 1 << 31; // 0x8000_0000
              string constant SLUG_ETH = "addr"; // <=> COIN_TYPE_ETH
              string constant SLUG_DEFAULT = "default"; // <=> COIN_TYPE_DEFAULT
              string constant TLD_REVERSE = "reverse";
              /// @dev Library for generating reverse names according to ENSIP-19.
              /// https://docs.ens.domains/ensip/19
              library ENSIP19 {
                  /// @dev The supplied address was `0x`.
                  error EmptyAddress();
                  /// @dev Extract Chain ID from `coinType`.
                  /// @param coinType The coin type.
                  /// @return The Chain ID or 0 if non-EVM Chain.
                  function chainFromCoinType(
                      uint256 coinType
                  ) internal pure returns (uint32) {
                      if (coinType == COIN_TYPE_ETH) return CHAIN_ID_ETH;
                      coinType ^= COIN_TYPE_DEFAULT;
                      return uint32(coinType < COIN_TYPE_DEFAULT ? coinType : 0);
                  }
                  /// @dev Determine if Coin Type is for an EVM address.
                  /// @param coinType The coin type.
                  /// @return True if coin type represents an EVM address.
                  function isEVMCoinType(uint256 coinType) internal pure returns (bool) {
                      return coinType == COIN_TYPE_DEFAULT || chainFromCoinType(coinType) > 0;
                  }
                  /// @dev Generate Reverse Name from Address + Coin Type.
                  ///      Reverts `EmptyAddress` if `addressBytes` is `0x`.
                  /// @param addressBytes The input address.
                  /// @param coinType The coin type.
                  /// @return The ENS reverse name, eg. `1234abcd.addr.reverse`.
                  function reverseName(
                      bytes memory addressBytes,
                      uint256 coinType
                  ) internal pure returns (string memory) {
                      if (addressBytes.length == 0) {
                          revert EmptyAddress();
                      }
                      return
                          string(
                              abi.encodePacked(
                                  HexUtils.bytesToHex(addressBytes),
                                  bytes1("."),
                                  coinType == COIN_TYPE_ETH
                                      ? SLUG_ETH
                                      : coinType == COIN_TYPE_DEFAULT
                                          ? SLUG_DEFAULT
                                          : HexUtils.unpaddedUintToHex(coinType, true),
                                  bytes1("."),
                                  TLD_REVERSE
                              )
                          );
                  }
                  /// @dev Parse Reverse Name into Address + Coin Type.
                  ///      Matches: /^[0-9a-fA-F]+\\.([0-9a-f]{1,64}|addr|default)\\.reverse$/.
                  ///      Reverts `DNSDecodingFailed`.
                  /// @param name The DNS-encoded name.
                  /// @return addressBytes The address or empty if invalid.
                  /// @return coinType The coin type.
                  function parse(
                      bytes memory name
                  ) internal pure returns (bytes memory addressBytes, uint256 coinType) {
                      (, uint256 offset) = NameCoder.readLabel(name, 0);
                      bool valid;
                      (addressBytes, valid) = HexUtils.hexToBytes(name, 1, offset);
                      if (!valid || addressBytes.length == 0) return ("", 0); // addressBytes not 1+ hex
                      (valid, coinType) = parseNamespace(name, offset);
                      if (!valid) return ("", 0); // invalid namespace
                  }
                  /// @dev Parse Reverse Namespace into Coin Type.
                  ///      Matches: /^([0-9a-f]{1,64}|addr|default)\\.reverse$/.
                  ///      Reverts `DNSDecodingFailed`.
                  /// @param name The DNS-encoded name.
                  /// @param offset The offset to begin parsing.
                  /// @return valid True if a valid reverse namespace.
                  /// @return coinType The coin type.
                  function parseNamespace(
                      bytes memory name,
                      uint256 offset
                  ) internal pure returns (bool valid, uint256 coinType) {
                      (bytes32 labelHash, uint256 offsetTLD) = NameCoder.readLabel(
                          name,
                          offset
                      );
                      if (labelHash == keccak256(bytes(SLUG_ETH))) {
                          coinType = COIN_TYPE_ETH;
                      } else if (labelHash == keccak256(bytes(SLUG_DEFAULT))) {
                          coinType = COIN_TYPE_DEFAULT;
                      } else if (labelHash == bytes32(0)) {
                          return (false, 0); // no slug
                      } else {
                          (bytes32 word, bool validHex) = HexUtils.hexStringToBytes32(
                              name,
                              1 + offset,
                              offsetTLD
                          );
                          if (!validHex) return (false, 0); // invalid coinType or too long
                          coinType = uint256(word);
                      }
                      (labelHash, offset) = NameCoder.readLabel(name, offsetTLD);
                      if (labelHash != keccak256(bytes(TLD_REVERSE))) return (false, 0); // invalid tld
                      (labelHash, ) = NameCoder.readLabel(name, offset);
                      if (labelHash != bytes32(0)) return (false, 0); // not tld
                      valid = true;
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.4;
              library HexUtils {
                  /// @dev Convert `hexString[pos:end]` to `bytes32`.
                  ///      Accepts 0-64 hex-chars.
                  ///      Uses right alignment: `1` &rarr; `0000000000000000000000000000000000000000000000000000000000000001`.
                  /// @param hexString The string to parse.
                  /// @param pos The index to start parsing.
                  /// @param end The (exclusive) index to stop parsing.
                  /// @return word The parsed bytes32.
                  /// @return valid True if the parse was successful.
                  function hexStringToBytes32(
                      bytes memory hexString,
                      uint256 pos,
                      uint256 end
                  ) internal pure returns (bytes32 word, bool valid) {
                      uint256 nibbles = end - pos;
                      if (nibbles > 64 || end > hexString.length) {
                          return (bytes32(0), false); // too large or out of bounds
                      }
                      uint256 src;
                      assembly {
                          src := add(add(hexString, 32), pos)
                      }
                      valid = unsafeBytes(src, 0, nibbles);
                      assembly {
                          let pad := sub(32, shr(1, add(nibbles, 1))) // number of bytes
                          word := shr(shl(3, pad), mload(0)) // right align
                      }
                  }
                  /// @dev Convert `hexString[pos:end]` to `address`.
                  ///      Accepts exactly 40 hex-chars.
                  /// @param hexString The string to parse.
                  /// @param pos The index to start parsing.
                  /// @param end The (exclusive) index to stop parsing.
                  /// @return addr The parsed address.
                  /// @return valid True if the parse was successful.
                  function hexToAddress(
                      bytes memory hexString,
                      uint256 pos,
                      uint256 end
                  ) internal pure returns (address addr, bool valid) {
                      if (end - pos != 40) return (address(0), false); // wrong length
                      bytes32 word;
                      (word, valid) = hexStringToBytes32(hexString, pos, end);
                      addr = address(uint160(uint256(word)));
                  }
                  /// @dev Convert `hexString[pos:end]` to `bytes`.
                  ///      Accepts 0+ hex-chars.
                  /// @param pos The index to start parsing.
                  /// @param end The (exclusive) index to stop parsing.
                  /// @return v The parsed bytes.
                  /// @return valid True if the parse was successful.
                  function hexToBytes(
                      bytes memory hexString,
                      uint256 pos,
                      uint256 end
                  ) internal pure returns (bytes memory v, bool valid) {
                      uint256 nibbles = end - pos;
                      v = new bytes((1 + nibbles) >> 1); // round up
                      uint256 src;
                      uint256 dst;
                      assembly {
                          src := add(add(hexString, 32), pos)
                          dst := add(v, 32)
                      }
                      valid = unsafeBytes(src, dst, nibbles);
                  }
                  /// @dev Convert arbitrary hex-encoded memory to bytes.
                  ///      If nibbles is odd, leading hex-char is padded, eg. `F` &rarr; `0x0F`.
                  ///      Matches: /^[0-9a-f]*$/i.
                  /// @param src The memory offset of first hex-char of input.
                  /// @param dst The memory offset of first byte of output (cannot alias `src`).
                  /// @param nibbles The number of hex-chars to convert.
                  /// @return valid True if all characters were hex.
                  function unsafeBytes(
                      uint256 src,
                      uint256 dst,
                      uint256 nibbles
                  ) internal pure returns (bool valid) {
                      assembly {
                          function getHex(c, i) -> ascii {
                              c := byte(i, c)
                              // chars 48-57: 0-9
                              if and(gt(c, 47), lt(c, 58)) {
                                  ascii := sub(c, 48)
                                  leave
                              }
                              // chars 65-70: A-F
                              if and(gt(c, 64), lt(c, 71)) {
                                  ascii := add(sub(c, 65), 10)
                                  leave
                              }
                              // chars 97-102: a-f
                              if and(gt(c, 96), lt(c, 103)) {
                                  ascii := add(sub(c, 97), 10)
                                  leave
                              }
                              // invalid char
                              ascii := 0x100
                          }
                          valid := true
                          let end := add(src, nibbles)
                          if and(nibbles, 1) {
                              let b := getHex(mload(src), 0) // "f" -> 15
                              mstore8(dst, b) // write ascii byte
                              src := add(src, 1) // update pointers
                              dst := add(dst, 1)
                              if gt(b, 255) {
                                  valid := false
                                  src := end // terminate loop
                              }
                          }
                          for {} lt(src, end) {
                              src := add(src, 2) // 2 nibbles
                              dst := add(dst, 1) // per byte
                          } {
                              let word := mload(src) // read word (left aligned)
                              let b := or(shl(4, getHex(word, 0)), getHex(word, 1)) // "ff" -> 255
                              if gt(b, 255) {
                                  valid := false
                                  break
                              }
                              mstore8(dst, b) // write ascii byte
                          }
                      }
                  }
                  /// @dev Format `address` as a hex string.
                  /// @param addr The address to format.
                  /// @return hexString The corresponding hex string w/o a 0x-prefix.
                  function addressToHex(
                      address addr
                  ) internal pure returns (string memory hexString) {
                      // return bytesToHex(abi.encodePacked(addr));
                      hexString = new string(40);
                      uint256 dst;
                      assembly {
                          mstore(0, addr)
                          dst := add(hexString, 32)
                      }
                      unsafeHex(12, dst, 40);
                  }
                  /// @dev Format `uint256` as a variable-length hex string without zero padding.
                  /// * unpaddedUintToHex(0, true)  = "0"
                  /// * unpaddedUintToHex(1, true)  = "1"
                  /// * unpaddedUintToHex(0, false) = "00"
                  /// * unpaddedUintToHex(1, false) = "01"
                  /// @param value The number to format.
                  /// @param dropZeroNibble If true, the leading byte will use one nibble if less than 16.
                  /// @return hexString The corresponding hex string w/o an 0x-prefix.
                  function unpaddedUintToHex(
                      uint256 value,
                      bool dropZeroNibble
                  ) internal pure returns (string memory hexString) {
                      uint256 temp = value;
                      uint256 shift;
                      for (uint256 b = 128; b >= 8; b >>= 1) {
                          if (temp < (1 << b)) {
                              shift += b; // number of zero upper bits
                          } else {
                              temp >>= b; // shift away lower half
                          }
                      }
                      if (dropZeroNibble && temp < 16) shift += 4;
                      uint256 nibbles = 64 - (shift >> 2);
                      hexString = new string(nibbles);
                      uint256 dst;
                      assembly {
                          mstore(0, shl(shift, value)) // left-align
                          dst := add(hexString, 32)
                      }
                      unsafeHex(0, dst, nibbles);
                  }
                  /// @dev Format `bytes` as a hex string.
                  /// @param v The bytes to format.
                  /// @return hexString The corresponding hex string w/o a 0x-prefix.
                  function bytesToHex(
                      bytes memory v
                  ) internal pure returns (string memory hexString) {
                      uint256 nibbles = v.length << 1;
                      hexString = new string(nibbles);
                      uint256 src;
                      uint256 dst;
                      assembly {
                          src := add(v, 32)
                          dst := add(hexString, 32)
                      }
                      unsafeHex(src, dst, nibbles);
                  }
                  /// @dev Converts arbitrary memory to a hex string.
                  /// @param src The memory offset of first nibble of input.
                  /// @param dst The memory offset of first hex-char of output (can alias `src`).
                  /// @param nibbles The number of nibbles to convert and the byte-length of the output.
                  function unsafeHex(
                      uint256 src,
                      uint256 dst,
                      uint256 nibbles
                  ) internal pure {
                      unchecked {
                          for (uint256 end = dst + nibbles; dst < end; src += 32) {
                              uint256 word;
                              assembly {
                                  word := mload(src)
                              }
                              for (uint256 shift = 256; dst < end && shift > 0; dst++) {
                                  uint256 b = (word >> (shift -= 4)) & 15; // each nibble
                                  b = b < 10 ? b + 0x30 : b + 0x57; // ("a" - 10) => 0x57
                                  assembly {
                                      mstore8(dst, b)
                                  }
                              }
                          }
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import {HexUtils} from "../utils/HexUtils.sol";
              /// @dev Library for encoding/decoding names.
              ///
              /// An ENS name is stop-separated labels, eg. "aaa.bb.c".
              ///
              /// A DNS-encoded name is composed of byte length-prefixed labels with a terminator byte.
              /// eg. "\\x03aaa\\x02bb\\x01c\\x00".
              /// - maximum label length is 255 bytes.
              /// - length = 0 is reserved for the terminator (root).
              ///
              /// To encode a label larger than 255 bytes, use a hashed label.
              /// A label of any length can be converted to a hashed label.
              ///
              /// A hashed label is encoded as "[" + toHex(keccak256(label)) + "]".
              /// eg. [af2caa1c2ca1d027f1ac823b529d0a67cd144264b2789fa2ea4d63a67c7103cc] = "vitalik".
              /// - always 66 bytes.
              /// - matches: `/^\\[[0-9a-f]{64}\\]$/`.
              ///
              /// w/o hashed labels: `dns.length == 2 + ens.length` and the mapping is injective.
              ///  w/ hashed labels: `dns.length == 2 + ens.split('.').map(x => x.utf8Length).sum(n => n > 255 ? 66 : n)`.
              library NameCoder {
                  /// @dev The DNS-encoded name is malformed.
                  error DNSDecodingFailed(bytes dns);
                  /// @dev A label of the ENS name has an invalid size.
                  error DNSEncodingFailed(string ens);
                  /// @dev Same as `BytesUtils.readLabel()` but supports hashed labels.
                  ///      Only the last labelHash is zero.
                  ///      Disallows hashed label of zero (eg. `[0..0]`) to prevent confusion with terminator.
                  ///      Reverts `DNSDecodingFailed`.
                  /// @param name The DNS-encoded name.
                  /// @param idx The offset into `name` to start reading.
                  /// @return labelHash The resulting labelhash.
                  /// @return newIdx The offset into `name` of the next label.
                  function readLabel(
                      bytes memory name,
                      uint256 idx
                  ) internal pure returns (bytes32 labelHash, uint256 newIdx) {
                      if (idx >= name.length) revert DNSDecodingFailed(name); // "readLabel: expected length"
                      uint256 len = uint256(uint8(name[idx++]));
                      newIdx = idx + len;
                      if (newIdx > name.length) revert DNSDecodingFailed(name); // "readLabel: expected label"
                      if (len == 66 && name[idx] == "[" && name[newIdx - 1] == "]") {
                          bool valid;
                          (labelHash, valid) = HexUtils.hexStringToBytes32(
                              name,
                              idx + 1,
                              newIdx - 1
                          ); // will not revert
                          if (!valid || labelHash == bytes32(0)) {
                              revert DNSDecodingFailed(name); // "readLabel: malformed" or null literal
                          }
                      } else if (len > 0) {
                          assembly {
                              labelHash := keccak256(add(add(name, idx), 32), len)
                          }
                      }
                  }
                  /// @dev Same as `BytesUtils.namehash()` but supports hashed labels.
                  ///      Reverts `DNSDecodingFailed`.
                  /// @param name The DNS-encoded name.
                  /// @param idx The offset into name start hashing.
                  /// @return hash The resulting namehash.
                  function namehash(
                      bytes memory name,
                      uint256 idx
                  ) internal pure returns (bytes32 hash) {
                      (hash, idx) = readLabel(name, idx);
                      if (hash == bytes32(0)) {
                          if (idx != name.length) revert DNSDecodingFailed(name); // "namehash: Junk at end of name"
                      } else {
                          bytes32 parent = namehash(name, idx);
                          assembly {
                              mstore(0, parent)
                              mstore(32, hash)
                              hash := keccak256(0, 64)
                          }
                      }
                  }
                  /// @dev Convert DNS-encoded name to ENS name.
                  ///      Reverts `DNSDecodingFailed`.
                  /// @param dns The DNS-encoded name to convert, eg. `\\x03aaa\\x02bb\\x01c\\x00`.
                  /// @return ens The equivalent ENS name, eg. `aaa.bb.c`.
                  function decode(
                      bytes memory dns
                  ) internal pure returns (string memory ens) {
                      unchecked {
                          uint256 n = dns.length;
                          if (n == 1 && dns[0] == 0) return ""; // only valid answer is root
                          if (n < 3) revert DNSDecodingFailed(dns);
                          bytes memory v = new bytes(n - 2); // always 2-shorter
                          uint256 src;
                          uint256 dst;
                          while (src < n) {
                              uint8 len = uint8(dns[src++]);
                              if (len == 0) break;
                              uint256 end = src + len;
                              if (end > dns.length) revert DNSDecodingFailed(dns); // overflow
                              if (dst > 0) v[dst++] = "."; // skip first stop
                              while (src < end) {
                                  bytes1 x = dns[src++]; // read byte
                                  if (x == ".") revert DNSDecodingFailed(dns); // malicious label
                                  v[dst++] = x; // write byte
                              }
                          }
                          if (src != dns.length) revert DNSDecodingFailed(dns); // junk at end
                          return string(v);
                      }
                  }
                  /// @dev Convert ENS name to DNS-encoded name.
                  ///      Hashes labels longer than 255 bytes.
                  ///      Reverts `DNSEncodingFailed`.
                  /// @param ens The ENS name to convert, eg. `aaa.bb.c`.
                  /// @return dns The corresponding DNS-encoded name, eg. `\\x03aaa\\x02bb\\x01c\\x00`.
                  function encode(
                      string memory ens
                  ) internal pure returns (bytes memory dns) {
                      unchecked {
                          uint256 n = bytes(ens).length;
                          if (n == 0) return hex"00"; // root
                          dns = new bytes(n + 2);
                          uint256 start;
                          assembly {
                              start := add(dns, 32) // first byte of output
                          }
                          uint256 end = start; // remember position to write length
                          for (uint256 i; i < n; i++) {
                              bytes1 x = bytes(ens)[i]; // read byte
                              if (x == ".") {
                                  start = _createHashedLabel(start, end);
                                  if (start == 0) revert DNSEncodingFailed(ens);
                                  end = start; // jump to next position
                              } else {
                                  assembly {
                                      end := add(end, 1) // increase length
                                      mstore(end, x) // write byte
                                  }
                              }
                          }
                          start = _createHashedLabel(start, end);
                          if (start == 0) revert DNSEncodingFailed(ens);
                          assembly {
                              mstore8(start, 0) // terminal byte
                              mstore(dns, sub(start, add(dns, 31))) // truncate length
                          }
                      }
                  }
                  /// @dev Write the label length.
                  ///      If longer than 255, writes a hashed label instead.
                  /// @param start The memory offset of the length-prefixed label.
                  /// @param end The memory offset at the end of the label.
                  /// @return next The memory offset for the next label.
                  ///              Returns 0 if label is empty (handled by caller).
                  function _createHashedLabel(
                      uint256 start,
                      uint256 end
                  ) internal pure returns (uint256 next) {
                      uint256 size = end - start; // length of label
                      if (size > 255) {
                          assembly {
                              mstore(0, keccak256(add(start, 1), size)) // compute hash of label
                          }
                          HexUtils.unsafeHex(0, start + 2, 64); // override label with hex(hash)
                          assembly {
                              mstore8(add(start, 1), 0x5B) // "["
                              mstore8(add(start, 66), 0x5D) // "]"
                          }
                          size = 66;
                      }
                      if (size > 0) {
                          assembly {
                              mstore8(start, size) // update length
                          }
                          next = start + 1 + size; // advance
                      }
                  }
              }
              //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 4 of 5: ExponentialPremiumPriceOracle
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
              pragma solidity ^0.8.0;
              import "../utils/Context.sol";
              /**
               * @dev Contract module which provides a basic access control mechanism, where
               * there is an account (an owner) that can be granted exclusive access to
               * specific functions.
               *
               * By default, the owner account will be the one that deploys the contract. This
               * can later be changed with {transferOwnership}.
               *
               * This module is used through inheritance. It will make available the modifier
               * `onlyOwner`, which can be applied to your functions to restrict their use to
               * the owner.
               */
              abstract contract Ownable is Context {
                  address private _owner;
                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                  /**
                   * @dev Initializes the contract setting the deployer as the initial owner.
                   */
                  constructor() {
                      _transferOwnership(_msgSender());
                  }
                  /**
                   * @dev Throws if called by any account other than the owner.
                   */
                  modifier onlyOwner() {
                      _checkOwner();
                      _;
                  }
                  /**
                   * @dev Returns the address of the current owner.
                   */
                  function owner() public view virtual returns (address) {
                      return _owner;
                  }
                  /**
                   * @dev Throws if the sender is not the owner.
                   */
                  function _checkOwner() internal view virtual {
                      require(owner() == _msgSender(), "Ownable: caller is not the owner");
                  }
                  /**
                   * @dev Leaves the contract without owner. It will not be possible to call
                   * `onlyOwner` functions anymore. Can only be called by the current owner.
                   *
                   * NOTE: Renouncing ownership will leave the contract without an owner,
                   * thereby removing any functionality that is only available to the owner.
                   */
                  function renounceOwnership() public virtual onlyOwner {
                      _transferOwnership(address(0));
                  }
                  /**
                   * @dev Transfers ownership of the contract to a new account (`newOwner`).
                   * Can only be called by the current owner.
                   */
                  function transferOwnership(address newOwner) public virtual onlyOwner {
                      require(newOwner != address(0), "Ownable: new owner is the zero address");
                      _transferOwnership(newOwner);
                  }
                  /**
                   * @dev Transfers ownership of the contract to a new account (`newOwner`).
                   * Internal function without access restriction.
                   */
                  function _transferOwnership(address newOwner) internal virtual {
                      address oldOwner = _owner;
                      _owner = newOwner;
                      emit OwnershipTransferred(oldOwner, newOwner);
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Provides information about the current execution context, including the
               * sender of the transaction and its data. While these are generally available
               * via msg.sender and msg.data, they should not be accessed in such a direct
               * manner, since when dealing with meta-transactions the account sending and
               * paying for execution may not be the actual sender (as far as an application
               * is concerned).
               *
               * This contract is only required for intermediate, library-like contracts.
               */
              abstract contract Context {
                  function _msgSender() internal view virtual returns (address) {
                      return msg.sender;
                  }
                  function _msgData() internal view virtual returns (bytes calldata) {
                      return msg.data;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts 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);
              }
              //SPDX-License-Identifier: MIT
              pragma solidity ~0.8.17;
              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);
                  }
              }
              //SPDX-License-Identifier: MIT
              pragma solidity >=0.8.17 <0.9.0;
              interface IPriceOracle {
                  struct Price {
                      uint256 base;
                      uint256 premium;
                  }
                  /**
                   * @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 base premium tuple of base price + premium price
                   */
                  function price(
                      string calldata name,
                      uint256 expires,
                      uint256 duration
                  ) external view returns (Price calldata);
              }
              //SPDX-License-Identifier: MIT
              pragma solidity ~0.8.17;
              import "./IPriceOracle.sol";
              import "./StringUtils.sol";
              import "@openzeppelin/contracts/access/Ownable.sol";
              import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
              interface AggregatorInterface {
                  function latestAnswer() external view returns (int256);
              }
              // StablePriceOracle sets a price in USD, based on an oracle.
              contract StablePriceOracle is IPriceOracle {
                  using StringUtils for *;
                  // Rent in base price units by length
                  uint256 public immutable price1Letter;
                  uint256 public immutable price2Letter;
                  uint256 public immutable price3Letter;
                  uint256 public immutable price4Letter;
                  uint256 public immutable price5Letter;
                  // Oracle address
                  AggregatorInterface public immutable usdOracle;
                  event RentPriceChanged(uint256[] prices);
                  constructor(AggregatorInterface _usdOracle, uint256[] memory _rentPrices) {
                      usdOracle = _usdOracle;
                      price1Letter = _rentPrices[0];
                      price2Letter = _rentPrices[1];
                      price3Letter = _rentPrices[2];
                      price4Letter = _rentPrices[3];
                      price5Letter = _rentPrices[4];
                  }
                  function price(
                      string calldata name,
                      uint256 expires,
                      uint256 duration
                  ) external view override returns (IPriceOracle.Price memory) {
                      uint256 len = name.strlen();
                      uint256 basePrice;
                      if (len >= 5) {
                          basePrice = price5Letter * duration;
                      } else if (len == 4) {
                          basePrice = price4Letter * duration;
                      } else if (len == 3) {
                          basePrice = price3Letter * duration;
                      } else if (len == 2) {
                          basePrice = price2Letter * duration;
                      } else {
                          basePrice = price1Letter * duration;
                      }
                      return
                          IPriceOracle.Price({
                              base: attoUSDToWei(basePrice),
                              premium: attoUSDToWei(_premium(name, expires, duration))
                          });
                  }
                  /**
                   * @dev Returns the pricing premium in wei.
                   */
                  function premium(
                      string calldata name,
                      uint256 expires,
                      uint256 duration
                  ) external view returns (uint256) {
                      return attoUSDToWei(_premium(name, expires, duration));
                  }
                  /**
                   * @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());
                      return (amount * 1e8) / ethPrice;
                  }
                  function weiToAttoUSD(uint256 amount) internal view returns (uint256) {
                      uint256 ethPrice = uint256(usdOracle.latestAnswer());
                      return (amount * ethPrice) / 1e8;
                  }
                  function supportsInterface(
                      bytes4 interfaceID
                  ) public view virtual returns (bool) {
                      return
                          interfaceID == type(IERC165).interfaceId ||
                          interfaceID == type(IPriceOracle).interfaceId;
                  }
              }
              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 (uint256) {
                      uint256 len;
                      uint256 i = 0;
                      uint256 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 5 of 5: 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");
                  _;
                }
              }