Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
422480a
Add comprehensive ENSv2 documentation
wentelteefje Apr 27, 2026
f3b5a22
ENSv2 docs: structural improvements, widgets, and FrenCallout
wentelteefje Apr 27, 2026
6ef27db
Remove working files and clean up .gitignore
wentelteefje Apr 27, 2026
16f979b
Change overview title, add what's new in v2 to PermReg page
wentelteefje Apr 28, 2026
69150c9
ERC1155Singleton fixes, reverse resolution page, widget subsections
wentelteefje Apr 29, 2026
cb63ffb
- Add ContractReference expandable component for contract reference s…
wentelteefje Apr 30, 2026
fcccad3
Move widgets to H3 sections, add title for frencallout
wentelteefje Apr 30, 2026
b71f987
Add new migration page template (WIP)
wentelteefje Apr 30, 2026
c156303
Fix migration page: correct BONUS_PERIOD (62 days, not 90), add time …
wentelteefje May 1, 2026
1adec41
Update migration page for case study revisions
wentelteefje May 5, 2026
91503fd
Add self-hosted ABC fonts and Tailwind font families
ali-rasheed Apr 29, 2026
9a730bb
Use bunx for all Vocs script invocations.
ali-rasheed May 1, 2026
2cad38f
Apply ENS typography defaults and font tuning.
ali-rasheed May 1, 2026
78848c4
Update src/pages/contracts/ensv2/mutable-token-ids.mdx
wentelteefje May 7, 2026
80f217a
Revert "Use bunx for all Vocs script invocations."
ali-rasheed May 7, 2026
9b1f084
Align docs design tokens with ENS Core palette and document UI patterns.
ali-rasheed May 7, 2026
693ee7c
Adjust profile and button shape/ring styling to match updated UI lang…
ali-rasheed May 7, 2026
31859f4
Ignore local Netlify metadata in the repository.
ali-rasheed May 7, 2026
e21f001
Fix resolver scope to per-account, restore 28-day grace period, add n…
wentelteefje May 8, 2026
380ab0e
Remove misleading from overview, clarify v1 registrations and renewa…
wentelteefje May 9, 2026
f0567e9
Add on-chain DNSSEC note to DNS resolvers page, add RegistryCreated e…
wentelteefje May 12, 2026
18e8100
Merge remote-tracking branch 'upstream/master' into feature/ensv2-docs
wentelteefje May 12, 2026
9371ade
Add verifiable-factory repo link, clarify BatchRegistrar for ENSv1 mi…
wentelteefje May 12, 2026
ab9af22
Fix reverse resolution intro to credit v1 for multi-chain support, fi…
wentelteefje May 12, 2026
e11efbb
restructure overview page, clarify factory is default not but not req…
wentelteefje May 13, 2026
835cdf5
style(docs): refresh global styles and design tokens
ali-rasheed May 13, 2026
9588c49
chore(vocs): tune theme and normalize config formatting
ali-rasheed May 13, 2026
3d888b2
style(components): use semi-mono for ENS name labels
ali-rasheed May 13, 2026
521d84b
style(docs): refresh global styles and design tokens
ali-rasheed May 13, 2026
d9da958
chore(vocs): tune theme and normalize config formatting
ali-rasheed May 13, 2026
9adffed
style(components): use semi-mono for ENS name labels
ali-rasheed May 13, 2026
afec27a
Merge branch 'ensdomains:ens-brand-refresh' into ens-brand-refresh
ali-rasheed May 13, 2026
09d1977
Apply fixes to migration page based on post-audit branch
wentelteefje May 14, 2026
acf81a8
Merge pull request #1 from ali-rasheed/ens-brand-refresh
wentelteefje May 14, 2026
b9c1d19
adjust eth registrar, perm resolver, migration and registry temp page…
wentelteefje May 18, 2026
7998689
more registrar fixes
wentelteefje May 18, 2026
af66267
overhaul permissioned registry prose, refactor metadata for post-audi…
wentelteefje May 20, 2026
266e87f
overhaul universal resolver v2 page, verify and fix dns-resolvers pag…
wentelteefje May 20, 2026
22e633b
Overhaul registry-template page, fix verifiable-factory technical ina…
wentelteefje May 27, 2026
559342c
Address Ram's comments on EAC, PermReg pages
wentelteefje May 27, 2026
b4462d2
pull scripts/ensips.ts from upstream master
wentelteefje May 27, 2026
de678fd
Fix technical inaccuracies across ENSv2 docs (dns-resolvers, emancipa…
wentelteefje Jun 3, 2026
0251d20
Add MonoDiagram component for box-drawing diagrams, replace code bloc…
wentelteefje Jun 3, 2026
5b878fb
Fix cross-page consistency issues, broken links, and factual inaccura…
wentelteefje Jun 4, 2026
67e22d9
Align EAC, PermReg, and PermResolver docs with post-audit PRs #333 an…
wentelteefje Jun 8, 2026
778d004
Add proxy deployment code example to Verifiable Factory
wentelteefje Jun 8, 2026
86c2fd8
Fix code example accuracy and add missing examples across ENSv2 docs …
wentelteefje Jun 8, 2026
4da9572
Add contract developers tutorial (subname registrar guide); add resol…
wentelteefje Jun 11, 2026
8d7e352
Refactor subregistry deployment on Subregistrar guide page, add some …
wentelteefje Jun 11, 2026
de1055b
Fix indexing page: event order, TokenResource at registration only, P…
wentelteefje Jul 2, 2026
6240b74
Add ENS Explorer (Sepolia) link to ENSv2 overview
wentelteefje Jul 2, 2026
0b3e708
Add sepolia-official-v1-20260525-r2 ENSv2 deployments table to overv…
wentelteefje Jul 9, 2026
25fe646
Expand indexing page with subgraph research findings; fix migration p…
wentelteefje Jul 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions src/components/ensv2/FrenCallout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import type { PropsWithChildren } from 'react'

type Fren = 'earl' | 'bittu' | 'kuzco' | 'lili' | 'peanut'
type Variant = 'note' | 'tip' | 'warning'

const FREN_IMAGES: Record<Fren, string> = {
earl: '/img/frens/Earl_padding.png',
bittu: '/img/frens/Bittu_padding.png',
kuzco: '/img/frens/Kuzco_padding.png',
lili: '/img/frens/Lili_padding.png',
peanut: '/img/frens/Peanut_padding.png',
}

const VARIANT_STYLES: Record<Variant, { bg: string; border: string }> = {
note: {
bg: 'var(--vocs-color_noteBackground, #f0f4f8)',
border: 'var(--vocs-color_noteBorder, #d0d7de)',
},
tip: {
bg: 'var(--vocs-color_tipBackground, #f0fdf4)',
border: 'var(--vocs-color_tipBorder, #86efac)',
},
warning: {
bg: 'var(--vocs-color_warningBackground, #fffbeb)',
border: 'var(--vocs-color_warningBorder, #fcd34d)',
},
}

interface FrenCalloutProps {
fren: Fren
variant?: Variant
title?: string
}

export function FrenCallout({
fren,
variant = 'note',
title,
children,
}: PropsWithChildren<FrenCalloutProps>) {
const style = VARIANT_STYLES[variant]
const image = FREN_IMAGES[fren]

return (
<aside
style={{
display: 'flex',
gap: '16px',
alignItems: 'flex-start',
padding: '16px',
borderRadius: '8px',
border: `1px solid ${style.border}`,
backgroundColor: style.bg,
marginBottom: '16px',
fontSize: '14px',
lineHeight: 1.6,
}}
>
<img
src={image}
alt={fren}
style={{
width: '56px',
height: '56px',
objectFit: 'contain',
flexShrink: 0,
marginTop: '2px',
}}
/>
<div style={{ width: '100%' }}>
{title && (
<div style={{ fontWeight: 600, marginBottom: '4px' }}>{title}</div>
)}
{children}
</div>
</aside>
)
}
169 changes: 169 additions & 0 deletions src/components/ensv2/IdExplorer.tsx

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sliders don't seem to change any of the values for me

Image

@wentelteefje wentelteefje May 6, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They even increment in your animation :D

  • tokenVersionId increments the lower 32 bits of Token ID.
  • eacVersionId increments the lower 32 bits of resource.

The canonicalId always zeroes out the lower 32 bits, so nothing changes there and the labelhash is just a constant 256bit hash. But it's good feedback that you didn't even realize them changing. Maybe I should add some visual effect to the values when the slider is used.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh whoops, ya i was looking at the left side 😅

seems maybe weird to display the same value 4 times when only the right side changes, but not a big deal for now

Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import { useMemo, useState } from 'react'
import { keccak256, toHex } from 'viem'

import { Input } from '../ui/Input'

function SplitId({
label,
upper,
lower,
lowerLabel,
}: {
label: string
upper: string
lower: string
lowerLabel: string
}) {
const full = `0x${upper}${lower}`
return (
<div className="flex flex-col gap-1">
<label className="text-grey px-2 leading-none font-semibold">{label}</label>
<div className="flex gap-1 items-center">
<div className="flex-1 min-w-0">
<input
className="border-border disabled:bg-grey-light disabled:text-grey z-0 w-full rounded-md border px-3 py-2.5 leading-none font-mono text-xs font-normal outline-none truncate"
value={`0x${upper}...`}
disabled
title="Upper 224 bits (from labelhash)"
/>
</div>
<div className="w-[120px] shrink-0">
<input
className="border-border disabled:bg-grey-light z-0 w-full rounded-md border px-3 py-2.5 leading-none font-mono text-xs font-bold outline-none text-[var(--vocs-color_textAccent)]"
value={lower}
disabled
title={lowerLabel}
/>
</div>
<button
className="!bg-background text-text-secondary hover:text-text border-border rounded border p-1.5 text-xs leading-none transition shrink-0"
onClick={async () => {
await navigator.clipboard.writeText(full)
}}
title="Copy full value"
>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
</svg>
</button>
</div>
<div className="flex gap-1 px-2">
<span className="flex-1 text-xs text-grey truncate">upper 224 bits (labelhash)</span>
<span className="w-[120px] shrink-0 text-xs text-grey">{lowerLabel}</span>
<span className="w-[28px] shrink-0" />
</div>
</div>
)
}

export function IdExplorer() {
const [label, setLabel] = useState('alice')
const [eacVersionId, setEacVersionId] = useState(0)
const [tokenVersionId, setTokenVersionId] = useState(0)

const computed = useMemo(() => {
if (!label) return null
try {
const labelhashHex = keccak256(toHex(label))
const labelhashBigInt = BigInt(labelhashHex)

// Canonical ID: zero the lower 32 bits
const canonicalId = labelhashBigInt ^ (labelhashBigInt & 0xFFFFFFFFn)

// Token ID: canonical ID | tokenVersionId
const tokenId = canonicalId | BigInt(tokenVersionId)

// Resource: canonical ID | eacVersionId
const resource = canonicalId | BigInt(eacVersionId)

// Split into upper (56 hex = 224 bits) and lower (8 hex = 32 bits)
const fullLabelhash = labelhashHex.slice(2).padStart(64, '0')
const upperHex = fullLabelhash.slice(0, 56)

const canonicalLower = '00000000'
const tokenLower = BigInt(tokenVersionId).toString(16).padStart(8, '0')
const resourceLower = BigInt(eacVersionId).toString(16).padStart(8, '0')

return {
labelhash: labelhashHex,
labelhashLower: fullLabelhash.slice(56),
upperHex,
canonicalLower,
tokenLower,
resourceLower,
}
} catch {
return null
}
}, [label, eacVersionId, tokenVersionId])

return (
<div className="flex flex-col gap-4">
<Input
label="Label"
value={label}
onChange={(e) => setLabel(e.target.value)}
placeholder="alice"
/>

<div className="flex gap-3">
<div className="flex-1">
<label className="text-grey px-2 leading-none font-semibold text-sm">
tokenVersionId: {tokenVersionId}
</label>
<input
type="range"
min={0}
max={10}
value={tokenVersionId}
onChange={(e) => setTokenVersionId(Number(e.target.value))}
className="w-full mt-2"
/>
</div>
<div className="flex-1">
<label className="text-grey px-2 leading-none font-semibold text-sm">
eacVersionId: {eacVersionId}
</label>
<input
type="range"
min={0}
max={10}
value={eacVersionId}
onChange={(e) => setEacVersionId(Number(e.target.value))}
className="w-full mt-2"
/>
</div>
</div>

{computed && (
<>
<SplitId
label="Labelhash"
upper={computed.upperHex}
lower={computed.labelhashLower}
lowerLabel="lower 32 bits"
/>
<SplitId
label="Canonical ID"
upper={computed.upperHex}
lower={computed.canonicalLower}
lowerLabel="zeroed"
/>
<SplitId
label="Token ID"
upper={computed.upperHex}
lower={computed.tokenLower}
lowerLabel="tokenVersionId"
/>
<SplitId
label="Resource"
upper={computed.upperHex}
lower={computed.resourceLower}
lowerLabel="eacVersionId"
/>
</>
)}
</div>
)
}
141 changes: 141 additions & 0 deletions src/components/ensv2/ResourceCalculator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { useMemo, useState } from 'react'
import { keccak256, namehash, encodeAbiParameters, toHex } from 'viem'
import { packetToBytes } from 'viem/ens'

import { Input } from '../ui/Input'

type RecordType = 'name' | 'text' | 'addr'

export function ResourceCalculator() {
const [name, setName] = useState('alice.eth')
const [recordType, setRecordType] = useState<RecordType>('text')
const [textKey, setTextKey] = useState('avatar')
const [coinType, setCoinType] = useState('60')

const computed = useMemo(() => {
if (!name) return null
try {
const node = namehash(name)

let part: `0x${string}`
let partLabel: string
let authorizeFunction: string
let authorizeArgs: string

const dnsName = toHex(packetToBytes(name))

if (recordType === 'name') {
part = '0x0000000000000000000000000000000000000000000000000000000000000000'
partLabel = 'bytes32(0) (name-level)'
authorizeFunction = 'authorizeNameRoles'
authorizeArgs = `(${dnsName}, roleBitmap, account, true)`
} else if (recordType === 'text') {
// partHash(key) = keccak256(bytes(key))
part = keccak256(toHex(textKey))
partLabel = `partHash("${textKey}") = keccak256(bytes("${textKey}"))`
authorizeFunction = 'authorizeTextRoles'
authorizeArgs = `(${dnsName}, "${textKey}", account, true)`
} else {
// partHash(coinType) = keccak256(abi.encode(coinType))
const coinTypeNum = BigInt(coinType || '0')
part = keccak256(
encodeAbiParameters([{ type: 'uint256' }], [coinTypeNum])
)
partLabel = `partHash(${coinType}) = keccak256(abi.encode(${coinType}))`
authorizeFunction = 'authorizeAddrRoles'
authorizeArgs = `(${dnsName}, ${coinType}, account, true)`
}

// resource = keccak256(node, part)
// Special case: resource(0, 0) = ROOT_RESOURCE = 0
let resource: string
if (node === '0x0000000000000000000000000000000000000000000000000000000000000000' &&
part === '0x0000000000000000000000000000000000000000000000000000000000000000') {
resource = '0x0000000000000000000000000000000000000000000000000000000000000000'
} else {
resource = keccak256(
encodeAbiParameters(
[{ type: 'bytes32' }, { type: 'bytes32' }],
[node, part]
)
)
}

return {
node,
part,
partLabel,
resource,
authorizeFunction,
authorizeArgs,
dnsName,
}
} catch {
return null
}
}, [name, recordType, textKey, coinType])

return (
<div className="flex flex-col gap-4">
<Input
label="Name"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="alice.eth"
/>

<div className="flex flex-col gap-2">
<label className="text-grey px-2 leading-none font-semibold">
Scope
</label>
<div className="flex gap-2 px-2">
{(['name', 'text', 'addr'] as const).map((type) => (
<button
key={type}
onClick={() => setRecordType(type)}
className={`rounded-md border px-3 py-1.5 text-sm font-medium transition ${
recordType === type
? 'border-[var(--vocs-color_borderAccent)] bg-[var(--vocs-color_borderAccent)] text-white'
: 'border-border hover:border-[var(--vocs-color_borderAccent)]'
}`}
>
{type === 'name' ? 'Name-level' : type === 'text' ? 'Text key' : 'Coin type'}
</button>
))}
</div>
</div>

{recordType === 'text' && (
<Input
label="Text key"
value={textKey}
onChange={(e) => setTextKey(e.target.value)}
placeholder="avatar"
/>
)}

{recordType === 'addr' && (
<Input
label="Coin type"
value={coinType}
onChange={(e) => setCoinType(e.target.value)}
placeholder="60"
/>
)}

{computed && (
<>
<Input label="node (namehash)" value={computed.node} disabled copyButton />
<Input label={`part (${computed.partLabel})`} value={computed.part} disabled copyButton />
<Input label="resource = keccak256(node, part)" value={computed.resource} disabled copyButton />
<Input
label={`To grant: ${computed.authorizeFunction}`}
value={computed.authorizeArgs}
disabled
copyButton
/>
</>
)}
</div>
)
}
Loading