Skip to content

Commit ef5472a

Browse files
feat: detect incoming transactions using accounts API (#4927)
Remove polling on each new block via `PollingBlockTracker`. Polling will occur every 30 seconds and support multiple chains simultaneously. Remove `etherscan` util and `EtherscanRemoteTransactionSource`. Create `accounts-api` util and `AccountsApiRemoteTransactionSource`. Remove `ETHERSCAN_SUPPORTED_NETWORKS` constant. Replace block number based query optimisation with cursors. Use single instance of `IncomingTransactionHelper`. Remove `IncomingTransactionHelper` instance creation from `MultichainTrackingHelper`. Move caching logic inside `RemoteTransactionSource` via `getCache` and `updateCache` properties.
1 parent 3a7d680 commit ef5472a

21 files changed

+1257
-2766
lines changed

packages/transaction-controller/CHANGELOG.md

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

88
## [Unreleased]
99

10+
### Added
11+
12+
- Retrieve incoming transactions using Accounts API ([#4927](https://github.com/MetaMask/core/pull/4927))
13+
- Add `INCOMING_TRANSACTIONS_SUPPORTED_CHAIN_IDS` constant.
14+
15+
### Changed
16+
17+
- **BREAKING:** Retrieve incoming transactions using Accounts API ([#4927](https://github.com/MetaMask/core/pull/4927))
18+
- Rename `TransactionControllerIncomingTransactionBlockReceivedEvent` to `TransactionControllerIncomingTransactionsReceivedEvent`.
19+
- Replace `networkClientIds` argument with `chainIds` in following methods:
20+
- `startIncomingTransactionPolling`
21+
- `stopIncomingTransactionPolling`
22+
- `updateIncomingTransactions`
23+
24+
### Removed
25+
26+
- **BREAKING:** Retrieve incoming transactions using Accounts API ([#4927](https://github.com/MetaMask/core/pull/4927))
27+
- Remove `ETHERSCAN_SUPPORTED_NETWORKS` constant.
28+
- Remove types:
29+
- `EtherscanTransactionMeta`
30+
- `RemoteTransactionSource`
31+
- `RemoteTransactionSourceRequest`
32+
1033
## [41.1.0]
1134

1235
### Added

packages/transaction-controller/jest.config.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ module.exports = merge(baseConfig, {
1717
// An object that configures minimum threshold enforcement for coverage results
1818
coverageThreshold: {
1919
global: {
20-
branches: 93.23,
21-
functions: 97.61,
22-
lines: 98.26,
23-
statements: 98.28,
20+
branches: 91.76,
21+
functions: 94.76,
22+
lines: 96.83,
23+
statements: 96.82,
2424
},
2525
},
2626

packages/transaction-controller/src/TransactionController.test.ts

Lines changed: 50 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -657,11 +657,6 @@ describe('TransactionController', () => {
657657
(controller as any).update(() => state);
658658
}
659659

660-
multichainTrackingHelperClassMock.mock.calls[0][0].createIncomingTransactionHelper(
661-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
662-
{} as any,
663-
);
664-
665660
multichainTrackingHelperClassMock.mock.calls[0][0].createPendingTransactionTracker(
666661
// eslint-disable-next-line @typescript-eslint/no-explicit-any
667662
{} as any,
@@ -777,6 +772,8 @@ describe('TransactionController', () => {
777772
}
778773

779774
beforeEach(() => {
775+
jest.resetAllMocks();
776+
780777
jest.spyOn(Date, 'now').mockImplementation(() => {
781778
timeCounter += 1;
782779
return timeCounter;
@@ -2613,21 +2610,6 @@ describe('TransactionController', () => {
26132610
expect(controller.state.transactions).toHaveLength(1);
26142611
expect(controller.state.transactions[0].id).toBe('4');
26152612
});
2616-
});
2617-
2618-
describe('handleMethodData', () => {
2619-
it('invokes helper', async () => {
2620-
const { controller } = setupController();
2621-
2622-
methodDataHelperMock.lookup.mockResolvedValueOnce(METHOD_DATA_MOCK);
2623-
2624-
const result = await controller.handleMethodData(
2625-
'mockMethodData',
2626-
NETWORK_CLIENT_ID_MOCK,
2627-
);
2628-
2629-
expect(result).toStrictEqual(METHOD_DATA_MOCK);
2630-
});
26312613

26322614
it('updates state when helper emits update event', async () => {
26332615
const { controller } = setupController();
@@ -4106,41 +4088,13 @@ describe('TransactionController', () => {
41064088

41074089
// TODO: Replace `any` with type
41084090
// eslint-disable-next-line @typescript-eslint/no-explicit-any
4109-
await (incomingTransactionHelperMock.hub.on as any).mock.calls[0][1]({
4110-
added: [TRANSACTION_META_MOCK, TRANSACTION_META_2_MOCK],
4111-
updated: [],
4112-
});
4113-
4114-
expect(controller.state.transactions).toStrictEqual([
4115-
{ ...TRANSACTION_META_MOCK, networkClientId: NETWORK_CLIENT_ID_MOCK },
4116-
{ ...TRANSACTION_META_2_MOCK, networkClientId: NETWORK_CLIENT_ID_MOCK },
4091+
await (incomingTransactionHelperMock.hub.on as any).mock.calls[0][1]([
4092+
TRANSACTION_META_MOCK,
4093+
TRANSACTION_META_2_MOCK,
41174094
]);
4118-
});
4119-
4120-
it('updates existing transactions in state', async () => {
4121-
const { controller } = setupController({
4122-
options: {
4123-
state: {
4124-
transactions: [TRANSACTION_META_MOCK, TRANSACTION_META_2_MOCK],
4125-
},
4126-
},
4127-
});
4128-
4129-
const updatedTransaction = {
4130-
...TRANSACTION_META_MOCK,
4131-
networkClientId: NETWORK_CLIENT_ID_MOCK,
4132-
status: 'failed',
4133-
};
4134-
4135-
// TODO: Replace `any` with type
4136-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
4137-
await (incomingTransactionHelperMock.hub.on as any).mock.calls[0][1]({
4138-
added: [],
4139-
updated: [updatedTransaction],
4140-
});
41414095

41424096
expect(controller.state.transactions).toStrictEqual([
4143-
updatedTransaction,
4097+
{ ...TRANSACTION_META_MOCK, networkClientId: NETWORK_CLIENT_ID_MOCK },
41444098
{ ...TRANSACTION_META_2_MOCK, networkClientId: NETWORK_CLIENT_ID_MOCK },
41454099
]);
41464100
});
@@ -4152,58 +4106,74 @@ describe('TransactionController', () => {
41524106

41534107
// TODO: Replace `any` with type
41544108
// eslint-disable-next-line @typescript-eslint/no-explicit-any
4155-
await (incomingTransactionHelperMock.hub.on as any).mock.calls[0][1]({
4156-
added: [TRANSACTION_META_MOCK, TRANSACTION_META_2_MOCK],
4157-
updated: [],
4158-
});
4109+
await (incomingTransactionHelperMock.hub.on as any).mock.calls[0][1]([
4110+
TRANSACTION_META_MOCK,
4111+
TRANSACTION_META_2_MOCK,
4112+
]);
41594113

41604114
expect(controller.state.transactions).toStrictEqual([
41614115
{ ...TRANSACTION_META_2_MOCK, networkClientId: NETWORK_CLIENT_ID_MOCK },
41624116
]);
41634117
});
4164-
});
41654118

4166-
describe('on incoming transaction helper lastFetchedBlockNumbers event', () => {
4167-
it('updates state', async () => {
4168-
const { controller } = setupController();
4119+
it('publishes TransactionController:incomingTransactionsReceived', async () => {
4120+
const listener = jest.fn();
41694121

4170-
const lastFetchedBlockNumbers = {
4171-
key: 234,
4172-
};
4122+
const { messenger } = setupController();
4123+
messenger.subscribe(
4124+
'TransactionController:incomingTransactionsReceived',
4125+
listener,
4126+
);
41734127

41744128
// TODO: Replace `any` with type
41754129
// eslint-disable-next-line @typescript-eslint/no-explicit-any
4176-
await (incomingTransactionHelperMock.hub.on as any).mock.calls[1][1]({
4177-
lastFetchedBlockNumbers,
4178-
blockNumber: 123,
4179-
});
4130+
await (incomingTransactionHelperMock.hub.on as any).mock.calls[0][1]([
4131+
TRANSACTION_META_MOCK,
4132+
TRANSACTION_META_2_MOCK,
4133+
]);
41804134

4181-
expect(controller.state.lastFetchedBlockNumbers).toStrictEqual(
4182-
lastFetchedBlockNumbers,
4183-
);
4135+
expect(listener).toHaveBeenCalledTimes(1);
4136+
expect(listener).toHaveBeenCalledWith([
4137+
{ ...TRANSACTION_META_MOCK, networkClientId: NETWORK_CLIENT_ID_MOCK },
4138+
{ ...TRANSACTION_META_2_MOCK, networkClientId: NETWORK_CLIENT_ID_MOCK },
4139+
]);
41844140
});
41854141

4186-
it('publishes TransactionController:incomingTransactionBlockReceived', async () => {
4187-
const blockNumber = 123;
4142+
it('does not publish TransactionController:incomingTransactionsReceived if no new transactions', async () => {
41884143
const listener = jest.fn();
41894144

41904145
const { messenger } = setupController();
4146+
41914147
messenger.subscribe(
4192-
'TransactionController:incomingTransactionBlockReceived',
4148+
'TransactionController:incomingTransactionsReceived',
41934149
listener,
41944150
);
41954151

41964152
// TODO: Replace `any` with type
41974153
// eslint-disable-next-line @typescript-eslint/no-explicit-any
4198-
await (incomingTransactionHelperMock.hub.on as any).mock.calls[1][1]({
4199-
lastFetchedBlockNumbers: {
4200-
key: 234,
4154+
await (incomingTransactionHelperMock.hub.on as any).mock.calls[0][1]([]);
4155+
4156+
expect(listener).toHaveBeenCalledTimes(0);
4157+
});
4158+
});
4159+
4160+
describe('on incoming transaction helper updateCache call', () => {
4161+
it('updates state', async () => {
4162+
const { controller } = setupController();
4163+
const key = 'testKey';
4164+
const value = 123;
4165+
4166+
// TODO: Replace `any` with type
4167+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
4168+
incomingTransactionHelperClassMock.mock.calls[0][0].updateCache(
4169+
(cache) => {
4170+
cache[key] = value;
42014171
},
4202-
blockNumber,
4203-
});
4172+
);
42044173

4205-
expect(listener).toHaveBeenCalledTimes(1);
4206-
expect(listener).toHaveBeenCalledWith(blockNumber);
4174+
expect(controller.state.lastFetchedBlockNumbers).toStrictEqual({
4175+
[key]: value,
4176+
});
42074177
});
42084178
});
42094179

0 commit comments

Comments
 (0)