Skip to content

Feat/add config registry controller#7668

Open
wantedsystem wants to merge 61 commits intomainfrom
feat/add-config-registry-controller
Open

Feat/add config registry controller#7668
wantedsystem wants to merge 61 commits intomainfrom
feat/add-config-registry-controller

Conversation

@wantedsystem
Copy link
Contributor

@wantedsystem wantedsystem commented Jan 20, 2026

Explanation

Core Package (@metamask/config-registry-controller)

What was delivered:

  • New controller that fetches network configurations from a remote API
  • Automatic updates every 24 hours
  • Filters to show only featured, active, non-testnet networks
  • Feature flag control to enable/disable the feature
  • Fallback to static network list when disabled (in client side extension and mobile)

Business value:

  • Networks can be updated remotely without extension releases
  • Users get the additional network configurations automatically

References

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Note

Medium Risk
Introduces a new remotely-fetched network configuration source with polling, feature-flag gating, and ETag caching; failures or schema mismatches could affect how clients derive network lists when enabled. Scope is mostly additive and isolated to a new package with extensive tests.

Overview
Adds a new @metamask/config-registry-controller package that polls a remote config-registry API (daily by default) to populate persisted network configuration state, with ETag-based cache validation and fallback to a provided static config when the configRegistryApiEnabled remote feature flag is disabled/unavailable.

Includes a ConfigRegistryApiService (env-aware endpoints, retry/circuit-break policy integration, response validation via superstruct) plus selectors/filters (e.g. selectFeaturedNetworks) and comprehensive unit tests. Repo wiring updates include CODEOWNERS/teams mapping, TS project references, README package list/graph, Lavamoat allowlist, and workspace lockfile.

Written by Cursor Bugbot for commit e6575ef. This will update automatically on new commits. Configure here.

cursor[bot]

This comment was marked as outdated.

cursor[bot]

This comment was marked as outdated.

@socket-security
Copy link

socket-security bot commented Jan 20, 2026

No dependency changes detected. Learn more about Socket for GitHub.

👍 No dependency changes detected in pull request

cursor[bot]

This comment was marked as outdated.

cursor[bot]

This comment was marked as outdated.

@mikesposito

This comment was marked as outdated.

@mikesposito

This comment was marked as outdated.

@mikesposito
Copy link
Member

@metamaskbot publish-previews

@github-actions
Copy link
Contributor

Preview builds have been published. See these instructions for more information about preview builds.

Expand for full list of packages and versions.
{
  "@metamask-previews/account-tree-controller": "4.0.0-preview-ab102d86",
  "@metamask-previews/accounts-controller": "35.0.2-preview-ab102d86",
  "@metamask-previews/address-book-controller": "7.0.1-preview-ab102d86",
  "@metamask-previews/analytics-controller": "1.0.0-preview-ab102d86",
  "@metamask-previews/announcement-controller": "8.0.0-preview-ab102d86",
  "@metamask-previews/app-metadata-controller": "2.0.0-preview-ab102d86",
  "@metamask-previews/approval-controller": "8.0.0-preview-ab102d86",
  "@metamask-previews/assets-controller": "0.0.0-preview-ab102d86",
  "@metamask-previews/assets-controllers": "95.3.0-preview-ab102d86",
  "@metamask-previews/base-controller": "9.0.0-preview-ab102d86",
  "@metamask-previews/bridge-controller": "64.6.1-preview-ab102d86",
  "@metamask-previews/bridge-status-controller": "64.4.3-preview-ab102d86",
  "@metamask-previews/build-utils": "3.0.4-preview-ab102d86",
  "@metamask-previews/chain-agnostic-permission": "1.4.0-preview-ab102d86",
  "@metamask-previews/claims-controller": "0.4.1-preview-ab102d86",
  "@metamask-previews/composable-controller": "12.0.0-preview-ab102d86",
  "@metamask-previews/config-registry-controller": "0.0.1-preview-ab102d86",
  "@metamask-previews/connectivity-controller": "0.1.0-preview-ab102d86",
  "@metamask-previews/controller-utils": "11.18.0-preview-ab102d86",
  "@metamask-previews/core-backend": "5.0.0-preview-ab102d86",
  "@metamask-previews/delegation-controller": "2.0.0-preview-ab102d86",
  "@metamask-previews/earn-controller": "11.1.0-preview-ab102d86",
  "@metamask-previews/eip-5792-middleware": "2.1.0-preview-ab102d86",
  "@metamask-previews/eip-7702-internal-rpc-middleware": "0.1.0-preview-ab102d86",
  "@metamask-previews/eip1193-permission-middleware": "1.0.3-preview-ab102d86",
  "@metamask-previews/ens-controller": "19.0.2-preview-ab102d86",
  "@metamask-previews/error-reporting-service": "3.0.1-preview-ab102d86",
  "@metamask-previews/eth-block-tracker": "15.0.1-preview-ab102d86",
  "@metamask-previews/eth-json-rpc-middleware": "23.0.0-preview-ab102d86",
  "@metamask-previews/eth-json-rpc-provider": "6.0.0-preview-ab102d86",
  "@metamask-previews/foundryup": "1.0.1-preview-ab102d86",
  "@metamask-previews/gas-fee-controller": "26.0.2-preview-ab102d86",
  "@metamask-previews/gator-permissions-controller": "1.1.0-preview-ab102d86",
  "@metamask-previews/json-rpc-engine": "10.2.1-preview-ab102d86",
  "@metamask-previews/json-rpc-middleware-stream": "8.0.8-preview-ab102d86",
  "@metamask-previews/keyring-controller": "25.0.0-preview-ab102d86",
  "@metamask-previews/logging-controller": "7.0.1-preview-ab102d86",
  "@metamask-previews/message-manager": "14.1.0-preview-ab102d86",
  "@metamask-previews/messenger": "0.3.0-preview-ab102d86",
  "@metamask-previews/multichain-account-service": "5.1.0-preview-ab102d86",
  "@metamask-previews/multichain-api-middleware": "1.2.6-preview-ab102d86",
  "@metamask-previews/multichain-network-controller": "3.0.2-preview-ab102d86",
  "@metamask-previews/multichain-transactions-controller": "7.0.0-preview-ab102d86",
  "@metamask-previews/name-controller": "9.0.0-preview-ab102d86",
  "@metamask-previews/network-controller": "29.0.0-preview-ab102d86",
  "@metamask-previews/network-enablement-controller": "4.1.0-preview-ab102d86",
  "@metamask-previews/notification-services-controller": "21.0.0-preview-ab102d86",
  "@metamask-previews/permission-controller": "12.2.0-preview-ab102d86",
  "@metamask-previews/permission-log-controller": "5.0.0-preview-ab102d86",
  "@metamask-previews/perps-controller": "0.0.0-preview-ab102d86",
  "@metamask-previews/phishing-controller": "16.1.0-preview-ab102d86",
  "@metamask-previews/polling-controller": "16.0.2-preview-ab102d86",
  "@metamask-previews/preferences-controller": "22.0.0-preview-ab102d86",
  "@metamask-previews/profile-metrics-controller": "3.0.0-preview-ab102d86",
  "@metamask-previews/profile-sync-controller": "27.0.0-preview-ab102d86",
  "@metamask-previews/ramps-controller": "4.1.0-preview-ab102d86",
  "@metamask-previews/rate-limit-controller": "7.0.0-preview-ab102d86",
  "@metamask-previews/remote-feature-flag-controller": "4.0.0-preview-ab102d86",
  "@metamask-previews/sample-controllers": "4.0.2-preview-ab102d86",
  "@metamask-previews/seedless-onboarding-controller": "7.1.0-preview-ab102d86",
  "@metamask-previews/selected-network-controller": "26.0.2-preview-ab102d86",
  "@metamask-previews/shield-controller": "5.0.0-preview-ab102d86",
  "@metamask-previews/signature-controller": "39.0.1-preview-ab102d86",
  "@metamask-previews/storage-service": "0.0.1-preview-ab102d86",
  "@metamask-previews/subscription-controller": "5.4.0-preview-ab102d86",
  "@metamask-previews/token-search-discovery-controller": "4.0.0-preview-ab102d86",
  "@metamask-previews/transaction-controller": "62.9.2-preview-ab102d86",
  "@metamask-previews/transaction-pay-controller": "11.0.2-preview-ab102d86",
  "@metamask-previews/user-operation-controller": "41.0.2-preview-ab102d86"
}

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

apiNetworks.forEach((registryConfig) => {
const { chainId } = registryConfig;
newConfigs[chainId] = registryConfig;
});
Copy link

Choose a reason for hiding this comment

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

Duplicate chainId handling relies on array order not priority

Low Severity

The network deduplication logic in _executePoll simply overwrites newConfigs[chainId] with each iteration, meaning the last array element with a given chainId wins regardless of priority. The test titled "handles duplicate chainIds by keeping highest priority network" passes only because the high-priority entry happens to appear last in the mock array. If the API returns duplicates in a different order, the lower-priority entry would silently win. No priority-based sorting or comparison is performed.

Fix in Cursor Fix in Web

@MetaMask MetaMask deleted a comment from cursor bot Feb 20, 2026
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

// Skip fetch when API is disabled; client uses static config.
if (!isApiEnabled) {
return;
}
Copy link

Choose a reason for hiding this comment

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

Fallback config not stored for runtime revert

Medium Severity

When the feature flag is disabled, _executePoll returns early without modifying state. The fallbackConfig is only used during construction (to set initial state) and is never stored as a class field. This means if the flag transitions from enabled→disabled after a successful fetch, previously fetched API data persists in state instead of reverting to the fallback. Since configs has persist: true, even across app restarts the persisted API data takes precedence over fallbackConfig in the constructor's state.configs?.networks ?? { ...fallbackConfig } check. The PR claims "Fallback to static network list when disabled" but this only holds when no API data was ever fetched and persisted.

Additional Locations (1)

Fix in Cursor Fix in Web

symbol: string(),
decimals: number(),
coingeckoCoinId: string(),
});
Copy link

Choose a reason for hiding this comment

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

Duplicate identical schemas for asset validation

Low Severity

NativeAssetSchema and GovernanceAssetSchema are byte-for-byte identical — both define the exact same six fields (assetId, imageUrl, name, symbol, decimals, coingeckoCoinId). Having two copies means any future field change (e.g., from an API contract update) needs to be applied in both places, creating a risk that one is updated while the other is missed, causing silent validation mismatches.

Fix in Cursor Fix in Web

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

// Skip fetch when API is disabled; client uses static config.
if (!isApiEnabled) {
return;
}
Copy link

Choose a reason for hiding this comment

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

Disabling flag keeps remote configs

Medium Severity

When isConfigRegistryApiEnabled returns false, _executePoll returns early without restoring fallbackConfig, so previously fetched remote state.configs.networks can remain active even though the feature is disabled. This conflicts with the “fallback to static network list when disabled” behavior and can leave users seeing remote networks after turning the flag off.

Fix in Cursor Fix in Web

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We will handle this in client side (extension and mobile)

@wantedsystem
Copy link
Contributor Author

@metamaskbot publish-previews

@github-actions
Copy link
Contributor

Preview builds have been published. See these instructions for more information about preview builds.

Expand for full list of packages and versions.
{
  "@metamask-previews/account-tree-controller": "4.1.1-preview-32ed9958c",
  "@metamask-previews/accounts-controller": "36.0.1-preview-32ed9958c",
  "@metamask-previews/address-book-controller": "7.0.1-preview-32ed9958c",
  "@metamask-previews/ai-controllers": "0.1.0-preview-32ed9958c",
  "@metamask-previews/analytics-controller": "1.0.0-preview-32ed9958c",
  "@metamask-previews/analytics-data-regulation-controller": "0.0.0-preview-32ed9958c",
  "@metamask-previews/announcement-controller": "8.0.0-preview-32ed9958c",
  "@metamask-previews/app-metadata-controller": "2.0.0-preview-32ed9958c",
  "@metamask-previews/approval-controller": "8.0.0-preview-32ed9958c",
  "@metamask-previews/assets-controller": "2.0.2-preview-32ed9958c",
  "@metamask-previews/assets-controllers": "100.0.2-preview-32ed9958c",
  "@metamask-previews/base-controller": "9.0.0-preview-32ed9958c",
  "@metamask-previews/bridge-controller": "67.1.1-preview-32ed9958c",
  "@metamask-previews/bridge-status-controller": "67.0.1-preview-32ed9958c",
  "@metamask-previews/build-utils": "3.0.4-preview-32ed9958c",
  "@metamask-previews/chain-agnostic-permission": "1.4.0-preview-32ed9958c",
  "@metamask-previews/claims-controller": "0.4.2-preview-32ed9958c",
  "@metamask-previews/client-controller": "1.0.0-preview-32ed9958c",
  "@metamask-previews/compliance-controller": "1.0.0-preview-32ed9958c",
  "@metamask-previews/composable-controller": "12.0.0-preview-32ed9958c",
  "@metamask-previews/config-registry-controller": "0.0.0-preview-32ed9958c",
  "@metamask-previews/connectivity-controller": "0.1.0-preview-32ed9958c",
  "@metamask-previews/controller-utils": "11.19.0-preview-32ed9958c",
  "@metamask-previews/core-backend": "6.0.0-preview-32ed9958c",
  "@metamask-previews/delegation-controller": "2.0.1-preview-32ed9958c",
  "@metamask-previews/earn-controller": "11.1.1-preview-32ed9958c",
  "@metamask-previews/eip-5792-middleware": "2.1.0-preview-32ed9958c",
  "@metamask-previews/eip-7702-internal-rpc-middleware": "0.1.0-preview-32ed9958c",
  "@metamask-previews/eip1193-permission-middleware": "1.0.3-preview-32ed9958c",
  "@metamask-previews/ens-controller": "19.0.3-preview-32ed9958c",
  "@metamask-previews/error-reporting-service": "3.0.1-preview-32ed9958c",
  "@metamask-previews/eth-block-tracker": "15.0.1-preview-32ed9958c",
  "@metamask-previews/eth-json-rpc-middleware": "23.1.0-preview-32ed9958c",
  "@metamask-previews/eth-json-rpc-provider": "6.0.0-preview-32ed9958c",
  "@metamask-previews/foundryup": "1.0.1-preview-32ed9958c",
  "@metamask-previews/gas-fee-controller": "26.0.3-preview-32ed9958c",
  "@metamask-previews/gator-permissions-controller": "2.0.0-preview-32ed9958c",
  "@metamask-previews/json-rpc-engine": "10.2.2-preview-32ed9958c",
  "@metamask-previews/json-rpc-middleware-stream": "8.0.8-preview-32ed9958c",
  "@metamask-previews/keyring-controller": "25.1.0-preview-32ed9958c",
  "@metamask-previews/logging-controller": "7.0.1-preview-32ed9958c",
  "@metamask-previews/message-manager": "14.1.0-preview-32ed9958c",
  "@metamask-previews/messenger": "0.3.0-preview-32ed9958c",
  "@metamask-previews/multichain-account-service": "7.0.0-preview-32ed9958c",
  "@metamask-previews/multichain-api-middleware": "1.2.7-preview-32ed9958c",
  "@metamask-previews/multichain-network-controller": "3.0.4-preview-32ed9958c",
  "@metamask-previews/multichain-transactions-controller": "7.0.1-preview-32ed9958c",
  "@metamask-previews/name-controller": "9.0.0-preview-32ed9958c",
  "@metamask-previews/network-controller": "30.0.0-preview-32ed9958c",
  "@metamask-previews/network-enablement-controller": "4.1.2-preview-32ed9958c",
  "@metamask-previews/notification-services-controller": "22.0.0-preview-32ed9958c",
  "@metamask-previews/permission-controller": "12.2.0-preview-32ed9958c",
  "@metamask-previews/permission-log-controller": "5.0.0-preview-32ed9958c",
  "@metamask-previews/perps-controller": "0.0.0-preview-32ed9958c",
  "@metamask-previews/phishing-controller": "16.3.0-preview-32ed9958c",
  "@metamask-previews/polling-controller": "16.0.3-preview-32ed9958c",
  "@metamask-previews/preferences-controller": "22.1.0-preview-32ed9958c",
  "@metamask-previews/profile-metrics-controller": "3.0.1-preview-32ed9958c",
  "@metamask-previews/profile-sync-controller": "27.1.0-preview-32ed9958c",
  "@metamask-previews/ramps-controller": "9.0.0-preview-32ed9958c",
  "@metamask-previews/rate-limit-controller": "7.0.0-preview-32ed9958c",
  "@metamask-previews/remote-feature-flag-controller": "4.0.0-preview-32ed9958c",
  "@metamask-previews/sample-controllers": "4.0.3-preview-32ed9958c",
  "@metamask-previews/seedless-onboarding-controller": "8.0.0-preview-32ed9958c",
  "@metamask-previews/selected-network-controller": "26.0.3-preview-32ed9958c",
  "@metamask-previews/shield-controller": "5.0.1-preview-32ed9958c",
  "@metamask-previews/signature-controller": "39.0.4-preview-32ed9958c",
  "@metamask-previews/storage-service": "1.0.0-preview-32ed9958c",
  "@metamask-previews/subscription-controller": "6.0.0-preview-32ed9958c",
  "@metamask-previews/transaction-controller": "62.18.0-preview-32ed9958c",
  "@metamask-previews/transaction-pay-controller": "16.0.0-preview-32ed9958c",
  "@metamask-previews/user-operation-controller": "41.0.3-preview-32ed9958c"
}

@wantedsystem wantedsystem requested a review from mcmire February 23, 2026 15:19
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Copy link
Contributor

@mcmire mcmire left a comment

Choose a reason for hiding this comment

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

This looks a lot better! The only thing I really noticed was the extra export in index.ts but everything else is minor.

(Also, I know there are some comments that Cursor has made. I haven't taken a look at these, although I'll comment on the one you pinged me on.)


const controllerName = 'ConfigRegistryController';

export const DEFAULT_POLLING_INTERVAL = inMilliseconds(1, Duration.Day);
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice! I forgot we have this utility.

...args: Parameters<ServicePolicy['onDegraded']>
): ReturnType<ServicePolicy['onDegraded']> {
return this.#policy.onDegraded(...args);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't see this comment, but that's a fair question. These are standard methods on data service classes. They allow consumers to respond to when the API is degraded or unavailable.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

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.

3 participants