Skip to content
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: Migrate eth_accounts and permittedChains to CAIP-25 endowment #27847

Open
wants to merge 255 commits into
base: main
Choose a base branch
from

Conversation

jiexi
Copy link
Contributor

@jiexi jiexi commented Oct 14, 2024

Description

This PR replaces the replaces the internal eth_accounts and endowment:permittedChains permission structure with a CAIP-25 endowment. It adds adapter logic to translate to and from the new internal CAIP-25 permissions. This change should be transparent to wallet users and to dapps except for one two cases, see below. This change is required in order to support CAIP-25 and CAIP-27 requests in a follow-up PR that enables the Multichain API.

Open in GitHub Codespaces

Related issues

Related: MetaMask/core#4784

Manual testing steps

There should be no user or dapp facing difference in behavior except:

  • When calling wallet_revokePermissions and specifying either eth_accounts or endowment:permitted-chains, the entire CAIP-25 permission will be revoked. It will appear to the dapp as if both eth_accounts and endowment:permitted-chains were revoked.
  • When calling wallet_getPermissions for a permitted dapp when the wallet is locked, eth_accounts should be returned in addition to endowment:permitted-chains. Currently there is a regression on main where only endowment:permitted-chains gets returned when the wallet is locked.
await window.ethereum.request({
 "method": "wallet_revokePermissions",
 "params": [
  {
    eth_accounts: {}
  }
],
});

await window.ethereum.request({
 "method": "wallet_revokePermissions",
 "params": [
  {
    'endowment:permitted-chains': {}
  }
],
});

await window.ethereum.request({
 "method": "wallet_getPermissions",
 "params": [],
});

Locked Wallet Behavior with dapp connected

Other than the one noted item below, this behavior matches that in main

  • eth_accounts returns []
  • wallet_getPermissions returns permissions incl eth_accounts
  • wallet_revokePermissions works as usual and revokes eth_accounts and revoke permitted-chains together
    • Note this fixes a regression in main where eth_accounts and permitted-chains aren't revoked as a pair if either is revoked
  • eth_requestAccounts prompts for unlock, after unlock returns accounts if any are permitted, otherwise shows connection prompt
  • wallet_requestPermissions prompts for unlock
  • signature methods fails with method or accounts not authorized
  • non-signature methods work as usual
  • accountsChanged empty array on lock. no event after revokePermissions which makes sense since the dapp was told empty array on lock and now it's actually empty array so no changes have occurred as far as the dapp should be concerned.

Screenshots/Recordings

Before

After

Pre-merge author checklist

Pre-merge reviewer checklist

  • I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed).
  • I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots.

Copy link
Contributor

CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes.

Copy link

socket-security bot commented Oct 14, 2024

Copy link

socket-security bot commented Oct 14, 2024

👍 Dependency issues cleared. Learn more about Socket for GitHub ↗︎

This PR previously contained dependency changes with security issues that have been resolved, removed, or ignored.

View full report↗︎

@jiexi
Copy link
Contributor Author

jiexi commented Oct 14, 2024

@metamask-bot update-policies

@jiexi
Copy link
Contributor Author

jiexi commented Oct 15, 2024

@metamaskbot update-policies

@metamaskbot
Copy link
Collaborator

Policies updated.
👀 Please review the diff for suspicious new powers.

🧠 Learn how: https://lavamoat.github.io/guides/policy-diff/#what-to-look-for-when-reviewing-a-policy-diff


const isSnap = snapsPrefixes.some((prefix) => origin.startsWith(prefix));
const scopes: InternalScopesObject = {};
const scopeStrings: CaipChainId[] = isSnap
Copy link
Member

Choose a reason for hiding this comment

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

I don't entirely follow the reasoning here, what about Snaps that have accounts created?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

They'll still be stored under the wallet:eip155 scope a few lines below this

Copy link
Member

Choose a reason for hiding this comment

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

Hmm, I don't follow that either, what is that scope supposed to represent?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

that scope represents evm wallet methods, i.e. methods that do not belong to a particular evm chain, so something like personal_sign would belong here . It's kind of awkward right now because our current Signing UI implementation does require chainId context and so some signing methods do belong to a chain specific scope (i.e. eip155:1 etc) and so personal_sign does not exist in wallet:eip155 like in my example above, but I won't go into the specifics there

Copy link
Contributor Author

Choose a reason for hiding this comment

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

basically, it's a scope for evm stuff, but not chain specific

Copy link
Member

Choose a reason for hiding this comment

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

I understand that, I'm just having trouble following whether the permissions are being migrated correctly.

Is granting wallet:eip155 the same as granting access to all chains by default? E.g. the old eth_accounts behaviour? If not, then I don't understand fully how the migration works for Snaps.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The only reason is that snaps currently do not have permittedChains permissions and so granting permissions to eip155:<chainId> scopes would effectively be giving snaps permittedChains permissions. My understanding was that we wanted to hold off on doing this until we are ready to release snap selected networks and chain switching

Copy link
Member

Choose a reason for hiding this comment

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

That's fine, but it still doesn't answer my question as to what permission are we then actually granting. What does giving them wallet:eip155 grant them permission to do?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

permission to call any EIP-1193 window.ethereum exposed method, incl signing methods for the accounts specified in the wallet:eip155 scope, on the globally selected network

Copy link
Contributor

Choose a reason for hiding this comment

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

OK sorry obviously I misunderstood your line of questioning here in my previous answer.

So given that Snaps may already have eth_accounts permissions on technically all chains, why aren't we granting that permission in the same way as for dapps?

I think (?) I understand the question better now.

So the new permission format (modeled closely after CAIP-25 request structure) nests accounts under specific chain permissions:

      "eip155:10": {
        "accounts:" ["eip155:10:0x0910e12C68d02B561a34569E1367c9AAb42bd810" 
        ,"eip155:10:0x806627172aF48bD5B0765D3449A7DEf80d6576ff"]
      },
      "eip155:42161": {
        "accounts":["eip155:42161:0x0910e12C68d02B561a34569E1367c9AAb42bd810", "eip155:42161:0x806627172aF48bD5B0765D3449A7DEf80d6576ff"],
      },

so if we were to grant permissions to snaps in the same way we would necessarily be constraining these account permissions to the enumerated chains.

Putting snaps account permissions into wallet:eip155 is a temporary way to get around this entanglement of chain and account permissions. Really in the new permissions structure accounts permissions shouldn't really be separable from chain permissions.

Now its also the case we currently don't have any cases enabled where any accounts are not included in all the chain scopes so, until we do, the way it works is all permissioned accounts are permissioned on all permissioned chains. But this still doesn't lead to as broad of a permission as the legacy eth_accounts permission (before chain Permissions) since it was unconstrained by what subset of enabled chains the dapp has been granted. That's why we've carved out this temporary solution for snaps until they have chain permissions as well.

updatedEthAccounts,
);

permissionController.updateCaveat(
Copy link
Member

Choose a reason for hiding this comment

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

Is it not simpler to do this via grantPermissionsIncremental?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Mark, Alex, and I discussed a path to get to potentially get to that point, but prerequisite work would involve making a lot more component level changes that we are opting not to do at this time

Copy link
Member

Choose a reason for hiding this comment

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

At the very least we should create a ticket to follow-up on this!

Copy link
Contributor

Choose a reason for hiding this comment

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

Making a ticket for this now

Copy link
Contributor

Choose a reason for hiding this comment

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

# Conflicts:
#	app/scripts/controllers/permissions/specifications.js
#	lavamoat/browserify/beta/policy.json
#	lavamoat/browserify/flask/policy.json
#	lavamoat/browserify/main/policy.json
@jiexi
Copy link
Contributor Author

jiexi commented Dec 18, 2024

@metamaskbot update-policies

@metamaskbot
Copy link
Collaborator

Policies updated.
👀 Please review the diff for suspicious new powers.

🧠 Learn how: https://lavamoat.github.io/guides/policy-diff/#what-to-look-for-when-reviewing-a-policy-diff

@metamaskbot
Copy link
Collaborator

Builds ready [059ecad]
Page Load Metrics (1969 ± 74 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeHomefirstPaint16712170195915273
domContentLoaded16212149192815072
load16812249196915474
domInteractive27124522914
backgroundConnect996382914
firstReactRender1882522411
getState792212311
initialActions01000
loadScripts11601702145013665
setupStore785242612
uiStartup195330192292249119
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: 15.68 KiB (0.28%)
  • ui: 590 Bytes (0.01%)
  • common: 132.94 KiB (1.66%)

@jiexi
Copy link
Contributor Author

jiexi commented Dec 19, 2024

One thing I remembered is that the removeNetwork flow doesn't properly revoke scopes from existing CAIP-25 permissions because that case just isn't being handled in main right now. @Gudahtt I just want to confirm again that that particular problem will be resolved after this PR is merged (and is not expected to be included as part of this PR).

@metamaskbot
Copy link
Collaborator

Builds ready [a7ba043]
Page Load Metrics (1768 ± 64 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeHomefirstPaint28720231704351169
domContentLoaded15421983173512861
load15672033176813264
domInteractive25123442512
backgroundConnect775342211
firstReactRender1699532914
getState56219189
initialActions01000
loadScripts1151145413129545
setupStore680162110
uiStartup175126782126303145
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: 15.75 KiB (0.29%)
  • ui: 832 Bytes (0.01%)
  • common: 132.92 KiB (1.65%)

@metamaskbot
Copy link
Collaborator

Builds ready [352f964]
Page Load Metrics (1711 ± 78 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeHomefirstPaint15342217170016278
domContentLoaded15252184167816278
load15342220171116378
domInteractive25155503015
backgroundConnect988332512
firstReactRender1697492713
getState668182010
initialActions00000
loadScripts11151513123410349
setupStore75710115
uiStartup169524502022210101
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: 15.75 KiB (0.28%)
  • ui: 691 Bytes (0.01%)
  • common: 132.92 KiB (1.59%)

@metamaskbot
Copy link
Collaborator

Builds ready [e749a8a]
Page Load Metrics (1887 ± 60 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeHomefirstPaint34120451797350168
domContentLoaded16032184186112761
load16292205188712460
domInteractive29116582311
backgroundConnect1081272010
firstReactRender17103412914
getState666242110
initialActions01000
loadScripts11831643141711455
setupStore771262411
uiStartup179826402173214103
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: 15.75 KiB (0.28%)
  • ui: 691 Bytes (0.01%)
  • common: 132.92 KiB (1.59%)

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.

8 participants