Skip to content

Commit 8f621ec

Browse files
fix: include authorizations in nonce calculation (#7446)
## Explanation When determining the next transaction nonce, factor any `authorizationList` nonces on pending type-4 transactions. ## References Related to [#23881](MetaMask/metamask-mobile#23881) ## Checklist - [x] I've updated the test suite for new or updated code as appropriate - [x] I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate - [x] I've communicated my changes to consumers by [updating changelogs for packages I've changed](https://github.com/MetaMask/core/tree/main/docs/contributing.md#updating-changelogs) - [x] I've introduced [breaking changes](https://github.com/MetaMask/core/tree/main/docs/breaking-changes.md) in this PR and have prepared draft pull requests for clients and consumer packages to resolve them <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Include EIP-7702 authorizationList nonces when preparing transactions for the nonce tracker; update tests and changelog. > > - **Utils (`src/utils/nonce.ts`)**: > - Extend `getAndFormatTransactionsForNonceTracker` to include nonces from `txParams.authorizationList` via `flatMap`. > - Filter out entries without a `txParams.nonce`; continue excluding `isTransfer`/`isUserOperation` and mismatched `chainId`/`from`/`status`. > - **Tests (`src/utils/nonce.test.ts`)**: > - Add test ensuring authorization nonces are included. > - Update fixtures to cover user operations and missing nonce cases. > - **Docs**: > - Update `CHANGELOG.md` under Unreleased: fixed pending authorizations included in nonce calculation. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 361aae6. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 11cf87a commit 8f621ec

File tree

3 files changed

+115
-17
lines changed

3 files changed

+115
-17
lines changed

packages/transaction-controller/CHANGELOG.md

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

88
## [Unreleased]
99

10+
### Fixed
11+
12+
- Include pending authorizations in nonce calculation ([#7446](https://github.com/MetaMask/core/pull/7446))
13+
1014
## [62.7.0]
1115

1216
### Added

packages/transaction-controller/src/utils/nonce.test.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,32 @@ describe('nonce', () => {
165165
},
166166
status: TransactionStatus.confirmed,
167167
},
168+
{
169+
id: '5',
170+
chainId: '0x2',
171+
networkClientId: 'testNetworkClientId',
172+
isUserOperation: true,
173+
time: 123460,
174+
txParams: {
175+
from: fromAddress,
176+
gas: '0x104',
177+
value: '0x204',
178+
nonce: '0x5',
179+
},
180+
status: TransactionStatus.confirmed,
181+
},
182+
{
183+
id: '5',
184+
chainId: '0x2',
185+
networkClientId: 'testNetworkClientId',
186+
time: 123460,
187+
txParams: {
188+
from: fromAddress,
189+
gas: '0x104',
190+
value: '0x204',
191+
},
192+
status: TransactionStatus.confirmed,
193+
},
168194
];
169195

170196
const expectedResult: NonceTrackerTransaction[] = [
@@ -189,5 +215,57 @@ describe('nonce', () => {
189215

190216
expect(result).toStrictEqual(expectedResult);
191217
});
218+
219+
it('includes authorization nonces from authorizationList', () => {
220+
const fromAddress = '0x123';
221+
const inputTransactions: TransactionMeta[] = [
222+
{
223+
id: '1',
224+
chainId: '0x1',
225+
networkClientId: 'testNetworkClientId',
226+
time: 123456,
227+
txParams: {
228+
from: fromAddress,
229+
gas: '0x100',
230+
value: '0x200',
231+
nonce: '0x1',
232+
authorizationList: [
233+
{
234+
chainId: '0x1',
235+
address: '0xabc',
236+
nonce: '0x2',
237+
r: '0x0',
238+
s: '0x0',
239+
yParity: '0x0',
240+
},
241+
{
242+
chainId: '0x1',
243+
address: '0xdef',
244+
nonce: '0x3',
245+
r: '0x0',
246+
s: '0x0',
247+
yParity: '0x0',
248+
},
249+
],
250+
},
251+
status: TransactionStatus.confirmed,
252+
},
253+
];
254+
255+
const result = getAndFormatTransactionsForNonceTracker(
256+
'0x1',
257+
fromAddress,
258+
[TransactionStatus.confirmed],
259+
inputTransactions,
260+
);
261+
262+
expect(result).toHaveLength(3);
263+
expect(result[0].txParams.nonce).toBe('0x1');
264+
expect(result[1].txParams.nonce).toBe('0x2');
265+
expect(result[2].txParams.nonce).toBe('0x3');
266+
expect(result[0].status).toBe(TransactionStatus.confirmed);
267+
expect(result[1].status).toBe(TransactionStatus.confirmed);
268+
expect(result[2].status).toBe(TransactionStatus.confirmed);
269+
});
192270
});
193271
});

packages/transaction-controller/src/utils/nonce.ts

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -69,27 +69,43 @@ export function getAndFormatTransactionsForNonceTracker(
6969
): NonceTrackerTransaction[] {
7070
return transactions
7171
.filter(
72-
({ chainId, isTransfer, isUserOperation, status, txParams: { from } }) =>
72+
({
73+
chainId,
74+
isTransfer,
75+
isUserOperation,
76+
status,
77+
txParams: { from, nonce },
78+
}) =>
7379
!isTransfer &&
7480
!isUserOperation &&
7581
chainId === currentChainId &&
7682
transactionStatuses.includes(status) &&
77-
from.toLowerCase() === fromAddress.toLowerCase(),
83+
from.toLowerCase() === fromAddress.toLowerCase() &&
84+
nonce,
7885
)
79-
.map(({ status, txParams: { from, gas, value, nonce } }) => {
80-
// the only value we care about is the nonce
81-
// but we need to return the other values to satisfy the type
82-
// TODO: refactor nonceTracker to not require this
83-
/* istanbul ignore next */
84-
return {
86+
.flatMap(
87+
({
8588
status,
86-
history: [{}],
87-
txParams: {
88-
from: from ?? '',
89-
gas: gas ?? '',
90-
value: value ?? '',
91-
nonce: nonce ?? '',
92-
},
93-
};
94-
});
89+
txParams: { authorizationList, from, gas, value, nonce },
90+
}) => {
91+
const authorizationNonces = (authorizationList ?? [])
92+
.map((authorization) => authorization.nonce)
93+
.filter((authorizationNonce) => authorizationNonce !== undefined);
94+
95+
// the only value we care about is the nonce
96+
// but we need to return the other values to satisfy the type
97+
// TODO: refactor nonceTracker to not require this
98+
/* istanbul ignore next */
99+
return [nonce, ...authorizationNonces].map((currentNonce) => ({
100+
status,
101+
history: [{}],
102+
txParams: {
103+
from: from ?? '',
104+
gas: gas ?? '',
105+
value: value ?? '',
106+
nonce: currentNonce ?? '',
107+
},
108+
}));
109+
},
110+
);
95111
}

0 commit comments

Comments
 (0)