Skip to content

Commit 4644e1e

Browse files
authored
fix: cp-12.16.0 Follow up on the publishBatch hook (#31401)
## **Description** - This PR is mainly refactoring based on the feedback in the [main PR](#31267) that was merged + it updates the STX controller to the latest version. [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/31401?quickstart=1) ## **Related issues** Fixes: ## **Manual testing steps** 1. Have STX enabled 2. Be on Ethereum mainnet 3. Go to the Send feature 4. You will be able to choose your gas token if you have enough balance for it and its a supported token ## **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. --------- Signed-off-by: dan437 <[email protected]>
1 parent 12505e7 commit 4644e1e

File tree

6 files changed

+76
-62
lines changed

6 files changed

+76
-62
lines changed

app/scripts/controller-init/confirmations/transaction-controller-init.ts

+31-24
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,26 @@ function getControllers(
224224
};
225225
}
226226

227+
function getSmartTransactionCommonParams(flatState: ControllerFlatState) {
228+
// UI state is required to support shared selectors to avoid duplicate logic in frontend and backend.
229+
// Ideally all backend logic would instead rely on messenger event / state subscriptions.
230+
const uiState = getUIState(flatState);
231+
232+
// @ts-expect-error Smart transaction selector types does not match controller state
233+
const isSmartTransaction = getIsSmartTransaction(uiState);
234+
235+
// @ts-expect-error Smart transaction selector types does not match controller state
236+
const featureFlags = getFeatureFlagsByChainId(uiState);
237+
238+
const isHardwareWalletAccount = isHardwareWallet(uiState);
239+
240+
return {
241+
isSmartTransaction,
242+
featureFlags,
243+
isHardwareWalletAccount,
244+
};
245+
}
246+
227247
function publishSmartTransactionHook(
228248
transactionController: TransactionController,
229249
smartTransactionsController: SmartTransactionsController,
@@ -232,29 +252,22 @@ function publishSmartTransactionHook(
232252
transactionMeta: TransactionMeta,
233253
signedTransactionInHex: Hex,
234254
) {
235-
// UI state is required to support shared selectors to avoid duplicate logic in frontend and backend.
236-
// Ideally all backend logic would instead rely on messenger event / state subscriptions.
237-
const uiState = getUIState(flatState);
238-
239-
// @ts-expect-error Smart transaction selector types does not match controller state
240-
const isSmartTransaction = getIsSmartTransaction(uiState);
255+
const { isSmartTransaction, featureFlags, isHardwareWalletAccount } =
256+
getSmartTransactionCommonParams(flatState);
241257

242258
if (!isSmartTransaction) {
243259
// Will cause TransactionController to publish to the RPC provider as normal.
244260
return { transactionHash: undefined };
245261
}
246262

247-
// @ts-expect-error Smart transaction selector types does not match controller state
248-
const featureFlags = getFeatureFlagsByChainId(uiState);
249-
250263
return submitSmartTransactionHook({
251264
transactionMeta,
252265
signedTransactionInHex,
253266
transactionController,
254267
smartTransactionsController,
255268
controllerMessenger: hookControllerMessenger,
256269
isSmartTransaction,
257-
isHardwareWallet: isHardwareWallet(uiState),
270+
isHardwareWallet: isHardwareWalletAccount,
258271
// @ts-expect-error Smart transaction selector return type does not match FeatureFlags type from hook
259272
featureFlags,
260273
});
@@ -273,21 +286,16 @@ function publishBatchSmartTransactionHook({
273286
flatState: ControllerFlatState;
274287
transactions: PublishBatchHookTransaction[];
275288
}) {
276-
// UI state is required to support shared selectors to avoid duplicate logic in frontend and backend.
277-
// Ideally all backend logic would instead rely on messenger event / state subscriptions.
278-
const uiState = getUIState(flatState);
279-
280-
// @ts-expect-error Smart transaction selector types does not match controller state
281-
const isSmartTransaction = getIsSmartTransaction(uiState);
289+
const { isSmartTransaction, featureFlags, isHardwareWalletAccount } =
290+
getSmartTransactionCommonParams(flatState);
282291

283292
if (!isSmartTransaction) {
284293
// Will cause TransactionController to publish to the RPC provider as normal.
285-
return undefined;
294+
throw new Error(
295+
'publishBatchSmartTransactionHook: Smart Transaction is required for batch submissions',
296+
);
286297
}
287298

288-
// @ts-expect-error Smart transaction selector types does not match controller state
289-
const featureFlags = getFeatureFlagsByChainId(uiState);
290-
291299
// Get transactionMeta based on the last transaction ID
292300
const lastTransaction = transactions[transactions.length - 1];
293301
const transactionMeta = getTransactionById(
@@ -297,10 +305,9 @@ function publishBatchSmartTransactionHook({
297305

298306
// If we couldn't find the transaction, we should handle that gracefully
299307
if (!transactionMeta) {
300-
console.warn(
301-
`Publish batch hook: could not find transaction with id ${lastTransaction.id}`,
308+
throw new Error(
309+
`publishBatchSmartTransactionHook: Could not find transaction with id ${lastTransaction.id}`,
302310
);
303-
return undefined;
304311
}
305312

306313
return submitBatchSmartTransactionHook({
@@ -309,7 +316,7 @@ function publishBatchSmartTransactionHook({
309316
smartTransactionsController,
310317
controllerMessenger: hookControllerMessenger,
311318
isSmartTransaction,
312-
isHardwareWallet: isHardwareWallet(uiState),
319+
isHardwareWallet: isHardwareWalletAccount,
313320
// @ts-expect-error Smart transaction selector return type does not match FeatureFlags type from hook
314321
featureFlags,
315322
transactionMeta,

app/scripts/controller-init/messengers/transaction-controller-messenger.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -98,11 +98,11 @@ export function getTransactionControllerInitMessenger(
9898
'SmartTransactionsController:smartTransaction',
9999
],
100100
allowedActions: [
101+
'ApprovalController:acceptRequest',
101102
'ApprovalController:addRequest',
102103
'ApprovalController:endFlow',
103104
'ApprovalController:startFlow',
104105
'ApprovalController:updateRequestState',
105-
'ApprovalController:acceptRequest',
106106
'NetworkController:getEIP1559Compatibility',
107107
'RemoteFeatureFlagController:getState',
108108
'SwapsController:setApproveTxId',

app/scripts/lib/transaction/smart-transactions.test.ts

+16-8
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,13 @@ function withRequest<ReturnValue>(
7979
const startFlowSpy = jest.fn().mockResolvedValue({ id: 'approvalId' });
8080
messenger.registerActionHandler('ApprovalController:startFlow', startFlowSpy);
8181

82-
const addRequestSpy = jest.fn().mockImplementation(() => ({
83-
then: (callback: () => void) => {
84-
addRequestCallback = callback;
85-
},
86-
}));
82+
const addRequestSpy = jest.fn().mockImplementation(() => {
83+
return Promise.resolve().then(() => {
84+
if (typeof addRequestCallback === 'function') {
85+
addRequestCallback();
86+
}
87+
});
88+
});
8789
messenger.registerActionHandler(
8890
'ApprovalController:addRequest',
8991
addRequestSpy,
@@ -142,7 +144,8 @@ function withRequest<ReturnValue>(
142144
txParams: {
143145
from: addressFrom,
144146
to: '0x1678a085c290ebd122dc42cba69373b5953b831d',
145-
gasPrice: '0x77359400',
147+
maxFeePerGas: '0x2fd8a58d7',
148+
maxPriorityFeePerGas: '0xaa0f8a94',
146149
gas: '0x7b0d',
147150
nonce: '0x4b',
148151
},
@@ -551,6 +554,7 @@ describe('submitSmartTransactionHook', () => {
551554

552555
const endFlowSpy = jest.fn();
553556
const acceptRequestSpy = jest.fn();
557+
const addRequestSpy = jest.fn(() => Promise.resolve());
554558

555559
// Create a mock messenger
556560
const mockMessenger = {
@@ -564,6 +568,9 @@ describe('submitSmartTransactionHook', () => {
564568
if (method === 'ApprovalController:acceptRequest') {
565569
return acceptRequestSpy(...args);
566570
}
571+
if (method === 'ApprovalController:addRequest') {
572+
return addRequestSpy();
573+
}
567574
return undefined;
568575
}),
569576
subscribe: jest.fn(),
@@ -631,8 +638,9 @@ describe('submitBatchSmartTransactionHook', () => {
631638
},
632639
},
633640
async ({ request }) => {
634-
const result = await submitBatchSmartTransactionHook(request);
635-
expect(result).toEqual({ results: [] });
641+
await expect(submitBatchSmartTransactionHook(request)).rejects.toThrow(
642+
'submitBatch: Smart Transaction is required for batch submissions',
643+
);
636644
},
637645
);
638646
});

app/scripts/lib/transaction/smart-transactions.ts

+22-23
Original file line numberDiff line numberDiff line change
@@ -217,11 +217,11 @@ class SmartTransactionHook {
217217
}
218218

219219
async submitBatch() {
220-
// Will cause TransactionController to publish to the RPC provider as normal.
221-
const useRegularTransactionSubmit = undefined;
222-
220+
// No fallback to regular transaction submission
223221
if (!this.#isSmartTransaction) {
224-
return useRegularTransactionSubmit;
222+
throw new Error(
223+
'submitBatch: Smart Transaction is required for batch submissions',
224+
);
225225
}
226226

227227
if (this.#shouldShowStatusPage) {
@@ -233,7 +233,7 @@ class SmartTransactionHook {
233233
const uuid = submitTransactionResponse?.uuid;
234234

235235
if (!uuid) {
236-
throw new Error('No smart transaction UUID');
236+
throw new Error('submitBatch: No smart transaction UUID');
237237
}
238238

239239
await this.#processApprovalIfNeeded(uuid);
@@ -244,7 +244,7 @@ class SmartTransactionHook {
244244

245245
if (transactionHash === null) {
246246
throw new Error(
247-
'Transaction does not have a transaction hash, there was a problem',
247+
'submitBatch: Transaction does not have a transaction hash, there was a problem',
248248
);
249249
}
250250

@@ -263,24 +263,29 @@ class SmartTransactionHook {
263263

264264
return submitBatchResponse;
265265
} catch (error) {
266-
log.error('Error in smart transaction publish batch hook', error);
266+
log.error(
267+
'submitBatch: Error in smart transaction publish batch hook',
268+
error,
269+
);
267270
this.#onApproveOrReject();
268271
throw error;
269272
}
270273
}
271274

272-
/**
273-
* Ends an existing approval flow and clears the shared approval flow ID
274-
*
275-
* @param approvalFlowId - The ID of the approval flow to end
276-
* @returns Promise that resolves when the flow is successfully ended or errors are handled
277-
*/
278-
async #endExistingApprovalFlow(approvalFlowId: string): Promise<void> {
275+
async #endApprovalFlow(flowId: string): Promise<void> {
279276
try {
280-
// End the existing flow
281277
await this.#controllerMessenger.call('ApprovalController:endFlow', {
282-
id: approvalFlowId,
278+
id: flowId,
283279
});
280+
} catch (error) {
281+
// If the flow is already ended, we can ignore the error.
282+
}
283+
}
284+
285+
async #endExistingApprovalFlow(approvalFlowId: string): Promise<void> {
286+
try {
287+
// End the existing flow
288+
await this.#endApprovalFlow(approvalFlowId);
284289

285290
// Accept the request to close the UI
286291
await this.#controllerMessenger.call(
@@ -327,13 +332,7 @@ class SmartTransactionHook {
327332
return;
328333
}
329334
this.#approvalFlowEnded = true;
330-
try {
331-
this.#controllerMessenger.call('ApprovalController:endFlow', {
332-
id: this.#approvalFlowId,
333-
});
334-
} catch (error) {
335-
// If the flow is already ended, we can ignore the error.
336-
}
335+
this.#endApprovalFlow(this.#approvalFlowId);
337336

338337
// Clear the shared approval flow ID when we end the flow
339338
if (SmartTransactionHook.#sharedApprovalFlowId === this.#approvalFlowId) {

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@
315315
"@metamask/scure-bip39": "^2.0.3",
316316
"@metamask/selected-network-controller": "^19.0.0",
317317
"@metamask/signature-controller": "^26.0.0",
318-
"@metamask/smart-transactions-controller": "^16.2.0",
318+
"@metamask/smart-transactions-controller": "^16.3.0",
319319
"@metamask/snaps-controllers": "^11.1.0",
320320
"@metamask/snaps-execution-environments": "^7.1.0",
321321
"@metamask/snaps-rpc-methods": "^12.0.0",

yarn.lock

+5-5
Original file line numberDiff line numberDiff line change
@@ -6344,9 +6344,9 @@ __metadata:
63446344
languageName: node
63456345
linkType: hard
63466346

6347-
"@metamask/smart-transactions-controller@npm:^16.2.0":
6348-
version: 16.2.0
6349-
resolution: "@metamask/smart-transactions-controller@npm:16.2.0"
6347+
"@metamask/smart-transactions-controller@npm:^16.3.0":
6348+
version: 16.3.0
6349+
resolution: "@metamask/smart-transactions-controller@npm:16.3.0"
63506350
dependencies:
63516351
"@babel/runtime": "npm:^7.24.1"
63526352
"@ethereumjs/tx": "npm:^5.2.1"
@@ -6368,7 +6368,7 @@ __metadata:
63686368
optional: true
63696369
"@metamask/approval-controller":
63706370
optional: true
6371-
checksum: 10/3cf74a5ab01ce62e32d0c006d1519bdaccf3252636d83be9537c915caba049e151d981b74a58e4a1122defcf6a1d13d689d53b8f3cbbc31870d2896fa43a5b31
6371+
checksum: 10/0dc4fc7ce58c45af6598b8209646e8ce1eff7f31fc3fd7abe6ceae5d3ead9e4eafa7445eff6ecdc2b606fea9af30658adf5e2e15fce3bdbfdcee41bceb31795e
63726372
languageName: node
63736373
linkType: hard
63746374

@@ -27376,7 +27376,7 @@ __metadata:
2737627376
"@metamask/scure-bip39": "npm:^2.0.3"
2737727377
"@metamask/selected-network-controller": "npm:^19.0.0"
2737827378
"@metamask/signature-controller": "npm:^26.0.0"
27379-
"@metamask/smart-transactions-controller": "npm:^16.2.0"
27379+
"@metamask/smart-transactions-controller": "npm:^16.3.0"
2738027380
"@metamask/snaps-controllers": "npm:^11.1.0"
2738127381
"@metamask/snaps-execution-environments": "npm:^7.1.0"
2738227382
"@metamask/snaps-rpc-methods": "npm:^12.0.0"

0 commit comments

Comments
 (0)