feat!: migrate Role Manager to operator profile runtime#96
Conversation
Migrate from monolithic ContractAdapter to profile-based EcosystemRuntime: - Replace AdapterProvider with RuntimeProvider, getAdapter with getRuntime - Introduce RoleManagerRuntime (OperatorEcosystemRuntime + relayer) and RoleManagerAdapter compatibility shim for incremental hook migration - Add legacyOperatorRuntime bridge for adapters still on createAdapter - Migrate WalletSyncProvider to activeRuntime API with ecosystem-scoped wallet session support and simplified chain switch logic - Update all hooks/tests from ContractAdapter to RoleManagerAdapter types - Remove broken contract-adapter-compat.d.ts
…m-core locally Require createRuntime for operator profile. Extend pnpmfile for adapter-evm-core local dev.
Apply the same promote-then-dispose handoff pattern from the shared RuntimeProvider/WalletStateProvider layer to the app-local runtime path. Superseded runtimes are disposed after their replacement is promoted to state, cancelled loads are disposed immediately, and the active runtime is disposed on unmount. Disposal is deferred via setTimeout to avoid RuntimeDisposedError in React's dev-mode commit phase. Updates tests to assert correct disposal behavior.
Migrate all production code and tests from the flattened RoleManagerAdapter interface to capability-qualified RoleManagerRuntime access (runtime.addressing, runtime.explorer, runtime.accessControl, runtime.query, runtime.contractLoading, runtime.relayer). Remove toRoleManagerAdapter(), getAdapter(), getAccessControlService(), and the dual-path fallback in useAccessControlService / useContractRegistration.
Port the RC adapter resolution mechanism from ui-builder so staging Docker builds can consume pre-release adapter packages. - Add scripts/resolve-staging-adapters.cjs that queries npm for "rc" and "latest" dist-tags, picks the newer version, and runs a surgical pnpm add --save-exact for adapter packages only. - Update Dockerfile with ADAPTER_DIST_TAG build arg and resolution step between frozen-lockfile install and build. - Pass ADAPTER_DIST_TAG=rc in the staging workflow build-args.
Bump all @openzeppelin/adapter-* and adapters-vite dependencies to ^2.0.0 for the capability adapters migration. Extend .pnpmfile.cjs to widen adapter caret ranges to include pre-release versions during resolution, so pnpm can resolve to RC packages when the stable version hasn't shipped yet. Once stable 2.0.0 is published, pnpm naturally resolves to it instead.
- WalletSyncProvider: read activeRuntime/isRuntimeLoading, pass wallet + networkCatalog as separate props to NetworkSwitchManager, skip chain switch on initial sync and cross-ecosystem switches - Nest getExplorerUrl/getExplorerTxUrl under runtime.explorer in mocks - Update error assertions from "runtime" to "adapter" wording - Fix fake timer cascading timeouts in useNetworkAdapter tests
There was a problem hiding this comment.
Pull request overview
This PR migrates the Role Manager app from the legacy monolithic ContractAdapter usage to the capability-based operator EcosystemRuntime, aligning Role Manager with the newer runtime architecture used across the OpenZeppelin UI ecosystem.
Changes:
- Replace adapter construction/consumption with
createRuntime('operator', networkConfig)and capability-based access throughout hooks/components. - Add runtime lifecycle management (including disposal) on network switches/unmounts.
- Improve staging/Docker build behavior by optionally overriding adapter packages using an npm dist-tag and widening prerelease resolution behavior in pnpm.
Reviewed changes
Copilot reviewed 84 out of 85 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| scripts/resolve-staging-adapters.cjs | Adds dist-tag-based adapter resolution + selective pnpm add overrides for staging builds. |
| Dockerfile | Adds an optional staging step to override adapters after frozen install. |
| apps/role-manager/src/utils/explorer-urls.ts | Switches explorer URL helpers from adapter methods to runtime explorer capability. |
| apps/role-manager/src/types/dashboard.ts | Replaces adapter fields with runtime fields in dashboard-related types. |
| apps/role-manager/src/types/contracts.ts | Updates form/hook return types to runtime-based shape. |
| apps/role-manager/src/pages/Roles.tsx | Consumes runtime from context and uses runtime-based explorer URL generation. |
| apps/role-manager/src/pages/Dashboard.tsx | Migrates dashboard data + explorer link generation to runtime capabilities. |
| apps/role-manager/src/pages/AddressBook.tsx | Passes addressing capability (and resolver) instead of adapter to widget. |
| apps/role-manager/src/hooks/useSelectedContract.ts | Exposes runtime + runtime loading state from ContractContext. |
| apps/role-manager/src/hooks/useRollbackAdminDelayDialog.ts | Uses runtime-based mutation hook wiring + analytics ecosystem source. |
| apps/role-manager/src/hooks/useRolesPageData.ts | Rewires all data hooks to runtime-based capability usage. |
| apps/role-manager/src/hooks/useRoleChangesPageData.ts | Migrates history/search validation/explorer URL helpers to runtime capabilities. |
| apps/role-manager/src/hooks/useRevokeRoleDialog.ts | Uses runtime-based revoke mutation hook. |
| apps/role-manager/src/hooks/useRenounceDialog.ts | Uses runtime-based renounce mutations. |
| apps/role-manager/src/hooks/usePendingTransfers.ts | Uses runtime-based ownership/admin queries + explorer URL helpers. |
| apps/role-manager/src/hooks/useOwnershipTransferDialog.ts | Uses runtime-based transfer mutation + expiration metadata/current block queries. |
| apps/role-manager/src/hooks/useNetworkServiceHealthCheck.ts | Switches service checks to relayer capability. |
| apps/role-manager/src/hooks/useNetworkAdapter.ts | Replaces adapter loader with runtime loader + disposal handoff logic. |
| apps/role-manager/src/hooks/useManageRolesDialog.ts | Uses runtime-based grant/revoke mutations. |
| apps/role-manager/src/hooks/useExpirationMetadata.ts | Reads access control service from runtime and uses runtime network id. |
| apps/role-manager/src/hooks/useDashboardData.ts | Migrates dashboard queries and export behavior to runtime-based hooks. |
| apps/role-manager/src/hooks/useCurrentBlock.ts | Uses runtime.query.getCurrentBlock() instead of adapter method. |
| apps/role-manager/src/hooks/useContractSchemaLoader.ts | Loads schema via runtime contractLoading capability. |
| apps/role-manager/src/hooks/useContractSchema.ts | Updates schema orchestration to runtime-based loader. |
| apps/role-manager/src/hooks/useContractRolesEnriched.ts | Uses runtime access control capability for enriched roles query. |
| apps/role-manager/src/hooks/useContractRegistration.ts | Registers contracts directly via runtime accessControl capability. |
| apps/role-manager/src/hooks/useContractHistory.ts | Uses runtime access control capability for history queries. |
| apps/role-manager/src/hooks/useContractForm.ts | Migrates network-specific address validation to runtime addressing capability. |
| apps/role-manager/src/hooks/useContractData.ts | Updates roles/ownership/admin queries to runtime accessControl capability. |
| apps/role-manager/src/hooks/useContractCapabilities.ts | Migrates capability detection to runtime accessControl capability. |
| apps/role-manager/src/hooks/useChangeAdminDelayDialog.ts | Uses runtime-based change delay mutation + analytics. |
| apps/role-manager/src/hooks/useCancelAdminTransferDialog.ts | Uses runtime-based cancel admin transfer mutation + analytics. |
| apps/role-manager/src/hooks/useBlockTimeEstimate.ts | Uses runtime-based current block polling. |
| apps/role-manager/src/hooks/useAuthorizedAccountsPageData.ts | Rewires authorized accounts page data to runtime hooks. |
| apps/role-manager/src/hooks/useAssignRoleDialog.ts | Uses runtime-based grant role mutation hook. |
| apps/role-manager/src/hooks/useAdminTransferDialog.ts | Uses runtime-based admin transfer mutation + runtime expiration/current block. |
| apps/role-manager/src/hooks/useAccessControlService.ts | Simplifies service access to runtime.accessControl. |
| apps/role-manager/src/hooks/useAccessControlMutations.ts | Migrates all mutations/export to runtime-based access control service. |
| apps/role-manager/src/hooks/useAcceptOwnershipDialog.ts | Uses runtime-based accept ownership mutation hook. |
| apps/role-manager/src/hooks/useAcceptAdminTransferDialog.ts | Uses runtime-based accept admin transfer mutation hook. |
| apps/role-manager/src/hooks/tests/useSelectedContract.test.tsx | Updates tests to runtime-based mocking/expectations. |
| apps/role-manager/src/hooks/tests/useRollbackAdminDelayDialog.test.tsx | Updates dialog test to runtime shape. |
| apps/role-manager/src/hooks/tests/useRolesPageData.test.tsx | Updates mocks/expectations for runtime. |
| apps/role-manager/src/hooks/tests/useRoleChangesPageData.test.tsx | Updates mocks for runtime explorer capability shape. |
| apps/role-manager/src/hooks/tests/useRevokeRoleDialog.test.tsx | Updates test factories to runtime + accessControl capability. |
| apps/role-manager/src/hooks/tests/useRenounceDialog.test.tsx | Updates dialog test to runtime shape. |
| apps/role-manager/src/hooks/tests/usePendingTransfers.test.tsx | Updates mocks to runtime explorer shape. |
| apps/role-manager/src/hooks/tests/useOwnershipTransferDialog.test.tsx | Updates factories to runtime query/addressing/accessControl. |
| apps/role-manager/src/hooks/tests/useNetworkServiceHealthCheck.test.tsx | Updates mocks to runtime relayer capability shape. |
| apps/role-manager/src/hooks/tests/useNetworkAdapter.test.ts | Adds disposal-focused runtime lifecycle tests. |
| apps/role-manager/src/hooks/tests/useManageRolesDialog.test.tsx | Updates test factories to runtime + accessControl capability. |
| apps/role-manager/src/hooks/tests/useDashboardData.test.tsx | Updates hook tests to runtime parameterization. |
| apps/role-manager/src/hooks/tests/useCurrentBlock.test.tsx | Updates mocks to runtime query.getCurrentBlock. |
| apps/role-manager/src/hooks/tests/useContractSchemaLoader.test.tsx | Updates mocks to runtime contractLoading capability. |
| apps/role-manager/src/hooks/tests/useContractSchema.test.tsx | Updates mocks to runtime contractLoading capability. |
| apps/role-manager/src/hooks/tests/useContractRolesEnriched.test.tsx | Updates parameter types to runtime. |
| apps/role-manager/src/hooks/tests/useContractRegistration.test.ts | Updates mocks to runtime accessControl registration. |
| apps/role-manager/src/hooks/tests/useContractHistory.test.tsx | Updates mocks to runtime accessControl capability. |
| apps/role-manager/src/hooks/tests/useContractForm.test.ts | Updates mocks to runtime addressing capability. |
| apps/role-manager/src/hooks/tests/useContractData.test.tsx | Updates mocks to runtime accessControl capability. |
| apps/role-manager/src/hooks/tests/useContractCapabilities.test.tsx | Updates mocks to runtime accessControl capability. |
| apps/role-manager/src/hooks/tests/useChangeAdminDelayDialog.test.tsx | Updates dialog test to runtime shape. |
| apps/role-manager/src/hooks/tests/useBlockTimeEstimate.test.tsx | Updates to runtime query.getCurrentBlock. |
| apps/role-manager/src/hooks/tests/useAuthorizedAccountsPageData.test.tsx | Updates mocks/expectations for runtime. |
| apps/role-manager/src/hooks/tests/useAssignRoleDialog.test.tsx | Updates dialog test to runtime shape. |
| apps/role-manager/src/hooks/tests/useAcceptOwnershipDialog.test.tsx | Updates dialog test to runtime shape. |
| apps/role-manager/src/core/runtimeAdapter.ts | Introduces RoleManagerRuntime type extending operator runtime with relayer. |
| apps/role-manager/src/core/ecosystems/ecosystemManager.ts | Switches to operator runtime creation and ensures relayer capability presence. |
| apps/role-manager/src/context/WalletSyncProvider.tsx | Migrates wallet sync logic to active runtime + updated chain-switch gating. |
| apps/role-manager/src/context/ContractContext.tsx | Contract context now stores runtime instead of adapter. |
| apps/role-manager/src/context/BlockTimeContext.tsx | Block time estimate now uses runtime-based current block polling. |
| apps/role-manager/src/context/tests/WalletSyncProvider.test.tsx | Updates tests for runtime-based wallet sync + switch manager mounting behavior. |
| apps/role-manager/src/context/tests/ContractContext.test.tsx | Updates contract context tests to runtime shape. |
| apps/role-manager/src/context/tests/AliasLabelBridge.test.tsx | Updates selected contract mock shape to runtime. |
| apps/role-manager/src/components/Roles/AssignRoleDialog.tsx | Uses runtime addressing capability for validation + runtime loading state. |
| apps/role-manager/src/components/Ownership/TransferOwnershipDialog.tsx | Uses runtime addressing capability + runtime loading state. |
| apps/role-manager/src/components/Contracts/AddContractForm.tsx | Uses runtime contractLoading/typeMapping/addressing for dynamic fields. |
| apps/role-manager/src/components/Contracts/AddContractDialog.tsx | Uses runtime for schema loading + access control capability. |
| apps/role-manager/src/components/Admin/TransferAdminDialog.tsx | Uses runtime addressing capability + runtime loading state. |
| apps/role-manager/src/App.tsx | Switches provider wiring from AdapterProvider to RuntimeProvider. |
| apps/role-manager/package.json | Bumps adapter/UI dependencies to v2 runtime-capable packages. |
| .pnpmfile.cjs | Adds additional local-dev mappings and widens adapter prerelease resolution. |
| .github/workflows/docker-stg.yaml | Enables staging dist-tag override via ADAPTER_DIST_TAG=rc. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
Comments suppressed due to low confidence (2)
apps/role-manager/src/hooks/useNetworkAdapter.ts:52
- The JSDoc example in this hook still refers to
adapterandadapter.isValidAddress(), but the hook now returns aruntimeand address validation lives underruntime.addressing.isValidAddress(). Updating the example (and variable names in it) will prevent copy/paste mistakes for future callers.
apps/role-manager/src/components/Contracts/AddContractForm.tsx:375 - User-facing copy still says "Loading adapter..." even though this flow now loads a runtime. This can be confusing given the adapter→runtime migration (and the related error block below also says "Failed to load adapter"). Consider updating these strings to refer to the runtime/network runtime instead.
{/* Adapter Loading State */}
{isRuntimeLoading && (
<div className="flex items-center gap-2 rounded-md border border-border bg-muted px-3 py-2 text-sm text-muted-foreground">
<Loader2 className="h-4 w-4 animate-spin" />
<span>Loading adapter...</span>
</div>
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Use Object.create instead of spread in attachRelayerCapability to preserve runtime proxy behavior and object identity - Use execFileSync in resolve-staging-adapters to prevent shell injection - Include @openzeppelin/adapters-vite in prerelease version widening - Update stale adapter terminology in comments, variables, JSDoc, and user-facing copy to use runtime language
The previous commit modified .pnpmfile.cjs without updating the lockfile checksum, causing CI to fail with ERR_PNPM_LOCKFILE_CONFIG_MISMATCH on --frozen-lockfile.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 84 out of 85 changed files in this pull request and generated 4 comments.
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Use execFileSync for pnpm add in resolve-staging-adapters to eliminate remaining shell injection vector; remove unused execSync import - Dispose runtime immediately after extracting addressing capability in AddressBook.resolveAddressing to prevent resource leaks - Dispose previous promoted runtime on getRuntime error path in useNetworkAdapter so stale connections don't persist - Update Dockerfile comment to reflect that .pnpmfile.cjs now also runs the prerelease-widening hook (not purely a no-op in Docker)
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 84 out of 85 changed files in this pull request and generated 3 comments.
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
Comments suppressed due to low confidence (1)
apps/role-manager/src/hooks/useExpirationMetadata.ts:83
- The in-function guard comment still refers to "adapters" implementing
getExpirationMetadata, but this hook now goes throughruntime/accessControlcapabilities. Updating this wording would keep the guidance accurate for the new runtime-based architecture.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…ents - useContractSchema: @param now says "runtime" instead of "adapter" - useExpirationMetadata: file-level JSDoc updated to reference runtime's accessControl capability instead of "adapter.getExpirationMetadata()" - AddContractDialog: inline comment updated to "Runtime for schema loading"
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 84 out of 85 changed files in this pull request and generated 1 comment.
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
Comments suppressed due to low confidence (1)
apps/role-manager/src/context/WalletSyncProvider.tsx:77
- Naming/docs are still adapter-centric in this runtime-based implementation (
isAdapterReady,setIsAdapterReady, and the surrounding comment). This makes the wallet sync logic harder to follow now that adapters are removed. Consider renaming these toisRuntimeReady(or similar) and updating the comment accordingly to keep terminology consistent throughout the migration.
// Track pending network switch (follows UI Builder pattern)
const [networkToSwitchTo, setNetworkToSwitchTo] = useState<string | null>(null);
// Track if adapter is ready for network switch
const [isAdapterReady, setIsAdapterReady] = useState(false);
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Strip build metadata (+...) before parsing and validate that major/minor/patch are finite numbers to avoid silent NaN comparisons.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 84 out of 85 changed files in this pull request and generated 3 comments.
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…stVersion - useExpirationMetadata guard comment now references runtime/accessControl - App.tsx provider-hierarchy comment updated to "runtime session" - pickBestVersion only prefers latest over rc when latest is a stable (non-prerelease) release, preventing incorrect downgrades
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 84 out of 85 changed files in this pull request and generated 1 comment.
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
Comments suppressed due to low confidence (1)
apps/role-manager/src/hooks/useNetworkServiceHealthCheck.ts:55
checkServicesreturns early whenruntime/networkConfigis missing (or when no enabled service forms exist) without resettingisChecking. If a previous check setisChecking=trueand then gets superseded (checkId increments), itsfinallywon’t clear the flag (checkId mismatch), leaving the hook stuck in a perpetual "checking" state. SetisCheckingtofalse(and ideally increment the checkId / cancel in-flight) on these early-return paths as well.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
allowAdapterPrereleases now computes the caret ceiling correctly for major-zero ranges (^0.3.0 -> <0.4.0 instead of <1.0.0), matching npm/pnpm caret semantics and preventing unintended major upgrades.
Summary
Migrates the Role Manager app from monolithic
ContractAdapter+getAccessControlService()consumption to theoperatorprofile runtime from the capability-based adapter architecture.What changed
ecosystemManager.ts— usesecosystemDefinition.createRuntime('operator', networkConfig)instead of constructing adapter classesuseNetworkAdapter— managesEcosystemRuntimelifecycle with proper disposal on network switch and provider unmountuseAccessControlService— simplified to readAccessControlCapabilitydirectly from the runtime instead of dual-pathadapter.accessControl ?? adapter.getAccessControlService?.()EcosystemRuntime;RoleManagerAdaptercompatibility layer removedCross-repo coordination
Merge order:
Test plan
pnpm test)