Skip to content

Commit 975db53

Browse files
authored
Merge branch 'main' into fix/remove-async-operation-from-updateTransactionGasFees
2 parents 6f064e8 + b9b6e96 commit 975db53

File tree

8 files changed

+238
-72
lines changed

8 files changed

+238
-72
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@metamask/core-monorepo",
3-
"version": "341.0.0",
3+
"version": "342.0.0",
44
"private": true,
55
"description": "Monorepo for packages shared between MetaMask clients",
66
"repository": {

packages/assets-controllers/src/CurrencyRateController.test.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,4 +537,39 @@ describe('CurrencyRateController', () => {
537537

538538
controller.destroy();
539539
});
540+
541+
it('skips updating empty or undefined native currencies', async () => {
542+
jest.spyOn(global.Date, 'now').mockImplementation(() => getStubbedDate());
543+
const cryptoCompareHost = 'https://min-api.cryptocompare.com';
544+
nock(cryptoCompareHost)
545+
.get('/data/pricemulti?fsyms=ETH&tsyms=xyz') // fsyms query only includes non-empty native currencies
546+
.reply(200, {
547+
ETH: { XYZ: 1000 },
548+
})
549+
.persist();
550+
551+
const messenger = getRestrictedMessenger();
552+
const controller = new CurrencyRateController({
553+
messenger,
554+
state: { currentCurrency: 'xyz' },
555+
});
556+
557+
const nativeCurrencies = ['ETH', undefined, ''];
558+
559+
await controller.updateExchangeRate(nativeCurrencies);
560+
561+
const conversionDate = getStubbedDate() / 1000;
562+
expect(controller.state).toStrictEqual({
563+
currentCurrency: 'xyz',
564+
currencyRates: {
565+
ETH: {
566+
conversionDate,
567+
conversionRate: 1000,
568+
usdConversionRate: null,
569+
},
570+
},
571+
});
572+
573+
controller.destroy();
574+
});
540575
});

packages/assets-controllers/src/CurrencyRateController.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,9 @@ export class CurrencyRateController extends StaticIntervalPollingController<Curr
157157
*
158158
* @param nativeCurrencies - The native currency symbols to fetch exchange rates for.
159159
*/
160-
async updateExchangeRate(nativeCurrencies: string[]): Promise<void> {
160+
async updateExchangeRate(
161+
nativeCurrencies: (string | undefined)[],
162+
): Promise<void> {
161163
const releaseLock = await this.mutex.acquire();
162164
try {
163165
const { currentCurrency } = this.state;
@@ -167,6 +169,10 @@ export class CurrencyRateController extends StaticIntervalPollingController<Curr
167169
const testnetSymbols = Object.values(TESTNET_TICKER_SYMBOLS);
168170
const nativeCurrenciesToFetch = nativeCurrencies.reduce(
169171
(acc, nativeCurrency) => {
172+
if (!nativeCurrency) {
173+
return acc;
174+
}
175+
170176
acc[nativeCurrency] = testnetSymbols.includes(nativeCurrency)
171177
? FALL_BACK_VS_CURRENCY
172178
: nativeCurrency;

packages/earn-controller/CHANGELOG.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.10.0]
11+
12+
### Changed
13+
14+
- **BREAKING:** Updated `EarnController` methods (`refreshPooledStakingData`, `refreshPooledStakes`, and `refreshStakingEligibility`) to use an options bag parameter ([#5537](https://github.com/MetaMask/core/pull/5537))
15+
1016
## [0.9.0]
1117

1218
### Changed
@@ -69,7 +75,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6975

7076
- Initial release ([#5271](https://github.com/MetaMask/core/pull/5271))
7177

72-
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/earn-controller@0.9.0...HEAD
78+
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/earn-controller@0.10.0...HEAD
79+
[0.10.0]: https://github.com/MetaMask/core/compare/@metamask/earn-controller@0.9.0...@metamask/earn-controller@0.10.0
7380
[0.9.0]: https://github.com/MetaMask/core/compare/@metamask/earn-controller@0.8.0...@metamask/earn-controller@0.9.0
7481
[0.8.0]: https://github.com/MetaMask/core/compare/@metamask/earn-controller@0.7.0...@metamask/earn-controller@0.8.0
7582
[0.7.0]: https://github.com/MetaMask/core/compare/@metamask/earn-controller@0.6.0...@metamask/earn-controller@0.7.0

packages/earn-controller/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@metamask/earn-controller",
3-
"version": "0.9.0",
3+
"version": "0.10.0",
44
"description": "Manages state for earning features and coordinates interactions between staking services, SDK integrations, and other controllers to enable users to participate in various earning opportunities",
55
"keywords": [
66
"MetaMask",

packages/earn-controller/src/EarnController.test.ts

Lines changed: 129 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,12 @@ const createMockInternalAccount = ({
9898
};
9999
};
100100

101+
const mockAccount1Address = '0x1234';
102+
103+
const mockAccount2Address = '0xabc';
104+
101105
const mockPooledStakes = {
102-
account: '0x1234',
106+
account: mockAccount1Address,
103107
lifetimeRewards: '100',
104108
assets: '1000',
105109
exitRequests: [],
@@ -208,7 +212,7 @@ const setupController = ({
208212
})),
209213

210214
mockGetSelectedAccount = jest.fn(() => ({
211-
address: '0x1234',
215+
address: mockAccount1Address,
212216
})),
213217
}: {
214218
options?: Partial<ConstructorParameters<typeof EarnController>[0]>;
@@ -389,25 +393,40 @@ describe('EarnController', () => {
389393
expect(mockedStakingApiService.getPooledStakes).toHaveBeenNthCalledWith(
390394
// First call occurs during setupController()
391395
2,
392-
['0x1234'],
396+
[mockAccount1Address],
393397
1,
394398
false,
395399
);
396400
});
397401

398402
it('invalidates cache when refreshing state', async () => {
399403
const { controller } = setupController();
400-
await controller.refreshPooledStakingData(true);
404+
await controller.refreshPooledStakingData({ resetCache: true });
401405

402406
expect(mockedStakingApiService.getPooledStakes).toHaveBeenNthCalledWith(
403407
// First call occurs during setupController()
404408
2,
405-
['0x1234'],
409+
[mockAccount1Address],
406410
1,
407411
true,
408412
);
409413
});
410414

415+
it('refreshes state using options.address', async () => {
416+
const { controller } = setupController();
417+
await controller.refreshPooledStakingData({
418+
address: mockAccount2Address,
419+
});
420+
421+
expect(mockedStakingApiService.getPooledStakes).toHaveBeenNthCalledWith(
422+
// First call occurs during setupController()
423+
2,
424+
[mockAccount2Address],
425+
1,
426+
false,
427+
);
428+
});
429+
411430
it('handles API errors gracefully', async () => {
412431
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
413432
mockedStakingApiService = {
@@ -464,12 +483,12 @@ describe('EarnController', () => {
464483
describe('refreshPooledStakes', () => {
465484
it('fetches without resetting cache when resetCache is false', async () => {
466485
const { controller } = setupController();
467-
await controller.refreshPooledStakes(false);
486+
await controller.refreshPooledStakes({ resetCache: false });
468487

469488
// Assertion on second call since the first is part of controller setup.
470489
expect(mockedStakingApiService.getPooledStakes).toHaveBeenNthCalledWith(
471490
2,
472-
['0x1234'],
491+
[mockAccount1Address],
473492
1,
474493
false,
475494
);
@@ -482,80 +501,138 @@ describe('EarnController', () => {
482501
// Assertion on second call since the first is part of controller setup.
483502
expect(mockedStakingApiService.getPooledStakes).toHaveBeenNthCalledWith(
484503
2,
485-
['0x1234'],
504+
[mockAccount1Address],
486505
1,
487506
false,
488507
);
489508
});
490509

491510
it('fetches while resetting cache', async () => {
492511
const { controller } = setupController();
493-
await controller.refreshPooledStakes(true);
512+
await controller.refreshPooledStakes({ resetCache: true });
494513

495514
// Assertion on second call since the first is part of controller setup.
496515
expect(mockedStakingApiService.getPooledStakes).toHaveBeenNthCalledWith(
497516
2,
498-
['0x1234'],
517+
[mockAccount1Address],
499518
1,
500519
true,
501520
);
502521
});
503-
});
504522

505-
describe('subscription handlers', () => {
506-
const firstAccount = createMockInternalAccount({
507-
address: '0x1234',
523+
it('fetches using active account (default)', async () => {
524+
const { controller } = setupController();
525+
await controller.refreshPooledStakes();
526+
527+
// Assertion on second call since the first is part of controller setup.
528+
expect(mockedStakingApiService.getPooledStakes).toHaveBeenNthCalledWith(
529+
2,
530+
[mockAccount1Address],
531+
1,
532+
false,
533+
);
508534
});
509535

510-
it('updates vault data when network changes', () => {
511-
const { controller, messenger } = setupController();
536+
it('fetches using options.address override', async () => {
537+
const { controller } = setupController();
538+
await controller.refreshPooledStakes({ address: mockAccount2Address });
512539

513-
jest
514-
.spyOn(controller, 'refreshPooledStakingVaultMetadata')
515-
.mockResolvedValue();
516-
jest
517-
.spyOn(controller, 'refreshPooledStakingVaultDailyApys')
518-
.mockResolvedValue();
519-
jest
520-
.spyOn(controller, 'refreshPooledStakingVaultApyAverages')
521-
.mockResolvedValue();
540+
// Assertion on second call since the first is part of controller setup.
541+
expect(mockedStakingApiService.getPooledStakes).toHaveBeenNthCalledWith(
542+
2,
543+
[mockAccount2Address],
544+
1,
545+
false,
546+
);
547+
});
548+
});
522549

523-
jest.spyOn(controller, 'refreshPooledStakes').mockResolvedValue();
550+
describe('refreshStakingEligibility', () => {
551+
it('fetches staking eligibility using active account (default)', async () => {
552+
const { controller } = setupController();
524553

525-
messenger.publish(
526-
'NetworkController:stateChange',
527-
{
528-
...getDefaultNetworkControllerState(),
529-
selectedNetworkClientId: '2',
530-
},
531-
[],
532-
);
554+
await controller.refreshStakingEligibility();
533555

556+
// Assertion on second call since the first is part of controller setup.
534557
expect(
535-
controller.refreshPooledStakingVaultMetadata,
536-
).toHaveBeenCalledTimes(1);
537-
expect(
538-
controller.refreshPooledStakingVaultDailyApys,
539-
).toHaveBeenCalledTimes(1);
558+
mockedStakingApiService.getPooledStakingEligibility,
559+
).toHaveBeenNthCalledWith(2, [mockAccount1Address]);
560+
});
561+
562+
it('fetches staking eligibility using options.address override', async () => {
563+
const { controller } = setupController();
564+
await controller.refreshStakingEligibility({
565+
address: mockAccount2Address,
566+
});
567+
568+
// Assertion on second call since the first is part of controller setup.
540569
expect(
541-
controller.refreshPooledStakingVaultApyAverages,
542-
).toHaveBeenCalledTimes(1);
543-
expect(controller.refreshPooledStakes).toHaveBeenCalledTimes(1);
570+
mockedStakingApiService.getPooledStakingEligibility,
571+
).toHaveBeenNthCalledWith(2, [mockAccount2Address]);
544572
});
573+
});
545574

546-
it('updates staking eligibility when selected account changes', () => {
547-
const { controller, messenger } = setupController();
575+
describe('subscription handlers', () => {
576+
const account = createMockInternalAccount({
577+
address: mockAccount2Address,
578+
});
548579

549-
jest.spyOn(controller, 'refreshStakingEligibility').mockResolvedValue();
550-
jest.spyOn(controller, 'refreshPooledStakes').mockResolvedValue();
580+
describe('On network change', () => {
581+
it('updates vault data when network changes', () => {
582+
const { controller, messenger } = setupController();
583+
584+
jest
585+
.spyOn(controller, 'refreshPooledStakingVaultMetadata')
586+
.mockResolvedValue();
587+
jest
588+
.spyOn(controller, 'refreshPooledStakingVaultDailyApys')
589+
.mockResolvedValue();
590+
jest
591+
.spyOn(controller, 'refreshPooledStakingVaultApyAverages')
592+
.mockResolvedValue();
593+
594+
jest.spyOn(controller, 'refreshPooledStakes').mockResolvedValue();
595+
596+
messenger.publish(
597+
'NetworkController:stateChange',
598+
{
599+
...getDefaultNetworkControllerState(),
600+
selectedNetworkClientId: '2',
601+
},
602+
[],
603+
);
604+
605+
expect(
606+
controller.refreshPooledStakingVaultMetadata,
607+
).toHaveBeenCalledTimes(1);
608+
expect(
609+
controller.refreshPooledStakingVaultDailyApys,
610+
).toHaveBeenCalledTimes(1);
611+
expect(
612+
controller.refreshPooledStakingVaultApyAverages,
613+
).toHaveBeenCalledTimes(1);
614+
expect(controller.refreshPooledStakes).toHaveBeenCalledTimes(1);
615+
});
616+
});
551617

552-
messenger.publish(
553-
'AccountsController:selectedAccountChange',
554-
firstAccount,
555-
);
618+
describe('On selected account change', () => {
619+
// TEMP: Workaround for issue: https://github.com/MetaMask/accounts-planning/issues/887
620+
it('uses event payload account address to update staking eligibility', () => {
621+
const { controller, messenger } = setupController();
556622

557-
expect(controller.refreshStakingEligibility).toHaveBeenCalledTimes(1);
558-
expect(controller.refreshPooledStakes).toHaveBeenCalledTimes(1);
623+
jest.spyOn(controller, 'refreshStakingEligibility').mockResolvedValue();
624+
jest.spyOn(controller, 'refreshPooledStakes').mockResolvedValue();
625+
626+
messenger.publish('AccountsController:selectedAccountChange', account);
627+
628+
expect(controller.refreshStakingEligibility).toHaveBeenNthCalledWith(
629+
1,
630+
{ address: account.address },
631+
);
632+
expect(controller.refreshPooledStakes).toHaveBeenNthCalledWith(1, {
633+
address: account.address,
634+
});
635+
});
559636
});
560637
});
561638
});

0 commit comments

Comments
 (0)