Port latest ENSv2 deployment to .dos - #1
Merged
Merged
Conversation
…nsdomains#273) - moved `event EnhancedAccessControl.EACRolesChanged` before callbacks - moved `event PermissionedRegistry.TokenRegenerated` before `_mint()` - added tests
* added `test_unwrapped_safeTransferFrom_unauthorizedCaller()` * added `test_unwrapped_nameDataMismatch()` * added `test_MIN_DATA_SIZE()` * updated `test_wrapped_migrate()` * updated `test_unwrapped_invalidData()` * updated `test_wrapped_invalidData()` * updated `test_migrate_invalidData()`
- restored *"next resource while expired"* logic from ensdomains#245 - added more tests
…sdomains#281) - added `PermissionedRegistry._register(..., bool checkRoles)` - changed `PermissionedRegistry.register()` to call `_register(..., true)` - changed `WrapperRegistry._inject()` to call `_register(..., false)` - removed questionable function specifiers
- updated `PermissionedResolver`
* changed `resource(0, 0)` to be `ROOT_RESOURCE`
* changed `grantTextRoles(name, key, account)` to `authorizeTextRoles(name, key, account, grant)`
* changed `grantAddrRoles(name, coinType, account)` to `authorizeAddrRoles(name, coinType, account, grant)`
* disabled `revokeRoles()`
* changed `grantNameRoles(name, roles, account)` to `authorizeNameRoles(name, roles, account, grant)`
* added `IDataResolver` support
* added `authorizeDataRoles(name, key, account)`
- added tests
- minor simplification of `EAC._get{Settable|Revokable}Roles()`
- removed `StorageTester.sol` and test
- added configurable `--chainId` to devnet - changed default to `31337` - changed e2e tests to use `--chainId 1`
…y-upgrade-gate fix: gate wrapper registry upgrades
…strar-inception fix: advance reverse registrar inception on direct writes
- changed `PermissionedRegistry._update()` to check roles after `super._update()`
* transfers roles immediately after each `ROLE_CAN_TRANSFER_ADMIN` check
* added tests
- added `uint256[256] private __gap` to `EnhancedAccessControl` - added `uint256[256] private __gap` to `PermissionedRegistry`
…able-factory chore: update verifiable factory
* feat: add reverse registrar hca adapters * fix: mirror reverse namespace at root * chore: migrate post-audit linting * chore: simplify solgrid scripts * chore: split solgrid format check * chore: make format script ci-aware * chore: split format check script * chore: relax constructor notice lint * chore: lint test solidity files * chore: remove redundant constructor notices * Add `RegistryCreated` event (ensdomains#258) - added `event IRegistryEvents.RegistryCreated()` - modified `PermissionedRegistry` to emit event during constructor - modified `UserRegistry` to emit event during `initialize()` - modified `WrapperRegistry` to emit event during `initialize()` - moved `WrapperRegistry` upgrade role logic to `_subregistryRoleBitmapFromFuses()` * Tag migrations onchain (ensdomains#287) - changed `PermissionedRegistry` to assign token `ROLE_WAS_RESERVED` when registered via reservation * fix: assign reverse namespace to owner * fix: align reverse adapter section headers * test: split reverse hca adapter suites * style: format reverse hca adapters * Add `LabelStore` (ensdomains#260) * added `ILabelStore` - added `event Label(bytes32 labelHash, string label)` * added `LabelStore` - added `setLabel(label)` and `getLabel(anyId) returns (label)` * updated `PermissionedRegistry`, `UserRegistry`, and related constructors * Add `Graveyard` (ensdomains#266) - added Graveyard and tests * has no ENSv2 dependencies * added `clear(names[])` - moved some common migration logic to `LibMigration` - changed `UnlockedMigrationController` to transfer unwrapped ERC-721 to `Graveyard` - changed `UnlockedMigrationController` to unwrap unlocked ERC-1155 to `Graveyard` - changed `LockedMigrationController` to unwrap detached ERC-1155 to `Graveyard` - changed `LockedMigrationController` to transfer locked ERC-1155 to `Graveyard` * Add ENSIP-24 to `DNSTXTResolver` (ensdomains#285) * Change `PermissionedRegistry.renew()` to allow revive from root (ensdomains#282) * Refactor `ETHRegistrar` and add `ETHRenewerV1` (ensdomains#286) * refactored `StandardRentPriceOracle` and tests - fully immutable except payment tokens - uses EAC - separate role for `disablePaymentToken()` - changed to pricing via components - added some helpers * `getLength(label): number` * `getPaymentTokenRatio(paymentToken): (numer, denom)` * `applyDiscount(x, duration): % of x` * `convertUnits(x, paymentToken): amount` * `getBasePrice(label, duration): cost` - changed discount to cliffs * changed `IETHRegistrar` - split renew functions into `IETHRenewer` - added `getRegisterPrice(...): (base, premium)` — relative to current state * added `IETHRegistrar` - added `GRACE_PERIOD(): seconds` — specific to the renewer - added `getRemainingGracePeriod(label): seconds` - added `getRenewPrice(...): amount` — relative to current state * refactored `ETHRegistrar` and tests - uses `Ownable` - implements `IETHRegistrar` and `IETHRenewer` - `renew()` only does registrations w/grace - no ENSv1 references * added `ETHRenewerV1` and tests - uses `Ownable` - implements `IETHRenewer` - `renew()` only does reservations with grace and syncs ENSv1 `BaseRegistrar` - `syncWrapper(labels[])` — sync wrapped with unwrapped (trustless) - `transferRegistrarOwnership(newOwner)` — like security controller * changed `PermissionedRegistry` - `register()` of `RESERVED` allows past expiry - added tests * added postlaunch e2e tests * removed `IERC165` from `IEnhancedAccessControl` * removed `IRegistry` from `PermissionedRegistry` * removed `HCA` from `MockERC20` * renamed `ETH_REGISTRAR_V1` to `BASE_REGISTRAR` * `StandardRegistrar.sol` constants are mirrored in `deploy-constants.ts` * `deploy/StandardPriceOracle.ts` prints a discount table * updated deploy scripts * Add `PublicResolverV2` (ensdomains#263) - added `IAddressSet` - added `PermissionedAddressSet` and tests — manages a set of addresses with EAC - added `PublicResolverV2` which is like `PublicResolver` except `isAuthorized()` uses ENSv2 - added `PublicResolverV2.t.sol` - added tests to `LockedMigrationController.t.sol` - added deploy scripts * include `PublicResolverV1` in set on devnet - fixed e2e test for whitelisted PublicResolver * feat(premigration): include v1 grace-period names with 62-day default (ensdomains#302) * test(premigration): align e2e expectations with relaxed past-expiry guard (ensdomains#303) PR ensdomains#286 relaxed PermissionedRegistry._register so that reservations (owner == address(0)) only revert when expiry == 0; past expiries are allowed. PR ensdomains#302 widened pre-migration eligibility to include names inside v1's 90-day grace period, but the rewritten e2e tests still asserted the old "past v2 expiry => failure" behavior. Update three test expectations so just-expired-but-still-claimable names are counted as successful reservations rather than failures. * Add `IUniversalResolverV2` (ensdomains#267) - added `IUniversalResolverV2` - updated `UniversalResolverV2` * restored `findParentRegistry()` * added `IUniversalResolverV2` - added `UniversalResolverV2.t.sol` * Add `ITokenizedRegistry`, `ITemporalRegistry`, and `IOwnedRegistry` (ensdomains#262) - added `ITokenizedRegistry` — `findTokenId(label): token` - added `ITemporalRegistry` — `findExpiry(label): expiry` - added `IOwnedRegistry` — `findOwner(label): owner` - changed `IStandardRegistry` to inherit them * Refactor Registry Metadata (ensdomains#253) - removed `IRegistryMetadata` - removed `MetadataMixin` - removed `BaseUriRegistryMetadata` - removed `SimpleRegistryMetadata` - removed metadata from all constructors - added `IRegistryURIRenderer` - changed `PermissionedRegistry` * added `ROLE_SET_URI` and `ROLE_SET_URI_ADMIN` * added `setURI(uri, renderer)` * added `event URIChanged(uri, renderer, sender)` * added `uri(tokenId)` — uses renderer if it exists * feat(devnet): add mainnet-fork mode to runDevnet (ensdomains#305) * feat(devnet): add mainnet-fork mode to runDevnet Introduces `--forkUrl` (and optional `--forkBlock`) so the devnet can boot against a forked mainnet with canonical v1 contracts preserved and v2 contracts deployed fresh on top. - Pre-populate `deployments/devnet-<chainId>/` with canonical v1 `{address, abi}` entries from `lib/ens-contracts/deployments/mainnet/` so `rocketh.get(name)` resolves to mainnet addresses without redeploys. - Mirror the canonical `.chain` header so rocketh's chainId/genesisHash validation passes against the forked node. - Enable anvil `autoImpersonate` and fund the DAO multisig plus every live `.owner()` of the relevant Ownable v1 contracts via `anvil_setBalance`, letting deploy scripts that issue `account: owner` writes succeed. - Skip the `lib/ens-contracts/deploy` scripts list on fork; v2 scripts still run. - Branch `activateV2` to (a) sign as the DAO via autoImpersonate, (b) skip `LegacyETHRegistrarController` removal (doesn't exist on canonical mainnet), (c) grant `accounts.deployer` registrar-controller rights so test harnesses can drive v1.BaseRegistrar from the mnemonic, and (d) renounce NameWrapper ownership only when still non-zero. - Skip `setupEnsDotEth` on fork to avoid colliding with real subdomains. - Omit anvil `--auto-impersonate false` in non-fork mode (prool encodes `false` as a positional arg that trips anvil's subcommand parser). * feat(devnet): auto-activate v2 + /deployments + env-var fallbacks + --quiet Four ergonomics fixes to the fork-mode devnet that previously forced downstream consumers (morticia's mainnet-fork e2e runner) to paper over gaps in setup: 1. Auto-invoke env.activateV2() when --forkUrl is set. Previously runDevnet returned activateV2 from the env object but never called it, so on a fresh fork the v2 Graveyard / ETHRenewerV1 weren't BaseRegistrar controllers and the BaseRegistrar's owner was still the ENS DAO multisig — every clear() simulation hit ONLY_CONTROLLER. Gating to --forkUrl preserves the existing greenfield-devnet behaviour (its consumers grant controller roles from test-side setup). 2. Env-var fallbacks for the two fork-mode flags: --forkUrl <- FORK_URL, --forkBlock <- FORK_BLOCK. Matches the pattern used by every downstream consumer. 3. GET /deployments on the healthcheck server returns a JSON map of { rocketh deployment name -> checksummed address } plus chainId, so consumers can discover addresses (Graveyard, ETHRenewerV1, migration controllers, etc.) without parsing deployments/devnet-{chainId}/*.json files directly. 4. --quiet flag (or DEVNET_QUIET=1) suppresses the two console.table blocks that print named-accounts and contract-addresses. The 'Ready! <Xms>' line and healthcheck-listening message are retained. Cleaner output when runDevnet is consumed as a subprocess. Smoke-tested standalone with FORK_URL=publicnode + DEVNET_QUIET=1: the new /deployments endpoint returns the v2 graveyard, BaseRegistrar.controllers(graveyard) returns true post-boot, and BaseRegistrar.owner() is ETHRenewerV1. * fix(devnet): remove LegacyETHRegistrarController on mainnet-fork activateV2 * Add `UniversalResolver.findOwner(name)` (ensdomains#308) * added `LibRegistry.findOwner(root, name offset): owner` * added `UniversalResolver.findOwner(name): owner` * updated `PublicResolverV2.authorize()` to use `findOwner()` * Fix `foundry.lock` (ensdomains#310) * Add Encoded Label Support to `Graveyard` (ensdomains#309) * updated `Graveyard.clear()` to process encoded labels using modified encoding * Add `ExtendedDNSResolver` Support to `DNSTXTResolver` (ensdomains#311) * update `DNSTXTResolver` to support `ExtendedDNSResolver` context format: just `addr(60)` * Add `MigrationHelper` (ensdomains#261) * Bump `ens-contracts` and remove `ICompositeResolver.requiresOffchain()` (ensdomains#314) * Fix: `_isMigratableChild()` allows ABANDONED and returns false once expiry is non-zero (ensdomains#313) * feat: require hca interaction * Add `MigrationHelper` deploy script (ensdomains#317) * Remove `ExtendedDNSResolver` artifact (ensdomains#316) - removed `ExtendedDNSResolver_53f64de872aad627467a34836be1e2b63713a438.json` - remapped `dnsname` to `dnstxt` * `DNSTXTResolver` is backwards-compatible with `ExtendedDNSResolver` context * fixes bug with `addr(coinType)` response on `ExtendedDNSResolver` deployment * feat: hardcode deferred hca implementation * style: format hca tests * refactor: clean up hca factory interface * refactor: remove nexus hca proxy dependency * refactor: use yul hca proxy bytecode * Improve pre migration csv parsing (ensdomains#319) * feat: update hca interaction flow * feat: expose deferred hca implementation * Add `IContractNamer` (ensdomains#297) - added `IContractNamer` - added `AccountNamerLib` - updated `L2ReverseRegistrar` * changed `setNameForOwnableWithSignature` to `setNameForContractWithSignature` * replaced `_ownsContract()` and `modifier authorized()` with `AccountNamerLib` * added `IContractNamer` support - updated `ReverseRegistrarHCAAdapter` * added `claimForContract(contract, resolver)` - updated `DefaultReverseRegistrarHCAAdapter` * added `setNameForContract(contract, name)` - added `ContractNamer` as shared upgradeable namer - added `DelegatedContractNamer` mixin - updated `PermissionedRegistry`, `PermissionedAddressSet`, and `StableRentPriceOracle` * added `ROLE_CAN_NAME` and `ROLE_CAN_NAME_ADMIN` * implemented `IContractNamer` with `hasRootRoles(ROLE_CAN_NAME, account)` - updated non-`Ownable/EAC` core contracts to use `DelegatedContractNamer` - cleaned `foundry.lock` - updated readme - added tests * feat: record hca on account creation * perf: compute hca account address * test: simplify reverse registrar hca fixture * fix: restore reverse mirror deploy * fix: update hca factory interface selector * fix: restore contract namer deploy * fix: avoid spaced hca coverage name * chore: revert coverage script change * fix: skip mainnet hca owner setup * chore: allow manual docker image branch builds * chore: quote github actions trigger key * Add test for emancipation concern (ensdomains#327) * Finish Contract Naming (ensdomains#328) - named `ReverseRegistrarHCAAdapter` - named `DefaultReverseRegistrarHCAAdapter` - named `MigrationHelper` - added upgrade test for `ContractNamer` * Emit `ParentUpdated` during `WrapperRegistry.initialize()` (ensdomains#324) * Change `WrapperRegistry` to use "virtual owner" (ensdomains#331) - changed `WrapperRegistry` to have at most one account with admin * roles granted to "virtual owner" — actual owner is parent registry * `_msgSender()` remaps from token owner to "virtual owner" - changed `WrapperRegistry` to respect frozen `NameWrapper` fuses for `ROLE_RENEW`, `ROLE_UPGRADE`, and `ROLE_CAN_NAME` (like `ROLE_REGISTRAR`) * Utilize `PermissionRegistry.setApprovalForAll()` in `EAC` (ensdomains#333) - changed `EnhancedAccessControl` * added virtual `_getRoles(resource, account): roles` which permits logic <ins>before</ins> permission checks * added private `_effectiveRoles(resource, account)` which is root + resource * refactored to utilize those functions - changed `PermissionedRegistry` * overrode `_getRoles()` to account for token expiry and approval * simplified `_getSettableRoles()` and `_getRevokableRoles()` since `_checkedRoles()` already handles expiry - changed `WrapperRegistry` * removed `_msgSender()` hack * removed `IContractNamer` override * overrode `_getRoles()` to account for virtual owner — applies <ins>after</ins> expiry and approval checks, so approvals are not virtualized and do not survive transfer --- * `EAC` reverts use the caller address * `PermissionRegistry.roles()` returns the same roles for the token owner AND virtual owner AND approved * `test_revokeRoles_whileReserved()` returns false instead of reverting * `test_revokeRoles_whileExpired()` returns false instead of reverting * Remove HCA (ensdomains#332) - removed all instances of HCA - replaced all `Context._msgSender()` with `msg.sender` - simplified reverse adapters - added `ERC1155Singleton._checkApproved()` - simplified `WrapperRegistry._getRoles()` * add permit support * cleanup * derp * More `PermissionedRegistry` Tests (ensdomains#334) - added more `PermissionedRegistry` tests - added more `WrapperRegistry` tests * Add `setters[]` to `PermissionedResolver.initialize()` (Redo) (ensdomains#336) - added `bytes[] calldata setters` to `initialize()` - removed `InvalidOwner` revert * fix(ci): repair codecov pr uploads * fix(migration): preserve CAN_EXTEND_EXPIRY when migrating emancipated names [NM-0919] (ensdomains#343) * fix(migration): preserve CAN_EXTEND_EXPIRY when migrating emancipated names Grant ROLE_RENEW and ROLE_RENEW_ADMIN to the owner when an emancipated (non-locked) wrapped name with the CAN_EXTEND_EXPIRY fuse is migrated, so the v1 self-renewal right carries over to v2. Previously the fixed registration bitmap silently dropped it, leaving only the parent owner able to renew the name. * style(migration): apply solgrid formatting * Fix `ETHRenewerV1.syncWrapper()` juggle (ensdomains#344) - bumped `lib/ens-contracts` (deploy fix) - changed `ETHRenewerV1` * takes `NameWapper` instead of `BaseRegistrar` * juggles `NameWrapper` instead of `WrappedController` * fixed `syncWrapper()` test - added test to `migration.test.ts` for `syncWrapper()` * Fix `RegistryRolesLib` comments (ensdomains#345) * Improved Coverage (ensdomains#339) - changed from `365.25 days` to `365 days` for pricing - canonicalized `ROLE_CAN_NAME` for `IContractNamer` (some were `ROLE_SET_NAME`) - changed `Graveyard` * fixed `clear()` logic for locked parent * added `error NameRequiresPreimage()` - changed `ERC1155Singleton` * fixed `balanceOf(address(0), *) == 1` - improved test coverage: `97.88%` → `99.92%` * Fix `WrapperRegistry` prevents revive if `CANNOT_CREATE_SUBDOMAIN` (ensdomains#346) * poc * test: cover WrapperRegistry revive success path Add positive-path coverage for WrapperRegistry._canRevive: when CANNOT_CREATE_SUBDOMAIN is not burned, a root role holder can revive an expired child. This exercises super._canRevive, closing the codecov patch gap that was failing CI. * ci: make per-flag codecov patch status informational Per-flag patch statuses fail on cross-cutting changes when a flag's test suite includes the changed files in its report but does not exercise the changed lines. Mark per-flag patch as informational so it reports without blocking; the aggregate patch gate still enforces coverage. --------- Co-authored-by: Ramesh Nair <ram@hiddentao.com> * fix typo (ensdomains#348) * Add comment (ensdomains#349) * Prevent `Graveyard.clear()` on unregistered .eth (ensdomains#351) - check `nameExpires()` before `register()` - added tests * Bump verifiable-factory (ensdomains#352) * Bump verifiable-factory * Align foundry.lock with bumped verifiable-factory foundry.lock still pinned lib/verifiable-factory to 04ec76f (old two-argument verifyContract API) while the submodule gitlink and UserRegistry.t.sol now require 5ef7b1a (one-argument API). Setups that hydrate Foundry deps from the lock file would restore the old revision and fail to compile. Update the lock entry to match the gitlink. * Fix `PermissionRegistry._getSettableRoles()` when expired (ensdomains#353) - added `getOwner(anyId): owner` - refactored `findOwner(label): owner` to use `getOwner(...)` - changed `_getSettableRoles(...)` to check if registered - fixed `_randomRoleBitmap(...)` for better fuzzing — a typical iteration wouldn't set a single bit and `(true, true)` would always set 2+ bits - avoid checking `PUBLIC_RESOLVER_SET.includes(...)` if `resolver` is unset - avoid checking `isApprovedForAll()` if `owner == account` * Add `ETHRenewerV1.setRegistrarResolver()` (ensdomains#355) In ENSv2, `ETHRenewer` <ins>must</ins> own the ENSv1 `BaseRegistrar` to perform the controller juggle, see: `syncWrapper()`. `BaseRegistrar.setResolver()` uses `onlyOwner` instead of `onlyController` so only the singular owner can call this function. Effectively, `ETHRenewerV1` is a `RegistrarSecurityController` without any `BaseRegistrar` controller management (since ENSv1 is disabled.) --- `ETHRenewerV1` already has `transferRegistrarOwnership()`, this PR adds `setRegistrarResolver()`, which makes changing the ENSv1 resolver for "eth" simple. Without this change, the following steps would be required: 1. `ETHRenewerV1.transferRegistrarOwnership(DAO)` 1. `BaseRegistrar.setResolver(<resolver>)` 1. `BaseRegistrar.transferOwnership(ETHRenewerV1)` --- **Usage**: the ENSv1 "eth" resolver should be set to the `ENSV2Resolver` at launch, and cleared (via this function) once the initial migration period is over (TBD). * feat: migration/sepolia deploy updates (core) (ensdomains#342) * feat: sepolia deploy snapshot (core) * style: format solidity * fix: read hardhat coverage inputs from build info * fix: repair canonical parent on ethregistry reruns * feat: add migration rehearsal harness * fix: add v1 controller grant for testnet premigration registrar * chore: track sepolia deployment records * fix(migration): address Codex review feedback on PR ensdomains#342 - TestnetV1PremigrationRegistrar: apply resolver records while the registrar still owns the node, then hand off ownership (ensdomains#5); set reverse records for the registrant rather than the caller (ensdomains#3); add the continuity bonus to every v2 reservation (ensdomains#7). - Deploy the testnet registrar as a controller of both reverse registrars so the owner-targeted reverse records are authorized (#1). - Reverse-registrar adapters resolve their v1 contracts via getV1 (ensdomains#9). - Treat sepolia-dev as sepolia for the known top URP and v1 deployment lookups (ensdomains#6, ensdomains#11). - Run the v1 root-TLD mirror before DNSTLDResolver so root TLDs keep their v1 fallback (ensdomains#12). * fix(migration): preserve network v1 owner default; revoke suffix roles on failure Address outstanding Codex review feedback on PR ensdomains#342: - resolveMigrationSigners no longer defaults v1Owner to the migration owner when --v1-owner is omitted. Leaving it unset lets the deploy logic fall back to the network-configured v1 owner, so v1-owner transactions (and the deferred JSONL `from`) target the real v1 owner on sepolia/mainnet instead of the migration/Hardhat account. - registerSuffixesViaBatchRegistrar wraps the batch loop in try/finally so the temporary REGISTRAR|RENEW root roles are always revoked, even if a suffix batch registration reverts partway through. * test(e2e): gate live external DNS resolution tests behind env flag The taytems.xyz / raffy.xyz resolution cases resolve real third-party DNS records through the live dnssec-oracle.ens.domains CCIP gateway, so they fail in CI whenever that gateway or those external records are unavailable (independent of any contract change). Gate them behind RUN_LIVE_DNS_TESTS so CI stays deterministic; the onchain .ens.eth DNS cases still run. * fix(migration): require an output file when deferring v1-owner transactions Deferring v1-owner transactions without an output file silently dropped the calldata: only a one-line summary was logged, the JSONL append was skipped, and a fake-success receipt was returned, so phase-1 could appear to succeed while v1-owner actions were neither broadcast nor saved for execute-owner-txs. Fail fast when deferring is requested without a file so the calldata is always persisted for replay. * fix(premigration): adopt post-audit bonus-days reservation model Replace the continuity-expiry pre-migration model with the audited post-audit version: each v2 reservation expiry is the name's v1 expiry plus a configurable --bonus-period-days (default 62), gated on the v1 90-day grace period. Drops the --min-expiry-days and --skip-existing-reservations flags and the hardcoded continuity bonus. Reconcile the feat-only consumers to the new API: - migration.ts: imports, inline reserve/verify logic, phase 2/4 wiring, and premigration run/verify CLI options switch to --bonus-period-days - premigration-run Hardhat task and plugin options - migration.md phases 2 & 4 Retain --account (impersonation signer) in preMigration.ts since the fork rehearsal harness drives pre-migration via impersonated accounts; it is orthogonal to the expiry model. * fix(premigration): support multicall on non-mainnet chains resolveChain now returns viem's sepolia chain for chain id 11155111 and attaches the canonical Multicall3 address to synthesized custom chains, so preMigration's batched multicall no longer fails with ChainDoesNotSupportContract on Sepolia and other non-mainnet networks. Also document running pre-migration against a Sepolia fork (including the fresh-deployer requirement for EIP-7702-delegated default accounts) and ignore the local .dev scratch directory. * feat(migration): support fork-full rehearsal against already-migrated chains Make the phased migration fork rehearsal work against a chain where a prior v1->v2 hand-off has already happened (e.g. current Sepolia), while still running the full from-scratch rehearsal on a pristine chain (mainnet today, or a repeat mainnet run after a bug-fix redeploy). - Detect a completed v1 hand-off from on-chain controller state and skip the live v1 registration smokes when v1 registration is already disabled; a pristine chain still exercises them. - Impersonate the v1 owner during the in-process phase-1 deploy so the resolver hand-off write has a signer on the fork. - Derive the URP cut-over baseline from the canonical top proxy's live implementation instead of a static deployment reference, so the managed proxy is seeded transparently and verification matches current state. - Pass the Graveyard address through from the in-memory deploy instead of reading it from disk, so phase 8 works without saved deployments. - Consolidate the repeated fork chain setup into a helper. * feat(migration): support live re-migration and address review feedback Add the ability to re-run the migration on a chain that has already been migrated once (important for the live migration, not just fork rehearsals), and resolve the open Codex review threads on PR ensdomains#342. Live re-migration: - Reclaim v1 BaseRegistrar ownership from a prior deployment's ETHRenewerV1 back to the v1 owner (via the prior renewer's transferRegistrarOwnership, signed by its own owner) so the EOA-signed handoff phases can run again. Wired into the fork rehearsal before phase 8 and exposed as a standalone `phase reclaim-v1-registrar-ownership` command for live operators. Review feedback: - Align the testnet premigration registrar's reservation expiry with the bonus-days verifier by dropping the stray extra second. - Route the v1 reverse/default-reverse adapter controller grants through the v1 owner instead of the v2 admin. - Only attach an owner/urManager private key during phase deploy when it controls the resolved account, so an explicit or DAO owner is not replaced by the deployer key; mirror the fix for the Hardhat deploy-v2 task. - Keep the free-mint mock payment tokens off mainnet and whitelist real mainnet USDC/DAI in the rent price oracle instead. Docs: - Document the post-migration auto-detection and the narrowed `--calldata-only` scope; refine the universal resolver setup notes. * fix(migration): handle real payment tokens and clean-testnet owner key Address two Codex review findings that follow from the mainnet token and phase-deploy owner-key changes: - Only attach an owner/urManager/v1Owner private key in the clean-testnet path when it controls the resolved account, so an explicit owner is not signed for by the deployer key (mirrors the phase deploy-v2 fix). - Make the mock payment token optional: gate the paid v2-registrar smokes on a mintable token and verify the phase-9 role grant directly where none exists (mainnet, which whitelists real USDC/DAI), instead of assuming a free-mint mock. * refactor(migration): reorder phases and authorize v1 renewer early Restructure the v1 → v2 migration from nine phases to seven so unmigrated names stay renewable throughout the migration window and public resolution flips only once everything else is live: - authorize ETHRenewerV1 as a v1 controller early (new phase 4) instead of at final hand-off, splitting it from the BaseRegistrar ownership transfer - run the final pre-migration sync afterwards (phase 5) so renewed expiries are picked up - bundle the v2-controller steps (disable BatchRegistrar, authorize handoff controllers, transfer v1 ownership to ETHRenewerV1, enable v2 ETHRegistrar) into phase 6 - move the Universal Resolver cutover to phase 7, run last - deploy the reverse-registrar adapters as part of phase 1 and tag their deploy scripts with migration:phase1:deploy-v2 - reclaim v1 registrar ownership before the early renewer authorization on re-migration runs Update docs/migration.md and console phase labels to match. * feat(migration): add --fresh flag to redeploy v2 over an archived namespace phase deploy-v2 is idempotent against its deployment namespace, so re-running into a populated deployments/<env>/ reuses the existing contracts instead of deploying fresh. Add an opt-in --fresh flag that archives the current namespace to <env>-<YYYYMMDD>-r<N> (date from the archived set's recorded deploy time, with an auto-incrementing revision) and deploys fresh into the clean namespace. --fresh implies --save-deployments and works for both sepolia and mainnet. Record a .deployment.json metadata file in each saved namespace capturing the original deploy time; the archiver reads it back (falling back to the latest .migrations.json timestamp) to name the archived folder. Track deployments/README.md and document the namespace layout, the --fresh workflow, the git-tracking model, and the v1 override layer. * docs(migration): add live Sepolia deployment runbook Document the end-to-end fresh Sepolia deployment using phase deploy-v2 --fresh followed by phases 2-7, including the deferred v1-owner transaction flow. Explain the three signer keys (DEPLOYER_KEY, SEPOLIA_V1_OWNER_KEY, SEPOLIA_TOP_URP_OWNER_KEY) and which roles and phase commands each one covers, plus the renewal-gap and pre-migration caveats and the mainnet multisig difference. * test(e2e): gate flaky live-mainnet v1 verification behind opt-in flag The two preMigration verification tests that read live mainnet state were hardcoded to a free public RPC and flaked in CI on request timeouts. Gate them behind RUN_LIVE_MAINNET_TESTS (matching the existing RUN_SEPOLIA_FORK_MIGRATION_TEST and RUN_LIVE_DNS_TESTS conventions) and make the RPC configurable via MAINNET_RPC_URL. The input-validation cases, which make no network calls, keep running unconditionally. * fix(migration): skip env signer fallback when impersonated account differs runPreMigrationCommand unconditionally resolved the batch signer from the env key chain even when the caller passed an impersonated account, then forwarded both --private-key and --account to preMigration (which prefers the key). When the env deployer key did not control the BatchRegistrar owner, the batch run signed as the wrong address and onlyOwner reverted despite the account being impersonated. Only consult the env fallback key when no account was supplied, or when it actually controls that account; otherwise forward the impersonated account alone. Add a focused test covering each signer-resolution branch. * refactor(migration): make phase deploy-v2 fresh by default with --resume Replace the deploy-v2 --fresh / --save-deployments flags with fresh-as-default behavior plus a --resume flag to continue an interrupted deploy into the existing namespace. Drop the hardcoded phase-1 transaction-count figure from the docs. * feat(migration): reuse existing intermediate URP instead of deploying a new top URP Treat the top URP and intermediate (managed) URP as pre-existing fixtures and reuse them where they already exist. A fresh v2 deployment on a network whose top URP already fronts an intermediate URP we administer now performs a single on-chain mutation — upgrading the intermediate URP to the new implementation — leaving the externally-administered top URP untouched. - add KNOWN_INTERMEDIATE_URP map and adopt it in the managed-URP deploy step, falling back to deploy-fresh on bootstrap networks - stop deploying a fresh top URP (adopt the canonical address only; clean-testnet still builds its own self-owned stack) - skip the top-URP setup steps when the top URP already serves an implementation or already fronts the intermediate URP - make switchTopUrpToManaged idempotent and skip the switch in the cutover when reuse is already in place - wire securityCouncil/urManager to the intermediate URP admin per network - extract a shared knownProxyNetworkName helper - update universal resolver and migration docs for the reuse vs bootstrap split * feat(migration): commit fresh sepolia v2 deployment + fix live deploy signing Deploy the fresh v2 contract set into deployments/sepolia/ and track all sepolia* namespaces (live + archived) so real deploys are committed. Fixes surfaced during the live Sepolia run: - loadDotEnv: strip inline `#` comments and surrounding whitespace from unquoted values (an inline comment previously corrupted private keys); preserve `#` inside quoted values. - phase deploy-v2: wire the v1Owner account to a local key when one controls it, so a keyless v1Owner sharing the deployer address can no longer shadow the deployer's signer and force node-side signing that a remote RPC rejects. - docs: add reclaim-v1-registrar-ownership to the Phase 1 runbook (before the deferred-tx replay on an already-migrated chain) and the CLI reference, plus deployer/.env hygiene notes. * chore(deployments): track real deploy namespaces by default Invert the deployments .gitignore from allow-list to deny-list: track live and dated-archive namespaces by default and ignore only the local rehearsal/runtime ones (*-fork, *-clean-*). The previous blanket `deployments/*` ignore caused editors to dim the committed sets even though they were force-tracked. * fix(migration): address Codex review findings (mainnet deploy + signer/CSV correctness) Make the migration tooling mainnet-capable and fix signer/CSV correctness issues flagged in PR review. Mainnet deploy: - Reverse-registrar adapters: drop the mainnet early-return so the v1-owner setController grant is recorded as a deferred owner tx instead of being silently skipped. - Defer owner/DAO writes (not just v1-owner) during deploy when owner differs from the deployer, so PublicResolverSet.approve is replayable on mainnet. - Populate the real mainnet wrapper-aware PublicResolverV3/V4 addresses. Signer handling: - set-v1-reverse-default-resolver: resolve the v1-owner key (not the deployer), add a phase CLI command with --calldata-only and an owner guard. - phase deploy-v2: honour an explicit --deployer instead of overriding it with DEPLOYER_KEY. - clean-testnet phase-0 deployV1: only attach a key that controls v1Owner. - Standalone fork-full/clean-testnet CLIs: hydrate env signer keys on the non-state-control path and detect Tenderly virtual RPCs (shared helper). CSV / verify: - Drop TheGraph rows with no decodable labelName before export. - Verify only asserts the premigration fallback resolver for RESERVED names. - Normalize the label-column header case-insensitively (throwing when absent) and CSV-escape labels when rewriting the premigration CSV. - Reclaim skips the live RegistrarSecurityController instead of treating it as a prior renewer. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1WiTqnbW7bLbqejYkyBN1 * feat(migration): add contract source-code verification script Add a dedicated verify script that submits deployed contracts to Etherscan and Sourcify via @rocketh/verifier. The verifier rebuilds the solc standard-JSON input from each artifact's recorded metadata, which needs the literal source content of every file; hardhat-compiled artifacts embed it but forge-compiled ones record only hashes and URLs. The script backfills missing content from disk (checked against the recorded hash) before submitting, so a contract verifies regardless of which compiler produced its artifact. Wire up verify / verify:sepolia / verify:mainnet npm scripts and document the step in the migration runbook. * docs(migration): tighten and correct migration guides - update phased-deploy role-grant reference from phase 9 to phase 6 - condense premigration and prepareMigration docs, trimming verbose sections - document fresh-by-default deploy behaviour for phase deploy-v2 - align README usage examples with current flow * fix(migration): correct stale phase-number reference in comment The reclaimV1RegistrarOwnership comment referenced non-existent 'controller phases (8 and 9)'; the migration has only phases 1-7. Reword to describe the owner-signed v1 controller steps conceptually. * refactor(migration): extract owner-gated write helpers Collapse the repeated owner-read + key-guard + impersonate + wallet + write + receipt skeleton into resolveOwnerGatedWallet / sendOwnerGatedWrite (owner()-gated) and sendAdminWrite (account passed in). Apply across the v1 registrar/reverse/renewer writes, the URP switches, and the registrar enable/disable. The disable-v1-registrars loop now shares the same wallet resolver, gaining the key/owner guard it previously lacked. * refactor(migration): dedupe orchestrator signer and smoke blocks Compute the v1-owner signer once (v1OwnerSigner) and spread it at the four controller-change phases instead of repeating the impersonate/key ternary. Collapse the three v1 smoke register+assert pairs into registerSmokeV1 / assertSmokeV1Owner closures over the shared context. * refactor(migration): add registry/role read helpers Extract resolveRegistry (explicit address or deployment artifact) and readHasRegistrarRoles, replacing the duplicated registry/abi resolution and hasRootRoles reads in the v2 registrar enable/disable/verify paths and removing the registry! non-null assertions. * refactor(migration): reduce CLI handler boilerplate Add withNetworkRpc (parse network + resolve rpc url) and route all 24 action handlers through it, dropping the repeated requireRpcUrl call. Add v1OwnerKeyFromEnv for the v1-owner key env fallback and addV1OwnerWriteOptions for the shared private-key/impersonate-owner/calldata-only option trio, applied across the v1-owner-gated phase commands. * refactor(migration): share deploy provider/chain preamble and address logging Extract resolveDeployProviderAndChain (provider shim, chain-id override, chain resolution) and logDeployedAddresses, replacing the byte-for-byte identical preambles and print loops in deployV1 and deployV2. * fix(migration): make upgrade-managed-urp idempotent When reusing a managed URP that already fronts the target UniversalResolverV2 (e.g. a deterministic redeploy lands the same implementation address), the proxy reverts upgradeTo with SameImplementation. Skip the upgrade when the managed URP is already at the target implementation, mirroring the existing 'top URP already fronts managed URP' guard in switchTopUrpToManaged. Surfaced by a fork full sepolia rehearsal re-run against an already-migrated namespace. --------- Co-authored-by: Ramesh Nair <ram@hiddentao.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(migration): auto-generate per-network address docs; note Sepolia URP rollback (ensdomains#361) * docs(readme): note temporary Sepolia intermediate-URP rollback Record that the intermediate URP has been repointed from the fresh deployments/sepolia stack to the previous sepolia-official-v1-20260525-r2 UniversalResolverV2, restoring v1-name resolution through the public entrypoint, with the revert command. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CYSpMNUyiWpnmB1FSJUErH * feat(migration): auto-generate contract address docs per network Add a shared generator that renders deployment namespaces as Etherscan-linked markdown tables under docs/addresses/<network>.md. Wire it into the end of phase deploy-v2 (persisted deploys only) and expose a standalone `bun run docs:addresses` CLI. Link the tables from the README and move the temporary managed-URP operational note alongside them. Generate the current Sepolia table. --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * patch coverage below 90% is shown but never fails (ensdomains#364) * Add `WrapperRegistry._getRoles()` support for virtual owner approvals (ensdomains#356) - changed `WrapperRegistry._getRoles()` to check `parent.isApprovedForAll()` * Revert "Add `WrapperRegistry._getRoles()` support for virtual owner approvals (ensdomains#356)" This reverts commit 39457d8. --------- Co-authored-by: tate <tate@ens.domains> Co-authored-by: Ram <ram@hiddentao.com> Co-authored-by: v1rtl <hi@v1rtl.site> Co-authored-by: TateB <yo@taytems.xyz> Co-authored-by: Makoto Inoue <2630+makoto@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Summary
48b3e2d39513b9dd32ef1850877a29009bc807b9.dosregistrar that preserves the audited upstream registration logic.dosforward resolution, DOS reverse resolution, and resolver proxy supportValidation
tsc --noEmit: passedforge script --broadcastagainst local Anvil chain 3939: passed, followed by on-chain ownership, role, oracle ratio, registry, and reverse namespace checksDeployment boundary
This PR changes contract source only and does not deploy to DOS Chain. The existing BENS indexer in
DOS-Chainuses the legacy registrar ABI and event layout. It must be updated and validated in a separate cross-repo change before these contracts are deployed to any shared environment.