This repository was archived by the owner on May 13, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
feat: include domain logics into ens-utils
#341
Open
FrancoAguzzi
wants to merge
10
commits into
main
Choose a base branch
from
francoaguzzi/sc-25546/include-domain-logics-into-ens-utils
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
90a13c5
feat: [SC-25546] Add domain related logics to ens-utils (Registration…
FrancoAguzzi 302ec4e
feat: [SC-25546] Include @types/node into ens-utils
FrancoAguzzi c2fb824
feat: [SC-25546] Include changeset in this PR since it updates ens-ut…
FrancoAguzzi 55b5446
feat: [SC-25546] Delete unneeded changeset
FrancoAguzzi cd5e39a
feat: [SC-25546] Update UserOwnershipOfDomain enum definition
FrancoAguzzi 71fa009
feat: [SC-25546] Move functions and delete registration.ts
FrancoAguzzi 013a54f
feat: [SC-25546] Add unit tests scenarios description for ethregistra…
FrancoAguzzi abfcb40
fix: [SC-25546] Optimize comments in ethregistrar.ts
FrancoAguzzi f118a91
Merge remote-tracking branch 'origin/main' into francoaguzzi/sc-25546…
FrancoAguzzi 1263f48
feat: [SC-25399] Remove expiryTimestamp field from Registration
FrancoAguzzi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@namehash/ens-utils": minor | ||
| --- | ||
|
|
||
| Add domain related logics to ens-utils (Registration, DomainName, DomainCard, etc.) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,151 @@ | ||
| import { NFTRef } from "./nft"; | ||
| import { ENSName } from "./ensname"; | ||
| import { Address, isAddressEqual } from "./address"; | ||
| import { keccak256, labelhash as labelHash } from "viem"; | ||
| import { Registration } from "./ethregistrar"; | ||
|
|
||
| export interface DomainCard { | ||
| name: ENSName; | ||
|
|
||
| /** | ||
| * A reference to the NFT associated with `name`. | ||
| * | ||
| * null if and only if one or more of the following are true: | ||
| * 1. name is not normalized | ||
| * 2. name is not currently minted (name is on primary market, not secondary market) and the name is not currently expired in grace period | ||
| * 3. we don't know a strategy to generate a NFTRef for the name on the specified chain (ex: name is associated with an unknown registrar) | ||
| */ | ||
| nft: NFTRef | null; | ||
| registration: Registration; | ||
| ownerAddress: Address | null; | ||
| managerAddress: Address | null; | ||
| /** Former owner address is only set when the domain is in Grace Period */ | ||
| formerOwnerAddress: Address | null; | ||
| /** Former manager address is only set when the domain is in Grace Period */ | ||
| formerManagerAddress: Address | null; | ||
| } | ||
|
|
||
| /* Defines the ownership of a domain for a given address */ | ||
| export const UserOwnershipOfDomain = { | ||
| /* NoOwner: If domain has no owner */ | ||
| NoOwner: "NoOwner", | ||
|
|
||
| /* NotOwner: If domain has an owner but user is not the owner */ | ||
| NotOwner: "NotOwner", | ||
|
|
||
| /* FormerOwner: If user is owner of the domain and domain is in Grace Period */ | ||
| FormerOwner: "FormerOwner", | ||
|
|
||
| /* ActiveOwner: If user is owner of the domain and domain is not in Grace Period */ | ||
| ActiveOwner: "ActiveOwner", | ||
| }; | ||
| export type UserOwnershipOfDomain = | ||
| (typeof UserOwnershipOfDomain)[keyof typeof UserOwnershipOfDomain]; | ||
|
|
||
| /** | ||
| * Returns the ownership status of a domain in comparison to the current user's address | ||
| * @param domain Domain that is being checked. If null, returns UserOwnershipOfDomain.NoOwner | ||
| * @param currentUserAddress Address of the current user. | ||
| * If null, returns UserOwnershipOfDomain.NoOwner or UserOwnershipOfDomain.NotOwner | ||
| * @returns UserOwnershipOfDomain | ||
| */ | ||
| export const getCurrentUserOwnership = ( | ||
| domain: DomainCard | null, | ||
|
FrancoAguzzi marked this conversation as resolved.
|
||
| currentUserAddress: Address | null, | ||
|
FrancoAguzzi marked this conversation as resolved.
|
||
| ): UserOwnershipOfDomain => { | ||
| const formerDomainOwnerAddress = | ||
| domain && domain.formerOwnerAddress ? domain.formerOwnerAddress : null; | ||
| const ownerAddress = | ||
| domain && domain.ownerAddress ? domain.ownerAddress : null; | ||
|
|
||
| if (currentUserAddress && formerDomainOwnerAddress) { | ||
| const isFormerOwner = | ||
| formerDomainOwnerAddress && | ||
| isAddressEqual(formerDomainOwnerAddress, currentUserAddress); | ||
|
|
||
| if (isFormerOwner) { | ||
| return UserOwnershipOfDomain.FormerOwner; | ||
| } | ||
|
|
||
| const isOwner = | ||
| ownerAddress && isAddressEqual(currentUserAddress, ownerAddress); | ||
|
|
||
| if (isOwner) { | ||
| return UserOwnershipOfDomain.ActiveOwner; | ||
| } | ||
| } | ||
|
|
||
| if (!ownerAddress) { | ||
| return UserOwnershipOfDomain.NoOwner; | ||
| } | ||
|
|
||
| return UserOwnershipOfDomain.NotOwner; | ||
| }; | ||
|
|
||
| export enum ParseNameErrorCode { | ||
| Empty = "Empty", | ||
| TooShort = "TooShort", | ||
| UnsupportedTLD = "UnsupportedTLD", | ||
| UnsupportedSubdomain = "UnsupportedSubdomain", | ||
| MalformedName = "MalformedName", | ||
| MalformedLabelHash = "MalformedLabelHash", | ||
| } | ||
|
|
||
| type ParseNameErrorDetails = { | ||
| normalizedName: string | null; | ||
| displayName: string | null; | ||
| }; | ||
| export class ParseNameError extends Error { | ||
| public readonly errorCode: ParseNameErrorCode; | ||
| public readonly errorDetails: ParseNameErrorDetails | null; | ||
|
|
||
| constructor( | ||
| message: string, | ||
| errorCode: ParseNameErrorCode, | ||
| errorDetails: ParseNameErrorDetails | null, | ||
| ) { | ||
| super(message); | ||
|
|
||
| this.errorCode = errorCode; | ||
| this.errorDetails = errorDetails; | ||
| } | ||
| } | ||
|
|
||
| export const DEFAULT_TLD = "eth"; | ||
|
|
||
| export const DefaultParseNameError = new ParseNameError( | ||
| "Empty name", | ||
| ParseNameErrorCode.Empty, | ||
| null, | ||
| ); | ||
|
|
||
| export const hasMissingNameFormat = (label: string) => | ||
| new RegExp("\\[([0123456789abcdef]*)\\]").test(label) && label.length === 66; | ||
|
|
||
| const labelhash = (label: string) => labelHash(label); | ||
|
|
||
| const keccak = (input: Buffer | string) => { | ||
| let out = null; | ||
| if (Buffer.isBuffer(input)) { | ||
| out = keccak256(input); | ||
| } else { | ||
| out = labelhash(input); | ||
| } | ||
| return out.slice(2); // cut 0x | ||
| }; | ||
|
|
||
| const initialNode = | ||
| "0000000000000000000000000000000000000000000000000000000000000000"; | ||
|
|
||
| export const namehashFromMissingName = (inputName: string): string => { | ||
| let node = initialNode; | ||
|
|
||
| const split = inputName.split("."); | ||
| const labels = [split[0].slice(1, -1), keccak(split[1])]; | ||
|
|
||
| for (let i = labels.length - 1; i >= 0; i--) { | ||
| const labelSha = labels[i]; | ||
| node = keccak(Buffer.from(node + labelSha, "hex")); | ||
| } | ||
| return "0x" + node; | ||
| }; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.