ENSOpenSubdomainRegistry

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev _Available since v3.1._
 */
interface IERC1155Receiver is IERC165 {
    /**
     * @dev Handles the receipt of a single ERC1155 token type. This function is
     * called at the end of a `safeTransferFrom` after the balance has been updated.
     *
     * NOTE: To accept the transfer, this must return
     * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
     * (i.e. 0xf23a6e61, or its own function selector).
     *
     * @param operator The address which initiated the transfer (i.e. msg.sender)
     * @param from The address which previously owned the token
     * @param id The ID of the token being transferred
     * @param value The amount of tokens being transferred
     * @param data Additional data with no specified format
     * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
     */
    function onERC1155Received(
        address operator,
        address from,
        uint256 id,
        uint256 value,
        bytes calldata data
    ) external returns (bytes4);

    /**
     * @dev Handles the receipt of a multiple ERC1155 token types. This function
     * is called at the end of a `safeBatchTransferFrom` after the balances have
     * been updated.
     *
     * NOTE: To accept the transfer(s), this must return
     * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
     * (i.e. 0xbc197c81, or its own function selector).
     *
     * @param operator The address which initiated the batch transfer (i.e. msg.sender)
     * @param from The address which previously owned the token
     * @param ids An array containing ids of each token being transferred (order and length must match values array)
     * @param values An array containing amounts of each token being transferred (order and length must match ids array)
     * @param data Additional data with no specified format
     * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
     */
    function onERC1155BatchReceived(
        address operator,
        address from,
        uint256[] calldata ids,
        uint256[] calldata values,
        bytes calldata data
    ) external returns (bytes4);
}
"
    },
    "@openzeppelin/contracts/utils/introspection/ERC165.sol": {
      "content": "// 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;
    }
}
"
    },
    "@openzeppelin/contracts/utils/introspection/IERC165.sol": {
      "content": "// 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);
}
"
    },
    "contracts/ENSOpenSubdomainRegistry.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "./interfaces/INameWrapper.sol";
import "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
import "@openzeppelin/contracts/utils/introspection/ERC165.sol";

interface IENS {
    function setOwner(bytes32 node, address owner) external;
    function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external;
    function setResolver(bytes32 node, address resolver) external;
    function owner(bytes32 node) external view returns (address);
    function resolver(bytes32 node) external view returns (address);
}

interface IENSResolver {
    function setAddr(bytes32 node, address addr) external;
    function setText(bytes32 node, string calldata key, string calldata value) external;
    function addr(bytes32 node) external view returns (address);
    function text(bytes32 node, string calldata key) external view returns (string memory);
}

/**
 * @title ENSSubdomainRegistry
 * @dev Ultra-simple subdomain registry allowing anyone to register subdomains for free
 */


contract ENSOpenSubdomainRegistry is IERC1155Receiver {
    IENS public immutable ens;
    INameWrapper public nameWrapper;
    bytes32 public immutable parentNode;
//    address public immutable defaultResolver;
    bool public isParentWrapped;

    // Simple registry to track subdomains that have been registered
    mapping(string => bool) public registeredSubdomains;

    // Events
    event SubdomainRegistered(string indexed subdomain, address indexed owner);
    event ParentDomainOwnershipReturned(address indexed newOwner);

    // Original owner of the parent domain who can reclaim it
    address public immutable parentDomainController;



    modifier validSubdomain(string memory subdomain) {
        require(bytes(subdomain).length > 0, "Empty subdomain");
        require(bytes(subdomain).length <= 63, "Subdomain too long");
        require(_isValidLabel(subdomain), "Invalid characters");
        _;
    }

    constructor(
            address _ens,
            bytes32 _parentNode,
    //        address _defaultResolver,
            address recoverAddress,
            address _nameWrapper
        ) {
            ens = IENS(_ens);
            parentNode = _parentNode;
    //        defaultResolver = _defaultResolver;
            parentDomainController = recoverAddress != address(0) ? recoverAddress : msg.sender;
            nameWrapper = INameWrapper(_nameWrapper);
            isParentWrapped = ens.owner(_parentNode) == _nameWrapper;

        }

    /**
     * @dev Return ownership of the parent domain to the original owner
     * This allows the original owner to renew the domain or make changes
     * Can only be called by the original owner
     */
    function reclaimParentDomain() external {
        require(msg.sender == parentDomainController, "Not the parent domain controller");

        if (isParentWrapped) {
            // For wrapped domains, check if this contract owns the wrapped token
            require(nameWrapper.ownerOf(uint256(parentNode)) == address(this),
                   "Contract doesn't own wrapped token");

            // Transfer the wrapped token back to original owner
            nameWrapper.safeTransferFrom(
                address(this),
                parentDomainController,
                uint256(parentNode),
                1,
                ""
            );
        } else {
            // Original logic for unwrapped domains
            require(ens.owner(parentNode) == address(this),
                   "Contract is not the parent domain coontroller");
            ens.setOwner(parentNode, parentDomainController);
        }

        emit ParentDomainOwnershipReturned(parentDomainController);
    }

    /**
     * @dev Register a new subdomain
     */
    function registerSubdomain(
        string memory subdomain,
        address subdomainOwner
    ) public validSubdomain(subdomain) {
        require(!registeredSubdomains[subdomain], "Subdomain already registered");
        require(subdomainOwner != address(0), "Invalid owner");

        // Mark subdomain as registered
        registeredSubdomains[subdomain] = true;
        // Set up ENS records
        bytes32 label = keccak256(bytes(subdomain));
     

        if (isParentWrapped) {
            // Use NameWrapper for wrapped domains
            nameWrapper.setSubnodeOwner(
                parentNode,
                subdomain,  // string for NameWrapper
                subdomainOwner,
                0,  // fuses
                0   // expiry
            );
        } else {
            // Use ENS registry for unwrapped domains
            ens.setSubnodeOwner(parentNode, label, subdomainOwner);

    //        if (defaultResolver != address(0)) {
    //            ens.setResolver(subnode, defaultResolver);
    //        }
        }
        emit SubdomainRegistered(subdomain, subdomainOwner);
    }

    /**
     * @dev Simple registration for caller
     */
    function registerSubdomainSimple(
        string memory subdomain
    ) external validSubdomain(subdomain) {
        registerSubdomain(subdomain, msg.sender);
    }

    // No admin functions needed - registry is permanently configured at deployment

    // =====================================
    // VIEW FUNCTIONS
    // =====================================

    /**
     * @dev Check if subdomain is available
     */
    function isSubdomainAvailable(string memory subdomain) external view returns (bool) {
        return !registeredSubdomains[subdomain];
    }



    // =====================================
    // INTERNAL FUNCTIONS
    // =====================================

    function _isValidLabel(string memory label) internal pure returns (bool) {
        bytes memory b = bytes(label);
        if (b.length == 0) return false;

        for (uint256 i = 0; i < b.length; i++) {
            bytes1 char = b[i];
            if (!(
                (char >= 0x30 && char <= 0x39) || // 0-9
                (char >= 0x61 && char <= 0x7A) || // a-z
                (char == 0x2D && i != 0 && i != b.length - 1) // - not at start/end
            )) {
                return false;
            }
        }
        return true;
    }

        /**
    * @dev Handle the receipt of a single ERC1155 token type.
    * Required for receiving NameWrapper tokens
    */
    function onERC1155Received(
        address,
        address,
        uint256,
        uint256,
        bytes calldata
    ) external pure returns (bytes4) {
        return IERC1155Receiver.onERC1155Received.selector;
    }

    /**
    * @dev Handle the receipt of multiple ERC1155 token types.
    */
    function onERC1155BatchReceived(
        address,
        address,
        uint256[] calldata,
        uint256[] calldata,
        bytes calldata
    ) external pure returns (bytes4) {
        return IERC1155Receiver.onERC1155BatchReceived.selector;
    }

    /**
    * @dev See {IERC165-supportsInterface}.
    */
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return interfaceId == type(IERC1155Receiver).interfaceId;
    }

// No emergency functions - registry is permanently configured at deployment
}
"
    },
    "contracts/interfaces/INameWrapper.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

interface INameWrapper {
    function ownerOf(uint256 tokenId) external view returns (address);

    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        uint256 amount,
        bytes calldata data
    ) external;

    function setSubnodeOwner(
        bytes32 parentNode,
        string calldata label,
        address owner,
        uint32 fuses,
        uint64 expiry
    ) external returns (bytes32);

    function unwrapETH2LD(
        bytes32 labelhash,
        address registrant,
        address controller
    ) external;
}
"
    }
  },
  "settings": {
    "optimizer": {
      "enabled": true,
      "runs": 200
    },
    "viaIR": true,
    "evmVersion": "paris",
    "outputSelection": {
      "*": {
        "*": [
          "evm.bytecode",
          "evm.deployedBytecode",
          "devdoc",
          "userdoc",
          "metadata",
          "abi"
        ]
      }
    }
  }
}}

Tags:
ERC165, Multisig, Multi-Signature, Factory|addr:0x9ab0b1629fc094b1142f9fe9456cdc34a130c4e5|verified:true|block:23732485|tx:0xf8824004499b952320a93fa387dce79e5b8542f3ab072e7287453bb50b5686b8|first_check:1762347742

Submitted on: 2025-11-05 14:02:24

Comments

Log in to comment.

No comments yet.