-
-
Notifications
You must be signed in to change notification settings - Fork 187
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Add CAIP-25 permission and adapters to @metamask/multichain
#4784
base: main
Are you sure you want to change the base?
Conversation
## Explanation This PR fixes a lot of the linting and typescript errors. still some left but this covers a lot of it. <!-- Thanks for your contribution! Take a moment to answer these questions so that reviewers have the information they need to properly understand your changes: * What is the current state of things and why does it need to change? * What is the solution your changes offer and how does it work? * Are there any changes whose purpose might not obvious to those unfamiliar with the domain? * If your primary goal was to update one package but you found you had to update another one along the way, why did you do so? * If you had to upgrade a dependency, why did you do so? --> ## References <!-- Are there any issues that this pull request is tied to? Are there other links that reviewers should consult to understand these changes better? Are there client or consumer pull requests to adopt any breaking changes? For example: * Fixes #12345 * Related to #67890 --> ## Changelog <!-- If you're making any consumer-facing changes, list those changes here as if you were updating a changelog, using the template below as a guide. (CATEGORY is one of BREAKING, ADDED, CHANGED, DEPRECATED, REMOVED, or FIXED. For security-related issues, follow the Security Advisory process.) Please take care to name the exact pieces of the API you've added or changed (e.g. types, interfaces, functions, or methods). If there are any breaking changes, make sure to offer a solution for consumers to follow once they upgrade to the changes. Finally, if you're only making changes to development scripts or tests, you may replace the template below with "None". --> ### `@metamask/package-a` - **<CATEGORY>**: Your change here - **<CATEGORY>**: Your change here ### `@metamask/package-b` - **<CATEGORY>**: Your change here - **<CATEGORY>**: Your change here ## 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 highlighted breaking changes using the "BREAKING" category above as appropriate - [ ] I've prepared draft pull requests for clients and consumer packages to resolve any breaking changes --------- Co-authored-by: Jiexi Luan <jiexiluan@gmail.com>
## Explanation <!-- Thanks for your contribution! Take a moment to answer these questions so that reviewers have the information they need to properly understand your changes: * What is the current state of things and why does it need to change? * What is the solution your changes offer and how does it work? * Are there any changes whose purpose might not obvious to those unfamiliar with the domain? * If your primary goal was to update one package but you found you had to update another one along the way, why did you do so? * If you had to upgrade a dependency, why did you do so? --> Added ESM exports for multichain package ## References <!-- Are there any issues that this pull request is tied to? Are there other links that reviewers should consult to understand these changes better? Are there client or consumer pull requests to adopt any breaking changes? For example: * Fixes #12345 * Related to #67890 --> ## Changelog <!-- If you're making any consumer-facing changes, list those changes here as if you were updating a changelog, using the template below as a guide. (CATEGORY is one of BREAKING, ADDED, CHANGED, DEPRECATED, REMOVED, or FIXED. For security-related issues, follow the Security Advisory process.) Please take care to name the exact pieces of the API you've added or changed (e.g. types, interfaces, functions, or methods). If there are any breaking changes, make sure to offer a solution for consumers to follow once they upgrade to the changes. Finally, if you're only making changes to development scripts or tests, you may replace the template below with "None". --> ### `@metamask/package-a` - **<CATEGORY>**: Your change here - **<CATEGORY>**: Your change here ### `@metamask/package-b` - **<CATEGORY>**: Your change here - **<CATEGORY>**: Your change here ## 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 highlighted breaking changes using the "BREAKING" category above as appropriate - [ ] I've prepared draft pull requests for clients and consumer packages to resolve any breaking changes
@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.
|
@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.
|
@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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, I've taken some time to look over the CAIPs and cross-check it with the code here this time. There is certainly a lot of validation code in this PR but that is all dictated in the spec, so it makes sense.
One thing I'm noticing is that we seem to just be checking the CAIP-25 request in this PR, and throwing if there are possible errors, but we're not creating a session, nor are we producing a response. Would that be up to the clients, or is that something that will automatically be handled somewhere else? Ah, I just noticed you have #4813 upcoming! I haven't reviewed this PR with that in mind. Feel free to reject a change if it doesn't fit into the bigger plan.
}; | ||
|
||
// Scope is already a CAIP-2 ID and has no references to flatten | ||
if (!namespace || reference || !references) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would only check if references
is essentially absent, but if it's an empty array it would proceed — is that okay?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not understanding the case where !namespace
happens. I will need to loop back on that but there shouldn't be a case where there is no extractable namespace from the scopeString
that I can think of?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also had some followup thoughts on your original question that I'm asking upstream in the validation flow that precedes this: https://github.com/MetaMask/core/pull/4784/files#r1833212893
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This definitely looks broken in the case where references
is empty. It would return an empty object then, essentially deleting the scope object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// These non-prefixed types represent CAIP-217 Scope and | ||
// ScopeObject as defined by the spec but without | ||
// namespace-only Scopes (except for "wallet") and without | ||
// the `references` array of CAIP References on the ScopeObject. | ||
// These deviations from the spec are necessary as MetaMask | ||
// does not support wildcarded Scopes, i.e. Scopes that only | ||
// specify a namespace but no specific reference. | ||
export type ScopeString = CaipChainId | KnownCaipNamespace.Wallet; | ||
export type ScopeObject = { | ||
methods: string[]; | ||
notifications: string[]; | ||
accounts: CaipAccountId[]; | ||
rpcDocuments?: string[]; | ||
rpcEndpoints?: string[]; | ||
}; | ||
export type ScopesObject = Record<CaipChainId, ScopeObject> & { | ||
[KnownCaipNamespace.Wallet]?: ScopeObject; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar as above, would it be worth encapsulating these notes in JSDoc? Here is an attempt at expanding upon these slightly. If this is not a correct understanding then feel free to adjust.
// These non-prefixed types represent CAIP-217 Scope and | |
// ScopeObject as defined by the spec but without | |
// namespace-only Scopes (except for "wallet") and without | |
// the `references` array of CAIP References on the ScopeObject. | |
// These deviations from the spec are necessary as MetaMask | |
// does not support wildcarded Scopes, i.e. Scopes that only | |
// specify a namespace but no specific reference. | |
export type ScopeString = CaipChainId | KnownCaipNamespace.Wallet; | |
export type ScopeObject = { | |
methods: string[]; | |
notifications: string[]; | |
accounts: CaipAccountId[]; | |
rpcDocuments?: string[]; | |
rpcEndpoints?: string[]; | |
}; | |
export type ScopesObject = Record<CaipChainId, ScopeObject> & { | |
[KnownCaipNamespace.Wallet]?: ScopeObject; | |
}; | |
/** | |
* Represents a `scopeString` as defined in | |
* [CAIP-217](https://chainagnostic.org/CAIPs/caip-217), with the exception that | |
* CAIP namespaces (aside from "wallet") are disallowed. This is necessary as | |
* MetaMask does not support "wildcarded" scopes in CAIP-25 requests, i.e. | |
* scopes that only specify a CAIP namespace but no specific reference (e.g. | |
* "eip155" vs. "eip155:1"). | |
*/ | |
export type ScopeString = CaipChainId | KnownCaipNamespace.Wallet; | |
/** | |
* Represents a `scopeObject` as defined in | |
* [CAIP-217](https://chainagnostic.org/CAIPs/caip-217), with the exception that | |
* the `references` property is disallowed. This is necessary as MetaMask does | |
* not support "wildcarded" scopes in CAIP-25 requests, i.e. scopes that only | |
* specify a CAIP namespace but no specific reference (e.g. "eip155" vs. | |
* "eip155:1"). | |
*/ | |
export type ScopeObject = { | |
methods: string[]; | |
notifications: string[]; | |
accounts: CaipAccountId[]; | |
rpcDocuments?: string[]; | |
rpcEndpoints?: string[]; | |
}; | |
/** | |
* Represents a keyed `scopeObject` as defined in | |
* [CAIP-217](https://chainagnostic.org/CAIPs/caip-217), with the exception that | |
* `scopeObject`s do not contain `references`. This is necessary as MetaMask | |
* does not support "wildcarded" scopes in CAIP-25 requests, i.e. scopes that | |
* only specify a CAIP namespace but no specific reference (e.g. "eip155" vs. | |
* "eip155:1"). | |
*/ | |
export type ScopesObject = Record<CaipChainId, ScopeObject> & { | |
[KnownCaipNamespace.Wallet]?: ScopeObject; | |
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually it seems like in addition to the deviations listed above, accounts
is also a required property in our version whereas in the spec it is optional. Is there a reason for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// These non-prefixed types represent CAIP-217 Scope and
// ScopeObject as defined by the spec but without
// namespace-only Scopes (except for "wallet") and without
// thereferences
array of CAIP References on the ScopeObject.
// These deviations from the spec are necessary as MetaMask
// does not support wildcarded Scopes, i.e. Scopes that only
// specify a namespace but no specific reference.
Ok so I think Jiexi's original comment is confusing since it suggests that we don't allow CAIP-25 requests that use the following format:
...
"method": "wallet_createSession",
"params": {
"requiredScopes": {
"eip155": {
"references": ["1", "137"],
"methods": ["eth_sendTransaction", "eth_signTransaction", "eth_sign", "get_balance", "personal_sign"],
"notifications": ["accountsChanged", "chainChanged"]
},
...
Where the scopeString is only a namespace and then the references
are specified in an array.
In fact we do allow this request format. What I have to assume Jiexi's original comment was meaning to indicate is that in our internal representations of this request/session we flatten out this format such that it becomes:
...
"method": "wallet_createSession",
"params": {
"requiredScopes": {
"eip155:1": {
"methods": ["eth_sendTransaction", "eth_signTransaction", "eth_sign", "get_balance", "personal_sign"],
"notifications": ["accountsChanged", "chainChanged"]
},
"eip155:137": {
"methods": ["eth_sendTransaction", "eth_signTransaction", "eth_sign", "get_balance", "personal_sign"],
"notifications": ["accountsChanged", "chainChanged"]
},
}
...
This is the reason for the two sets of types, those prefixed with "External" and those without:
ExternalScopeString
&ScopeString
ExternalScopeObject
&ScopeObject
ExternalScopesObject
&ScopesObject
Given this I'm going to rephrase the JSDoc comments slightly and push them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
☝️ is also the explanation for this question👇
Actually it seems like in addition to the deviations listed above, accounts is also a required property in our version whereas in the spec it is optional. Is there a reason for this?
Notice that the ExternalScopeObject
:
export type ExternalScopeObject = Omit<ScopeObject, 'accounts'> & {
references?: CaipReference[];
accounts?: CaipAccountId[];
};
resets the accounts
property as optional. This is because the ExternalScopeObject
is the type we expect in the CAIP-25 request itself for which, as you mentioned, the accounts
property is optional.
in the internal ScopeObject
however this property is not optional.
Perhaps using an internal
prefix would be less confusing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps using an internal prefix would be less confusing?
Yeah, I think so. Either that or something like Canonical
or Normalized
. Maybe Internal
is best though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think using Internal
/External
(or Unofficial
/Official
, or MetaMask
/Canonical
, or something) would help. Otherwise it gives the impression that the spec is deviating from something we are setting, when it's the other way around.
And updating the JSDoc to represent what we are actually doing would be good too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On reviewing this again I don't think any of my comments are blocking, so I approve. Since we have another PR coming after this one we have another opportunity to tweak the names of ScopesObject
if we can think of something better.
); | ||
}; | ||
|
||
export const getEthAccounts = ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Could you add TSDoc entries for each of these exported functions? Ideally we'd have them for all functions, but they're especially useful for the ones that get exported.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added here: 8194afd
// namespace-only Scopes (except for "wallet") and without | ||
// the `references` array of CAIP References on the ScopeObject. | ||
// These deviations from the spec are necessary as MetaMask | ||
// does not support wildcarded Scopes, i.e. Scopes that only |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Off-topic question: What was the reason for not supporting wildcard scopes? Or was that just cut from the initial version for now to reduce scope?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume this question was addressed by #4784 (comment)
// Methods that do not belong to an ecosystem | ||
export const KnownWalletRpcMethods: string[] = [ | ||
'wallet_registerOnboarding', | ||
'wallet_scanQRCode', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm. Technically it was introduced by an EIP, but it does seem useful outside of an Ethereum context. So maybe that's reason enough for it to belong here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah that was our thinking
return false; | ||
} | ||
|
||
// These assume that the namespace has a notion of chainIds |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm getting a little confused about where we landed on this question - whether we should be enforcing that scopes always contain a reference (either in the scopeString itself or in a references array)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we are enforcing this, then there are a couple of validation cases I think we're missing:
ScopeString
contains namespace only and references
is either absent or an empty array.
Co-authored-by: Elliot Winkler <elliot.winkler@gmail.com>
New dependencies detected. Learn more about Socket for GitHub ↗︎
|
Explanation
This PR updates
@metamask/multichain
to provide types, CAIP-25 permission, and helpers/adapters for the new permission, which can be shared across the extension & mobile clients.These tools and utilities will be used in both clients (mobile + extension)'s multichain API implementations.
File Overview
packages/multichain/src/adapters/
: Helpers that get and set legacy permission values from and to the new CAIP-25 permissionpackages/multichain/src/caip25Permission.ts
: Constants, types, mutators, and a specification builder for a CAIP-25 permissionpackages/multichain/src/index.ts
: Barrel exportpackages/multichain/src/scope/
: Types for CAIP-217 and our internal normalized/flattened version of them. Additionally contains helpers for validating shape, normalizing/merging, and checking support (i.e. if the wallet is able to serve the chain with it's requested methods and notifications)References
Upstream: #4812
Downstream: #4813
Key Multichain API Standards implemented here:
scope
defintionOpen PR that uses this new package for migrating the legacy permissions to CAIP-25 permission in the extension: MetaMask/metamask-extension#27847
Changelog
@metamask/multichain
Checklist