// 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/ERC165.sol



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

// 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/ABIResolver.sol



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

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

  function ABI(
    bytes32 node,
    uint256 contentTypes
  ) external view virtual override returns (uint256, bytes memory) {
    mapping(uint256 => bytes) storage abiset = versionable_abis[
      recordVersions[node]
    ][node];

    for (
      uint256 contentType = 1;
      contentType <= contentTypes;
      contentType <<= 1
    ) {
      if ((contentType & contentTypes) != 0 && abiset[contentType].length > 0) {
        return (contentType, abiset[contentType]);
      }
    }

    return (0, bytes(""));
  }

  function supportsInterface(
    bytes4 interfaceID
  ) public view virtual override returns (bool) {
    return
      interfaceID == type(IABIResolver).interfaceId ||
      super.supportsInterface(interfaceID);
  }
}

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



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

import "../ResolverBase.sol";
import "./IAddrResolver.sol";
import "./IAddressResolver.sol";

abstract contract AddrResolver is
  IAddrResolver,
  IAddressResolver,
  ResolverBase
{
  uint256 private constant COIN_TYPE_ETH = 60;

  mapping(uint64 => mapping(bytes32 => mapping(uint256 => bytes))) versionable_addresses;

  function setAddr(bytes32 node, address a) external virtual authorised(node) {
    setAddr(node, COIN_TYPE_ETH, addressToBytes(a));
  }

  function addr(
    bytes32 node
  ) public view virtual override returns (address payable) {
    bytes memory a = addr(node, COIN_TYPE_ETH);
    if (a.length == 0) {
      return payable(0);
    }
    return bytesToAddress(a);
  }

  function setAddr(
    bytes32 node,
    uint256 coinType,
    bytes memory a
  ) public virtual authorised(node) {
    emit AddressChanged(node, coinType, a);
    if (coinType == COIN_TYPE_ETH) {
      emit AddrChanged(node, bytesToAddress(a));
    }
    versionable_addresses[recordVersions[node]][node][coinType] = a;
  }

  function addr(
    bytes32 node,
    uint256 coinType
  ) public view virtual override returns (bytes memory) {
    return versionable_addresses[recordVersions[node]][node][coinType];
  }

  function supportsInterface(
    bytes4 interfaceID
  ) public view virtual override returns (bool) {
    return
      interfaceID == type(IAddrResolver).interfaceId ||
      interfaceID == type(IAddressResolver).interfaceId ||
      super.supportsInterface(interfaceID);
  }

  function bytesToAddress(
    bytes memory b
  ) internal pure returns (address payable a) {
    require(b.length == 20);
    assembly {
      a := div(mload(add(b, 32)), exp(256, 12))
    }
  }

  function addressToBytes(address a) internal pure returns (bytes memory b) {
    b = new bytes(20);
    assembly {
      mstore(add(b, 32), mul(a, exp(256, 12)))
    }
  }
}

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



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

  function setContenthash(
    bytes32 node,
    bytes calldata hash
  ) external virtual authorised(node) {
    versionable_hashes[recordVersions[node]][node] = hash;
    emit ContenthashChanged(node, hash);
  }

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

// 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/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/InterfaceResolver.sol



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

  function setInterface(
    bytes32 node,
    bytes4 interfaceID,
    address implementer
  ) external virtual authorised(node) {
    versionable_interfaces[recordVersions[node]][node][
      interfaceID
    ] = implementer;
    emit InterfaceChanged(node, interfaceID, implementer);
  }

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

// 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/profiles/IVersionableResolver.sol



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

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



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

  function setName(
    bytes32 node,
    string calldata newName
  ) external virtual authorised(node) {
    versionable_names[recordVersions[node]][node] = newName;
    emit NameChanged(node, newName);
  }

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

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



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

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

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

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



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

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

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

// File: contracts/resolvers/PublicResolver.sol



//SPDX-License-Identifier: MIT
pragma solidity >=0.8.17 <0.9.0;

import "@openzeppelin/contracts/access/Ownable.sol";
import "../registry/SNS.sol";
import "./profiles/ABIResolver.sol";
import "./profiles/AddrResolver.sol";
import "./profiles/ContentHashResolver.sol";
import "./profiles/InterfaceResolver.sol";
import "./profiles/NameResolver.sol";
import "./profiles/PubkeyResolver.sol";
import "./profiles/TextResolver.sol";

contract PublicResolver is
  Ownable,
  ABIResolver,
  AddrResolver,
  ContentHashResolver,
  // DNSResolver,
  InterfaceResolver,
  NameResolver,
  PubkeyResolver,
  TextResolver
{
  SNS immutable sns;
  address public trustedReverseRegistrar;
  address public trustedSeeDAOController;
  address public trustedSeeDAOSubdomainController;

  mapping(address => mapping(address => bool)) private _operatorApprovals;

  mapping(address => mapping(bytes32 => mapping(address => bool)))
    private _tokenApprovals;

  event ApprovalForAll(
    address indexed owner,
    address indexed operator,
    bool approved
  );

  event Approved(
    address owner,
    bytes32 indexed node,
    address indexed delegate,
    bool indexed approved
  );

  constructor(
    SNS _sns,
    address _trustedReverseRegistrar,
    address _trustedSeeDAOController,
    address _trustedSeeDAOSubdomainController
  ) {
    sns = _sns;
    trustedReverseRegistrar = _trustedReverseRegistrar;
    trustedSeeDAOController = _trustedSeeDAOController;
    trustedSeeDAOSubdomainController = _trustedSeeDAOSubdomainController;
  }

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

  function isApprovedForAll(
    address account,
    address operator
  ) public view returns (bool) {
    return _operatorApprovals[account][operator];
  }

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

  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 == trustedReverseRegistrar ||
      msg.sender == trustedSeeDAOController ||
      msg.sender == trustedSeeDAOSubdomainController
    ) {
      return true;
    }
    address owner = sns.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(
      ABIResolver,
      AddrResolver,
      ContentHashResolver,
      InterfaceResolver,
      NameResolver,
      PubkeyResolver,
      TextResolver
    )
    returns (bool)
  {
    return super.supportsInterface(interfaceID);
  }

  function updateReverseRegistrar(
    address _trustedReverseRegistrar
  ) external onlyOwner {
    trustedReverseRegistrar = _trustedReverseRegistrar;
  }

  function updateSeeDAOController(
    address _trustedSeeDAOController
  ) external onlyOwner {
    trustedSeeDAOController = _trustedSeeDAOController;
  }

  function updateSeeDAOSubdomainController(
    address _trustedSeeDAOSubdomainController
  ) external onlyOwner {
    trustedSeeDAOSubdomainController = _trustedSeeDAOSubdomainController;
  }
}

// File: contracts/resolvers/ResolverBase.sol



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

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