Skip to content

Commit 6a741a2

Browse files
committed
fix: type-4 transaction simulation (#5552)
Simulate type-4 transactions using a code override in order to verify the impact of any included `data`. Relates to: [#4506](MetaMask/MetaMask-planning#4506) See `CHANGELOG.md`. - [ ] I've updated the test suite for new or updated code as appropriate - [ ] I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate - [ ] I've highlighted breaking changes using the "BREAKING" category above as appropriate - [ ] I've prepared draft pull requests for clients and consumer packages to resolve any breaking changes
1 parent edcf5ae commit 6a741a2

File tree

3 files changed

+55
-4
lines changed

3 files changed

+55
-4
lines changed

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

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ import {
7676
WalletDevice,
7777
} from './types';
7878
import { addTransactionBatch } from './utils/batch';
79-
import { getDelegationAddress } from './utils/eip7702';
79+
import { DELEGATION_PREFIX, getDelegationAddress } from './utils/eip7702';
8080
import { addGasBuffer, estimateGas, updateGas } from './utils/gas';
8181
import { updateGasFees } from './utils/gas-fees';
8282
import { getGasFeeFlow } from './utils/gas-flow';
@@ -2061,6 +2061,33 @@ describe('TransactionController', () => {
20612061
expect(getSimulationDataMock).toHaveBeenCalledTimes(0);
20622062
expect(controller.state.transactions[0].simulationData).toBeUndefined();
20632063
});
2064+
2065+
it('with sender code if type 4', async () => {
2066+
getSimulationDataMock.mockResolvedValueOnce(SIMULATION_DATA_MOCK);
2067+
2068+
const { controller } = setupController();
2069+
2070+
await controller.addTransaction(
2071+
{
2072+
from: ACCOUNT_MOCK,
2073+
to: ACCOUNT_MOCK,
2074+
authorizationList: [
2075+
{
2076+
address: ACCOUNT_2_MOCK,
2077+
},
2078+
],
2079+
},
2080+
{
2081+
networkClientId: NETWORK_CLIENT_ID_MOCK,
2082+
},
2083+
);
2084+
2085+
await flushPromises();
2086+
2087+
expect(getSimulationDataMock).toHaveBeenCalledWith(expect.any(Object), {
2088+
senderCode: DELEGATION_PREFIX + ACCOUNT_2_MOCK.slice(2),
2089+
});
2090+
});
20642091
});
20652092

20662093
describe('on approve', () => {

packages/transaction-controller/src/TransactionController.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ import { NonceTracker } from '@metamask/nonce-tracker';
4646
import type { RemoteFeatureFlagControllerGetStateAction } from '@metamask/remote-feature-flag-controller';
4747
import { errorCodes, rpcErrors, providerErrors } from '@metamask/rpc-errors';
4848
import type { Hex, Json } from '@metamask/utils';
49-
import { add0x, hexToNumber } from '@metamask/utils';
49+
import { add0x, hexToNumber, remove0x } from '@metamask/utils';
5050
import { Mutex } from 'async-mutex';
5151
// This package purposefully relies on Node's EventEmitter module.
5252
// eslint-disable-next-line import-x/no-nodejs-modules
@@ -107,6 +107,7 @@ import {
107107
} from './types';
108108
import { addTransactionBatch, isAtomicBatchSupported } from './utils/batch';
109109
import {
110+
DELEGATION_PREFIX,
110111
generateEIP7702BatchTransaction,
111112
getDelegationAddress,
112113
signAuthorizationList,
@@ -3839,6 +3840,12 @@ export class TransactionController extends BaseController<
38393840
};
38403841

38413842
if (this.#isSimulationEnabled()) {
3843+
const authorizationAddress = txParams?.authorizationList?.[0]?.address;
3844+
3845+
const senderCode =
3846+
authorizationAddress &&
3847+
((DELEGATION_PREFIX + remove0x(authorizationAddress)) as Hex);
3848+
38423849
simulationData = await this.#trace(
38433850
{ name: 'Simulate', parentContext: traceContext },
38443851
() =>
@@ -3852,6 +3859,7 @@ export class TransactionController extends BaseController<
38523859
},
38533860
{
38543861
blockTime,
3862+
senderCode,
38553863
},
38563864
),
38573865
);

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

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ type ParsedEvent = {
5858

5959
type GetSimulationDataOptions = {
6060
blockTime?: number;
61+
senderCode?: Hex;
6162
};
6263

6364
const log = createModuleLogger(projectLogger, 'simulation');
@@ -115,7 +116,7 @@ export async function getSimulationData(
115116
options: GetSimulationDataOptions = {},
116117
): Promise<SimulationData> {
117118
const { chainId, from, to, value, data } = request;
118-
const { blockTime } = options;
119+
const { blockTime, senderCode } = options;
119120

120121
log('Getting simulation data', request);
121122

@@ -138,6 +139,13 @@ export async function getSimulationData(
138139
time: toHex(blockTime),
139140
},
140141
}),
142+
...(senderCode && {
143+
overrides: {
144+
[from]: {
145+
code: senderCode,
146+
},
147+
},
148+
}),
141149
});
142150

143151
const transactionError = response.transactions?.[0]?.error;
@@ -324,7 +332,8 @@ async function getTokenBalanceChanges(
324332
events: ParsedEvent[],
325333
options: GetSimulationDataOptions,
326334
): Promise<SimulationTokenBalanceChange[]> {
327-
const { blockTime } = options;
335+
const { from } = request;
336+
const { blockTime, senderCode } = options;
328337
const balanceTxs = getTokenBalanceTransactions(request, events);
329338

330339
log('Generated balance transactions', [...balanceTxs.after.values()]);
@@ -346,6 +355,13 @@ async function getTokenBalanceChanges(
346355
time: toHex(blockTime),
347356
},
348357
}),
358+
...(senderCode && {
359+
overrides: {
360+
[from]: {
361+
code: senderCode,
362+
},
363+
},
364+
}),
349365
});
350366

351367
log('Balance simulation response', response);

0 commit comments

Comments
 (0)