Skip to content

Commit 7e9d3e0

Browse files
cursoragentn3ps
andcommitted
fix: preserve local pay activity transactions
Co-authored-by: Francis Nepomuceno <n3ps@users.noreply.github.com>
1 parent ff4138f commit 7e9d3e0

5 files changed

Lines changed: 161 additions & 6 deletions

File tree

app/components/Views/UnifiedTransactionsView/helpers/transformations.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,17 @@ describe('selectTransactions', () => {
7575

7676
expect(result.pages[0].data).toHaveLength(0);
7777
});
78+
79+
it('filters out transactions with excluded hashes', () => {
80+
const excluded = buildTransaction({ hash: '0xEXCLUDED' });
81+
const normal = buildTransaction({ hash: '0xnormal' });
82+
83+
const result = selectTransactions({
84+
address,
85+
excludedTxHashes: new Set(['0xexcluded']),
86+
})(buildData([excluded, normal]));
87+
88+
expect(result.pages[0].data).toHaveLength(1);
89+
expect(result.pages[0].data[0].hash).toBe('0xnormal');
90+
});
7891
});

app/components/Views/UnifiedTransactionsView/helpers/transformations.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,15 @@ function isIncomingNativeTransfer(
8585
function shouldSkipTransaction(
8686
address: string,
8787
transaction: V1TransactionByHashResponse,
88+
excludedTxHashes?: Set<string>,
8889
) {
8990
const rawFrom = transaction.from?.toLowerCase();
9091
const rawTo = transaction.to?.toLowerCase();
92+
const hash = transaction.hash?.toLowerCase();
93+
94+
if (hash && excludedTxHashes?.has(hash)) {
95+
return true;
96+
}
9197

9298
if (rawFrom !== address && rawTo !== address) {
9399
return true;
@@ -120,11 +126,12 @@ function shouldSkipTransaction(
120126
function transformTransactions(
121127
address: string,
122128
transactions: V1TransactionByHashResponse[],
129+
excludedTxHashes?: Set<string>,
123130
): TransactionViewModel[] {
124131
const filteredTransactions = [];
125132

126133
for (const tx of transactions) {
127-
if (shouldSkipTransaction(address, tx)) {
134+
if (shouldSkipTransaction(address, tx, excludedTxHashes)) {
128135
continue;
129136
}
130137

@@ -146,12 +153,18 @@ function transformTransactions(
146153
});
147154
}
148155

149-
export function selectTransactions({ address }: { address: string }) {
156+
export function selectTransactions({
157+
address,
158+
excludedTxHashes,
159+
}: {
160+
address: string;
161+
excludedTxHashes?: Set<string>;
162+
}) {
150163
return (data: InfiniteData<V4MultiAccountTransactionsResponse>) => ({
151164
...data,
152165
pages: data.pages.map((page) => ({
153166
...page,
154-
data: transformTransactions(address, page.data),
167+
data: transformTransactions(address, page.data, excludedTxHashes),
155168
})),
156169
});
157170
}

app/components/Views/UnifiedTransactionsView/useTransactionsQuery.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ import { selectEvmAddress } from '../../../selectors/accountsController';
77
import { selectEvmEnabledCaipNetworks } from '../../../selectors/networkEnablementController';
88
import { selectTransactions } from './helpers/transformations';
99
import { MINUTE } from '../../../constants/time';
10+
import { selectRequiredTransactionHashes } from '../../../selectors/transactionController';
1011

1112
export const useTransactionsQuery = () => {
1213
const evmAddress = useSelector(selectEvmAddress) || '';
1314
const networks = useSelector(selectEvmEnabledCaipNetworks);
15+
const excludedTxHashes = useSelector(selectRequiredTransactionHashes);
1416
const accountAddresses = evmAddress
1517
? [toCaipAccountId(KnownCaipNamespace.Eip155, '0', evmAddress)]
1618
: [];
@@ -23,8 +25,8 @@ export const useTransactionsQuery = () => {
2325
});
2426

2527
const selectFn = useMemo(
26-
() => selectTransactions({ address: evmAddress }),
27-
[evmAddress],
28+
() => selectTransactions({ address: evmAddress, excludedTxHashes }),
29+
[evmAddress, excludedTxHashes],
2830
);
2931

3032
// @ts-expect-error apiClient returns v5 types, repo still in v4

app/selectors/transactionController.test.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import { TransactionType } from '@metamask/transaction-controller';
44
import {
55
selectTransactions,
66
selectLastWithdrawTokenByType,
7+
selectLocalTransactions,
78
selectNonReplacedTransactions,
9+
selectRequiredTransactionHashes,
810
selectSwapsTransactions,
911
selectTransactionMetadataById,
1012
selectSortedTransactions,
@@ -96,6 +98,87 @@ describe('TransactionController Selectors', () => {
9698
});
9799
});
98100

101+
describe('selectRequiredTransactionHashes', () => {
102+
it('returns hashes for required child transactions', () => {
103+
const state = {
104+
engine: {
105+
backgroundState: {
106+
TransactionController: {
107+
transactions: [
108+
{
109+
id: 'parent',
110+
requiredTransactionIds: ['child'],
111+
},
112+
{
113+
id: 'child',
114+
hash: '0xABC',
115+
},
116+
],
117+
},
118+
},
119+
},
120+
} as unknown as RootState;
121+
122+
expect(selectRequiredTransactionHashes(state)).toStrictEqual(
123+
new Set(['0xabc']),
124+
);
125+
});
126+
});
127+
128+
describe('selectLocalTransactions', () => {
129+
it('filters required child transactions before nonce dedupe', () => {
130+
const activeEvmAddress = '0x0000000000000000000000000000000000000001';
131+
const state = {
132+
engine: {
133+
backgroundState: {
134+
AccountsController: {
135+
internalAccounts: {
136+
selectedAccount: 'account-1',
137+
accounts: {
138+
'account-1': {
139+
id: 'account-1',
140+
address: activeEvmAddress,
141+
type: 'eip155:eoa',
142+
},
143+
},
144+
},
145+
},
146+
TransactionController: {
147+
transactions: [
148+
{
149+
id: 'child',
150+
hash: '0xCHILD',
151+
chainId: '0x1',
152+
time: 200,
153+
txParams: {
154+
from: activeEvmAddress,
155+
nonce: '0x1',
156+
},
157+
},
158+
{
159+
id: 'parent',
160+
chainId: '0x1',
161+
requiredTransactionIds: ['child'],
162+
time: 100,
163+
type: TransactionType.predictDeposit,
164+
txParams: {
165+
from: activeEvmAddress,
166+
nonce: '0x1',
167+
},
168+
},
169+
],
170+
},
171+
},
172+
},
173+
pendingSmartTransactionsForGroup: [],
174+
} as unknown as RootState;
175+
176+
expect(selectLocalTransactions(state).map(({ id }) => id)).toStrictEqual([
177+
'parent',
178+
]);
179+
});
180+
});
181+
99182
describe('selectTransactionMetadataById', () => {
100183
it('returns the transaction matching the given id', () => {
101184
const transactions = [

app/selectors/transactionController.ts

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,28 @@ const selectTransactionBatchesStrict = createSelector(
9393
(transactionControllerState) => transactionControllerState.transactionBatches,
9494
);
9595

96+
export const selectRequiredTransactionIds = createSelector(
97+
selectTransactionsStrict,
98+
(transactions) =>
99+
new Set(transactions.flatMap((tx) => tx.requiredTransactionIds ?? [])),
100+
);
101+
102+
export const selectRequiredTransactions = createSelector(
103+
[selectTransactionsStrict, selectRequiredTransactionIds],
104+
(transactions, requiredTransactionIds) =>
105+
transactions.filter((tx) => requiredTransactionIds.has(tx.id)),
106+
);
107+
108+
export const selectRequiredTransactionHashes = createSelector(
109+
selectRequiredTransactions,
110+
(transactions) =>
111+
new Set(
112+
transactions
113+
.map((tx) => tx.hash?.toLowerCase())
114+
.filter((hash): hash is string => Boolean(hash)),
115+
),
116+
);
117+
96118
export const selectTransactions = createDeepEqualSelector(
97119
selectTransactionsStrict,
98120
(transactions) => transactions,
@@ -168,9 +190,23 @@ export const selectLocalTransactions = createDeepEqualSelector(
168190
selectNonReplacedTransactions,
169191
selectPendingSmartTransactionsForSelectedAccountGroup,
170192
selectEvmAddress,
193+
selectRequiredTransactionHashes,
171194
],
172-
(nonReplacedTransactions, pendingSmartTransactions, activeEvmAddress) => {
195+
(
196+
nonReplacedTransactions,
197+
pendingSmartTransactions,
198+
activeEvmAddress,
199+
requiredTransactionHashes,
200+
) => {
173201
const transactions = nonReplacedTransactions.filter((transaction) => {
202+
const hash =
203+
'hash' in transaction && typeof transaction.hash === 'string'
204+
? transaction.hash.toLowerCase()
205+
: undefined;
206+
if (hash && requiredTransactionHashes.has(hash)) {
207+
return false;
208+
}
209+
174210
const fromAddress = transaction.txParams?.from;
175211
if (!fromAddress || !activeEvmAddress) {
176212
return false;
@@ -181,6 +217,14 @@ export const selectLocalTransactions = createDeepEqualSelector(
181217

182218
const pendingSmartTransactionsForActiveAddress =
183219
pendingSmartTransactions.filter((transaction) => {
220+
const hash =
221+
'hash' in transaction && typeof transaction.hash === 'string'
222+
? transaction.hash.toLowerCase()
223+
: undefined;
224+
if (hash && requiredTransactionHashes.has(hash)) {
225+
return false;
226+
}
227+
184228
const fromAddress = transaction.txParams?.from;
185229
if (!fromAddress || !activeEvmAddress) {
186230
return false;

0 commit comments

Comments
 (0)