Skip to content

feat!: migrate Role Manager to operator profile runtime#96

Merged
pasevin merged 19 commits intomainfrom
001-capability-adapters
Apr 3, 2026
Merged

feat!: migrate Role Manager to operator profile runtime#96
pasevin merged 19 commits intomainfrom
001-capability-adapters

Conversation

@pasevin
Copy link
Copy Markdown
Collaborator

@pasevin pasevin commented Apr 2, 2026

Summary

Migrates the Role Manager app from monolithic ContractAdapter + getAccessControlService() consumption to the operator profile runtime from the capability-based adapter architecture.

What changed

  • ecosystemManager.ts — uses ecosystemDefinition.createRuntime('operator', networkConfig) instead of constructing adapter classes
  • useNetworkAdapter — manages EcosystemRuntime lifecycle with proper disposal on network switch and provider unmount
  • useAccessControlService — simplified to read AccessControlCapability directly from the runtime instead of dual-path adapter.accessControl ?? adapter.getAccessControlService?.()
  • All hooks and components — consume narrow capability interfaces from EcosystemRuntime; RoleManagerAdapter compatibility layer removed
  • Test suite — all 80+ test files updated for runtime-based mocking

Cross-repo coordination

Merge order:

  1. openzeppelin-adapters — adapter packages with capability exports
  2. openzeppelin-ui — shared UI packages with capability props
  3. ui-builder — builder app migration
  4. role-manager (this PR)

Test plan

  • Role Manager app builds and runs against local adapter/UI tarballs
  • Full test suite passes (pnpm test)
  • Access control flows (register contract, assign/revoke roles, ownership transfer) work end-to-end
  • CI passes on this branch

pasevin added 5 commits April 1, 2026 17:46
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.
pasevin added 7 commits April 2, 2026 17:44
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
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

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 adapter and adapter.isValidAddress(), but the hook now returns a runtime and address validation lives under runtime.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.

Comment thread apps/role-manager/src/core/ecosystems/ecosystemManager.ts Outdated
Comment thread .pnpmfile.cjs Outdated
Comment thread scripts/resolve-staging-adapters.cjs
Comment thread apps/role-manager/src/components/Contracts/AddContractForm.tsx
Comment thread apps/role-manager/src/context/WalletSyncProvider.tsx
pasevin added 2 commits April 3, 2026 14:57
- 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.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

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.

Comment thread apps/role-manager/src/hooks/useNetworkAdapter.ts
Comment thread apps/role-manager/src/pages/AddressBook.tsx
Comment thread scripts/resolve-staging-adapters.cjs
Comment thread Dockerfile
- 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)
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

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 through runtime / accessControl capabilities. 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.

Comment thread apps/role-manager/src/hooks/useContractSchema.ts Outdated
Comment thread apps/role-manager/src/hooks/useExpirationMetadata.ts Outdated
Comment thread apps/role-manager/src/components/Contracts/AddContractDialog.tsx Outdated
…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"
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

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 to isRuntimeReady (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.

Comment thread scripts/resolve-staging-adapters.cjs
Strip build metadata (+...) before parsing and validate that
major/minor/patch are finite numbers to avoid silent NaN comparisons.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

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.

Comment thread apps/role-manager/src/hooks/useExpirationMetadata.ts Outdated
Comment thread apps/role-manager/src/App.tsx Outdated
Comment thread scripts/resolve-staging-adapters.cjs Outdated
…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
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

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

  • checkServices returns early when runtime/networkConfig is missing (or when no enabled service forms exist) without resetting isChecking. If a previous check set isChecking=true and then gets superseded (checkId increments), its finally won’t clear the flag (checkId mismatch), leaving the hook stuck in a perpetual "checking" state. Set isChecking to false (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.

Comment thread .pnpmfile.cjs
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.
@pasevin pasevin merged commit 180826c into main Apr 3, 2026
11 checks passed
@pasevin pasevin deleted the 001-capability-adapters branch April 3, 2026 15:38
@github-actions github-actions bot locked and limited conversation to collaborators Apr 3, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants