// File: @openzeppelin/contracts/access/Ownable.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling 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);
    }
}

// File: @openzeppelin/contracts/utils/Context.sol



// 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;
    }
}

// File: @openzeppelin/contracts/utils/introspection/IERC165.sol



// 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);
}

// File: contracts/registry/SNS.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

interface SNS {
  event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner);

  event Transfer(bytes32 indexed node, address owner);

  event NewResolver(bytes32 indexed node, address resolver);

  event NewTTL(bytes32 indexed node, uint64 ttl);

  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: contracts/resolvers/profiles/IABIResolver.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

interface IABIResolver {
  event ABIChanged(bytes32 indexed node, uint256 indexed contentType);

  function ABI(
    bytes32 node,
    uint256 contentTypes
  ) external view returns (uint256, bytes memory);
}

// File: contracts/resolvers/profiles/IAddressResolver.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

interface IAddressResolver {
  event AddressChanged(
    bytes32 indexed node,
    uint256 coinType,
    bytes newAddress
  );

  function addr(
    bytes32 node,
    uint256 coinType
  ) external view returns (bytes memory);
}

// File: contracts/resolvers/profiles/IAddrResolver.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

interface IAddrResolver {
  event AddrChanged(bytes32 indexed node, address a);

  function addr(bytes32 node) external view returns (address payable);
}

// File: contracts/resolvers/profiles/IContentHashResolver.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

interface IContentHashResolver {
  event ContenthashChanged(bytes32 indexed node, bytes hash);

  function contenthash(bytes32 node) external view returns (bytes memory);
}

// File: contracts/resolvers/profiles/IExtendedResolver.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

interface IExtendedResolver {
  function resolve(
    bytes memory name,
    bytes memory data
  ) external view returns (bytes memory);
}

// File: contracts/resolvers/profiles/IInterfaceResolver.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

interface IInterfaceResolver {
  event InterfaceChanged(
    bytes32 indexed node,
    bytes4 indexed interfaceID,
    address implementer
  );

  function interfaceImplementer(
    bytes32 node,
    bytes4 interfaceID
  ) external view returns (address);
}

// File: contracts/resolvers/profiles/INameResolver.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

interface INameResolver {
  event NameChanged(bytes32 indexed node, string name);

  function name(bytes32 node) external view returns (string memory);
}

// File: contracts/resolvers/profiles/IPubkeyResolver.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

interface IPubkeyResolver {
  event PubkeyChanged(bytes32 indexed node, bytes32 x, bytes32 y);

  function pubkey(bytes32 node) external view returns (bytes32 x, bytes32 y);
}

// File: contracts/resolvers/profiles/ITextResolver.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

interface ITextResolver {
  event TextChanged(
    bytes32 indexed node,
    string indexed indexedKey,
    string key,
    string value
  );

  function text(
    bytes32 node,
    string calldata key
  ) external view returns (string memory);
}

// File: contracts/resolvers/Resolver.sol



//SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import "./profiles/IABIResolver.sol";
import "./profiles/IAddressResolver.sol";
import "./profiles/IAddrResolver.sol";
import "./profiles/IContentHashResolver.sol";
import "./profiles/IInterfaceResolver.sol";
import "./profiles/INameResolver.sol";
import "./profiles/IPubkeyResolver.sol";
import "./profiles/ITextResolver.sol";
import "./profiles/IExtendedResolver.sol";

interface Resolver is
  IERC165,
  IABIResolver,
  IAddressResolver,
  IAddrResolver,
  IContentHashResolver,
  IInterfaceResolver,
  INameResolver,
  IPubkeyResolver,
  ITextResolver,
  IExtendedResolver
{
  function setApprovalForAll(address, bool) external;

  function approve(bytes32 node, address delegate, bool approved) external;

  function isApprovedForAll(address account, address operator) external;

  function isApprovedFor(
    address owner,
    bytes32 node,
    address delegate
  ) external;

  function setABI(
    bytes32 node,
    uint256 contentType,
    bytes calldata data
  ) external;

  function setAddr(bytes32 node, address addr) external;

  function setAddr(bytes32 node, uint256 coinType, bytes calldata a) external;

  function setContenthash(bytes32 node, bytes calldata hash) external;

  function setName(bytes32 node, string calldata _name) external;

  function setPubkey(bytes32 node, bytes32 x, bytes32 y) external;

  function setText(
    bytes32 node,
    string calldata key,
    string calldata value
  ) external;

  function setInterface(
    bytes32 node,
    bytes4 interfaceID,
    address implementer
  ) external;
}

// File: contracts/reverseRegistrar/IReverseRegistrar.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

interface IReverseRegistrar {
  function claimForAddr(
    address addr,
    address owner,
    address resolver
  ) 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);
}

// File: contracts/reverseRegistrar/ReverseRegistrar.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

import "../registry/SNS.sol";
import "./IReverseRegistrar.sol";
import "../resolvers/Resolver.sol";
import "../root/Controllable.sol";

contract ReverseRegistrar is Controllable, IReverseRegistrar {
  bytes32 constant lookup =
    0x3031323334353637383961626364656600000000000000000000000000000000;

  // value of `namehash('addr.reverse')`
  bytes32 constant ADDR_REVERSE_NODE =
    0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2;

  SNS public immutable sns;
  address public defaultResolver;

  event ReverseClaimed(address indexed addr, bytes32 indexed node);

  constructor(SNS _sns) {
    sns = _sns;
  }

  modifier authorised(address addr) {
    require(
      addr == msg.sender ||
        controllers[msg.sender] ||
        sns.isApprovedForAll(addr, msg.sender),
      "ReverseRegistrar: Caller is not a controller or authorised by address"
    );
    _;
  }

  function claimForAddr(
    address addr,
    address owner,
    address resolver
  ) public override authorised(addr) returns (bytes32) {
    bytes32 labelHash = sha3HexAddress(addr);
    bytes32 reverseNode = keccak256(
      abi.encodePacked(ADDR_REVERSE_NODE, labelHash)
    );
    emit ReverseClaimed(addr, reverseNode);
    sns.setSubnodeRecord(ADDR_REVERSE_NODE, labelHash, owner, resolver, 0);
    return reverseNode;
  }

  function setNameForAddr(
    address addr,
    address owner,
    address resolver,
    string memory name
  ) public override returns (bytes32) {
    bytes32 node = claimForAddr(addr, owner, resolver);
    Resolver(resolver).setName(node, name);
    return node;
  }

  function node(address addr) public pure override returns (bytes32) {
    return keccak256(abi.encodePacked(ADDR_REVERSE_NODE, sha3HexAddress(addr)));
  }

  function sha3HexAddress(address addr) private pure returns (bytes32 ret) {
    assembly {
      for {
        let i := 40
      } gt(i, 0) {

      } {
        i := sub(i, 1)
        mstore8(i, byte(and(addr, 0xf), lookup))
        addr := div(addr, 0x10)
        i := sub(i, 1)
        mstore8(i, byte(and(addr, 0xf), lookup))
        addr := div(addr, 0x10)
      }

      ret := keccak256(0, 40)
    }
  }
}

// File: contracts/root/Controllable.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/access/Ownable.sol";

contract Controllable is Ownable {
  mapping(address => bool) public controllers;

  event ControllerChanged(address indexed controller, bool enabled);

  modifier onlyController() {
    require(
      controllers[msg.sender],
      "Controllable: Caller is not a controller"
    );
    _;
  }

  function setController(address controller, bool enabled) public onlyOwner {
    controllers[controller] = enabled;
    emit ControllerChanged(controller, enabled);
  }
}