Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@metamask/core-monorepo",
"version": "373.0.0",
"version": "374.0.0",
"private": true,
"description": "Monorepo for packages shared between MetaMask clients",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion packages/assets-controllers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
"@metamask/providers": "^21.0.0",
"@metamask/snaps-controllers": "^11.2.1",
"@metamask/snaps-sdk": "^6.22.0",
"@metamask/transaction-controller": "^54.1.0",
"@metamask/transaction-controller": "^54.2.0",
"@types/jest": "^27.4.1",
"@types/lodash": "^4.14.191",
"@types/node": "^16.18.54",
Expand Down
2 changes: 1 addition & 1 deletion packages/bridge-controller/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
"@metamask/network-controller": "^23.2.0",
"@metamask/snaps-controllers": "^11.2.1",
"@metamask/superstruct": "^3.1.0",
"@metamask/transaction-controller": "^54.1.0",
"@metamask/transaction-controller": "^54.2.0",
"@types/jest": "^27.4.1",
"deepmerge": "^4.2.2",
"jest": "^27.5.1",
Expand Down
2 changes: 1 addition & 1 deletion packages/bridge-status-controller/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
"@metamask/gas-fee-controller": "^23.0.0",
"@metamask/network-controller": "^23.2.0",
"@metamask/snaps-controllers": "^11.2.1",
"@metamask/transaction-controller": "^54.1.0",
"@metamask/transaction-controller": "^54.2.0",
"@types/jest": "^27.4.1",
"deepmerge": "^4.2.2",
"jest": "^27.5.1",
Expand Down
2 changes: 1 addition & 1 deletion packages/earn-controller/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
"@metamask/accounts-controller": "^27.0.0",
"@metamask/auto-changelog": "^3.4.4",
"@metamask/network-controller": "^23.2.0",
"@metamask/transaction-controller": "^54.1.0",
"@metamask/transaction-controller": "^54.2.0",
"@types/jest": "^27.4.1",
"deepmerge": "^4.2.2",
"jest": "^27.5.1",
Expand Down
15 changes: 14 additions & 1 deletion packages/transaction-controller/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [54.2.0]

### Added

- Add optional `afterAdd` hook to constructor ([#5692](https://github.com/MetaMask/core/pull/5692))
- Add optional `txParamsOriginal` property to `TransactionMeta`.
- Add `AfterAddHook` type.

### Fixed

- Handle errors in `isAtomicBatchSupported` method ([#5704](https://github.com/MetaMask/core/pull/5704))

## [54.1.0]

### Changed
Expand Down Expand Up @@ -1520,7 +1532,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

All changes listed after this point were applied to this package following the monorepo conversion.

[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@54.1.0...HEAD
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@54.2.0...HEAD
[54.2.0]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@54.1.0...@metamask/transaction-controller@54.2.0
[54.1.0]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@54.0.0...@metamask/transaction-controller@54.1.0
[54.0.0]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@53.0.0...@metamask/transaction-controller@54.0.0
[53.0.0]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@52.3.0...@metamask/transaction-controller@53.0.0
Expand Down
2 changes: 1 addition & 1 deletion packages/transaction-controller/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@metamask/transaction-controller",
"version": "54.1.0",
"version": "54.2.0",
"description": "Stores transactions alongside their periodically updated statuses and manages interactions such as approval and cancellation",
"keywords": [
"MetaMask",
Expand Down
153 changes: 117 additions & 36 deletions packages/transaction-controller/src/TransactionController.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
/* eslint-disable jest/expect-expect */
import type { TypedTransaction } from '@ethereumjs/tx';
import { TransactionFactory } from '@ethereumjs/tx';
import type {
AddApprovalRequest,
Expand Down Expand Up @@ -548,6 +547,7 @@ describe('TransactionController', () => {
let gasFeePollerMock: jest.Mocked<GasFeePoller>;
let methodDataHelperMock: jest.Mocked<MethodDataHelper>;
let timeCounter = 0;
let signMock: jest.Mock;

const incomingTransactionHelperClassMock =
IncomingTransactionHelper as jest.MockedClass<
Expand Down Expand Up @@ -671,7 +671,7 @@ describe('TransactionController', () => {
mockNetworkClientConfigurationsByNetworkClientId as any,
getPermittedAccounts: async () => [ACCOUNT_MOCK],
hooks: {},
sign: async (transaction: TypedTransaction) => transaction,
sign: signMock,
transactionHistoryLimit: 40,
...givenOptions,
};
Expand Down Expand Up @@ -967,6 +967,8 @@ describe('TransactionController', () => {
getAccountAddressRelationshipMock.mockResolvedValue({
count: 1,
});

signMock = jest.fn().mockImplementation(async (transaction) => transaction);
});

describe('constructor', () => {
Expand Down Expand Up @@ -1416,7 +1418,6 @@ describe('TransactionController', () => {
expectedSignCalledTimes,
) => {
const { controller } = setupController();
const signSpy = jest.spyOn(controller, 'sign');

const { transactionMeta } = await controller.addTransaction(
{
Expand All @@ -1441,7 +1442,7 @@ describe('TransactionController', () => {

const { transactions } = controller.state;
expect(transactions).toHaveLength(expectedTransactionCount);
expect(signSpy).toHaveBeenCalledTimes(expectedSignCalledTimes);
expect(signMock).toHaveBeenCalledTimes(expectedSignCalledTimes);
},
);
});
Expand Down Expand Up @@ -2072,6 +2073,98 @@ describe('TransactionController', () => {
);
});

describe('with afterAdd hook', () => {
it('calls afterAdd hook', async () => {
const afterAddHook = jest.fn().mockResolvedValueOnce({});

const { controller } = setupController({
options: {
hooks: {
afterAdd: afterAddHook,
},
},
});

await controller.addTransaction(
{
from: ACCOUNT_MOCK,
to: ACCOUNT_MOCK,
},
{
networkClientId: NETWORK_CLIENT_ID_MOCK,
},
);

expect(afterAddHook).toHaveBeenCalledTimes(1);
});

it('updates transaction if update callback returned', async () => {
const updateTransactionMock = jest.fn();

const afterAddHook = jest
.fn()
.mockResolvedValueOnce({ updateTransaction: updateTransactionMock });

const { controller } = setupController({
options: {
hooks: {
afterAdd: afterAddHook,
},
},
});

await controller.addTransaction(
{
from: ACCOUNT_MOCK,
to: ACCOUNT_MOCK,
},
{
networkClientId: NETWORK_CLIENT_ID_MOCK,
},
);

expect(updateTransactionMock).toHaveBeenCalledTimes(1);
expect(updateTransactionMock).toHaveBeenCalledWith(
expect.objectContaining({
id: expect.any(String),
}),
);
});

it('saves original transaction params if update callback returned', async () => {
const updateTransactionMock = jest.fn();

const afterAddHook = jest
.fn()
.mockResolvedValueOnce({ updateTransaction: updateTransactionMock });

const { controller } = setupController({
options: {
hooks: {
afterAdd: afterAddHook,
},
},
});

await controller.addTransaction(
{
from: ACCOUNT_MOCK,
to: ACCOUNT_MOCK,
},
{
networkClientId: NETWORK_CLIENT_ID_MOCK,
},
);

expect(controller.state.transactions[0].txParamsOriginal).toStrictEqual(
expect.objectContaining({
from: ACCOUNT_MOCK,
to: ACCOUNT_MOCK,
}),
);
});
});

describe('updates simulation data', () => {
it('by default', async () => {
getSimulationDataMock.mockResolvedValueOnce(
Expand Down Expand Up @@ -2457,8 +2550,6 @@ describe('TransactionController', () => {
const { controller, mockTransactionApprovalRequest } =
setupController();

const signSpy = jest.spyOn(controller, 'sign');

const { result, transactionMeta } = await controller.addTransaction(
{
from: ACCOUNT_MOCK,
Expand All @@ -2483,7 +2574,7 @@ describe('TransactionController', () => {

await result;

expect(signSpy).not.toHaveBeenCalled();
expect(signMock).not.toHaveBeenCalled();

expect(controller.state.transactions).toMatchObject([
expect.objectContaining({
Expand Down Expand Up @@ -3308,17 +3399,13 @@ describe('TransactionController', () => {
});

it('rejects unknown transaction', async () => {
const { controller } = setupController({
network: MOCK_LINEA_SEPOLIA_NETWORK,
});
const { controller } = setupController();

await controller.stopTransaction('transactionIdMock', {
gasPrice: '0x1',
});

const signSpy = jest.spyOn(controller, 'sign');

expect(signSpy).toHaveBeenCalledTimes(0);
expect(signMock).toHaveBeenCalledTimes(0);
});

it('throws if no sign method', async () => {
Expand Down Expand Up @@ -5192,16 +5279,12 @@ describe('TransactionController', () => {
});

it('signs transactions and return raw transactions', async () => {
const signMock = jest
.fn()
.mockImplementation(async (transactionParams) =>
Promise.resolve(TransactionFactory.fromTxData(transactionParams)),
);
const { controller } = setupController({
options: {
sign: signMock,
},
});
signMock.mockImplementation(async (transactionParams) =>
Promise.resolve(TransactionFactory.fromTxData(transactionParams)),
);

const { controller } = setupController();

const mockTransactionParam = {
from: ACCOUNT_MOCK,
nonce: '0x1',
Expand Down Expand Up @@ -5231,11 +5314,10 @@ describe('TransactionController', () => {
it('throws if error while signing transaction', async () => {
const mockSignError = 'Error while signing transaction';

const signMock = jest
.fn()
.mockImplementation(async () =>
Promise.reject(new Error(mockSignError)),
);
signMock.mockImplementation(async () =>
Promise.reject(new Error(mockSignError)),
);

const { controller } = setupController({
options: {
sign: signMock,
Expand Down Expand Up @@ -5357,7 +5439,7 @@ describe('TransactionController', () => {
},
},
});
const signSpy = jest.spyOn(controller, 'sign');

const updateTransactionSpy = jest.spyOn(controller, 'updateTransaction');

await controller.addTransaction(paramsMock, {
Expand All @@ -5373,7 +5455,7 @@ describe('TransactionController', () => {

const transactionMeta = controller.state.transactions[0];

expect(signSpy).toHaveBeenCalledTimes(1);
expect(signMock).toHaveBeenCalledTimes(1);

expect(transactionMeta.txParams).toStrictEqual(
expect.objectContaining(paramsMock),
Expand All @@ -5390,18 +5472,17 @@ describe('TransactionController', () => {
});

it('adds a transaction and signing returns undefined', async () => {
signMock.mockResolvedValue(undefined);

const { controller, mockTransactionApprovalRequest } = setupController({
options: {
hooks: {
afterSign: () => false,
beforePublish: () => Promise.resolve(false),
getAdditionalSignArguments: () => [metadataMock],
},
// @ts-expect-error sign intentionally returns undefined
sign: async () => undefined,
},
});
const signSpy = jest.spyOn(controller, 'sign');

await controller.addTransaction(paramsMock, {
origin: 'origin',
Expand All @@ -5414,7 +5495,7 @@ describe('TransactionController', () => {
});
await wait(0);

expect(signSpy).toHaveBeenCalledTimes(1);
expect(signMock).toHaveBeenCalledTimes(1);
});

it('adds a transaction, signs and skips publish the transaction', async () => {
Expand All @@ -5427,7 +5508,7 @@ describe('TransactionController', () => {
},
},
});
const signSpy = jest.spyOn(controller, 'sign');

const updateTransactionSpy = jest.spyOn(controller, 'updateTransaction');

await controller.addTransaction(paramsMock, {
Expand All @@ -5445,7 +5526,7 @@ describe('TransactionController', () => {
expect.objectContaining(paramsMock),
);

expect(signSpy).toHaveBeenCalledTimes(1);
expect(signMock).toHaveBeenCalledTimes(1);
expect(updateTransactionSpy).toHaveBeenCalledTimes(1);
expect(updateTransactionSpy).toHaveBeenCalledWith(
expect.objectContaining({
Expand Down
Loading
Loading