{"id":15290562,"url":"https://github.com/sismo-core/ens-sdao","last_synced_at":"2025-08-31T20:39:33.870Z","repository":{"id":46069637,"uuid":"409247387","full_name":"sismo-core/ens-sdao","owner":"sismo-core","description":"Contracts of ENS Subdomain DAO (SDAO). Kickstart a ENS centric DAO.","archived":false,"fork":false,"pushed_at":"2021-11-16T18:08:59.000Z","size":1513,"stargazers_count":110,"open_issues_count":1,"forks_count":12,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-21T22:38:24.430Z","etag":null,"topics":["dao","ens","ethereum","sismo"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sismo-core.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-09-22T14:58:35.000Z","updated_at":"2024-11-03T20:49:07.000Z","dependencies_parsed_at":"2022-09-16T07:50:22.861Z","dependency_job_id":null,"html_url":"https://github.com/sismo-core/ens-sdao","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sismo-core%2Fens-sdao","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sismo-core%2Fens-sdao/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sismo-core%2Fens-sdao/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sismo-core%2Fens-sdao/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sismo-core","download_url":"https://codeload.github.com/sismo-core/ens-sdao/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248690855,"owners_count":21146218,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dao","ens","ethereum","sismo"],"created_at":"2024-09-30T16:08:37.935Z","updated_at":"2025-04-13T09:32:03.382Z","avatar_url":"https://github.com/sismo-core.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Subdomain DAOS\n\nA ENS Subdomain Decentralized Autonomous Organization (SDAO) is a DAO organized around a ENS domain name (e.g `sismo.eth`), which is owned by the DAO.\n\nSDAO members are all owners of a subdomain (e.g `ziki.sismo.eth`). You can read about Sismo's specific SDAO [here](https://blog.sismo.io/what-is-sismo-part-2-sismo-dao-a06f55d1b829).\n\nThis repository publishes a set of smart contracts that can be used to kickstart a SDAO. Please feel free to create issues or to connect with its maintainer: [@sismo_eth]() on twitter or contact at sismo.io\n\n# NPM Package\n\n## Installation\n\nIf using npm\n```console\nnpm install @sismo-core/ens-sdao\n```\nIf using yarn\n```console\nyarn add @sismo-core/ens-sdao\n```\n## Usage\n\nOnce the package installed, the contracts are available using regular solidity imports.\n\nYour Subdomain DAO contract can be constructed by using the available extensions.\n\nAs an example, here is the Sismo Subdomain DAO contract used for one of our releases\n```solidity\npragma solidity \u003e=0.8.4;\n\nimport {PublicResolver} from '@ensdomains/ens-contracts/contracts/resolvers/PublicResolver.sol';\nimport {ENS} from '@ensdomains/ens-contracts/contracts/registry/ENS.sol';\nimport {SDaoRegistrar} from '@sismo-core/ens-sdao/contracts/sdao/SDaoRegistrar.sol';\nimport {SDaoRegistrarLimited} from '@sismo-core/ens-sdao/contracts/sdao/extensions/SDaoRegistrarLimited.sol';\nimport {SDaoRegistrarReserved} from '@sismo-core/ens-sdao/contracts/sdao/extensions/SDaoRegistrarReserved.sol';\nimport {SDaoRegistrarERC721Generator, IERC721Minter} from '@sismo-core/ens-sdao/contracts/sdao/extensions/SDaoRegistrarERC721Generator.sol';\nimport {SDaoRegistrarCodeAccessible} from '@sismo-core/ens-sdao/contracts/sdao/extensions/SDaoRegistrarCodeAccessible.sol';\n\ncontract SismoSDaoRegistrar is\n  SDaoRegistrar,\n  SDaoRegistrarLimited,\n  SDaoRegistrarReserved,\n  SDaoRegistrarERC721Generator,\n  SDaoRegistrarCodeAccessible\n{\n  uint256 public _groupId;\n\n  event GroupIdUpdated(uint256 groupId);\n\n  constructor(\n    ENS ensAddr,\n    PublicResolver resolver,\n    IERC721Minter erc721Token,\n    bytes32 node,\n    address owner,\n    uint256 reservationDuration,\n    uint256 registrationLimit,\n    uint256 groupId,\n    address codeSigner\n  )\n    SDaoRegistrarCodeAccessible('Sismo', '1.0', codeSigner)\n    SDaoRegistrarERC721Generator(erc721Token)\n    SDaoRegistrarLimited(registrationLimit)\n    SDaoRegistrarReserved(reservationDuration)\n    SDaoRegistrar(ensAddr, resolver, node, owner)\n  {\n    _groupId = groupId;\n  }\n\n  function _beforeRegistration(address account, bytes32 labelHash)\n    internal\n    virtual\n    override(\n      SDaoRegistrar,\n      SDaoRegistrarReserved,\n      SDaoRegistrarLimited,\n      SDaoRegistrarERC721Generator\n    )\n  {\n    super._beforeRegistration(account, labelHash);\n  }\n\n  function _afterRegistration(address account, bytes32 labelHash)\n    internal\n    virtual\n    override(SDaoRegistrar, SDaoRegistrarLimited, SDaoRegistrarERC721Generator)\n  {\n    super._afterRegistration(account, labelHash);\n  }\n\n  function _getCurrentGroupId() internal view override returns (uint256) {\n    return _groupId;\n  }\n\n  function updateGroupId(uint256 groupId) external onlyOwner {\n    _groupId = groupId;\n    emit GroupIdUpdated(groupId);\n  }\n}\n```\n# SDAO Contracts\n\nThe core contract `SDaoRegistrar` distributes subdomains to registrants. This contract must be set as the owner of the DAO domain name (e.g `domain.eth`). It contains the registration logic.\n\nThe organisation of the code is heavily inspired from the [OpenZeppelin](https://openzeppelin.com/) contracts packages. We built modular extensions [extending contracts](https://docs.openzeppelin.com/contracts/3.x/extending-contracts) and presets. (see the [ERC20 approach](https://docs.openzeppelin.com/contracts/3.x/erc20)).\n\nExtensions are for developers to choose what additional features or restrictions should be applied to their SDaoRegistrar.\n\nA list of presets is also available. They are pre-configured contracts, ready to be deployed. Each of them uses a different set of extensions.\n\nThe code of this repository resolves around ENS, it is advised to be familiar with [Ethereum Name Service](https://ens.domains/) notions.\n\n## Core contract: SDaoRegistrar (FCFS)\n\nThe contract allows first-come first-served (FCFS) registration of a subdomain, e.g. `label.domain.eth` through the `register`  method. \nThe ownership of the subdomain is given to the registrant. The newly created subdomain resolves to the registrant.\n\n```solidity\nfunction register(string memory label)\n    public\n    virtual\n    override\n    onlyUnrestricted\n  {\n    _register(_msgSender(), label);\n  }\n```\n\nThe internal registration method `_register` is exposed internally for extensions of the `SDaoRegistrar`. Two hooks can be used: `_beforeRegistration` and `_afterRegistration`\n\n```solidity\nfunction _register(address account, string memory label) internal {\n    bytes32 labelHash = keccak256(bytes(label));\n    _beforeRegistration(account, labelHash);\n    // [...]: subdomain created, subdomain resolves towards accounts, subdomain owner is account\n    _afterRegistration(account, labelHash);\n  }\n```\n\n### Constructor\n\n```solidity\nconstructor(\n    ENS ensAddr, // ENS Registry\n    PublicResolver resolver, // Resolver to be used\n    bytes32 node,    // nameHash('domain.eth')\n    address owner    // owner of the registrar\n  )\n```\n\nThe node corresponds to the [namehash](https://docs.ens.domains/contract-api-reference/name-processing) of the domain of the SDAO, e.g. `domain.eth`.\n\n### Interface and Implementation\n\nSee `contracts/sdao/ISDaoRegistrar.sol` and `contracts/sdao/SDaoRegistrar.sol` for the exact interface and implementation.\n\n## Extensions\n\nAn extension is an abstract contract which inherits the `SDaoRegistrar` core contract.\n\nIt may add other public methods for registration using the internal registration method or/and implements the `beforeRegistration` and `afterRegistration` hooks.\n\nextensions are easy to read: `/contracts/sdao/extensions/*.sol`\n\n### SDaoRegistrarReserved Extension\n\n```solidity\nfunction _beforeRegistration(address account, bytes32 labelHash)\n    internal\n    virtual\n    override\n  {\n    super._beforeRegistration(account, labelHash);\n\n    if (block.timestamp - DAO_BIRTH_DATE \u003c= _reservationDuration) {\n      address dotEthSubdomainOwner = ENS_REGISTRY.owner(\n        keccak256(abi.encodePacked(ETH_NODE, labelHash))\n      );\n      require(\n        dotEthSubdomainOwner == address(0x0) ||\n          dotEthSubdomainOwner == _msgSender(),\n        'SDAO_REGISTRAR_RESERVED: SUBDOMAIN_RESERVED'\n      );\n    }\n  }\n  ```\n\nA reservation period is introduced during which registration of a subdomain `subdomain.domain.eth` is blocked if the related `subdomain.eth` is owned by someone else than the registrant. The reservation period can be updated by the owner of the contract.\n\nSee `contracts/sdao/extensions/SDaoRegistrarReserved.sol` for the implementation.\n\n### SDaoRegistrarLimited Extension\n\n```solidity\nfunction _beforeRegistration(address account, bytes32 labelHash)\n    internal\n    virtual\n    override\n  {\n    super._beforeRegistration(account, labelHash);\n\n    require(\n      _counter \u003c _registrationLimit,\n      'SDAO_REGISTRAR_LIMITED: REGISTRATION_LIMIT_REACHED'\n    );\n  }\n```\n```solidity\nfunction _afterRegistration(address account, bytes32 labelHash)\n    internal\n    virtual\n    override\n  {\n    super._afterRegistration(account, labelHash);\n\n    _counter += 1;\n  }\n```\n\nA counter for the number of registered subdomains and a registration limit number are added. If the counter reaches the registration limit, registration is blocked. The registration limit can be updated by the owner of the contract.\n\nSee `contracts/sdao/extensions/SDaoRegistrarLimited.sol` for the implementation.\n\n### SDaoRegistrarERC721Generator Extension\n```solidity\nfunction _afterRegistration(address account, bytes32 labelHash)\n    internal\n    virtual\n    override\n  {\n    super._afterRegistration(account, labelHash);\n\n    bytes32 childNode = keccak256(abi.encodePacked(ROOT_NODE, labelHash));\n    ERC721_TOKEN.mintTo(account, uint256(childNode));\n  }\n  ```\n  An ERC721 is minted and the registration is blocked if the balance of the registrant is not zero.\n  See `contracts/sdao/extensions/SDaoRegistrarERC721Generator.sol` for the implementation.\n### SDaoRegistrarERC1155Generator Extension\n```solidity\nfunction _afterRegistration(address account, bytes32 labelHash)\n    internal\n    virtual\n    override\n  {\n    super._afterRegistration(account, labelHash);\n\n    (uint256 id, bytes memory data) = _getToken(account, labelHash);\n\n    ERC1155_MINTER.mint(account, id, 1, data);\n  }\n```\n\nAn ERC1155 token is minted for the registrant after each registration. The ERC1155 token ID and data are left free to be implemented by the developer.\nThe registration is blocked if the balance of the registrant is not zero, based on a `balanceOf` method to be implemented by the developer.\nSee `contracts/sdao/extensions/SDaoRegistrarERC1155Generator.sol` for the implementation.\n\n\n### SDaoRegistrarCodeAccessible Extension\n```solidity\nfunction registerWithAccessCode(\n    string memory label,\n    address recipient,\n    bytes memory accessCode\n  ) external override {\n    // [...]\n    require(\n      !_consumed[digest],\n      'SDAO_REGISTRAR_LIMITED_CODE_ACCESSIBLE: ACCESS_CODE_ALREADY_CONSUMED'\n    );\n\n    require(\n      digest.recover(accessCode) == _codeSigner,\n      'SDAO_REGISTRAR_LIMITED_CODE_ACCESSIBLE: INVALID_ACCESS_CODE OR INVALID_SENDER'\n    );\n    // [...]\n  }\n```\n\nA new public method of registration `registerWithAccessCode` is added. It allows to register a subdomain if a valid access code is given.\n\nAn access code is a signed message according to the [EIP712](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md) specification. The message contains domain specific information set at the deployment of the contract and the data of the message. The data contains the recipient address, which will receive the subdomain, and a number called `group ID`.\n\nThe message must be signed by a specific `code signer` address in order to be valid. The `code signer` value is managed by the owner of the contract. The `group ID` is retrieved using an internal method that needs to be implemented in the final contract.\n\nAn access code can only be consumed once.\n\nSee `contracts/sdao/extensions/SDaoRegistrarCodeAccessible.sol` for the implementation.\n\n### SDaoRegistrarClaimable Extension\n\n```solidity\nfunction claim(string memory label, address account) public override {\n    bytes32 labelHash = keccak256(bytes(label));\n    address bookingAddress = ENS_LABEL_BOOKER.getBooking(labelHash);\n    require(\n      bookingAddress != address(0),\n      'SDAO_REGISTRAR_CLAIMABLE: LABEL_NOT_BOOKED'\n    );\n    require(\n      bookingAddress == _msgSender() || owner() == _msgSender(),\n      'SDAO_REGISTRAR_CLAIMABLE: SENDER_NOT_ALLOWED'\n    );\n\n    _register(account, label);\n\n    ENS_LABEL_BOOKER.deleteBooking(labelHash);\n  }\n```\n\nAn address of an ENS Label Booker is added. The latter manages a mapping of booking formed by a link between a subdomain and a booking address.\n\nA new public method of registration `claim` is added. It allows to register a subdomain if the sender of the message is the booking address associated to the subdomain.\n\nThe FCFS served registration is blocked if the subdomain is already booked.\n\nSee `contracts/sdao/extensions/SDaoRegistrarClaimable.sol` and `contracts/ens-label-booker/ENSLabelBooker.sol` for the implementation of the extension and the Label Booker contracts.\n\n## Presets\n\nA preset is an SDAO Registrar contract extended with a set of extensions. It may be considered as ready to use contracts or examples of the extensions usage.\n\n- Limited Reserved ERC1155 Preset \n  - hardhat task `deploy-sdao-preset-erc1155`)\n  - implements `SDaoRegistrarLimited`, `SDaoRegistrarReserved` and `SDaoRegistrarERC1155Generator` extensions.\n- Limited Reserved ERC721 Preset\n  - hardhat task: `deploy-sdao-preset-erc721`\n  - implements `SDaoRegistrarLimited`, `SDaoRegistrarReserved` and `SDaoRegistrarERC721Generator` extensions.\n- Code Accessible Preset\n  - hardhat task: `deploy-sdao-preset-code-accessible`\n  - implements `SDaoRegistrarCodeAccessible` extension\n- Claimable Preset\n  - hardhat task: `deploy-sdao-preset-claimable`\n  - implements `SDaoRegistrarClaimable` extension\n\nSee `contracts/sdao/presets/*.sol` for the implementations.\n\n## Hardhat scripts\n\n**Note:** Most of the scripts are used for development purposes. Deployment scripts for the various presets are available but it is strongly advised to understand them before using them.\n\n#### `deploy-ens-full`\n\n```ts\nconst deployedENS: DeployedEns = await HRE.run('deploy-ens-full', {sDao: true, log: true});\n// with \nexport type DeployedEns = {\n  ensDeployer: ENSDeployer;\n  registry: ENSRegistry;\n  registrar: EthRegistrar;\n  reverseRegistrar: ReverseRegistrar;\n  publicResolver: PublicResolver;\n};\n```\n\nDeploys a suite of ENS contracts. The deployed .eth Registrar is simplified in order to allow direct registration instead of the usual two steps registration process.\n\nThe boolean flag `sDao` allows to additionally deploy a SDAO Registrar.\n\nSee `tasks/deploy-ens-full.ts` for the script and the full list of parameters.\n\n#### `deploy-label-booker`\n\nDeploys a Label Booker contract.\n\nThe `name` of the targeted domain is given as parameter, i.e. `\u003cname\u003e.eth`.\n\nSee `tasks/deploy-label-booker.ts` for the script and the full list of parameters.\n\n\n## Development\n\n### NPM scripts\n#### `npm install`\n\nInstall the dependencies.\n\n#### `npm compile`\n\nCompile the smart contracts and generate the associated TypeScript types.\n\n#### `npm test`\n\nLaunch all the tests.\n\nIndividual scripts are available for each test suite.\n\nThe `contracts/test-utils` repository contains various helpful contracts for the tests, in particular the `ENSDeployer` that can be used in order to redeploy a suite of ENS contracts with a modified Registrar.\n\n## License\n\nThe code of this repository is released under the [MIT License](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsismo-core%2Fens-sdao","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsismo-core%2Fens-sdao","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsismo-core%2Fens-sdao/lists"}