Skip to content

Commit 4e2610b

Browse files
authored
Merge branch 'main' into fix/nft-grid-tooltip
2 parents 94b833f + 27d37f7 commit 4e2610b

File tree

84 files changed

+2799
-753
lines changed

Some content is hidden

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

84 files changed

+2799
-753
lines changed

.github/workflows/main.yml

+9-11
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ on:
1414
- synchronize
1515
merge_group:
1616

17+
env:
18+
# For a `pull_request` event, the branch is `github.head_ref``.
19+
# For a `push` event, the branch is `github.ref_name`.
20+
BRANCH: ${{ github.head_ref || github.ref_name }}
21+
# For a `pull_request` event, the fork is `github.event.pull_request.head.repo.fork`.
22+
# For a `push` event, the fork is `github.event.repository.fork`.
23+
IS_FORK: ${{ github.event.pull_request.head.repo.fork || github.event.repository.fork }}
24+
1725
jobs:
1826
prep-deps:
1927
runs-on: ubuntu-latest
@@ -124,10 +132,6 @@ jobs:
124132
needs:
125133
- prep-deps
126134
runs-on: ubuntu-latest
127-
env:
128-
# For a `pull_request` event, the branch is `github.head_ref``.
129-
# For a `push` event, the branch is `github.ref_name`.
130-
BRANCH: ${{ github.head_ref || github.ref_name }}
131135
steps:
132136
- name: Checkout and setup environment
133137
uses: MetaMask/action-checkout-and-setup@v1
@@ -175,12 +179,6 @@ jobs:
175179
env:
176180
EXTENSION_BUNDLESIZE_STATS_TOKEN: ${{ secrets.EXTENSION_BUNDLESIZE_STATS_TOKEN }}
177181
SELENIUM_BROWSER: chrome
178-
# For a `pull_request` event, the branch is `github.head_ref``.
179-
# For a `push` event, the branch is `github.ref_name`.
180-
BRANCH: ${{ github.head_ref || github.ref_name }}
181-
# For a `pull_request` event, the fork is `github.event.pull_request.head.repo.fork`.
182-
# For a `push` event, the fork is `github.event.repository.fork`.
183-
IS_FORK: ${{ github.event.pull_request.head.repo.fork || github.event.repository.fork }}
184182
permissions:
185183
contents: read
186184
# id-token permission is required for uploading to s3
@@ -203,7 +201,7 @@ jobs:
203201
run: yarn tsx test/e2e/mv3-perf-stats/bundle-size.ts --out test-artifacts/chrome
204202

205203
- name: Record bundle size at commit
206-
if: ${{ env.BRANCH == 'main' && !env.IS_FORK }}
204+
if: ${{ env.BRANCH == 'main' && env.IS_FORK == 'false'}}
207205
run: ./.github/scripts/bundle-stats-commit.sh
208206

209207
- name: Upload 'bundle-size' to S3

app/images/slide-bridge-icon.svg

+13-16
Loading

app/images/slide-card-icon.svg

+19-15
Loading

app/images/slide-fund-icon.svg

+19-19
Loading

app/images/slide-sell-icon.svg

+8-10
Loading

app/manifest/v3/_base.json

+8
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@
1818
},
1919
"commands": {
2020
"_execute_browser_action": {
21+
"suggested_key": {
22+
"windows": "Alt+Shift+M",
23+
"mac": "Alt+Shift+M",
24+
"chromeos": "Alt+Shift+M",
25+
"linux": "Alt+Shift+M"
26+
}
27+
},
28+
"_execute_action": {
2129
"suggested_key": {
2230
"default": "Alt+Shift+M",
2331
"windows": "Alt+Shift+M",

app/scripts/controller-init/assets/token-rates-controller-init.test.ts

+35-1
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,59 @@ import {
33
TokenRatesControllerMessenger,
44
} from '@metamask/assets-controllers';
55
import { Messenger } from '@metamask/base-controller';
6+
import { PreferencesController } from '@metamask/preferences-controller';
67
import { buildControllerInitRequestMock } from '../test/utils';
78
import { ControllerInitRequest } from '../types';
89
import { getTokenRatesControllerMessenger } from '../messengers/assets';
910
import { TokenRatesControllerInit } from './token-rates-controller-init';
1011

1112
jest.mock('@metamask/assets-controllers');
1213

14+
/**
15+
* Build a mock PreferencesController.
16+
* This returns a partial mock that includes the state property expected by the TokenRatesController (for example, `useCurrencyRateCheck`).
17+
*
18+
* @param {Partial<PreferencesController>} partialMock - The partial mock to be merged with the default mock.
19+
* @returns {PreferencesController} The mock PreferencesController.
20+
*/
21+
22+
function buildControllerMock(
23+
partialMock?: Partial<PreferencesController>,
24+
): PreferencesController {
25+
const defaultPreferencesControllerMock = {
26+
state: { useCurrencyRateCheck: true },
27+
};
28+
29+
// @ts-expect-error Incomplete mock, just includes properties used by code-under-test.
30+
return {
31+
...defaultPreferencesControllerMock,
32+
...partialMock,
33+
};
34+
}
35+
36+
/**
37+
* Build a mock init request.
38+
*
39+
* Notice that we also mock the getController method to return the
40+
* stubbed PreferencesController.
41+
*/
1342
function buildInitRequestMock(): jest.Mocked<
1443
ControllerInitRequest<TokenRatesControllerMessenger>
1544
> {
1645
const baseControllerMessenger = new Messenger();
1746

18-
return {
47+
const requestMock = {
1948
...buildControllerInitRequestMock(),
2049
controllerMessenger: getTokenRatesControllerMessenger(
2150
baseControllerMessenger,
2251
),
2352
initMessenger: undefined,
2453
};
54+
55+
// @ts-expect-error Incomplete mock, just includes properties used by code-under-test.
56+
requestMock.getController.mockReturnValue(buildControllerMock());
57+
58+
return requestMock;
2559
}
2660

2761
describe('TokenRatesControllerInit', () => {

app/scripts/controller-init/assets/token-rates-controller-init.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@ import { TokenRatesControllerMessenger } from '../messengers/assets';
1616
export const TokenRatesControllerInit: ControllerInitFunction<
1717
TokenRatesController,
1818
TokenRatesControllerMessenger
19-
> = ({ controllerMessenger, persistedState }) => {
19+
> = (request) => {
20+
const { controllerMessenger, getController, persistedState } = request;
21+
const preferencesController = () => getController('PreferencesController');
22+
2023
const controller = new TokenRatesController({
2124
messenger: controllerMessenger,
2225
state: persistedState.TokenRatesController,
2326
tokenPricesService: new CodefiTokenPricesServiceV2(),
24-
disabled: !persistedState.PreferencesController?.useCurrencyRateCheck,
27+
disabled: !preferencesController().state?.useCurrencyRateCheck,
2528
});
2629

2730
return {

app/scripts/lib/snap-keyring/keyring-snaps-permissions.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1+
import { KeyringRpcMethod } from '@metamask/keyring-api';
12
import {
2-
SubjectType,
33
SubjectMetadataController,
4+
SubjectType,
45
} from '@metamask/permission-controller';
5-
import { KeyringRpcMethod } from '@metamask/keyring-api';
66

77
/**
88
* The origins of the Portfolio dapp.

app/scripts/metamask-controller.js

+31-4
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,9 @@ import fetchWithCache from '../../shared/lib/fetch-with-cache';
264264
import { MultichainNetworks } from '../../shared/constants/multichain/networks';
265265
import { BRIDGE_API_BASE_URL } from '../../shared/constants/bridge';
266266
import { BridgeStatusAction } from '../../shared/types/bridge-status';
267+
///: BEGIN:ONLY_INCLUDE_IF(solana)
268+
import { addDiscoveredSolanaAccounts } from '../../shared/lib/accounts';
269+
///: END:ONLY_INCLUDE_IF
267270
import {
268271
///: BEGIN:ONLY_INCLUDE_IF(build-mmi)
269272
handleMMITransactionUpdate,
@@ -1859,14 +1862,14 @@ export default class MetamaskController extends EventEmitter {
18591862
NftController: NftControllerInit,
18601863
AssetsContractController: AssetsContractControllerInit,
18611864
NftDetectionController: NftDetectionControllerInit,
1865+
TokenRatesController: TokenRatesControllerInit,
18621866
///: BEGIN:ONLY_INCLUDE_IF(multichain)
18631867
MultichainAssetsController: MultichainAssetsControllerInit,
18641868
MultichainAssetsRatesController: MultichainAssetsRatesControllerInit,
18651869
MultichainBalancesController: MultichainBalancesControllerInit,
18661870
MultichainTransactionsController: MultichainTransactionsControllerInit,
18671871
///: END:ONLY_INCLUDE_IF
18681872
MultichainNetworkController: MultichainNetworkControllerInit,
1869-
TokenRatesController: TokenRatesControllerInit,
18701873
AuthenticationController: AuthenticationControllerInit,
18711874
UserStorageController: UserStorageControllerInit,
18721875
NotificationServicesController: NotificationServicesControllerInit,
@@ -4816,10 +4819,26 @@ export default class MetamaskController extends EventEmitter {
48164819
? { id: keyringId }
48174820
: { type: KeyringTypes.hd };
48184821

4819-
const accounts = await this.keyringController.withKeyring(
4822+
const {
4823+
accounts,
4824+
///: BEGIN:ONLY_INCLUDE_IF(solana)
4825+
entropySource,
4826+
///: END:ONLY_INCLUDE_IF
4827+
} = await this.keyringController.withKeyring(
48204828
keyringSelector,
4821-
async ({ keyring }) => {
4822-
return await keyring.getAccounts();
4829+
async ({
4830+
keyring,
4831+
///: BEGIN:ONLY_INCLUDE_IF(solana)
4832+
metadata,
4833+
///: END:ONLY_INCLUDE_IF
4834+
}) => {
4835+
const keyringAccounts = await keyring.getAccounts();
4836+
return {
4837+
accounts: keyringAccounts,
4838+
///: BEGIN:ONLY_INCLUDE_IF(solana)
4839+
entropySource: metadata.id,
4840+
///: END:ONLY_INCLUDE_IF
4841+
};
48234842
},
48244843
);
48254844
let address = accounts[accounts.length - 1];
@@ -4860,6 +4879,14 @@ export default class MetamaskController extends EventEmitter {
48604879
},
48614880
);
48624881
}
4882+
///: BEGIN:ONLY_INCLUDE_IF(solana)
4883+
const keyring = await this.getSnapKeyring();
4884+
await addDiscoveredSolanaAccounts(
4885+
this.controllerMessenger,
4886+
entropySource,
4887+
keyring,
4888+
);
4889+
///: END:ONLY_INCLUDE_IF
48634890
} catch (e) {
48644891
log.warn(`Failed to add accounts with balance. Error: ${e}`);
48654892
} finally {

app/scripts/metamask-controller.test.js

+78-2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
BtcAccountType,
1818
BtcMethod,
1919
EthAccountType,
20+
SolScope,
2021
} from '@metamask/keyring-api';
2122
import { Messenger } from '@metamask/base-controller';
2223
import { LoggingController, LogType } from '@metamask/logging-controller';
@@ -36,6 +37,7 @@ import {
3637
Caip25EndowmentPermissionName,
3738
} from '@metamask/chain-agnostic-permission';
3839
import { PermissionDoesNotExistError } from '@metamask/permission-controller';
40+
import { KeyringInternalSnapClient } from '@metamask/keyring-internal-snap-client';
3941
import { createTestProviderTools } from '../../test/stub/provider';
4042
import {
4143
HardwareDeviceNames,
@@ -3957,7 +3959,7 @@ describe('MetaMaskController', () => {
39573959
const newlyAddedKeyringId =
39583960
metamaskController.keyringController.state.keyringsMetadata[
39593961
metamaskController.keyringController.state.keyringsMetadata.length -
3960-
1
3962+
2 // -1 for the snap keyring, -1 for the newly added keyring
39613963
].id;
39623964

39633965
const newSRP = Buffer.from(
@@ -3967,7 +3969,10 @@ describe('MetaMaskController', () => {
39673969
expect(
39683970
currentKeyrings.filter((kr) => kr.type === 'HD Key Tree'),
39693971
).toHaveLength(2);
3970-
expect(currentKeyrings).toHaveLength(previousKeyrings.length + 1);
3972+
expect(
3973+
currentKeyrings.filter((kr) => kr.type === 'Snap Keyring'),
3974+
).toHaveLength(1);
3975+
expect(currentKeyrings).toHaveLength(previousKeyrings.length + 2);
39713976
expect(newSRP).toStrictEqual(TEST_SEED_ALT);
39723977
});
39733978

@@ -3982,6 +3987,77 @@ describe('MetaMaskController', () => {
39823987
'This Secret Recovery Phrase has already been imported.',
39833988
);
39843989
});
3990+
3991+
///: BEGIN:ONLY_INCLUDE_IF(multi-srp)
3992+
it('discovers and creates Solana accounts through KeyringInternalSnapClient when importing a mnemonic', async () => {
3993+
const password = 'what-what-what';
3994+
jest.spyOn(metamaskController, 'getBalance').mockResolvedValue('0x0');
3995+
3996+
const mockDiscoverAccounts = jest
3997+
.fn()
3998+
.mockResolvedValueOnce([{ derivationPath: "m/44'/501'/0'/0'" }])
3999+
.mockResolvedValueOnce([{ derivationPath: "m/44'/501'/1'/0'" }])
4000+
.mockResolvedValueOnce([]); // Return empty array on third call to stop the discovery loop
4001+
4002+
jest
4003+
.spyOn(KeyringInternalSnapClient.prototype, 'discoverAccounts')
4004+
.mockImplementation(mockDiscoverAccounts);
4005+
4006+
const mockCreateAccount = jest.fn().mockResolvedValue(undefined);
4007+
const mockSnapKeyring = { createAccount: mockCreateAccount };
4008+
jest
4009+
.spyOn(metamaskController, 'getSnapKeyring')
4010+
.mockResolvedValue(mockSnapKeyring);
4011+
4012+
await metamaskController.createNewVaultAndRestore(password, TEST_SEED);
4013+
await metamaskController.importMnemonicToVault(TEST_SEED_ALT);
4014+
4015+
// Assert that discoverAccounts was called correctly
4016+
// Should be called 3 times (twice with discovered accounts, once with empty array)
4017+
expect(mockDiscoverAccounts).toHaveBeenCalledTimes(3);
4018+
4019+
// All calls should include the solana scopes
4020+
expect(mockDiscoverAccounts.mock.calls[0][0]).toStrictEqual(
4021+
expect.arrayContaining([
4022+
SolScope.Mainnet,
4023+
SolScope.Testnet,
4024+
SolScope.Devnet,
4025+
]),
4026+
);
4027+
4028+
// First call should be for index 0
4029+
expect(mockDiscoverAccounts.mock.calls[0][2]).toBe(0);
4030+
// Second call should be for index 1
4031+
expect(mockDiscoverAccounts.mock.calls[1][2]).toBe(1);
4032+
// Third call should be for index 2
4033+
expect(mockDiscoverAccounts.mock.calls[2][2]).toBe(2);
4034+
4035+
// Assert that createAccount was called correctly for each discovered account
4036+
expect(mockCreateAccount).toHaveBeenCalledTimes(2);
4037+
4038+
// All calls should use the solana snap ID
4039+
expect(mockCreateAccount.mock.calls[0][0]).toStrictEqual(
4040+
expect.stringContaining('solana-wallet'),
4041+
);
4042+
// First call should use derivation path on index 0
4043+
expect(mockCreateAccount.mock.calls[0][1]).toStrictEqual({
4044+
derivationPath: "m/44'/501'/0'/0'",
4045+
entropySource: expect.any(String),
4046+
});
4047+
// All calls should use the same internal options
4048+
expect(mockCreateAccount.mock.calls[0][2]).toStrictEqual({
4049+
displayConfirmation: false,
4050+
displayAccountNameSuggestion: false,
4051+
setSelectedAccount: false,
4052+
});
4053+
4054+
// Second call should use derivation path on index 1
4055+
expect(mockCreateAccount.mock.calls[1][1]).toStrictEqual({
4056+
derivationPath: "m/44'/501'/1'/0'",
4057+
entropySource: expect.any(String),
4058+
});
4059+
});
4060+
///: END:ONLY_INCLUDE_IF
39854061
});
39864062
});
39874063

lavamoat/browserify/beta/policy.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1441,7 +1441,7 @@
14411441
"@ethereumjs/tx": true,
14421442
"@metamask/eth-snap-keyring>@metamask/eth-sig-util": true,
14431443
"@metamask/keyring-api": true,
1444-
"@metamask/eth-snap-keyring>@metamask/keyring-internal-snap-client": true,
1444+
"@metamask/keyring-internal-snap-client": true,
14451445
"@metamask/keyring-api>@metamask/keyring-utils": true,
14461446
"@metamask/utils>@metamask/superstruct": true,
14471447
"@metamask/utils": true,
@@ -1617,7 +1617,7 @@
16171617
"@metamask/keyring-controller>ulid": true
16181618
}
16191619
},
1620-
"@metamask/eth-snap-keyring>@metamask/keyring-internal-snap-client": {
1620+
"@metamask/keyring-internal-snap-client": {
16211621
"packages": {
16221622
"@metamask/keyring-snap-client": true
16231623
}

lavamoat/browserify/flask/policy.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1441,7 +1441,7 @@
14411441
"@ethereumjs/tx": true,
14421442
"@metamask/eth-snap-keyring>@metamask/eth-sig-util": true,
14431443
"@metamask/keyring-api": true,
1444-
"@metamask/eth-snap-keyring>@metamask/keyring-internal-snap-client": true,
1444+
"@metamask/keyring-internal-snap-client": true,
14451445
"@metamask/keyring-api>@metamask/keyring-utils": true,
14461446
"@metamask/utils>@metamask/superstruct": true,
14471447
"@metamask/utils": true,
@@ -1617,7 +1617,7 @@
16171617
"@metamask/keyring-controller>ulid": true
16181618
}
16191619
},
1620-
"@metamask/eth-snap-keyring>@metamask/keyring-internal-snap-client": {
1620+
"@metamask/keyring-internal-snap-client": {
16211621
"packages": {
16221622
"@metamask/keyring-snap-client": true
16231623
}

lavamoat/browserify/main/policy.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1441,7 +1441,7 @@
14411441
"@ethereumjs/tx": true,
14421442
"@metamask/eth-snap-keyring>@metamask/eth-sig-util": true,
14431443
"@metamask/keyring-api": true,
1444-
"@metamask/eth-snap-keyring>@metamask/keyring-internal-snap-client": true,
1444+
"@metamask/keyring-internal-snap-client": true,
14451445
"@metamask/keyring-api>@metamask/keyring-utils": true,
14461446
"@metamask/utils>@metamask/superstruct": true,
14471447
"@metamask/utils": true,
@@ -1617,7 +1617,7 @@
16171617
"@metamask/keyring-controller>ulid": true
16181618
}
16191619
},
1620-
"@metamask/eth-snap-keyring>@metamask/keyring-internal-snap-client": {
1620+
"@metamask/keyring-internal-snap-client": {
16211621
"packages": {
16221622
"@metamask/keyring-snap-client": true
16231623
}

0 commit comments

Comments
 (0)