Skip to content

Commit 8de24e5

Browse files
chore(runway): cherry-pick feat: integrate earn-controller 12.0.0 to fix broken pooled-staking deposits and withdrawals (#28819)
- feat: integrate earn-controller 12.0.0 to fix broken pooled-staking deposits and withdrawals cp-7.73.0 (#28737) <!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once. --> ## **Description** This PR bumps the `@metamask/earn-controller` dependency to version `12.0.0`. This is required to address a release blocking bug where pooled-staking deposits and withdrawals are blocked. <!-- Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions: 1. What is the reason for the change? 2. What is the improvement/solution? --> ## **Changelog** <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry: bumped @metamask/earn-controller to 12.0.0 and fixed breaking changes ## **Related issues** Fixes: - [MUSD-619: Users are unable to stake ETH or lend from token details page](https://consensyssoftware.atlassian.net/browse/MUSD-619) ## **Manual testing steps** ```gherkin Feature: Ethereum Pooled-Staking Scenario: user wants to stake ETH Given user has no staked ETH When user visits Ethereum's asset details screen Then the "Stake" button is rendered in the empty state CTA Scenario: user wants to unstake ETH Given user has staked ETH When user visits Ethereum's asset details screen Then the "Unstake" button is rendered in the empty state CTA ``` ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> The Stake and Unstake buttons would not render due to EarnController initialization failures. ### **After** <!-- [screenshots/recordings] --> https://github.com/user-attachments/assets/ba0d15a5-d95a-47c6-8809-0bd64c4a4626 ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/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. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Medium risk because it upgrades a core dependency (`@metamask/earn-controller`) and changes EarnController initialization/messenger delegation, which can affect staking/lending flows at runtime. > > **Overview** > Upgrades `@metamask/earn-controller` from `^10.0.0` to `^12.0.0` (lockfile updated accordingly). > > Updates EarnController wiring to match the new API: removes the dedicated init messenger, delegates `NetworkController:getState` (and `AccountTreeController:stateChange`) through the main EarnController messenger, and changes `earnControllerInit` to construct the controller without `selectedNetworkClientId` and explicitly call `controller.init()`. > > Adjusts mocks/fixtures/snapshots to include the new `EarnController` state key `tron_staking: null` so tests and initial background state stay in sync. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit da2c3bf. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> [0770e7b](0770e7b) --------- Co-authored-by: Matthew Grainger <46547583+Matt561@users.noreply.github.com> Co-authored-by: Matthew Grainger <matthew.grainger@consensys.net>
1 parent 5720a29 commit 8de24e5

11 files changed

Lines changed: 45 additions & 104 deletions

File tree

app/components/UI/Earn/__mocks__/earnMockData.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ export const getMockEarnControllerState = ({
190190
}: Partial<GetMockEarnControllerStateOptions> = {}) => ({
191191
pooled_staking: getMockEarnControllerPooledStakingState(pooledStaking),
192192
lending: getMockEarnControllerLendingState(lending),
193+
tron_staking: null,
193194
lastUpdated: 0,
194195
});
195196

app/components/UI/Earn/hooks/useEarnSelector.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ describe('useEarnSelector', () => {
3939
markets: [],
4040
positions: [],
4141
},
42+
tron_staking: null,
4243
lastUpdated: 0,
4344
};
4445

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,20 @@
11
import { buildControllerInitRequestMock } from '../utils/test-utils';
22
import { ExtendedMessenger } from '../../ExtendedMessenger';
3-
import {
4-
getEarnControllerMessenger,
5-
EarnControllerInitMessenger,
6-
getEarnControllerInitMessenger,
7-
} from '../messengers/earn-controller-messenger';
8-
import { ControllerInitRequest } from '../types';
3+
import { getEarnControllerMessenger } from '../messengers/earn-controller-messenger';
94
import { earnControllerInit } from './earn-controller-init';
10-
import {
11-
EarnController,
12-
type EarnControllerMessenger,
13-
} from '@metamask/earn-controller';
5+
import { EarnController } from '@metamask/earn-controller';
146
import { MOCK_ANY_NAMESPACE, MockAnyNamespace } from '@metamask/messenger';
157

168
jest.mock('@metamask/earn-controller');
179

18-
function getInitRequestMock(): jest.Mocked<
19-
ControllerInitRequest<EarnControllerMessenger, EarnControllerInitMessenger>
20-
> {
10+
function getInitRequestMock() {
2111
const baseMessenger = new ExtendedMessenger<MockAnyNamespace, never>({
2212
namespace: MOCK_ANY_NAMESPACE,
2313
});
2414

2515
const requestMock = {
2616
...buildControllerInitRequestMock(baseMessenger),
2717
controllerMessenger: getEarnControllerMessenger(baseMessenger),
28-
initMessenger: getEarnControllerInitMessenger(baseMessenger),
2918
};
3019

3120
// @ts-expect-error: Partial mock.
@@ -39,11 +28,6 @@ function getInitRequestMock(): jest.Mocked<
3928
throw new Error(`Controller "${name}" not found.`);
4029
});
4130

42-
// @ts-expect-error: Partial mock.
43-
baseMessenger.registerActionHandler('NetworkController:getState', () => ({
44-
selectedNetworkClientId: 'mainnet',
45-
}));
46-
4731
return requestMock;
4832
}
4933

@@ -60,7 +44,11 @@ describe('EarnControllerInit', () => {
6044
expect(controllerMock).toHaveBeenCalledWith({
6145
messenger: expect.any(Object),
6246
addTransactionFn: expect.any(Function),
63-
selectedNetworkClientId: 'mainnet',
6447
});
6548
});
49+
50+
it('calls init() on the controller after construction', () => {
51+
const { controller } = earnControllerInit(getInitRequestMock());
52+
expect(controller.init).toHaveBeenCalledTimes(1);
53+
});
6654
});

app/core/Engine/controllers/earn-controller-init.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import {
33
EarnController,
44
EarnControllerMessenger,
55
} from '@metamask/earn-controller';
6-
import { EarnControllerInitMessenger } from '../messengers/earn-controller-messenger';
76

87
/**
98
* Initialize the earn controller.
@@ -14,20 +13,19 @@ import { EarnControllerInitMessenger } from '../messengers/earn-controller-messe
1413
*/
1514
export const earnControllerInit: ControllerInitFunction<
1615
EarnController,
17-
EarnControllerMessenger,
18-
EarnControllerInitMessenger
19-
> = ({ controllerMessenger, initMessenger, getController }) => {
20-
const networkState = initMessenger.call('NetworkController:getState');
16+
EarnControllerMessenger
17+
> = ({ controllerMessenger, getController }) => {
2118
const transactionController = getController('TransactionController');
2219

2320
const controller = new EarnController({
2421
messenger: controllerMessenger,
2522
addTransactionFn: transactionController.addTransaction.bind(
2623
transactionController,
2724
),
28-
selectedNetworkClientId: networkState.selectedNetworkClientId,
2925
});
3026

27+
controller.init();
28+
3129
return {
3230
controller,
3331
};

app/core/Engine/messengers/earn-controller-messenger.test.ts

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,12 @@ import {
55
MOCK_ANY_NAMESPACE,
66
type MockAnyNamespace,
77
} from '@metamask/messenger';
8-
import {
9-
getEarnControllerMessenger,
10-
getEarnControllerInitMessenger,
11-
EarnControllerInitMessenger,
12-
} from './earn-controller-messenger';
8+
import { getEarnControllerMessenger } from './earn-controller-messenger';
139
import { EarnControllerMessenger } from '@metamask/earn-controller';
1410

1511
type RootMessenger = Messenger<
1612
MockAnyNamespace,
17-
| MessengerActions<EarnControllerMessenger>
18-
| MessengerActions<EarnControllerInitMessenger>,
13+
MessengerActions<EarnControllerMessenger>,
1914
MessengerEvents<EarnControllerMessenger>
2015
>;
2116

@@ -33,13 +28,3 @@ describe('getEarnControllerMessenger', () => {
3328
expect(earnControllerMessenger).toBeInstanceOf(Messenger);
3429
});
3530
});
36-
37-
describe('getEarnControllerInitMessenger', () => {
38-
it('returns a messenger', () => {
39-
const rootMessenger: RootMessenger = getRootMessenger();
40-
const earnControllerInitMessenger =
41-
getEarnControllerInitMessenger(rootMessenger);
42-
43-
expect(earnControllerInitMessenger).toBeInstanceOf(Messenger);
44-
});
45-
});

app/core/Engine/messengers/earn-controller-messenger.ts

Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import {
44
MessengerEvents,
55
} from '@metamask/messenger';
66
import { EarnControllerMessenger } from '@metamask/earn-controller';
7-
import { NetworkControllerGetStateAction } from '@metamask/network-controller';
87
import { RootMessenger } from '../types';
98

109
/**
@@ -28,10 +27,12 @@ export function getEarnControllerMessenger(
2827
});
2928
rootMessenger.delegate({
3029
actions: [
30+
'NetworkController:getState',
3131
'NetworkController:getNetworkClientById',
3232
'AccountTreeController:getAccountsFromSelectedAccountGroup',
3333
],
3434
events: [
35+
'AccountTreeController:stateChange',
3536
'AccountTreeController:selectedAccountGroupChange',
3637
'TransactionController:transactionConfirmed',
3738
'NetworkController:networkDidChange',
@@ -40,34 +41,3 @@ export function getEarnControllerMessenger(
4041
});
4142
return messenger;
4243
}
43-
44-
type AllowedInitializationActions = NetworkControllerGetStateAction;
45-
46-
export type EarnControllerInitMessenger = ReturnType<
47-
typeof getEarnControllerInitMessenger
48-
>;
49-
50-
/**
51-
* Get a messenger restricted to the actions and events that the
52-
* earn controller initialization is allowed to handle.
53-
*
54-
* @param messenger - The controller messenger to restrict.
55-
* @returns The restricted controller messenger.
56-
*/
57-
export function getEarnControllerInitMessenger(rootMessenger: RootMessenger) {
58-
const messenger = new Messenger<
59-
'EarnControllerInitialization',
60-
AllowedInitializationActions,
61-
never,
62-
RootMessenger
63-
>({
64-
namespace: 'EarnControllerInitialization',
65-
parent: rootMessenger,
66-
});
67-
rootMessenger.delegate({
68-
actions: ['NetworkController:getState'],
69-
events: [],
70-
messenger,
71-
});
72-
return messenger;
73-
}

app/core/Engine/messengers/index.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,7 @@ import {
111111
getUserStorageControllerInitMessenger,
112112
} from './identity/user-storage-controller-messenger';
113113
import { getAuthenticationControllerMessenger } from './identity/authentication-controller-messenger';
114-
import {
115-
getEarnControllerInitMessenger,
116-
getEarnControllerMessenger,
117-
} from './earn-controller-messenger';
114+
import { getEarnControllerMessenger } from './earn-controller-messenger';
118115
import { getGeolocationApiServiceMessenger } from './geolocation-api-service-messenger';
119116
import { getGeolocationControllerMessenger } from './geolocation-controller-messenger';
120117
import { getRewardsDataServiceMessenger } from './rewards-data-service-messenger';
@@ -194,7 +191,7 @@ export const CONTROLLER_MESSENGERS = {
194191
},
195192
EarnController: {
196193
getMessenger: getEarnControllerMessenger,
197-
getInitMessenger: getEarnControllerInitMessenger,
194+
getInitMessenger: noop,
198195
},
199196
GeolocationApiService: {
200197
getMessenger: getGeolocationApiServiceMessenger,

app/util/logs/__snapshots__/index.test.ts.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ exports[`logs :: generateStateLogs Sanitized SeedlessOnboardingController State
145145
"pooled_staking": {
146146
"isEligible": false,
147147
},
148+
"tron_staking": null,
148149
},
149150
"GasFeeController": {
150151
"estimatedGasFeeTimeBounds": {},
@@ -967,6 +968,7 @@ exports[`logs :: generateStateLogs generates a valid json export 1`] = `
967968
"pooled_staking": {
968969
"isEligible": false,
969970
},
971+
"tron_staking": null,
970972
},
971973
"GasFeeController": {
972974
"estimatedGasFeeTimeBounds": {},

app/util/test/initial-background-state.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -695,7 +695,8 @@
695695
}
696696
],
697697
"isEligible": false
698-
}
698+
},
699+
"tron_staking": null
699700
},
700701
"DeFiPositionsController": {
701702
"allDeFiPositions": {},

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@
239239
"@metamask/design-system-react-native": "^0.12.0",
240240
"@metamask/design-system-twrnc-preset": "^0.3.0",
241241
"@metamask/design-tokens": "^8.2.2",
242-
"@metamask/earn-controller": "^10.0.0",
242+
"@metamask/earn-controller": "^12.0.0",
243243
"@metamask/eip-5792-middleware": "^2.0.0",
244244
"@metamask/eip1193-permission-middleware": "^1.0.2",
245245
"@metamask/ens-resolver-snap": "^1.1.0",

0 commit comments

Comments
 (0)