Skip to content

feat: prepare ENSv2 mainnet deployment - #4

Merged
JOY (JOY) merged 3 commits into
dosfrom
codex/ensv2-mainnet-deploy
Aug 12, 2026
Merged

feat: prepare ENSv2 mainnet deployment#4
JOY (JOY) merged 3 commits into
dosfrom
codex/ensv2-mainnet-deploy

Conversation

@JOY

Copy link
Copy Markdown

Summary

  • add a fail-closed DOS Mainnet ENSv2 deployment profile using the canonical WDOS contract
  • add a PowerShell 7 wrapper that validates RPC, genesis, signer, owner, WDOS, and balance before simulation or broadcast
  • align wrapped-domain ownership with the official BENS ENS model so token contract hashes resolve to registry contracts
  • add Mainnet preflight and subgraph ownership regression coverage

Validation

  • 920 Forge tests passed
  • 30 Matchstick mapping tests passed in Linux
  • 8 manifest renderer tests passed
  • subgraph codegen and build passed
  • PowerShell parser passed

No Mainnet transaction is broadcast by this PR.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces the mainnet deployment configuration and scripts for the DOS Name Service, including a Solidity deployment script, a PowerShell automation script, and comprehensive unit tests. It also updates the subgraph mapping logic to align with the canonical ENS model for wrapped names, ensuring Domain.owner points to the registry contract while tracking the token holder via wrappedOwner. The review feedback focuses on improving the robustness of the PowerShell script (Invoke-DeployDOSMainnet.ps1) by ensuring normalized environment variables are correctly propagated to Foundry and adding proper error handling/null-checks for external CLI commands before invoking string methods.

Comment on lines +53 to +66
$owner = $env:OWNER.Trim().ToLowerInvariant()
$beneficiary = if ([string]::IsNullOrWhiteSpace($env:BENEFICIARY)) {
$env:OWNER
} else {
$env:BENEFICIARY
}
$beneficiary = $beneficiary.Trim().ToLowerInvariant()
if ($owner -ne $expectedOwner) {
throw "OWNER does not match the canonical DOS Names owner"
}
if ($beneficiary -ne $expectedOwner) {
throw "BENEFICIARY does not match the canonical DOS Names beneficiary"
}
$env:BENEFICIARY = $beneficiary

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The environment variable $env:OWNER is not updated with the trimmed and normalized $owner address. If the user provides OWNER with leading/trailing whitespace or mixed casing, the PowerShell validation will pass, but the raw, uncleaned value will be passed to the Foundry script. This can cause vm.envAddress("OWNER") to fail or revert during execution.

Additionally, we can simplify the default assignment of $beneficiary by using the already cleaned $owner variable instead of $env:OWNER.

$owner = $env:OWNER.Trim().ToLowerInvariant()
if ($owner -ne $expectedOwner) {
    throw "OWNER does not match the canonical DOS Names owner"
}
$env:OWNER = $owner

$beneficiary = if ([string]::IsNullOrWhiteSpace($env:BENEFICIARY)) {
    $owner
} else {
    $env:BENEFICIARY
}
$beneficiary = $beneficiary.Trim().ToLowerInvariant()
if ($beneficiary -ne $expectedOwner) {
    throw "BENEFICIARY does not match the canonical DOS Names beneficiary"
}
$env:BENEFICIARY = $beneficiary

Comment on lines +71 to +102
$derivedDeployer = (& $cast wallet address --private-key $privateKey).Trim().ToLowerInvariant()
if ($LASTEXITCODE -ne 0 -or $derivedDeployer -ne $expectedDeployer) {
throw "PRIVATE_KEY does not match the canonical DOS Names deployer"
}

$chainId = [int]((& $cast chain-id --rpc-url $RpcUrl).Trim())
if ($LASTEXITCODE -ne 0 -or $chainId -ne $expectedChainId) {
throw "RPC chain ID does not match DOS Mainnet"
}

$genesis = (& $cast block 0 --rpc-url $RpcUrl --json | ConvertFrom-Json).hash.ToLowerInvariant()
if ($LASTEXITCODE -ne 0 -or $genesis -ne $expectedGenesisHash) {
throw "RPC genesis hash does not match DOS Mainnet"
}

$paymentTokenCode = (& $cast code $expectedPaymentToken --rpc-url $RpcUrl).Trim()
if ($LASTEXITCODE -ne 0 -or $paymentTokenCode -eq "0x" -or $paymentTokenCode -eq "0x0") {
throw "Canonical WDOS has no Mainnet bytecode"
}
$paymentTokenDecimals = [int]((& $cast call $expectedPaymentToken "decimals()(uint8)" --rpc-url $RpcUrl).Trim())
if ($LASTEXITCODE -ne 0 -or $paymentTokenDecimals -ne 18) {
throw "Canonical WDOS must use 18 decimals"
}

$balanceOutput = & $cast balance $expectedDeployer --rpc-url $RpcUrl
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($balanceOutput)) {
throw "Unable to read the canonical deployer balance"
}
$balance = [System.Numerics.BigInteger]::Parse($balanceOutput.Trim())
if ($balance -lt $minimumBalanceWei) {
throw "Canonical deployer balance is below the 1 DOS deployment floor"
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Several native command calls (such as cast wallet address, cast chain-id, cast block 0, cast code, and cast call) are executed in pipelines or subexpressions with immediate method calls (like .Trim(), .ToLowerInvariant(), or piping to ConvertFrom-Json) without checking $LASTEXITCODE or verifying if the output is null/empty first.

If any of these RPC or CLI commands fail (e.g., due to network timeouts, invalid keys, or RPC errors), the command will return null/empty, and calling methods on them will throw a generic and unhelpful PowerShell error (e.g., You cannot call a method on a null-valued expression or JSON parsing errors).

It is much more robust to capture the command output first, verify $LASTEXITCODE and that the output is not empty, and then perform the string operations or JSON parsing. This ensures clear, actionable error messages are presented to the operator during mainnet deployment.

$derivedDeployerOutput = & $cast wallet address --private-key $privateKey
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($derivedDeployerOutput)) {
    throw "Failed to derive deployer address from PRIVATE_KEY"
}
$derivedDeployer = $derivedDeployerOutput.Trim().ToLowerInvariant()
if ($derivedDeployer -ne $expectedDeployer) {
    throw "PRIVATE_KEY does not match the canonical DOS Names deployer"
}

$chainIdOutput = & $cast chain-id --rpc-url $RpcUrl
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($chainIdOutput)) {
    throw "Failed to retrieve chain ID from RPC"
}
$chainId = [int]($chainIdOutput.Trim())
if ($chainId -ne $expectedChainId) {
    throw "RPC chain ID does not match DOS Mainnet"
}

$blockJson = & $cast block 0 --rpc-url $RpcUrl --json
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($blockJson)) {
    throw "Failed to retrieve genesis block from RPC"
}
$genesis = (ConvertFrom-Json $blockJson).hash
if ([string]::IsNullOrWhiteSpace($genesis)) {
    throw "Genesis block hash is missing"
}
$genesis = $genesis.ToLowerInvariant()
if ($genesis -ne $expectedGenesisHash) {
    throw "RPC genesis hash does not match DOS Mainnet"
}

$paymentTokenCodeOutput = & $cast code $expectedPaymentToken --rpc-url $RpcUrl
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($paymentTokenCodeOutput)) {
    throw "Failed to retrieve bytecode for payment token"
}
$paymentTokenCode = $paymentTokenCodeOutput.Trim()
if ($paymentTokenCode -eq "0x" -or $paymentTokenCode -eq "0x0") {
    throw "Canonical WDOS has no Mainnet bytecode"
}

$decimalsOutput = & $cast call $expectedPaymentToken "decimals()(uint8)" --rpc-url $RpcUrl
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($decimalsOutput)) {
    throw "Failed to retrieve decimals for payment token"
}
$paymentTokenDecimals = [int]($decimalsOutput.Trim())
if ($paymentTokenDecimals -ne 18) {
    throw "Canonical WDOS must use 18 decimals"
}

$balanceOutput = & $cast balance $expectedDeployer --rpc-url $RpcUrl
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($balanceOutput)) {
    throw "Unable to read the canonical deployer balance"
}
$balance = [System.Numerics.BigInteger]::Parse($balanceOutput.Trim())
if ($balance -lt $minimumBalanceWei) {
    throw "Canonical deployer balance is below the 1 DOS deployment floor"
}

@JOY
JOY (JOY) merged commit 4b96839 into dos Aug 12, 2026
7 checks passed
@JOY
JOY (JOY) deleted the codex/ensv2-mainnet-deploy branch August 12, 2026 19:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant