diff --git a/packages/transaction-controller/CHANGELOG.md b/packages/transaction-controller/CHANGELOG.md index a6351b05d3..3596dabb36 100644 --- a/packages/transaction-controller/CHANGELOG.md +++ b/packages/transaction-controller/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Bump `@metamask/accounts-controller` from `^39.0.2` to `^39.0.3` ([#9231](https://github.com/MetaMask/core/pull/9231)) +### Fixed + +- Add explicit error when submitting a batch that requires EIP-7702 but the account or chain does not support it ([#9240](https://github.com/MetaMask/core/pull/9240)) + ## [68.1.1] ### Changed diff --git a/packages/transaction-controller/src/utils/batch.test.ts b/packages/transaction-controller/src/utils/batch.test.ts index e6527ffd73..d0bdf8c06e 100644 --- a/packages/transaction-controller/src/utils/batch.test.ts +++ b/packages/transaction-controller/src/utils/batch.test.ts @@ -630,6 +630,48 @@ describe('Batch Utils', () => { ).toBe(TransactionType.bridgeApproval); }); + it('throws if EIP-7702 is required but the account does not support it', async () => { + const publishBatchHook: jest.MockedFn = jest.fn(); + + doesAccountSupportEIP7702Mock.mockReturnValueOnce(false); + + await expect( + addTransactionBatch({ + ...request, + publishBatchHook, + request: { + ...request.request, + disableHook: true, + disableSequential: true, + }, + }), + ).rejects.toThrow('Account does not support EIP-7702'); + + expect(addTransactionMock).not.toHaveBeenCalled(); + expect(publishBatchHook).not.toHaveBeenCalled(); + }); + + it('throws if EIP-7702 is required but the chain does not support it', async () => { + const publishBatchHook: jest.MockedFn = jest.fn(); + + doesChainSupportEIP7702Mock.mockReturnValueOnce(false); + + await expect( + addTransactionBatch({ + ...request, + publishBatchHook, + request: { + ...request.request, + disableHook: true, + disableSequential: true, + }, + }), + ).rejects.toThrow('Chain does not support EIP-7702'); + + expect(addTransactionMock).not.toHaveBeenCalled(); + expect(publishBatchHook).not.toHaveBeenCalled(); + }); + it('returns provided batch ID', async () => { isAccountUpgradedToEIP7702Mock.mockResolvedValueOnce({ delegationAddress: undefined, diff --git a/packages/transaction-controller/src/utils/batch.ts b/packages/transaction-controller/src/utils/batch.ts index c5a2017460..26592ff057 100644 --- a/packages/transaction-controller/src/utils/batch.ts +++ b/packages/transaction-controller/src/utils/batch.ts @@ -127,6 +127,10 @@ export async function addTransactionBatch( messenger, request: transactionBatchRequest, } = request; + + const { disableHook, disable7702, disableSequential } = + transactionBatchRequest; + const sizeLimit = getBatchSizeLimit(messenger); validateBatchRequest({ @@ -143,7 +147,11 @@ export async function addTransactionBatch( transactionBatchRequest.from, ); - if (!transactionBatchRequest.disable7702 && accountCanUse7702) { + if (disableHook && disableSequential && !disable7702 && !accountCanUse7702) { + throw rpcErrors.internal('Account does not support EIP-7702'); + } + + if (!disable7702 && accountCanUse7702) { try { return await addTransactionBatchWith7702(request); } catch (error: unknown) { @@ -151,7 +159,7 @@ export async function addTransactionBatch( error instanceof JsonRpcError && error.message === 'Chain does not support EIP-7702'; - if (!isEIP7702NotSupportedError) { + if (!isEIP7702NotSupportedError || (disableHook && disableSequential)) { throw error; } } diff --git a/packages/transaction-pay-controller/CHANGELOG.md b/packages/transaction-pay-controller/CHANGELOG.md index 8512fc65d4..c575ca29f4 100644 --- a/packages/transaction-pay-controller/CHANGELOG.md +++ b/packages/transaction-pay-controller/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Bump `@metamask/assets-controllers` from `^109.2.1` to `^109.2.2` ([#9231](https://github.com/MetaMask/core/pull/9231)) +### Fixed + +- Require EIP-7702 for direct mUSD vault deposits and ensure transaction ID collection always stops after the batch attempt ([#9240](https://github.com/MetaMask/core/pull/9240)) + ## [23.14.0] ### Changed diff --git a/packages/transaction-pay-controller/src/strategy/fiat/fiat-direct-musd.test.ts b/packages/transaction-pay-controller/src/strategy/fiat/fiat-direct-musd.test.ts index 68e08b5dc0..b69b25e271 100644 --- a/packages/transaction-pay-controller/src/strategy/fiat/fiat-direct-musd.test.ts +++ b/packages/transaction-pay-controller/src/strategy/fiat/fiat-direct-musd.test.ts @@ -464,7 +464,8 @@ describe('fiat-direct-musd', () => { ).rejects.toThrow('Missing nested transactions'); }); - it('prefixes addTransactionBatch errors with Vault', async () => { + it('prefixes addTransactionBatch errors with Vault and stops collecting transaction IDs', async () => { + const endMock = jest.fn(); const callMock = jest.fn((action: string) => { if (action === 'TransactionPayController:getAmountData') { return Promise.resolve({ @@ -478,6 +479,7 @@ describe('fiat-direct-musd', () => { throw new Error(`Unexpected action: ${action}`); }); + collectTransactionIdsMock.mockReturnValue({ end: endMock }); await expect( submitDirectMusdVaultDeposit({ @@ -486,6 +488,7 @@ describe('fiat-direct-musd', () => { transaction: TRANSACTION_MOCK, }), ).rejects.toThrow('Vault: batch failed'); + expect(endMock).toHaveBeenCalledTimes(1); }); it('throws when no vault transactions are collected', async () => { diff --git a/packages/transaction-pay-controller/src/strategy/fiat/fiat-direct-musd.ts b/packages/transaction-pay-controller/src/strategy/fiat/fiat-direct-musd.ts index 9fedef9202..6c75bb26cd 100644 --- a/packages/transaction-pay-controller/src/strategy/fiat/fiat-direct-musd.ts +++ b/packages/transaction-pay-controller/src/strategy/fiat/fiat-direct-musd.ts @@ -286,6 +286,8 @@ export async function submitDirectMusdVaultDeposit({ try { await messenger.call('TransactionController:addTransactionBatch', { + disableHook: true, + disableSequential: true, from: moneyAccountAddress, isGasFeeSponsored: true, isInternal: true, @@ -306,10 +308,10 @@ export async function submitDirectMusdVaultDeposit({ }); } catch (error) { throw prefixError(error, VAULT_ERROR_PREFIX); + } finally { + end(); } - end(); - log('Submitted direct mUSD vault deposit', { moneyAccountAddress, nestedTransactionCount: nestedTransactions.length,