// File: contracts/interfaces/IEthRegistrarController.sol



//SPDX-License-Identifier: MIT
pragma solidity ~0.8.17;

import "./IPriceOracle.sol";

interface IEthRegistrarController {
    function rentPrice(
        string memory,
        uint256
    ) external view returns (IPriceOracle.Price memory);

    function available(string memory) external returns (bool);

    function makeCommitment(
        string memory name,
        address owner,
        uint256 duration,
        bytes32 secret,
        address resolver,
        bytes[] calldata data,
        bool reverseRecord,
        uint16 ownerControlledFuses
    ) external pure returns (bytes32);

    function commit(bytes32) external;

    function register(
        string memory name,
        address owner,
        uint256 duration,
        bytes32 secret,
        address resolver,
        bytes[] calldata data,
        bool reverseRecord,
        uint16 ownerControlledFuses
    ) external payable;

    function renew(string calldata, uint256) external payable;
}
// File: contracts/interfaces/INamekitEthRegistrar.sol



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

interface INamekitEthRegistrar {
    
    error WithdrawError();
    error Unauthorized();

    function generateReservationNumber(
        string memory label,
        address owner,
        uint256 duration,
        bytes32 secret,
        address resolver,
        bytes[] calldata data,
        bool reverseRecord,
        uint16 ownerControlledFuses
    ) view external returns(bytes32 reservationNumber);

    function requestPermissionToRegister(bytes32 reservationNumber) external;
    
    function completeRegistration(
        string memory label,
        address owner,
        uint256 duration,
        bytes32 secret,
        address resolver,
        bytes[] calldata data,
        bool reverseRecord,
        uint16 ownerControlledFuses
    ) external payable;

    function extendRegistration(string calldata label, uint256 duration) external payable;

    function withdraw() external;
}

// File: contracts/interfaces/IPriceOracle.sol



//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);
}
// File: contracts/NamekitEthRegistrar.sol



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

// ==============================================================
// |  ███╗░░██╗░█████╗░███╗░░░███╗███████╗██╗░░██╗██╗████████╗  |
// |  ████╗░██║██╔══██╗████╗░████║██╔════╝██║░██╔╝██║╚══██╔══╝  |
// |  ██╔██╗██║███████║██╔████╔██║█████╗░░█████═╝░██║░░░██║░░░  |
// |  ██║╚████║██╔══██║██║╚██╔╝██║██╔══╝░░██╔═██╗░██║░░░██║░░░  |
// |  ██║░╚███║██║░░██║██║░╚═╝░██║███████╗██║░╚██╗██║░░░██║░░░  |
// |  ╚═╝░░╚══╝╚═╝░░╚═╝╚═╝░░░░░╚═╝╚══════╝╚═╝░░╚═╝╚═╝░░░╚═╝░░░  |
// ==============================================================
// =================== NamekitEthRegistrar ======================
// ==============================================================
// About NameKit: https://namekit.io/
// NameHash repos: https://github.com/namehash

import "./interfaces/IEthRegistrarController.sol";
import "./interfaces/INamekitEthRegistrar.sol";

/**
 * @title NameKit .ETH Registrar
 * @author @alextnetto - blockful.io
 * @dev Register ENS names under .eth TLD
 */
contract NamekitEthRegistrar is INamekitEthRegistrar {
    /// @notice Treasury address for your NameKit deployment
    address public immutable treasury;
    /// @notice ENS EthRegistrarController smart contract address
    IEthRegistrarController public ethRegistrarController;
    /// @notice address that can set ethRegistrarController address
    address private temporaryAdmin;

    /**
     * @dev Initialize variables for your NameKit deployment treasury and the official ENS .eth registrar
     */
    constructor(address _treasury) {
        treasury = _treasury;
        temporaryAdmin = msg.sender;
    }

    /**
     * @dev Enable collection of refunds
     */
    receive() external payable {}

    /**
     * @dev Enable collection of refunds
     */
    fallback() external payable {}

    /**
     * @notice Generate a unique reservation number for registering a .eth domain with the Ethereum Name Service (ENS).
     * @dev This wrapper function seeks to improve the UX of what a user might be asked to confirm in their wallet.
     * The "label" param is a corrected naming of the "name" param on EthRegistrarController, which is actually a label.
     * The "salt" param is the "secret" param on EthRegistrarController
     * The "resolverRecords" param is the "data" param on EthRegistrarController
     * The "setPrimaryName" param is the "reverseRecord" param on EthRegistrarController
     * The "nameWrapperFuses" param is the "ownerControlledFuses" param on EthRegistrarController
     * @param label Label to be registered under the .eth top-level name.
     * @param owner Address that will be the initial owner and manager of the name
     * @param duration The initial time in seconds the name will be registered for.
     * The minimum duration is 2,419,200 seconds (28 days), defined on EthRegistrarController.
     * @param salt A salt that will contribute "randomness" to the reservationNumber. This same salt must be remembered and used when calling completeRegistration.
     * @param resolver The initial resolver smart contract address that will be used to resolve ENS records for the name.
     * @param resolverRecords An array of resolver records to associate with the domain.
     * @param setPrimaryName A boolean indicating whether the domain should be set as the primary name.
     * @param nameWrapperFuses An integer specifying fuses to the name wrapper.
     * @return reservationNumber A unique reservation number representing the commitment for registering the ENS domain.
     * @dev The reservation number is used to initiate the domain registration process when calling requestPermissionToRegister.
     */
    function generateReservationNumber(
        string memory label,
        address owner,
        uint256 duration,
        bytes32 salt,
        address resolver,
        bytes[] calldata resolverRecords,
        bool setPrimaryName,
        uint16 nameWrapperFuses
    ) view external returns (bytes32 reservationNumber) {
        return ethRegistrarController.makeCommitment(
            label,
            owner,
            duration,
            salt,
            resolver,
            resolverRecords,
            setPrimaryName,
            nameWrapperFuses
        );
    }

    /**
     * @notice Protect against front-running of a .eth name registration.
     * @dev This wrapper function seeks to improve the UX of what a user might be asked to confirm in their wallet.
     * A reservationNumber can be generated by calling the view function generateReservationNumber. 
     * This reservationNumber provides a secure proof that you wanted to register a given name before publicly 
     * revealing to the Ethereum network the name you intend to register (as is done when executing the completeRegistration function).
     *
     * To be valid, a reservationNumber (known as a "commitment" inside ENS smart contracts) must be "consumed"
     * by calling completeRegistration within the duration window from minCommitmentAge to maxCommitmentAge as
     * specified in the EthRegistrarController smart contract.
     *
     * Both of these variables specify a duration in seconds. As of the time this contract is deployed, these
     * values are currently defined as a minimum of 60 seconds and a maximum of 86,400 seconds (24 hours).
     * It should be noted that this function does not check if the name is currently available and makes
     * no guarantee the name will continue to be available in the future when any subsequent completeRegistration
     * call is executed.
     *
     * Additionally, if a reservationNumber ("commitment") has been created using this function at time X, it is an error to attempt to
     * create the exact same reservationNumber again (or extended it) until reaching time X + maxCommitmentAge.
     * @param reservationNumber Secure hash of the exact parameters that will later be used when calling completeRegistration.
     */
    function requestPermissionToRegister(bytes32 reservationNumber) public {
        ethRegistrarController.commit(reservationNumber);
    }

    /**
     * @notice Complete the registration of a .eth domain with the Ethereum Name Service (ENS)
     * @dev This wrapper function seeks to improve the UX of what a user might be asked to confirm in their wallet.
     * The "label" param is a corrected naming of the "name" param on EthRegistrarController, which is actually a label.
     * The "salt" param is the "secret" param on EthRegistrarController
     * The "resolverRecords" param is the "data" param on EthRegistrarController
     * The "setPrimaryName" param is the "reverseRecord" param on EthRegistrarController
     * The "nameWrapperFuses" param is the "ownerControlledFuses" param on EthRegistrarController
     * This function requires sending an appropriate registration fee as part of the transaction.
     * @param label Label to be registered under the .eth top-level name.
     * @param owner Address that will be the initial owner and manager of the name
     * @param duration The initial time in seconds the name will be registered for.
     * The minimum duration is 2,419,200 seconds (28 days), defined on EthRegistrarController.
     * @param salt The same salt that contributed “randomness” to the reservationNumber when generateReservationNumber was called.
     * @param resolver The initial resolver smart contract address that will be used to resolve ENS records for the name.
     * @param resolverRecords An array of resolver records to associate with the domain.
     * @param setPrimaryName A boolean indicating whether the domain should be set as the primary name.
     * @param nameWrapperFuses An integer specifying fuses to the name wrapper.
     */
    function completeRegistration(
        string memory label,
        address owner,
        uint256 duration,
        bytes32 salt,
        address resolver,
        bytes[] calldata resolverRecords,
        bool setPrimaryName,
        uint16 nameWrapperFuses
    ) public payable {
        ethRegistrarController.register{ value: msg.value }(
            label,
            owner,
            duration,
            salt,
            resolver,
            resolverRecords,
            setPrimaryName,
            nameWrapperFuses
        );
    }

    /**
     * @notice Extend the registered duration of a .eth name.
     * @dev The "label" param is a corrected naming of the "name" param on EthRegistrarController, which is actually a label.
     * @param label Label to extend under the .eth top-level name.
     * Ex.: exampleregistration would extend exampleregistration.eth
     * @param duration Time in seconds that the registration will be extended for.
     */
    function extendRegistration(string calldata label, uint256 duration) public payable {
        ethRegistrarController.renew{ value: msg.value }(label, duration);
    }

    /**
     * @notice Withdraw any Ether balance on this contract to the treasury address defined on your NameKit deployment.
     */
    function withdraw() public {
        (bool success, ) = treasury.call{ value: address(this).balance }("");
        if (!success) revert WithdrawError();
    }

    /**
     * @notice Set the address for the ETHRegistrarController contract on the current blockchain.
     * @dev The address for the ETHRegistrarController is different on Mainnet and Goerli.
     * For this contract to have the same address across networks the constructor params need to be the same.
     * Therefore we initialize this param after contract deployment. The deployer can call it just one time.
     */
    function setEthRegistrarController(address _ethRegistrarController) external {
        if (msg.sender != temporaryAdmin) revert Unauthorized();
        temporaryAdmin = address(0);

        ethRegistrarController = IEthRegistrarController(_ethRegistrarController);
    }
}