{"id":43395255,"url":"https://github.com/namehash/ens-v2-referral-programs","last_synced_at":"2026-02-02T14:25:25.020Z","repository":{"id":283351694,"uuid":"950872278","full_name":"namehash/ens-v2-referral-programs","owner":"namehash","description":"Permissionless Referral Program Contracts for ENSv2","archived":false,"fork":false,"pushed_at":"2025-03-19T19:01:57.000Z","size":27,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-03-19T20:22:20.232Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Solidity","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/namehash.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-03-18T20:10:33.000Z","updated_at":"2025-03-19T19:02:00.000Z","dependencies_parsed_at":"2025-03-19T20:22:22.891Z","dependency_job_id":"64786c1c-b70a-4198-962d-cb37be136229","html_url":"https://github.com/namehash/ens-v2-referral-programs","commit_stats":null,"previous_names":["namehash/ens-v2-referral-programs"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/namehash/ens-v2-referral-programs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/namehash%2Fens-v2-referral-programs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/namehash%2Fens-v2-referral-programs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/namehash%2Fens-v2-referral-programs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/namehash%2Fens-v2-referral-programs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/namehash","download_url":"https://codeload.github.com/namehash/ens-v2-referral-programs/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/namehash%2Fens-v2-referral-programs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29012919,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-02T12:48:30.580Z","status":"ssl_error","status_checked_at":"2026-02-02T12:46:38.384Z","response_time":58,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":[],"created_at":"2026-02-02T14:25:23.149Z","updated_at":"2026-02-02T14:25:25.004Z","avatar_url":"https://github.com/namehash.png","language":"Solidity","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!-- VERTICAL WHITESPACE --\u003e\n\n\u003cbr\u003e\n\n\u003c!-- BANNER IMAGE --\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://namehashlabs.org/ens-referral-program\"\u003e\n    \u003cpicture\u003e\n      \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\".github/banner-dark.png\"\u003e\n      \u003cimg alt=\"ENSv2 Referral Programs\" src=\".github/banner-light.png\" width=\"819\" height=\"auto\"\u003e\n    \u003c/picture\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n\u003c!-- VERTICAL WHITESPACE --\u003e\n\n\u003cbr\u003e\n\n# ENSv2 Referral Programs\n\nThis repo showcases a **permissionless ENSv2 Referral Program for name registrations and name renewals** and provides implementation contracts. These referral programs are permissionless to _create_ **and** (by default) are permissionless to _participate_ in. Many different referral programs may exist, each with their own reward logic and isolated commission treasury.\n\nThese contracts are designed to work with Namechain's **ETHRegistrar** but can be extended/modified to work with any ENSv2 Registry that supports registrations and renewals.\n\n## Goals\n\nThe goals of the referral program here are to enable enable _opt-in_ usage and maximize flexibility and rapid iteration. The deployment and usage of these referral programs are permissionless: ecosystem participants can decide whether or not to deploy a program, and clients can choose whether or not to use one. The contracts do _not_ modify core ENSv2 contracts, nor do they require any special behavior from the core ENSv2 contracts.\n\nBecause each program's logic and funds are isolated from the other's, rapid iteration with different referral program schemes and reward amounts is trivial. Many referral programs can be active at any one time, each with different terms and a different commission treasury. For illustration, this repo implements:\n\n1. [`PercentReferralProgram`](./src/examples/PercentReferralProgram.sol)\n    - rewards referrers with a constant commission `%` based on the total value of the registration/renewal as calculated by the ETHRegistry\n2. [`DurationReferralProgram`](./src/examples/DurationReferralProgram.sol)\n    - only rewards referrers that refer a registration/renewal with a duration greater than 1 year\n3. [`LoyaltyReferralProgram`](./src/examples/LoyaltyReferralProgram.sol)\n    - rewards referrers an additional 1% of registration/renewal value for every 100 years of duration they've facilitated\n4. [`AllowlistReferralProgram`](./src/examples/AllowlistReferralProgram.sol)\n    - uses `referrerData` to implement an allowlist of referrers, demonstrating arbitrary program-specific argument data \u0026 conditions\n\n## How it Works (TL;DR Version)\n\n1. Anyone can deploy a `ReferralProgram` contract with their own terms \u0026 funds\n    - for example, the ENS DAO may opt to sponsor one or many different `ReferralProgram`s, each with different terms \u0026 commission treasury\n2. ENS apps can direct their users to use a specific `ReferralProgram#register` or `ReferralProgram#renew` to register/renew names, including the additional `address referrer` and `bytes referrerData` arguments.\n3. The `ReferralProgram` forwards the request to the `ETHRegistrar` as normal, registering or renewing the relevant name\n4. The `ReferralProgram` can calculate the referrer's commission (if any), and optionally credit the referrer in the same transaction.\n    - arbitrary logic can be specified for how a `ReferralProgram` rewards referrers\n5. That's it! Many different `ReferralProgram` contracts can be created and deployed over time to experiment with different referral systems.\n\n## How it Works (~~Taylor's~~ Full Version)\n\n```mermaid\nflowchart LR\n    X(User A) --\u003e A\n    Y --\u003e A\n    Y(User B) --\u003e B\n    Y --\u003e C\n    Z(User C) --\u003e C\n\n    R(Referrer A)\n    T(Referrer B)\n\n    A --\u003e|comission| R\n\n    A[\"ReferralProgram 1\n    5 ETH\"] --\u003e|register/renew| D[ETHRegistrar]\n    B[\"ReferralProgram 2\n    1 ETH\"] --\u003e|register/renew| D\n    C[\"ReferralProgram 3\n    0 ETH\"] --\u003e|register/renew| D\n    D --\u003e E[ETHRegistry]\n\n    C --\u003e|comission| T\n```\n\nNamechain's `ETHRegistry` allows any address to register or renew a name on behalf of an `owner`, as long as the `msg.sender` pays for the registration or renewal. This flexible behavior allows for a host of UX benefits in ENSv2, one of which is this permissionless Referral Program implementation.\n\nFirst, observe the `IReferralProgram` interface specification and how it defines an event and two functions, `register` and `renew`. These functions mirror their `ETHRegistrar` counterparts, with the sole addition of the `referrer` and `referrerData` parameters.\n\n[**IReferralProgram.sol**](./src/IReferralProgram.sol)\n\n```solidity\ninterface IReferralProgram {\n    // ... exerpt ...\n\n    event Referral(string name, address referrer); // here\n\n    function register(\n        string calldata name,\n        address owner,\n        bytes32 secret,\n        IRegistry subregistry,\n        address resolver,\n        uint96 flags,\n        uint64 duration,\n        address referrer,            // here\n        bytes calldata referralData  // here\n    ) external payable returns (uint256 tokenId);\n\n    function renew(\n      string calldata name,\n      uint64 duration,\n      address referrer,              // here\n      bytes calldata referralData    // here\n    ) external payable;\n}\n```\n\nThe core logic of this interface is implemented by `ReferralProgram`. The `ReferralProgram` is constructed with an immutable reference to the `ETHRegistry` and seeded with its own commission treasury. Its fallback handler is payable, allowing anyone to contribute more funds to the referral program in the event it should be extended. The `ReferralProgram` is `Ownable` and its owner (the deployer) can optionally close the program and withdraw any un-rewarded funds.\n\nImportant functions, `register` and `renew` are highlighted in this exerpt. They each calculate the totalPrice to register or renew the relevant name, forward the request to the `ETHRegistrar`, and defer handling of referrals to an internal `_processReferral` function that individual referral program contracts must implement.\n\n[**ReferralProgram.sol**](./src/ReferralProgram.sol)\n\n```solidity\nabstract contract ReferralProgram is IReferralProgram, Ownable {\n    // ... exerpt ...\n\n    function register(\n        string calldata name,\n        address owner,\n        bytes32 secret,\n        IRegistry subregistry,\n        address resolver,\n        uint96 flags,\n        uint64 duration,\n        address referrer,\n        bytes calldata referralData\n    ) external payable override returns (uint256 tokenId) {\n        // calculate totalPrice\n        IPriceOracle.Price memory price = registrar.rentPrice(name, duration);\n        uint256 totalPrice = price.base + price.premium;\n\n        // forward registration, including the exact value of the registration to avoid refunds\n        // NOTE: if registrar.register reverts, so will the whole transaction\n        tokenId = registrar.register{value: totalPrice}(name, owner, secret, subregistry, resolver, flags, duration);\n\n        // refund sender any leftover change\n        if (msg.value \u003e totalPrice) {\n            payable(msg.sender).transfer(msg.value - totalPrice);\n        }\n\n        // process referral\n        _processReferral(name, duration, price, referrer, referralData);\n\n        return tokenId;\n    }\n\n    /**\n     * @dev Renew a name with referral information, forwarding renewal to registrar.\n     */\n    function renew(string calldata name, uint64 duration, address referrer, bytes calldata referralData)\n        external\n        payable\n        override\n    {\n        // calculate totalPrice\n        IPriceOracle.Price memory price = registrar.rentPrice(name, duration);\n        uint256 totalPrice = price.base + price.premium;\n\n        // forward renewal, including the exact value of the renewal to avoid refunds\n        registrar.renew{value: totalPrice}(name, duration);\n\n        // refund sender any leftover change\n        if (msg.value \u003e totalPrice) {\n            payable(msg.sender).transfer(msg.value - totalPrice);\n        }\n\n        // process referral\n        _processReferral(name, duration, price, referrer, referralData);\n\n        // emit standard Referral event\n        emit Referral(name, referrer);\n    }\n\n    /**\n     * @dev Process referral. Must be implemented by derived contracts.\n     *  If no ability to credit referral, MAY revert but SHOULD no-op instead.\n     * @param name The name up for registration/renewal\n     * @param duration The duration of the registration/renewal\n     * @param price The Price info for the registration/renewal\n     * @param referrer The address of the referrer\n     * @param referralData Additional data for the referral\n     */\n    function _processReferral(\n        string calldata name,\n        uint64 duration,\n        IPriceOracle.Price memory price,\n        address referrer,\n        bytes calldata referralData\n    ) internal virtual;\n}\n```\n\nFor example, here is the `PercentReferralProgram`, which rewards referrers with a deployer-specified percentage on top of the `totalPrice` spent to register/renew the name.\n\n[**examples/PercentReferralProgram.sol**](./src/examples/PercentReferralProgram.sol)\n\n```solidity\ncontract PercentReferralProgram is ReferralProgram {\n\n    // ... exerpt ...\n\n    function _processReferral(\n        string calldata name,\n        uint64,\n        IPriceOracle.Price memory price,\n        address referrer,\n        bytes calldata\n    ) internal virtual override {\n        // no-op if no referrer\n        if (referrer == address(0)) return;\n\n        // no-op if no program balance\n        uint256 balance = address(this).balance;\n        if (balance == 0) return;\n\n        // calculate commission amount (bips)\n        uint256 totalPrice = price.base + price.premium;\n        uint256 commission = (totalPrice * commissionPercent) / 10_000;\n\n        // ensure we can't spend more than program balance: Math.min(commission, balance)\n        // NOTE: also ensures that entire balance can be used, with no dust wei left behind\n        commission = commission \u003e balance ? balance : commission;\n\n        // pay referrer their commission\n        payable(referrer).transfer(commission);\n\n        // emit referral event w/ commission info\n        emit ReferralWithCommission(name, referrer, commission);\n    }\n}\n```\n\nWith this small implementation of `_processReferral`, anyone can reward users who facilitate the registration or renewals of domains in the ETHRegistry in ENSv2.\n\nTo demonstrate more complex referral program logic, we can look to the `LoyaltyReferralProgram`, which rewards referrers based on how much value they've referred to ENS in the past. For every 100 years of name registration or renewal facilitated by this referrer, the contract rewards them an additional 1% on top of the totalPrice as calculated but the ETHRegistrar, up to a maximim of 20%.\n\n[**examples/LoyaltyReferralProgram.sol**](./src/examples/LoyaltyReferralProgram.sol)\n\n```solidity\ncontract LoyaltyReferralProgram is ReferralProgram {\n    // ... exerpt ...\n\n    function _processReferral(\n        string calldata name,\n        uint64 duration,\n        IPriceOracle.Price memory price,\n        address referrer,\n        bytes calldata\n    ) internal override {\n        // no-op if no referrer\n        if (referrer == address(0)) return;\n\n        // update referrer's cumulative duration\n        // NOTE: placed before balance check so that referrers accrue loyalty despite availability\n        //       of commission funds\n        referralDurations[referrer] += duration;\n\n        // no-op if no program balance\n        uint256 balance = address(this).balance;\n        if (balance == 0) return;\n\n        // calculate commission percent (1% every 100 years)\n        uint256 commissionPercent = (referralDurations[referrer] * BONUS_RATE) / (YEARS_PER_BONUS * 365 days);\n\n        // ensure maximum commission percent: Math.max(commissionPercent, MAX_COMMISSION_PERCENT)\n        commissionPercent = commissionPercent \u003e MAX_COMMISSION_PERCENT ? MAX_COMMISSION_PERCENT : commissionPercent;\n\n        // calculate commission amount (bips)\n        uint256 totalPrice = price.base + price.premium;\n        uint256 commission = (totalPrice * commissionPercent) / 10_000;\n\n        // ensure we can't spend more than program balance: Math.min(commission, balance)\n        // NOTE: also ensures that entire balance can be used, with no dust wei left behind\n        commission = commission \u003e balance ? balance : commission;\n\n        // pay referrer their commission\n        payable(referrer).transfer(commission);\n\n        // emit referral event w/ commission info\n        emit ReferralWithCommission(name, referrer, commission);\n    }\n}\n```\n\nAdditional custom logic is possible as well, supported by the `referralData` parameter that has yet to be used. To demonstrate its use, we look to the `AllowlistReferralProgram` which only rewards referrers on a deployer-specified allowlist. The `referrerData` parameter allows a merkle proof to be provided, efficiently proving that the given referrer is allowed by the deployer.\n\n[**examples/AllowlistReferralProgram.sol**](./src/examples/AllowlistReferralProgram.sol)\n\n```solidity\ncontract AllowlistReferralProgram is PercentReferralProgram {\n    // ... exerpt ...\n\n    function _processReferral(\n        string calldata name,\n        uint64 duration,\n        IPriceOracle.Price memory price,\n        address referrer,\n        bytes calldata referralData\n    ) internal override {\n        // determine if referrer is in allowlist using merkle proof\n        bool valid = !MerkleProof.verify(abi.decode(referralData, (bytes32[])), merkleRoot, keccak256(abi.encodePacked(referrer)));\n\n        // if not on allowlist, proceed with registration/renewal without commission\n        if (valid) return;\n\n        // if the referrer is on the allowlist, pay them % commission as normal\n        super._processReferral(name, duration, price, referrer, referralData);\n    }\n}\n```\n\n### Note on Rejecting Referrals\n\nIn `_processReferral` contracts that don't reward a given referrer will likely wish to simple no-op and `return`—as we've demonstrated in these example implementations—allowing the client and user to continue registering or renewing their name without interruption. This behavior also allows apps to direct users to a specific referral program contract and if/when it runs out of funds, there is no interruption in the registrations or renewals.\n\n\n## License\n\nLicensed under the MIT License, Copyright © 2025-present [NameHash Labs](https://namehashlabs.org).\n\nSee [LICENSE](./LICENSE) for more information.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnamehash%2Fens-v2-referral-programs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnamehash%2Fens-v2-referral-programs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnamehash%2Fens-v2-referral-programs/lists"}