Skip to content

feat: add cash account keyring to keyring controller, and create cash account service#8204

Open
Jwhiles wants to merge 18 commits intomainfrom
add-cash-keyring
Open

feat: add cash account keyring to keyring controller, and create cash account service#8204
Jwhiles wants to merge 18 commits intomainfrom
add-cash-keyring

Conversation

@Jwhiles
Copy link

@Jwhiles Jwhiles commented Mar 16, 2026

Explanation

This PR adds the new cash account keyring, as a well as a new service which will be used to add accounts to that keyring.

This PR is dependant on this PR in the accounts repo being merged and published.

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

High Risk
High risk because it changes keyring management by adding a new built-in keyring type and derives new key material from the HD keyring mnemonic via messenger calls. It also introduces new preview keyring dependencies, so regressions could impact account/key handling.

Overview
Adds Cash account support end-to-end: KeyringController now includes KeyringTypes.cash and registers CashKeyring (from @metamask-previews/eth-cash-keyring) as a default built-in keyring.

Introduces a new package, @metamask/cash-account-service, exposing createCashAccount via the messenger to derive a Cash keyring from an HD keyring mnemonic (or return existing Cash keyring metadata if one already has accounts), with accompanying tests and build/config scaffolding.

Updates accounts-controller to display KeyringTypes.cash as Cash Account, expands tests to cover the new keyring type, and wires repo metadata (CODEOWNERS, teams.json, root tsconfig.build.json, yarn.lock) for the new package/dependencies.

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

@socket-security
Copy link

socket-security bot commented Mar 16, 2026

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​metamask-previews/​eth-cash-keyring@​0.0.0-25e4b24701007787100
Added@​metamask-previews/​keyring-api@​21.5.0-25e4b24771001009750
Updated@​metamask/​eth-hd-keyring@​13.0.0 ⏵ 13.1.09910010090 +4100

View full report

@Jwhiles
Copy link
Author

Jwhiles commented Mar 16, 2026

@metamaskbot publish-preview

@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": "5.0.1-preview-54bc644cd",
  "@metamask-previews/accounts-controller": "37.0.0-preview-54bc644cd",
  "@metamask-previews/address-book-controller": "7.0.1-preview-54bc644cd",
  "@metamask-previews/ai-controllers": "0.3.0-preview-54bc644cd",
  "@metamask-previews/analytics-controller": "1.0.0-preview-54bc644cd",
  "@metamask-previews/analytics-data-regulation-controller": "0.0.0-preview-54bc644cd",
  "@metamask-previews/announcement-controller": "8.0.0-preview-54bc644cd",
  "@metamask-previews/app-metadata-controller": "2.0.0-preview-54bc644cd",
  "@metamask-previews/approval-controller": "8.0.0-preview-54bc644cd",
  "@metamask-previews/assets-controller": "2.3.0-preview-54bc644cd",
  "@metamask-previews/assets-controllers": "100.2.1-preview-54bc644cd",
  "@metamask-previews/base-controller": "9.0.0-preview-54bc644cd",
  "@metamask-previews/base-data-service": "0.0.0-preview-54bc644cd",
  "@metamask-previews/bridge-controller": "69.1.0-preview-54bc644cd",
  "@metamask-previews/bridge-status-controller": "68.1.0-preview-54bc644cd",
  "@metamask-previews/build-utils": "3.0.4-preview-54bc644cd",
  "@metamask-previews/cash-account-service": "0.0.0-preview-54bc644cd",
  "@metamask-previews/chain-agnostic-permission": "1.4.0-preview-54bc644cd",
  "@metamask-previews/claims-controller": "0.4.3-preview-54bc644cd",
  "@metamask-previews/client-controller": "1.0.0-preview-54bc644cd",
  "@metamask-previews/compliance-controller": "1.0.1-preview-54bc644cd",
  "@metamask-previews/composable-controller": "12.0.0-preview-54bc644cd",
  "@metamask-previews/config-registry-controller": "0.1.1-preview-54bc644cd",
  "@metamask-previews/connectivity-controller": "0.1.0-preview-54bc644cd",
  "@metamask-previews/controller-utils": "11.19.0-preview-54bc644cd",
  "@metamask-previews/core-backend": "6.1.1-preview-54bc644cd",
  "@metamask-previews/delegation-controller": "2.0.2-preview-54bc644cd",
  "@metamask-previews/earn-controller": "11.1.2-preview-54bc644cd",
  "@metamask-previews/eip-5792-middleware": "3.0.0-preview-54bc644cd",
  "@metamask-previews/eip-7702-internal-rpc-middleware": "0.1.0-preview-54bc644cd",
  "@metamask-previews/eip1193-permission-middleware": "1.0.3-preview-54bc644cd",
  "@metamask-previews/ens-controller": "19.0.3-preview-54bc644cd",
  "@metamask-previews/error-reporting-service": "3.0.1-preview-54bc644cd",
  "@metamask-previews/eth-block-tracker": "15.0.1-preview-54bc644cd",
  "@metamask-previews/eth-json-rpc-middleware": "23.1.0-preview-54bc644cd",
  "@metamask-previews/eth-json-rpc-provider": "6.0.0-preview-54bc644cd",
  "@metamask-previews/foundryup": "1.0.1-preview-54bc644cd",
  "@metamask-previews/gas-fee-controller": "26.0.3-preview-54bc644cd",
  "@metamask-previews/gator-permissions-controller": "2.1.0-preview-54bc644cd",
  "@metamask-previews/geolocation-controller": "0.1.1-preview-54bc644cd",
  "@metamask-previews/json-rpc-engine": "10.2.3-preview-54bc644cd",
  "@metamask-previews/json-rpc-middleware-stream": "8.0.8-preview-54bc644cd",
  "@metamask-previews/keyring-controller": "25.1.0-preview-54bc644cd",
  "@metamask-previews/logging-controller": "7.0.1-preview-54bc644cd",
  "@metamask-previews/message-manager": "14.1.0-preview-54bc644cd",
  "@metamask-previews/messenger": "0.3.0-preview-54bc644cd",
  "@metamask-previews/multichain-account-service": "7.1.0-preview-54bc644cd",
  "@metamask-previews/multichain-api-middleware": "1.2.7-preview-54bc644cd",
  "@metamask-previews/multichain-network-controller": "3.0.5-preview-54bc644cd",
  "@metamask-previews/multichain-transactions-controller": "7.0.2-preview-54bc644cd",
  "@metamask-previews/name-controller": "9.0.0-preview-54bc644cd",
  "@metamask-previews/network-controller": "30.0.0-preview-54bc644cd",
  "@metamask-previews/network-enablement-controller": "4.2.0-preview-54bc644cd",
  "@metamask-previews/notification-services-controller": "23.0.0-preview-54bc644cd",
  "@metamask-previews/permission-controller": "12.2.0-preview-54bc644cd",
  "@metamask-previews/permission-log-controller": "5.0.0-preview-54bc644cd",
  "@metamask-previews/perps-controller": "1.0.1-preview-54bc644cd",
  "@metamask-previews/phishing-controller": "16.3.0-preview-54bc644cd",
  "@metamask-previews/polling-controller": "16.0.3-preview-54bc644cd",
  "@metamask-previews/preferences-controller": "23.0.0-preview-54bc644cd",
  "@metamask-previews/profile-metrics-controller": "3.0.3-preview-54bc644cd",
  "@metamask-previews/profile-sync-controller": "28.0.0-preview-54bc644cd",
  "@metamask-previews/ramps-controller": "12.0.0-preview-54bc644cd",
  "@metamask-previews/rate-limit-controller": "7.0.0-preview-54bc644cd",
  "@metamask-previews/remote-feature-flag-controller": "4.1.0-preview-54bc644cd",
  "@metamask-previews/sample-controllers": "4.0.3-preview-54bc644cd",
  "@metamask-previews/seedless-onboarding-controller": "8.1.0-preview-54bc644cd",
  "@metamask-previews/selected-network-controller": "26.0.3-preview-54bc644cd",
  "@metamask-previews/shield-controller": "5.0.1-preview-54bc644cd",
  "@metamask-previews/signature-controller": "39.0.5-preview-54bc644cd",
  "@metamask-previews/storage-service": "1.0.0-preview-54bc644cd",
  "@metamask-previews/subscription-controller": "6.0.1-preview-54bc644cd",
  "@metamask-previews/transaction-controller": "62.21.0-preview-54bc644cd",
  "@metamask-previews/transaction-pay-controller": "16.5.0-preview-54bc644cd",
  "@metamask-previews/user-operation-controller": "41.0.3-preview-54bc644cd"
}

@Jwhiles
Copy link
Author

Jwhiles commented Mar 16, 2026

@metamaskbot publish-preview

@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": "5.0.1-preview-0dac2dc2c",
  "@metamask-previews/accounts-controller": "37.0.0-preview-0dac2dc2c",
  "@metamask-previews/address-book-controller": "7.0.1-preview-0dac2dc2c",
  "@metamask-previews/ai-controllers": "0.3.0-preview-0dac2dc2c",
  "@metamask-previews/analytics-controller": "1.0.0-preview-0dac2dc2c",
  "@metamask-previews/analytics-data-regulation-controller": "0.0.0-preview-0dac2dc2c",
  "@metamask-previews/announcement-controller": "8.0.0-preview-0dac2dc2c",
  "@metamask-previews/app-metadata-controller": "2.0.0-preview-0dac2dc2c",
  "@metamask-previews/approval-controller": "8.0.0-preview-0dac2dc2c",
  "@metamask-previews/assets-controller": "2.3.0-preview-0dac2dc2c",
  "@metamask-previews/assets-controllers": "100.2.1-preview-0dac2dc2c",
  "@metamask-previews/base-controller": "9.0.0-preview-0dac2dc2c",
  "@metamask-previews/base-data-service": "0.0.0-preview-0dac2dc2c",
  "@metamask-previews/bridge-controller": "69.1.0-preview-0dac2dc2c",
  "@metamask-previews/bridge-status-controller": "68.1.0-preview-0dac2dc2c",
  "@metamask-previews/build-utils": "3.0.4-preview-0dac2dc2c",
  "@metamask-previews/cash-account-service": "0.0.0-preview-0dac2dc2c",
  "@metamask-previews/chain-agnostic-permission": "1.4.0-preview-0dac2dc2c",
  "@metamask-previews/claims-controller": "0.4.3-preview-0dac2dc2c",
  "@metamask-previews/client-controller": "1.0.0-preview-0dac2dc2c",
  "@metamask-previews/compliance-controller": "1.0.1-preview-0dac2dc2c",
  "@metamask-previews/composable-controller": "12.0.0-preview-0dac2dc2c",
  "@metamask-previews/config-registry-controller": "0.1.1-preview-0dac2dc2c",
  "@metamask-previews/connectivity-controller": "0.1.0-preview-0dac2dc2c",
  "@metamask-previews/controller-utils": "11.19.0-preview-0dac2dc2c",
  "@metamask-previews/core-backend": "6.1.1-preview-0dac2dc2c",
  "@metamask-previews/delegation-controller": "2.0.2-preview-0dac2dc2c",
  "@metamask-previews/earn-controller": "11.1.2-preview-0dac2dc2c",
  "@metamask-previews/eip-5792-middleware": "3.0.0-preview-0dac2dc2c",
  "@metamask-previews/eip-7702-internal-rpc-middleware": "0.1.0-preview-0dac2dc2c",
  "@metamask-previews/eip1193-permission-middleware": "1.0.3-preview-0dac2dc2c",
  "@metamask-previews/ens-controller": "19.0.3-preview-0dac2dc2c",
  "@metamask-previews/error-reporting-service": "3.0.1-preview-0dac2dc2c",
  "@metamask-previews/eth-block-tracker": "15.0.1-preview-0dac2dc2c",
  "@metamask-previews/eth-json-rpc-middleware": "23.1.0-preview-0dac2dc2c",
  "@metamask-previews/eth-json-rpc-provider": "6.0.0-preview-0dac2dc2c",
  "@metamask-previews/foundryup": "1.0.1-preview-0dac2dc2c",
  "@metamask-previews/gas-fee-controller": "26.0.3-preview-0dac2dc2c",
  "@metamask-previews/gator-permissions-controller": "2.1.0-preview-0dac2dc2c",
  "@metamask-previews/geolocation-controller": "0.1.1-preview-0dac2dc2c",
  "@metamask-previews/json-rpc-engine": "10.2.3-preview-0dac2dc2c",
  "@metamask-previews/json-rpc-middleware-stream": "8.0.8-preview-0dac2dc2c",
  "@metamask-previews/keyring-controller": "25.1.0-preview-0dac2dc2c",
  "@metamask-previews/logging-controller": "7.0.1-preview-0dac2dc2c",
  "@metamask-previews/message-manager": "14.1.0-preview-0dac2dc2c",
  "@metamask-previews/messenger": "0.3.0-preview-0dac2dc2c",
  "@metamask-previews/multichain-account-service": "7.1.0-preview-0dac2dc2c",
  "@metamask-previews/multichain-api-middleware": "1.2.7-preview-0dac2dc2c",
  "@metamask-previews/multichain-network-controller": "3.0.5-preview-0dac2dc2c",
  "@metamask-previews/multichain-transactions-controller": "7.0.2-preview-0dac2dc2c",
  "@metamask-previews/name-controller": "9.0.0-preview-0dac2dc2c",
  "@metamask-previews/network-controller": "30.0.0-preview-0dac2dc2c",
  "@metamask-previews/network-enablement-controller": "4.2.0-preview-0dac2dc2c",
  "@metamask-previews/notification-services-controller": "23.0.0-preview-0dac2dc2c",
  "@metamask-previews/permission-controller": "12.2.0-preview-0dac2dc2c",
  "@metamask-previews/permission-log-controller": "5.0.0-preview-0dac2dc2c",
  "@metamask-previews/perps-controller": "1.0.1-preview-0dac2dc2c",
  "@metamask-previews/phishing-controller": "16.3.0-preview-0dac2dc2c",
  "@metamask-previews/polling-controller": "16.0.3-preview-0dac2dc2c",
  "@metamask-previews/preferences-controller": "23.0.0-preview-0dac2dc2c",
  "@metamask-previews/profile-metrics-controller": "3.0.3-preview-0dac2dc2c",
  "@metamask-previews/profile-sync-controller": "28.0.0-preview-0dac2dc2c",
  "@metamask-previews/ramps-controller": "12.0.0-preview-0dac2dc2c",
  "@metamask-previews/rate-limit-controller": "7.0.0-preview-0dac2dc2c",
  "@metamask-previews/remote-feature-flag-controller": "4.1.0-preview-0dac2dc2c",
  "@metamask-previews/sample-controllers": "4.0.3-preview-0dac2dc2c",
  "@metamask-previews/seedless-onboarding-controller": "8.1.0-preview-0dac2dc2c",
  "@metamask-previews/selected-network-controller": "26.0.3-preview-0dac2dc2c",
  "@metamask-previews/shield-controller": "5.0.1-preview-0dac2dc2c",
  "@metamask-previews/signature-controller": "39.0.5-preview-0dac2dc2c",
  "@metamask-previews/storage-service": "1.0.0-preview-0dac2dc2c",
  "@metamask-previews/subscription-controller": "6.0.1-preview-0dac2dc2c",
  "@metamask-previews/transaction-controller": "62.21.0-preview-0dac2dc2c",
  "@metamask-previews/transaction-pay-controller": "16.5.0-preview-0dac2dc2c",
  "@metamask-previews/user-operation-controller": "41.0.3-preview-0dac2dc2c"
}

@Jwhiles
Copy link
Author

Jwhiles commented Mar 16, 2026

@metamaskbot publish-preview

@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": "5.0.1-preview-d86f25564",
  "@metamask-previews/accounts-controller": "37.0.0-preview-d86f25564",
  "@metamask-previews/address-book-controller": "7.0.1-preview-d86f25564",
  "@metamask-previews/ai-controllers": "0.3.0-preview-d86f25564",
  "@metamask-previews/analytics-controller": "1.0.0-preview-d86f25564",
  "@metamask-previews/analytics-data-regulation-controller": "0.0.0-preview-d86f25564",
  "@metamask-previews/announcement-controller": "8.0.0-preview-d86f25564",
  "@metamask-previews/app-metadata-controller": "2.0.0-preview-d86f25564",
  "@metamask-previews/approval-controller": "8.0.0-preview-d86f25564",
  "@metamask-previews/assets-controller": "2.3.0-preview-d86f25564",
  "@metamask-previews/assets-controllers": "100.2.1-preview-d86f25564",
  "@metamask-previews/base-controller": "9.0.0-preview-d86f25564",
  "@metamask-previews/base-data-service": "0.0.0-preview-d86f25564",
  "@metamask-previews/bridge-controller": "69.1.0-preview-d86f25564",
  "@metamask-previews/bridge-status-controller": "68.1.0-preview-d86f25564",
  "@metamask-previews/build-utils": "3.0.4-preview-d86f25564",
  "@metamask-previews/cash-account-service": "0.0.0-preview-d86f25564",
  "@metamask-previews/chain-agnostic-permission": "1.4.0-preview-d86f25564",
  "@metamask-previews/claims-controller": "0.4.3-preview-d86f25564",
  "@metamask-previews/client-controller": "1.0.0-preview-d86f25564",
  "@metamask-previews/compliance-controller": "1.0.1-preview-d86f25564",
  "@metamask-previews/composable-controller": "12.0.0-preview-d86f25564",
  "@metamask-previews/config-registry-controller": "0.1.1-preview-d86f25564",
  "@metamask-previews/connectivity-controller": "0.1.0-preview-d86f25564",
  "@metamask-previews/controller-utils": "11.19.0-preview-d86f25564",
  "@metamask-previews/core-backend": "6.1.1-preview-d86f25564",
  "@metamask-previews/delegation-controller": "2.0.2-preview-d86f25564",
  "@metamask-previews/earn-controller": "11.1.2-preview-d86f25564",
  "@metamask-previews/eip-5792-middleware": "3.0.0-preview-d86f25564",
  "@metamask-previews/eip-7702-internal-rpc-middleware": "0.1.0-preview-d86f25564",
  "@metamask-previews/eip1193-permission-middleware": "1.0.3-preview-d86f25564",
  "@metamask-previews/ens-controller": "19.0.3-preview-d86f25564",
  "@metamask-previews/error-reporting-service": "3.0.1-preview-d86f25564",
  "@metamask-previews/eth-block-tracker": "15.0.1-preview-d86f25564",
  "@metamask-previews/eth-json-rpc-middleware": "23.1.0-preview-d86f25564",
  "@metamask-previews/eth-json-rpc-provider": "6.0.0-preview-d86f25564",
  "@metamask-previews/foundryup": "1.0.1-preview-d86f25564",
  "@metamask-previews/gas-fee-controller": "26.0.3-preview-d86f25564",
  "@metamask-previews/gator-permissions-controller": "2.1.0-preview-d86f25564",
  "@metamask-previews/geolocation-controller": "0.1.1-preview-d86f25564",
  "@metamask-previews/json-rpc-engine": "10.2.3-preview-d86f25564",
  "@metamask-previews/json-rpc-middleware-stream": "8.0.8-preview-d86f25564",
  "@metamask-previews/keyring-controller": "25.1.0-preview-d86f25564",
  "@metamask-previews/logging-controller": "7.0.1-preview-d86f25564",
  "@metamask-previews/message-manager": "14.1.0-preview-d86f25564",
  "@metamask-previews/messenger": "0.3.0-preview-d86f25564",
  "@metamask-previews/multichain-account-service": "7.1.0-preview-d86f25564",
  "@metamask-previews/multichain-api-middleware": "1.2.7-preview-d86f25564",
  "@metamask-previews/multichain-network-controller": "3.0.5-preview-d86f25564",
  "@metamask-previews/multichain-transactions-controller": "7.0.2-preview-d86f25564",
  "@metamask-previews/name-controller": "9.0.0-preview-d86f25564",
  "@metamask-previews/network-controller": "30.0.0-preview-d86f25564",
  "@metamask-previews/network-enablement-controller": "4.2.0-preview-d86f25564",
  "@metamask-previews/notification-services-controller": "23.0.0-preview-d86f25564",
  "@metamask-previews/permission-controller": "12.2.0-preview-d86f25564",
  "@metamask-previews/permission-log-controller": "5.0.0-preview-d86f25564",
  "@metamask-previews/perps-controller": "1.0.1-preview-d86f25564",
  "@metamask-previews/phishing-controller": "16.3.0-preview-d86f25564",
  "@metamask-previews/polling-controller": "16.0.3-preview-d86f25564",
  "@metamask-previews/preferences-controller": "23.0.0-preview-d86f25564",
  "@metamask-previews/profile-metrics-controller": "3.0.3-preview-d86f25564",
  "@metamask-previews/profile-sync-controller": "28.0.0-preview-d86f25564",
  "@metamask-previews/ramps-controller": "12.0.0-preview-d86f25564",
  "@metamask-previews/rate-limit-controller": "7.0.0-preview-d86f25564",
  "@metamask-previews/remote-feature-flag-controller": "4.1.0-preview-d86f25564",
  "@metamask-previews/sample-controllers": "4.0.3-preview-d86f25564",
  "@metamask-previews/seedless-onboarding-controller": "8.1.0-preview-d86f25564",
  "@metamask-previews/selected-network-controller": "26.0.3-preview-d86f25564",
  "@metamask-previews/shield-controller": "5.0.1-preview-d86f25564",
  "@metamask-previews/signature-controller": "39.0.5-preview-d86f25564",
  "@metamask-previews/storage-service": "1.0.0-preview-d86f25564",
  "@metamask-previews/subscription-controller": "6.0.1-preview-d86f25564",
  "@metamask-previews/transaction-controller": "62.21.0-preview-d86f25564",
  "@metamask-previews/transaction-pay-controller": "16.5.0-preview-d86f25564",
  "@metamask-previews/user-operation-controller": "41.0.3-preview-d86f25564"
}

@Jwhiles
Copy link
Author

Jwhiles commented Mar 16, 2026

@metamaskbot publish-preview

@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": "5.0.1-preview-4183330c3",
  "@metamask-previews/accounts-controller": "37.0.0-preview-4183330c3",
  "@metamask-previews/address-book-controller": "7.0.1-preview-4183330c3",
  "@metamask-previews/ai-controllers": "0.3.0-preview-4183330c3",
  "@metamask-previews/analytics-controller": "1.0.0-preview-4183330c3",
  "@metamask-previews/analytics-data-regulation-controller": "0.0.0-preview-4183330c3",
  "@metamask-previews/announcement-controller": "8.0.0-preview-4183330c3",
  "@metamask-previews/app-metadata-controller": "2.0.0-preview-4183330c3",
  "@metamask-previews/approval-controller": "8.0.0-preview-4183330c3",
  "@metamask-previews/assets-controller": "2.3.0-preview-4183330c3",
  "@metamask-previews/assets-controllers": "100.2.1-preview-4183330c3",
  "@metamask-previews/base-controller": "9.0.0-preview-4183330c3",
  "@metamask-previews/base-data-service": "0.0.0-preview-4183330c3",
  "@metamask-previews/bridge-controller": "69.1.0-preview-4183330c3",
  "@metamask-previews/bridge-status-controller": "68.1.0-preview-4183330c3",
  "@metamask-previews/build-utils": "3.0.4-preview-4183330c3",
  "@metamask-previews/cash-account-service": "0.0.0-preview-4183330c3",
  "@metamask-previews/chain-agnostic-permission": "1.4.0-preview-4183330c3",
  "@metamask-previews/claims-controller": "0.4.3-preview-4183330c3",
  "@metamask-previews/client-controller": "1.0.0-preview-4183330c3",
  "@metamask-previews/compliance-controller": "1.0.1-preview-4183330c3",
  "@metamask-previews/composable-controller": "12.0.0-preview-4183330c3",
  "@metamask-previews/config-registry-controller": "0.1.1-preview-4183330c3",
  "@metamask-previews/connectivity-controller": "0.1.0-preview-4183330c3",
  "@metamask-previews/controller-utils": "11.19.0-preview-4183330c3",
  "@metamask-previews/core-backend": "6.1.1-preview-4183330c3",
  "@metamask-previews/delegation-controller": "2.0.2-preview-4183330c3",
  "@metamask-previews/earn-controller": "11.1.2-preview-4183330c3",
  "@metamask-previews/eip-5792-middleware": "3.0.0-preview-4183330c3",
  "@metamask-previews/eip-7702-internal-rpc-middleware": "0.1.0-preview-4183330c3",
  "@metamask-previews/eip1193-permission-middleware": "1.0.3-preview-4183330c3",
  "@metamask-previews/ens-controller": "19.0.3-preview-4183330c3",
  "@metamask-previews/error-reporting-service": "3.0.1-preview-4183330c3",
  "@metamask-previews/eth-block-tracker": "15.0.1-preview-4183330c3",
  "@metamask-previews/eth-json-rpc-middleware": "23.1.0-preview-4183330c3",
  "@metamask-previews/eth-json-rpc-provider": "6.0.0-preview-4183330c3",
  "@metamask-previews/foundryup": "1.0.1-preview-4183330c3",
  "@metamask-previews/gas-fee-controller": "26.0.3-preview-4183330c3",
  "@metamask-previews/gator-permissions-controller": "2.1.0-preview-4183330c3",
  "@metamask-previews/geolocation-controller": "0.1.1-preview-4183330c3",
  "@metamask-previews/json-rpc-engine": "10.2.3-preview-4183330c3",
  "@metamask-previews/json-rpc-middleware-stream": "8.0.8-preview-4183330c3",
  "@metamask-previews/keyring-controller": "25.1.0-preview-4183330c3",
  "@metamask-previews/logging-controller": "7.0.1-preview-4183330c3",
  "@metamask-previews/message-manager": "14.1.0-preview-4183330c3",
  "@metamask-previews/messenger": "0.3.0-preview-4183330c3",
  "@metamask-previews/multichain-account-service": "7.1.0-preview-4183330c3",
  "@metamask-previews/multichain-api-middleware": "1.2.7-preview-4183330c3",
  "@metamask-previews/multichain-network-controller": "3.0.5-preview-4183330c3",
  "@metamask-previews/multichain-transactions-controller": "7.0.2-preview-4183330c3",
  "@metamask-previews/name-controller": "9.0.0-preview-4183330c3",
  "@metamask-previews/network-controller": "30.0.0-preview-4183330c3",
  "@metamask-previews/network-enablement-controller": "4.2.0-preview-4183330c3",
  "@metamask-previews/notification-services-controller": "23.0.0-preview-4183330c3",
  "@metamask-previews/permission-controller": "12.2.0-preview-4183330c3",
  "@metamask-previews/permission-log-controller": "5.0.0-preview-4183330c3",
  "@metamask-previews/perps-controller": "1.0.1-preview-4183330c3",
  "@metamask-previews/phishing-controller": "16.3.0-preview-4183330c3",
  "@metamask-previews/polling-controller": "16.0.3-preview-4183330c3",
  "@metamask-previews/preferences-controller": "23.0.0-preview-4183330c3",
  "@metamask-previews/profile-metrics-controller": "3.0.3-preview-4183330c3",
  "@metamask-previews/profile-sync-controller": "28.0.0-preview-4183330c3",
  "@metamask-previews/ramps-controller": "12.0.0-preview-4183330c3",
  "@metamask-previews/rate-limit-controller": "7.0.0-preview-4183330c3",
  "@metamask-previews/remote-feature-flag-controller": "4.1.0-preview-4183330c3",
  "@metamask-previews/sample-controllers": "4.0.3-preview-4183330c3",
  "@metamask-previews/seedless-onboarding-controller": "8.1.0-preview-4183330c3",
  "@metamask-previews/selected-network-controller": "26.0.3-preview-4183330c3",
  "@metamask-previews/shield-controller": "5.0.1-preview-4183330c3",
  "@metamask-previews/signature-controller": "39.0.5-preview-4183330c3",
  "@metamask-previews/storage-service": "1.0.0-preview-4183330c3",
  "@metamask-previews/subscription-controller": "6.0.1-preview-4183330c3",
  "@metamask-previews/transaction-controller": "62.21.0-preview-4183330c3",
  "@metamask-previews/transaction-pay-controller": "16.5.0-preview-4183330c3",
  "@metamask-previews/user-operation-controller": "41.0.3-preview-4183330c3"
}

@Jwhiles Jwhiles changed the title feat: add cash keyring feat: add cash account keyring to keyring controller, and create cash account service Mar 17, 2026
@Jwhiles
Copy link
Author

Jwhiles commented Mar 17, 2026

@metamaskbot publish-preview

@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": "5.0.1-preview-a12b37cc3",
  "@metamask-previews/accounts-controller": "37.0.0-preview-a12b37cc3",
  "@metamask-previews/address-book-controller": "7.0.1-preview-a12b37cc3",
  "@metamask-previews/ai-controllers": "0.4.0-preview-a12b37cc3",
  "@metamask-previews/analytics-controller": "1.0.0-preview-a12b37cc3",
  "@metamask-previews/analytics-data-regulation-controller": "0.0.0-preview-a12b37cc3",
  "@metamask-previews/announcement-controller": "8.0.0-preview-a12b37cc3",
  "@metamask-previews/app-metadata-controller": "2.0.0-preview-a12b37cc3",
  "@metamask-previews/approval-controller": "8.0.0-preview-a12b37cc3",
  "@metamask-previews/assets-controller": "2.3.0-preview-a12b37cc3",
  "@metamask-previews/assets-controllers": "100.2.1-preview-a12b37cc3",
  "@metamask-previews/base-controller": "9.0.0-preview-a12b37cc3",
  "@metamask-previews/base-data-service": "0.0.0-preview-a12b37cc3",
  "@metamask-previews/bridge-controller": "69.1.0-preview-a12b37cc3",
  "@metamask-previews/bridge-status-controller": "68.1.0-preview-a12b37cc3",
  "@metamask-previews/build-utils": "3.0.4-preview-a12b37cc3",
  "@metamask-previews/cash-account-service": "0.0.0-preview-a12b37cc3",
  "@metamask-previews/chain-agnostic-permission": "1.4.0-preview-a12b37cc3",
  "@metamask-previews/claims-controller": "0.4.3-preview-a12b37cc3",
  "@metamask-previews/client-controller": "1.0.0-preview-a12b37cc3",
  "@metamask-previews/compliance-controller": "1.0.1-preview-a12b37cc3",
  "@metamask-previews/composable-controller": "12.0.0-preview-a12b37cc3",
  "@metamask-previews/config-registry-controller": "0.1.1-preview-a12b37cc3",
  "@metamask-previews/connectivity-controller": "0.1.0-preview-a12b37cc3",
  "@metamask-previews/controller-utils": "11.19.0-preview-a12b37cc3",
  "@metamask-previews/core-backend": "6.1.1-preview-a12b37cc3",
  "@metamask-previews/delegation-controller": "2.0.2-preview-a12b37cc3",
  "@metamask-previews/earn-controller": "11.1.2-preview-a12b37cc3",
  "@metamask-previews/eip-5792-middleware": "3.0.0-preview-a12b37cc3",
  "@metamask-previews/eip-7702-internal-rpc-middleware": "0.1.0-preview-a12b37cc3",
  "@metamask-previews/eip1193-permission-middleware": "1.0.3-preview-a12b37cc3",
  "@metamask-previews/ens-controller": "19.0.3-preview-a12b37cc3",
  "@metamask-previews/error-reporting-service": "3.0.1-preview-a12b37cc3",
  "@metamask-previews/eth-block-tracker": "15.0.1-preview-a12b37cc3",
  "@metamask-previews/eth-json-rpc-middleware": "23.1.0-preview-a12b37cc3",
  "@metamask-previews/eth-json-rpc-provider": "6.0.0-preview-a12b37cc3",
  "@metamask-previews/foundryup": "1.0.1-preview-a12b37cc3",
  "@metamask-previews/gas-fee-controller": "26.0.3-preview-a12b37cc3",
  "@metamask-previews/gator-permissions-controller": "2.1.0-preview-a12b37cc3",
  "@metamask-previews/geolocation-controller": "0.1.1-preview-a12b37cc3",
  "@metamask-previews/json-rpc-engine": "10.2.3-preview-a12b37cc3",
  "@metamask-previews/json-rpc-middleware-stream": "8.0.8-preview-a12b37cc3",
  "@metamask-previews/keyring-controller": "25.1.0-preview-a12b37cc3",
  "@metamask-previews/logging-controller": "7.0.1-preview-a12b37cc3",
  "@metamask-previews/message-manager": "14.1.0-preview-a12b37cc3",
  "@metamask-previews/messenger": "0.3.0-preview-a12b37cc3",
  "@metamask-previews/multichain-account-service": "7.1.0-preview-a12b37cc3",
  "@metamask-previews/multichain-api-middleware": "1.2.7-preview-a12b37cc3",
  "@metamask-previews/multichain-network-controller": "3.0.5-preview-a12b37cc3",
  "@metamask-previews/multichain-transactions-controller": "7.0.2-preview-a12b37cc3",
  "@metamask-previews/name-controller": "9.0.0-preview-a12b37cc3",
  "@metamask-previews/network-controller": "30.0.0-preview-a12b37cc3",
  "@metamask-previews/network-enablement-controller": "4.2.0-preview-a12b37cc3",
  "@metamask-previews/notification-services-controller": "23.0.0-preview-a12b37cc3",
  "@metamask-previews/permission-controller": "12.2.0-preview-a12b37cc3",
  "@metamask-previews/permission-log-controller": "5.0.0-preview-a12b37cc3",
  "@metamask-previews/perps-controller": "1.1.0-preview-a12b37cc3",
  "@metamask-previews/phishing-controller": "16.3.0-preview-a12b37cc3",
  "@metamask-previews/polling-controller": "16.0.3-preview-a12b37cc3",
  "@metamask-previews/preferences-controller": "23.0.0-preview-a12b37cc3",
  "@metamask-previews/profile-metrics-controller": "3.0.4-preview-a12b37cc3",
  "@metamask-previews/profile-sync-controller": "28.0.0-preview-a12b37cc3",
  "@metamask-previews/ramps-controller": "12.0.0-preview-a12b37cc3",
  "@metamask-previews/rate-limit-controller": "7.0.0-preview-a12b37cc3",
  "@metamask-previews/remote-feature-flag-controller": "4.1.0-preview-a12b37cc3",
  "@metamask-previews/sample-controllers": "4.0.3-preview-a12b37cc3",
  "@metamask-previews/seedless-onboarding-controller": "8.1.0-preview-a12b37cc3",
  "@metamask-previews/selected-network-controller": "26.0.3-preview-a12b37cc3",
  "@metamask-previews/shield-controller": "5.0.1-preview-a12b37cc3",
  "@metamask-previews/signature-controller": "39.0.5-preview-a12b37cc3",
  "@metamask-previews/storage-service": "1.0.0-preview-a12b37cc3",
  "@metamask-previews/subscription-controller": "6.0.1-preview-a12b37cc3",
  "@metamask-previews/transaction-controller": "62.22.0-preview-a12b37cc3",
  "@metamask-previews/transaction-pay-controller": "17.0.0-preview-a12b37cc3",
  "@metamask-previews/user-operation-controller": "41.0.3-preview-a12b37cc3"
}


export type CashAccountServiceActions = CashAccountServiceMethodActions;

type AllowedActions =
Copy link
Author

Choose a reason for hiding this comment

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

Possibly should export these to make it easier to instantiate this service

@Jwhiles Jwhiles marked this pull request as ready for review March 17, 2026 15:30
@Jwhiles Jwhiles requested review from a team as code owners March 17, 2026 15:30
* @param entropySource - The metadata id of the HD keyring to derive from.
* @returns The metadata of the newly created Cash keyring.
*/
async createCashAccount(entropySource: string): Promise<KeyringMetadata> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you make this method idempotent and ensure that it does not add a new keyring if one already exists?

Comment on lines +36 to +37
async ({ keyring }) => {
const hdKeyring = keyring as unknown as HdKeyring;
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's add a guard here, we should probably check that keyring.type is correct.

Also, HdKeyring already implements Keyring, so we shouldn't need the as unknown I think, just up-casting is enough (and the keyring.type check should """guarantee""" that we can upcast in a safe way).

Comment on lines +43 to +45
return hdKeyring.mnemonic;
},
)) as Uint8Array;
Copy link
Contributor

Choose a reason for hiding this comment

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

We shouldn't need to cast here neither. We already checked for !hdKeyring.mnemonic so Uint8 | null would be inferred as Uint8 from now on?

Suggested change
return hdKeyring.mnemonic;
},
)) as Uint8Array;
return hdKeyring.mnemonic;
},
));

/* eslint-disable @typescript-eslint/naming-convention */
simple = 'Simple Key Pair',
hd = 'HD Key Tree',
cash = 'Cash Keyring',
Copy link
Contributor

Choose a reason for hiding this comment

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

We should report this in the changelog

teams.json Outdated
"metamask/base-controller": "team-core-platform",
"metamask/base-data-service": "team-core-platform",
"metamask/build-utils": "team-core-platform",
"metamask/cash-account-service": "team-earn",
Copy link
Contributor

Choose a reason for hiding this comment

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

We probably should be owner of this too?

Suggested change
"metamask/cash-account-service": "team-earn",
"metamask/cash-account-service": "team-accounts-framework,team-earn",

Comment on lines +25 to +32
/**
* Creates a Cash keyring derived from the HD keyring identified by
* the given entropy source, and returns the new keyring's metadata.
*
* @param entropySource - The metadata id of the HD keyring to derive from.
* @returns The metadata of the newly created Cash keyring.
*/
async createCashAccount(entropySource: string): Promise<KeyringMetadata> {
Copy link
Contributor

Choose a reason for hiding this comment

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

So, I'm a bit lost with the naming here 😅

We use createCashAccount but this does not return a KeyringAccount...

From my understanding we should have those requirements:

  • Always have 1 Cash keyring per HD keyring (1-to-1 binding)
  • Always have 1 Cash account per Cash keyring

Maybe we should have 2 separate methods for this:

  • #getOrCreateCashKeyring(entropySource): Promise<CashKeyring>, this one has to be idempotent so we do not re-create the keyring everytime
  • getCashAccount(entropySource): Promise<KeyringAccount>, this would be the only public method of this service (this must also be idempotent).
    • I used get since cash accounts seems to be "implicitly" created for each HD keyrings we have, so semantically this looks more correct to me WDYT?

We could also listen for KeyringController:stateChange, but this one triggers quite a lot and might add a bit too much overhead (and we cannot use selector for this one since the state.keyrings reference is getting change on every vault update IIRC)

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, do we really need the KeyringMetadata here? Do you need to reference the keyring ID somewhere else?

Copy link
Member

Choose a reason for hiding this comment

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

@ccharly We possibly don't want 1 cash account per HD keyring. The user only expects to see one account. I've added that to the FAQ here

It's not necessarily wrong to have one but the additional ones are not required.

We also don't necessarily want 1 cash account per cash account keyring - we want between zero and one. The account is created when it is needed.

I mention it because you propose a getCashAccount(entropySource), but we do want to expose a method for getting the canonical one.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh then, if that's only 1 cash only no matter how many SRPs you have, then we should definitely not expose the entropySource as a parameter IMO.

We should be using the default primary SRP under the hood (that would be the service's logic)!

Like we hide all the necessary logic to create the MoneyKeyring and create the MoneyAccount. The consume can just use service.getMoneyAccount() to get it, that sounds pretty neat and simple to me.

WDYT @shane-t @Jwhiles?

Copy link
Author

Choose a reason for hiding this comment

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

That does sound neat, but there could be something valuable about having to explicitly call createMoneyAccount the first time we want to access it? I wonder if there may be some restrictions about which users we can create these accounts for - and making the creation process very explicit might help us avoid compliance issues.

That said, I don't actually know of any strict requirements along those lines. I'm just hypothesising! Happy to change the naming if you feel strongly.

Copy link
Member

Choose a reason for hiding this comment

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

I think what @ccharly is saying makes sense and it's probably compatible with the stipulation @Jwhiles highlights.

It does make sense to separate account initialisation for those reasons - one reason is that some other services (Ramps, etc) might behave differently before the user has the account (and as @Jwhiles mentioned, cash accounts might be geo-restricted).

It could be possible to use the default SRP under the hood, AND have an explicit, idempotent call to create the account. We do need a getMoneyAccount() method which resolve falsily until then

Copy link
Author

Choose a reason for hiding this comment

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

So we are leaning towards two methods?

getMoneyAccount -> returns money account if one exists, else null/false/whatever
createMoneyAccount -> creates and returns money account if none exists else it either throws, returns null, or returns the account depending on what we decide


## Earn Team
/packages/earn-controller @MetaMask/metamask-earn
/packages/cash-account-service @MetaMask/metamask-earn
Copy link
Contributor

Choose a reason for hiding this comment

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

We should probably be owner of this too, so this must be moved in the "Joint team" section


### Added

- Initial release
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
- Initial release
- Initial release ([#8204](https://github.com/MetaMask/core/pull/8204))


if (existingCashMetadata) {
return existingCashMetadata;
}
Copy link

Choose a reason for hiding this comment

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

Mnemonic fetched before existing keyring check breaks idempotency

Medium Severity

createCashAccount always retrieves the mnemonic from the HD keyring (lines 33–51) before checking whether a cash keyring already exists (lines 53–66). If the HD keyring identified by entropySource is missing or invalid, the method throws an error even when a valid cash keyring already exists and could simply be returned. Moving the existing-cash-keyring check before the mnemonic retrieval would make the method properly idempotent and avoid unnecessary work and lock acquisition on the KeyringController.

Fix in Cursor Fix in Web

@Jwhiles
Copy link
Author

Jwhiles commented Mar 19, 2026

@metamaskbot publish-preview

'KeyringController:addNewKeyring',
KeyringTypes.cash,
{ mnemonic },
);
Copy link

Choose a reason for hiding this comment

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

Race condition allows duplicate cash keyring creation

Medium Severity

createCashAccount has a TOCTOU (time-of-check-time-of-use) race condition. The existence check via withKeyring and the creation via addNewKeyring are separate mutex-guarded calls with the lock released in between. Two concurrent calls can both pass the existence check and each create a separate cash keyring.

Fix in Cursor Fix in Web

Copy link
Author

Choose a reason for hiding this comment

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

I added the check in response to your comment @danroc of

Can you make this method idempotent and ensure that it does not add a new keyring if one already exists?

I think this race condition is legit, and 'm struggling to see how to work around it without having a 'transaction' equivalent thing.

However, the money keyring only allows for a single account to be created anyway. So this protection is a duplicate. I wonder if we actually need it.

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.

There are 3 total unresolved issues (including 2 from previous reviews).

Fix All in Cursor

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

return accounts.length > 0 ? metadata : null;
},
)
.catch(() => null)) as KeyringMetadata | null;
Copy link

Choose a reason for hiding this comment

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

Blanket catch swallows errors beyond keyring-not-found

Low Severity

The .catch(() => null) on the withKeyring call silently swallows every possible error — not just the expected "keyring not found" case. If a cash keyring exists but keyring.getAccounts() throws due to corruption or an internal error, the failure is hidden and the code falls through to create a duplicate keyring instead of surfacing the real problem. Catching only the specific KeyringNotFound error would be safer.

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants