feat: add money account keyring to keyring controller, and create cash account service#8204
feat: add money account keyring to keyring controller, and create cash account service#8204
Conversation
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
03c7d08 to
54bc644
Compare
|
@metamaskbot publish-preview |
54bc644 to
03c168a
Compare
|
Preview builds have been published. See these instructions for more information about preview builds. Expand for full list of packages and versions. |
03c168a to
0dac2dc
Compare
|
@metamaskbot publish-preview |
|
Preview builds have been published. See these instructions for more information about preview builds. Expand for full list of packages and versions. |
0dac2dc to
d86f255
Compare
|
@metamaskbot publish-preview |
|
Preview builds have been published. See these instructions for more information about preview builds. Expand for full list of packages and versions. |
|
@metamaskbot publish-preview |
|
Preview builds have been published. See these instructions for more information about preview builds. Expand for full list of packages and versions. |
f872b00 to
ad6c4e1
Compare
|
@metamaskbot publish-preview |
|
Preview builds have been published. See these instructions for more information about preview builds. Expand for full list of packages and versions. |
|
|
||
| export type CashAccountServiceActions = CashAccountServiceMethodActions; | ||
|
|
||
| type AllowedActions = |
There was a problem hiding this comment.
Possibly should export these to make it easier to instantiate this service
| * @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> { |
There was a problem hiding this comment.
Can you make this method idempotent and ensure that it does not add a new keyring if one already exists?
| async ({ keyring }) => { | ||
| const hdKeyring = keyring as unknown as HdKeyring; |
There was a problem hiding this comment.
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).
| return hdKeyring.mnemonic; | ||
| }, | ||
| )) as Uint8Array; |
There was a problem hiding this comment.
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?
| 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', |
There was a problem hiding this comment.
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", |
There was a problem hiding this comment.
We probably should be owner of this too?
| "metamask/cash-account-service": "team-earn", | |
| "metamask/cash-account-service": "team-accounts-framework,team-earn", |
| /** | ||
| * 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> { |
There was a problem hiding this comment.
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 everytimegetCashAccount(entropySource): Promise<KeyringAccount>, this would be the only public method of this service (this must also be idempotent).- I used
getsince cash accounts seems to be "implicitly" created for each HD keyrings we have, so semantically this looks more correct to me WDYT?
- I used
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)
There was a problem hiding this comment.
Also, do we really need the KeyringMetadata here? Do you need to reference the keyring ID somewhere else?
There was a problem hiding this comment.
@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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
.github/CODEOWNERS
Outdated
|
|
||
| ## Earn Team | ||
| /packages/earn-controller @MetaMask/metamask-earn | ||
| /packages/cash-account-service @MetaMask/metamask-earn |
There was a problem hiding this comment.
We should probably be owner of this too, so this must be moved in the "Joint team" section
|
|
||
| ### Added | ||
|
|
||
| - Initial release |
There was a problem hiding this comment.
| - Initial release | |
| - Initial release ([#8204](https://github.com/MetaMask/core/pull/8204)) |
|
@metamaskbot publish-preview |
a175847 to
cd47082
Compare
5902b98 to
fa0c1cb
Compare
| "test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch" | ||
| }, | ||
| "dependencies": { | ||
| "@metamask/base-controller": "^9.0.0", |
There was a problem hiding this comment.
Unused @metamask/base-controller dependency in new package
Low Severity
@metamask/base-controller is listed as a production dependency and referenced in tsconfig.build.json, but no source file in the package imports from it. The service class does not extend BaseController (which is correct per the AGENTS.md guideline that stateless services should not be controllers). This dependency and its tsconfig reference appear to be unnecessary.
Additional Locations (1)
1b134e3 to
355208d
Compare
|
@metamaskbot publish-preview |
|
Preview builds have been published. Learn how to use preview builds in other projects. Expand for full list of packages and versions. |
|
@metamaskbot publish-preview |
|
Preview builds have been published. Learn how to use preview builds in other projects. Expand for full list of packages and versions. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
bbf5917 to
04f063b
Compare
|
@metamaskbot publish-preview |
|
Preview builds have been published. Learn how to use preview builds in other projects. Expand for full list of packages and versions. |


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
Note
Medium Risk
Medium risk because it extends core keyring infrastructure (new
KeyringTypes.moneyand default keyring builder) and introduces a service that derives new keyrings from HD mnemonics, which could affect account/key management flows if misused.Overview
Adds support for Money accounts by introducing
KeyringTypes.moneyand registeringMoneyKeyring(from@metamask/eth-money-keyring) as a built-in default keyring inKeyringController.Introduces a new package,
@metamask/money-account-service, exposing messenger-callable methods tocreateMoneyAccount(derive a Money keyring from an HD keyring’s mnemonic/entropy source) andgetMoneyAccount(return existing Money keyring metadata), with full unit tests and build/test/docs config.Updates
accounts-controllerto displayKeyringTypes.moneyasMoney Accountand extends tests accordingly, and wires repo ownership/build plumbing (CODEOWNERS,teams.json, roottsconfig.build.json,yarn.lock).Written by Cursor Bugbot for commit 638782e. This will update automatically on new commits. Configure here.