Skip to content

Commit 3ab2865

Browse files
jiexiMontoyaadonesky1FrederikBoldingmcmire
authored
feat: SIP-26 Integration (#29887)
[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/29887?quickstart=1) General Testing: - Run this in a flask build - Create Solana Accounts - Go to the [Multichain Test Dapp](https://metamask.github.io/test-dapp-multichain/latest/) (which now supports Solana!): [Video Demo of Testing with the Test Dapp](https://github.com/user-attachments/assets/0e785597-7237-4d05-9192-f06ef9617601) ## **Related issues** Ticket: https://github.com/orgs/MetaMask/projects/146/views/6?pane=issue&itemId=94617458&issue=MetaMask%7CMetaMask-planning%7C3989 Upstream Extension: #27782 Core: MetaMask/core#5191 ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** <!-- [screenshots/recordings] --> ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **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. --------- Co-authored-by: Christian Montoya <[email protected]> Co-authored-by: Alex <[email protected]> Co-authored-by: Frederik Bolding <[email protected]> Co-authored-by: Elliot Winkler <[email protected]>
1 parent 17b9874 commit 3ab2865

File tree

68 files changed

+3404
-643
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+3404
-643
lines changed

app/build-types/beta/manifest/_base.json

+4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
},
1212
"default_title": "MetaMask Beta"
1313
},
14+
"externally_connectable": {
15+
"matches": ["http://*/*", "https://*/*"],
16+
"ids": ["*"]
17+
},
1418
"icons": {
1519
"16": "images/icon-16.png",
1620
"19": "images/icon-19.png",

app/scripts/controllers/permissions/background-api.js

+125-35
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,24 @@ import {
66
import {
77
Caip25CaveatType,
88
Caip25EndowmentPermissionName,
9-
getEthAccounts,
10-
setEthAccounts,
11-
getPermittedEthChainIds,
12-
setPermittedEthChainIds,
13-
} from '@metamask/multichain';
9+
setPermittedChainIds,
10+
setPermittedAccounts,
11+
} from '@metamask/chain-agnostic-permission';
1412
import { isSnapId } from '@metamask/snaps-utils';
13+
import { parseCaipAccountId, parseCaipChainId } from '@metamask/utils';
14+
import {
15+
getCaipAccountIdsFromCaip25CaveatValue,
16+
isInternalAccountInPermittedAccountIds,
17+
} from '../../../../shared/lib/multichain/chain-agnostic-permission-utils/caip-accounts';
18+
import { getAllScopesFromCaip25CaveatValue } from '../../../../shared/lib/multichain/chain-agnostic-permission-utils/caip-chainids';
19+
import { getNetworkConfigurationsByCaipChainId } from '../../../../shared/modules/selectors/networks';
1520

1621
export function getPermissionBackgroundApiMethods({
1722
permissionController,
1823
approvalController,
24+
accountsController,
25+
networkController,
26+
multichainNetworkController,
1927
}) {
2028
// Returns the CAIP-25 caveat or undefined if it does not exist
2129
const getCaip25Caveat = (origin) => {
@@ -38,30 +46,98 @@ export function getPermissionBackgroundApiMethods({
3846
};
3947

4048
// To add more than one account when already connected to the dapp
41-
const addMoreAccounts = (origin, accounts) => {
49+
const addMoreAccounts = (origin, addresses) => {
4250
const caip25Caveat = getCaip25Caveat(origin);
4351
if (!caip25Caveat) {
4452
throw new Error(
4553
`Cannot add account permissions for origin "${origin}": no permission currently exists for this origin.`,
4654
);
4755
}
4856

49-
const ethAccounts = getEthAccounts(caip25Caveat.value);
57+
const internalAccounts = addresses.map((address) => {
58+
return accountsController.getAccountByAddress(address);
59+
});
60+
61+
// Only the first scope in the scopes array is needed because
62+
// setPermittedAccounts currently sets accounts on all matching
63+
// namespaces, not just the exact CaipChainId.
64+
const caipAccountIds = internalAccounts.map((internalAccount) => {
65+
return `${internalAccount.scopes[0]}:${internalAccount.address}`;
66+
});
5067

51-
const updatedEthAccounts = Array.from(
52-
new Set([...ethAccounts, ...accounts]),
68+
const existingPermittedAccountIds = getCaipAccountIdsFromCaip25CaveatValue(
69+
caip25Caveat.value,
5370
);
5471

55-
const updatedCaveatValue = setEthAccounts(
72+
const existingPermittedChainIds = getAllScopesFromCaip25CaveatValue(
5673
caip25Caveat.value,
57-
updatedEthAccounts,
74+
);
75+
76+
const updatedAccountIds = Array.from(
77+
new Set([...existingPermittedAccountIds, ...caipAccountIds]),
78+
);
79+
80+
let updatedPermittedChainIds = [...existingPermittedChainIds];
81+
82+
const allNetworksList = Object.keys(
83+
getNetworkConfigurationsByCaipChainId({
84+
networkConfigurationsByChainId:
85+
networkController.state.networkConfigurationsByChainId,
86+
multichainNetworkConfigurationsByChainId:
87+
multichainNetworkController.state
88+
.multichainNetworkConfigurationsByChainId,
89+
internalAccounts: accountsController.state.internalAccounts,
90+
}),
91+
);
92+
93+
updatedAccountIds.forEach((caipAccountAddress) => {
94+
const {
95+
chain: { namespace: accountNamespace },
96+
} = parseCaipAccountId(caipAccountAddress);
97+
98+
const existsSelectedChainForNamespace = updatedPermittedChainIds.some(
99+
(caipChainId) => {
100+
try {
101+
const { namespace: chainNamespace } = parseCaipChainId(caipChainId);
102+
return accountNamespace === chainNamespace;
103+
} catch (err) {
104+
return false;
105+
}
106+
},
107+
);
108+
109+
if (!existsSelectedChainForNamespace) {
110+
const chainIdsForNamespace = allNetworksList.filter((caipChainId) => {
111+
try {
112+
const { namespace: chainNamespace } = parseCaipChainId(caipChainId);
113+
return accountNamespace === chainNamespace;
114+
} catch (err) {
115+
return false;
116+
}
117+
});
118+
119+
updatedPermittedChainIds = [
120+
...updatedPermittedChainIds,
121+
...chainIdsForNamespace,
122+
];
123+
}
124+
});
125+
126+
const updatedCaveatValueWithChainIds = setPermittedChainIds(
127+
caip25Caveat.value,
128+
updatedPermittedChainIds,
129+
);
130+
131+
const updatedCaveatValueWithAccountIds = setPermittedAccounts(
132+
updatedCaveatValueWithChainIds,
133+
updatedAccountIds,
58134
);
59135

60136
permissionController.updateCaveat(
61137
origin,
62138
Caip25EndowmentPermissionName,
63139
Caip25CaveatType,
64-
updatedCaveatValue,
140+
updatedCaveatValueWithAccountIds,
65141
);
66142
};
67143

@@ -73,22 +149,26 @@ export function getPermissionBackgroundApiMethods({
73149
);
74150
}
75151

76-
const ethChainIds = getPermittedEthChainIds(caip25Caveat.value);
152+
const updatedChainIds = Array.from(
153+
new Set([
154+
...getAllScopesFromCaip25CaveatValue(caip25Caveat.value),
155+
...chainIds,
156+
]),
157+
);
77158

78-
const updatedEthChainIds = Array.from(
79-
new Set([...ethChainIds, ...chainIds]),
159+
const caveatValueWithChainIds = setPermittedChainIds(
160+
caip25Caveat.value,
161+
updatedChainIds,
80162
);
81163

82-
const caveatValueWithChains = setPermittedEthChainIds(
164+
const permittedAccountIds = getCaipAccountIdsFromCaip25CaveatValue(
83165
caip25Caveat.value,
84-
updatedEthChainIds,
85166
);
86167

87-
// ensure that the list of permitted eth accounts is set for the newly added eth scopes
88-
const ethAccounts = getEthAccounts(caveatValueWithChains);
89-
const caveatValueWithAccountsSynced = setEthAccounts(
90-
caveatValueWithChains,
91-
ethAccounts,
168+
// ensure that the list of permitted accounts is set for the newly added scopes
169+
const caveatValueWithAccountsSynced = setPermittedAccounts(
170+
caveatValueWithChainIds,
171+
permittedAccountIds,
92172
);
93173

94174
permissionController.updateCaveat(
@@ -147,33 +227,41 @@ export function getPermissionBackgroundApiMethods({
147227
addPermittedAccounts: (origin, accounts) =>
148228
addMoreAccounts(origin, accounts),
149229

150-
removePermittedAccount: (origin, account) => {
230+
removePermittedAccount: (origin, address) => {
151231
const caip25Caveat = getCaip25Caveat(origin);
152232
if (!caip25Caveat) {
153233
throw new Error(
154-
`Cannot remove account "${account}": No permissions exist for origin "${origin}".`,
234+
`Cannot remove account "${address}": No permissions exist for origin "${origin}".`,
155235
);
156236
}
157237

158-
const existingAccounts = getEthAccounts(caip25Caveat.value);
238+
const existingAccountIds = getCaipAccountIdsFromCaip25CaveatValue(
239+
caip25Caveat.value,
240+
);
159241

160-
const remainingAccounts = existingAccounts.filter(
161-
(existingAccount) => existingAccount !== account,
242+
const internalAccount = accountsController.getAccountByAddress(address);
243+
244+
const remainingAccountIds = existingAccountIds.filter(
245+
(existingAccountId) => {
246+
return !isInternalAccountInPermittedAccountIds(internalAccount, [
247+
existingAccountId,
248+
]);
249+
},
162250
);
163251

164-
if (remainingAccounts.length === existingAccounts.length) {
252+
if (remainingAccountIds.length === existingAccountIds.length) {
165253
return;
166254
}
167255

168-
if (remainingAccounts.length === 0) {
256+
if (remainingAccountIds.length === 0) {
169257
permissionController.revokePermission(
170258
origin,
171259
Caip25EndowmentPermissionName,
172260
);
173261
} else {
174-
const updatedCaveatValue = setEthAccounts(
262+
const updatedCaveatValue = setPermittedAccounts(
175263
caip25Caveat.value,
176-
remainingAccounts,
264+
remainingAccountIds,
177265
);
178266
permissionController.updateCaveat(
179267
origin,
@@ -196,13 +284,15 @@ export function getPermissionBackgroundApiMethods({
196284
);
197285
}
198286

199-
const existingEthChainIds = getPermittedEthChainIds(caip25Caveat.value);
287+
const existingChainIds = getAllScopesFromCaip25CaveatValue(
288+
caip25Caveat.value,
289+
);
200290

201-
const remainingChainIds = existingEthChainIds.filter(
291+
const remainingChainIds = existingChainIds.filter(
202292
(existingChainId) => existingChainId !== chainId,
203293
);
204294

205-
if (remainingChainIds.length === existingEthChainIds.length) {
295+
if (remainingChainIds.length === existingChainIds.length) {
206296
return;
207297
}
208298

@@ -212,7 +302,7 @@ export function getPermissionBackgroundApiMethods({
212302
Caip25EndowmentPermissionName,
213303
);
214304
} else {
215-
const updatedCaveatValue = setPermittedEthChainIds(
305+
const updatedCaveatValue = setPermittedChainIds(
216306
caip25Caveat.value,
217307
remainingChainIds,
218308
);

0 commit comments

Comments
 (0)