@feelyourprotocol/vm 8141.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/LICENSE +373 -0
  2. package/README.md +583 -0
  3. package/dist/cjs/bloom/index.d.ts +29 -0
  4. package/dist/cjs/bloom/index.d.ts.map +1 -0
  5. package/dist/cjs/bloom/index.js +76 -0
  6. package/dist/cjs/bloom/index.js.map +1 -0
  7. package/dist/cjs/buildBlock.d.ts +118 -0
  8. package/dist/cjs/buildBlock.d.ts.map +1 -0
  9. package/dist/cjs/buildBlock.js +363 -0
  10. package/dist/cjs/buildBlock.js.map +1 -0
  11. package/dist/cjs/constructors.d.ts +9 -0
  12. package/dist/cjs/constructors.d.ts.map +1 -0
  13. package/dist/cjs/constructors.js +75 -0
  14. package/dist/cjs/constructors.js.map +1 -0
  15. package/dist/cjs/emitEVMProfile.d.ts +9 -0
  16. package/dist/cjs/emitEVMProfile.d.ts.map +1 -0
  17. package/dist/cjs/emitEVMProfile.js +130 -0
  18. package/dist/cjs/emitEVMProfile.js.map +1 -0
  19. package/dist/cjs/index.d.ts +11 -0
  20. package/dist/cjs/index.d.ts.map +1 -0
  21. package/dist/cjs/index.js +36 -0
  22. package/dist/cjs/index.js.map +1 -0
  23. package/dist/cjs/package.json +3 -0
  24. package/dist/cjs/params.d.ts +3 -0
  25. package/dist/cjs/params.d.ts.map +1 -0
  26. package/dist/cjs/params.js +105 -0
  27. package/dist/cjs/params.js.map +1 -0
  28. package/dist/cjs/requests.d.ts +11 -0
  29. package/dist/cjs/requests.d.ts.map +1 -0
  30. package/dist/cjs/requests.js +208 -0
  31. package/dist/cjs/requests.js.map +1 -0
  32. package/dist/cjs/runBlock.d.ts +35 -0
  33. package/dist/cjs/runBlock.d.ts.map +1 -0
  34. package/dist/cjs/runBlock.js +797 -0
  35. package/dist/cjs/runBlock.js.map +1 -0
  36. package/dist/cjs/runFrameTx.d.ts +18 -0
  37. package/dist/cjs/runFrameTx.d.ts.map +1 -0
  38. package/dist/cjs/runFrameTx.js +313 -0
  39. package/dist/cjs/runFrameTx.js.map +1 -0
  40. package/dist/cjs/runTx.d.ts +18 -0
  41. package/dist/cjs/runTx.d.ts.map +1 -0
  42. package/dist/cjs/runTx.js +900 -0
  43. package/dist/cjs/runTx.js.map +1 -0
  44. package/dist/cjs/types.d.ts +452 -0
  45. package/dist/cjs/types.d.ts.map +1 -0
  46. package/dist/cjs/types.js +3 -0
  47. package/dist/cjs/types.js.map +1 -0
  48. package/dist/cjs/vm.d.ts +75 -0
  49. package/dist/cjs/vm.d.ts.map +1 -0
  50. package/dist/cjs/vm.js +111 -0
  51. package/dist/cjs/vm.js.map +1 -0
  52. package/dist/esm/bloom/index.d.ts +29 -0
  53. package/dist/esm/bloom/index.d.ts.map +1 -0
  54. package/dist/esm/bloom/index.js +72 -0
  55. package/dist/esm/bloom/index.js.map +1 -0
  56. package/dist/esm/buildBlock.d.ts +118 -0
  57. package/dist/esm/buildBlock.d.ts.map +1 -0
  58. package/dist/esm/buildBlock.js +358 -0
  59. package/dist/esm/buildBlock.js.map +1 -0
  60. package/dist/esm/constructors.d.ts +9 -0
  61. package/dist/esm/constructors.d.ts.map +1 -0
  62. package/dist/esm/constructors.js +72 -0
  63. package/dist/esm/constructors.js.map +1 -0
  64. package/dist/esm/emitEVMProfile.d.ts +9 -0
  65. package/dist/esm/emitEVMProfile.d.ts.map +1 -0
  66. package/dist/esm/emitEVMProfile.js +127 -0
  67. package/dist/esm/emitEVMProfile.js.map +1 -0
  68. package/dist/esm/index.d.ts +11 -0
  69. package/dist/esm/index.d.ts.map +1 -0
  70. package/dist/esm/index.js +11 -0
  71. package/dist/esm/index.js.map +1 -0
  72. package/dist/esm/package.json +3 -0
  73. package/dist/esm/params.d.ts +3 -0
  74. package/dist/esm/params.d.ts.map +1 -0
  75. package/dist/esm/params.js +102 -0
  76. package/dist/esm/params.js.map +1 -0
  77. package/dist/esm/requests.d.ts +11 -0
  78. package/dist/esm/requests.d.ts.map +1 -0
  79. package/dist/esm/requests.js +204 -0
  80. package/dist/esm/requests.js.map +1 -0
  81. package/dist/esm/runBlock.d.ts +35 -0
  82. package/dist/esm/runBlock.d.ts.map +1 -0
  83. package/dist/esm/runBlock.js +790 -0
  84. package/dist/esm/runBlock.js.map +1 -0
  85. package/dist/esm/runFrameTx.d.ts +18 -0
  86. package/dist/esm/runFrameTx.d.ts.map +1 -0
  87. package/dist/esm/runFrameTx.js +310 -0
  88. package/dist/esm/runFrameTx.js.map +1 -0
  89. package/dist/esm/runTx.d.ts +18 -0
  90. package/dist/esm/runTx.d.ts.map +1 -0
  91. package/dist/esm/runTx.js +896 -0
  92. package/dist/esm/runTx.js.map +1 -0
  93. package/dist/esm/types.d.ts +452 -0
  94. package/dist/esm/types.d.ts.map +1 -0
  95. package/dist/esm/types.js +2 -0
  96. package/dist/esm/types.js.map +1 -0
  97. package/dist/esm/vm.d.ts +75 -0
  98. package/dist/esm/vm.d.ts.map +1 -0
  99. package/dist/esm/vm.js +107 -0
  100. package/dist/esm/vm.js.map +1 -0
  101. package/dist/tsconfig.prod.cjs.tsbuildinfo +1 -0
  102. package/dist/tsconfig.prod.esm.tsbuildinfo +1 -0
  103. package/package.json +117 -0
  104. package/src/bloom/index.ts +83 -0
  105. package/src/buildBlock.ts +470 -0
  106. package/src/constructors.ts +91 -0
  107. package/src/emitEVMProfile.ts +151 -0
  108. package/src/index.ts +10 -0
  109. package/src/params.ts +104 -0
  110. package/src/requests.ts +293 -0
  111. package/src/runBlock.ts +1022 -0
  112. package/src/runFrameTx.ts +411 -0
  113. package/src/runTx.ts +1203 -0
  114. package/src/types.ts +511 -0
  115. package/src/vm.ts +147 -0
@@ -0,0 +1,900 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runTx = runTx;
4
+ exports.generateTxReceipt = generateTxReceipt;
5
+ const block_1 = require("@feelyourprotocol/block");
6
+ const common_1 = require("@feelyourprotocol/common");
7
+ const evm_1 = require("@feelyourprotocol/evm");
8
+ const tx_1 = require("@feelyourprotocol/tx");
9
+ const util_1 = require("@feelyourprotocol/util");
10
+ const debug_1 = require("debug");
11
+ const index_ts_1 = require("./bloom/index.js");
12
+ const emitEVMProfile_ts_1 = require("./emitEVMProfile.js");
13
+ const runFrameTx_ts_1 = require("./runFrameTx.js");
14
+ const debug = (0, debug_1.default)('vm:tx');
15
+ const debugGas = (0, debug_1.default)('vm:tx:gas');
16
+ const DEFAULT_HEADER = (0, block_1.createBlockHeader)();
17
+ let enableProfiler = false;
18
+ const initLabel = 'EVM journal init, address/slot warming, fee validation';
19
+ const balanceNonceLabel = 'Balance/Nonce checks and update';
20
+ const executionLabel = 'Execution';
21
+ const logsGasBalanceLabel = 'Logs, gas usage, account/miner balances';
22
+ const accountsCleanUpLabel = 'Accounts clean up';
23
+ const accessListLabel = 'Access list label';
24
+ const journalCacheCleanUpLabel = 'Journal/cache cleanup';
25
+ const receiptsLabel = 'Receipts';
26
+ const entireTxLabel = 'Entire tx';
27
+ // EIP-7702 flag: if contract code starts with these 3 bytes, it is a 7702-delegated EOA
28
+ const DELEGATION_7702_FLAG = new Uint8Array([0xef, 0x01, 0x00]);
29
+ /**
30
+ * Process EIP-7702 authorization list tuples.
31
+ * Sets delegation code for authorized accounts and calculates gas refunds.
32
+ *
33
+ * @param vm - The VM instance
34
+ * @param tx - The transaction (must support EIP7702EOACode capability)
35
+ * @param caller - The transaction sender address
36
+ * @param initialGasRefund - The current gas refund amount
37
+ * @returns The updated gas refund amount
38
+ */
39
+ async function processAuthorizationList(vm, tx, caller, initialGasRefund) {
40
+ let gasRefund = initialGasRefund;
41
+ const authorizationList = tx.authorizationList;
42
+ for (let i = 0; i < authorizationList.length; i++) {
43
+ const data = authorizationList[i];
44
+ // Validate chain ID
45
+ const chainId = data[0];
46
+ const chainIdBN = (0, util_1.bytesToBigInt)(chainId);
47
+ if (chainIdBN !== util_1.BIGINT_0 && chainIdBN !== vm.common.chainId()) {
48
+ continue;
49
+ }
50
+ // Validate nonce bounds
51
+ const authorityNonce = data[2];
52
+ if ((0, util_1.bytesToBigInt)(authorityNonce) >= util_1.MAX_UINT64) {
53
+ // Authority nonce >= 2^64 - 1. Bumping this nonce by one will not make this fit in an uint64.
54
+ // EIPs PR: https://github.com/ethereum/EIPs/pull/8938
55
+ continue;
56
+ }
57
+ // Validate signature malleability (s value)
58
+ const s = data[5];
59
+ if ((0, util_1.bytesToBigInt)(s) > util_1.SECP256K1_ORDER_DIV_2) {
60
+ // Malleability protection to avoid "flipping" a valid signature
61
+ continue;
62
+ }
63
+ // Validate yParity
64
+ const yParity = (0, util_1.bytesToBigInt)(data[3]);
65
+ if (yParity > util_1.BIGINT_1) {
66
+ continue;
67
+ }
68
+ // Recover authority address from signature
69
+ let authority;
70
+ try {
71
+ authority = (0, util_1.eoaCode7702RecoverAuthority)(data);
72
+ }
73
+ catch {
74
+ // Invalid signature
75
+ continue;
76
+ }
77
+ const accountMaybeUndefined = await vm.stateManager.getAccount(authority);
78
+ const accountExists = accountMaybeUndefined !== undefined;
79
+ const account = accountMaybeUndefined ?? new util_1.Account();
80
+ // Add authority address to warm addresses
81
+ vm.evm.journal.addAlwaysWarmAddress(authority.toString());
82
+ // EIP-7928: Add authority address to BAL (even if authorization fails later,
83
+ // the account was accessed to check nonce/code)
84
+ if (vm.common.isActivatedEIP(7928)) {
85
+ vm.evm.blockLevelAccessList.addAddress(authority.toString());
86
+ }
87
+ // Skip if account is a "normal" contract (not 7702-delegated)
88
+ if (account.isContract()) {
89
+ const code = await vm.stateManager.getCode(authority);
90
+ if (!(0, util_1.equalsBytes)(code.slice(0, 3), DELEGATION_7702_FLAG)) {
91
+ continue;
92
+ }
93
+ }
94
+ // Nonce validation
95
+ if (caller.toString() === authority.toString()) {
96
+ // Edge case: caller is the authority (self-signing delegation)
97
+ // Virtually bump the account nonce by one for comparison
98
+ if (account.nonce + util_1.BIGINT_1 !== (0, util_1.bytesToBigInt)(authorityNonce)) {
99
+ continue;
100
+ }
101
+ }
102
+ else if (account.nonce !== (0, util_1.bytesToBigInt)(authorityNonce)) {
103
+ continue;
104
+ }
105
+ // Calculate gas refund for existing accounts
106
+ if (accountExists) {
107
+ const refund = tx.common.param('perEmptyAccountCost') - tx.common.param('perAuthBaseGas');
108
+ gasRefund += refund;
109
+ }
110
+ // Update account nonce and store
111
+ account.nonce++;
112
+ await vm.evm.journal.putAccount(authority, account);
113
+ if (vm.common.isActivatedEIP(7928)) {
114
+ vm.evm.blockLevelAccessList.addNonceChange(authority.toString(), account.nonce, vm.evm.blockLevelAccessList.blockAccessIndex);
115
+ }
116
+ // Set delegation code
117
+ const address = data[1];
118
+ // Get current code before modifying (needed for BAL tracking)
119
+ const currentCode = vm.common.isActivatedEIP(7928)
120
+ ? await vm.stateManager.getCode(authority)
121
+ : undefined;
122
+ if ((0, util_1.equalsBytes)(address, new Uint8Array(20))) {
123
+ // Special case: clear delegation when delegating to zero address
124
+ // See EIP PR: https://github.com/ethereum/EIPs/pull/8929
125
+ await vm.stateManager.putCode(authority, new Uint8Array());
126
+ if (vm.common.isActivatedEIP(7928)) {
127
+ vm.evm.blockLevelAccessList.addCodeChange(authority.toString(), new Uint8Array(), vm.evm.blockLevelAccessList.blockAccessIndex, currentCode);
128
+ }
129
+ }
130
+ else {
131
+ const addressCode = (0, util_1.concatBytes)(DELEGATION_7702_FLAG, address);
132
+ await vm.stateManager.putCode(authority, addressCode);
133
+ if (vm.common.isActivatedEIP(7928)) {
134
+ vm.evm.blockLevelAccessList.addCodeChange(authority.toString(), addressCode, vm.evm.blockLevelAccessList.blockAccessIndex, currentCode);
135
+ }
136
+ }
137
+ }
138
+ return gasRefund;
139
+ }
140
+ /**
141
+ * Process selfdestruct cleanup for accounts marked for destruction.
142
+ * Handles EIP-6780 restrictions (only delete contracts created in same tx).
143
+ *
144
+ * @param vm - The VM instance
145
+ * @param results - The execution results containing selfdestruct list
146
+ */
147
+ async function processSelfdestructs(vm, results) {
148
+ if (results.execResult.selfdestruct === undefined) {
149
+ return;
150
+ }
151
+ const destroyedForBAL = new Set();
152
+ const finalizationLogs = [];
153
+ const sortedSelfdestructs = [...results.execResult.selfdestruct.entries()].sort(([a], [b]) => a.localeCompare(b));
154
+ for (const [addressToSelfdestructHex] of sortedSelfdestructs) {
155
+ const address = new util_1.Address((0, util_1.hexToBytes)(addressToSelfdestructHex));
156
+ // EIP-6780: Only delete contracts created in the same transaction
157
+ if (vm.common.isActivatedEIP(6780)) {
158
+ if (!results.execResult.createdAddresses.has(address.toString())) {
159
+ continue;
160
+ }
161
+ }
162
+ if (vm.common.isActivatedEIP(7708)) {
163
+ const account = await vm.stateManager.getAccount(address);
164
+ const finalizationBalance = account?.balance ?? util_1.BIGINT_0;
165
+ if (finalizationBalance > util_1.BIGINT_0) {
166
+ finalizationLogs.push((0, evm_1.createEIP7708SelfdestructLog)(address, finalizationBalance));
167
+ }
168
+ }
169
+ await vm.evm.journal.deleteAccount(address);
170
+ destroyedForBAL.add(address.toString());
171
+ if (vm.DEBUG) {
172
+ debug(`tx selfdestruct on address=${address}`);
173
+ }
174
+ }
175
+ if (finalizationLogs.length > 0) {
176
+ results.execResult.logs = [...(results.execResult.logs ?? []), ...finalizationLogs];
177
+ }
178
+ if (destroyedForBAL.size > 0 && vm.common.isActivatedEIP(7928)) {
179
+ vm.evm.blockLevelAccessList.cleanupSelfdestructed([...destroyedForBAL]);
180
+ }
181
+ }
182
+ /**
183
+ * Build the access list result from the journal's tracked accesses.
184
+ * Converts the internal Map format to the standard AccessList format.
185
+ *
186
+ * @param vm - The VM instance
187
+ * @returns The formatted access list
188
+ */
189
+ function buildAccessListResult(vm) {
190
+ const accessList = [];
191
+ for (const [address, storageSet] of vm.evm.journal.accessList) {
192
+ const item = {
193
+ address: `0x${address}`,
194
+ storageKeys: [],
195
+ };
196
+ for (const slot of storageSet) {
197
+ item.storageKeys.push(`0x${slot}`);
198
+ }
199
+ accessList.push(item);
200
+ }
201
+ return accessList;
202
+ }
203
+ /**
204
+ * Update the miner's account balance with the transaction fee.
205
+ * Handles both EIP-1559 (priority fee only) and legacy (full gas price) fee models.
206
+ *
207
+ * @param vm - The VM instance
208
+ * @param state - The state manager
209
+ * @param block - The block (optional)
210
+ * @param results - The transaction results to update with minerValue
211
+ * @param inclusionFeePerGas - The priority fee per gas (for EIP-1559)
212
+ */
213
+ async function updateMinerBalance(vm, state, block, results, inclusionFeePerGas) {
214
+ // Determine miner address based on consensus type
215
+ let miner;
216
+ if (vm.common.consensusType() === common_1.ConsensusType.ProofOfAuthority) {
217
+ miner = (0, block_1.cliqueSigner)(block?.header ?? DEFAULT_HEADER);
218
+ }
219
+ else {
220
+ miner = block?.header.coinbase ?? DEFAULT_HEADER.coinbase;
221
+ }
222
+ // Get or create miner account
223
+ let minerAccount = await state.getAccount(miner);
224
+ if (minerAccount === undefined) {
225
+ minerAccount = new util_1.Account();
226
+ }
227
+ // Calculate miner value: priority fee for EIP-1559, full amount for legacy
228
+ results.minerValue = vm.common.isActivatedEIP(1559)
229
+ ? results.totalGasSpent * inclusionFeePerGas
230
+ : results.amountSpent;
231
+ const minerOriginalBalance = minerAccount.balance;
232
+ minerAccount.balance += results.minerValue;
233
+ if (vm.common.isActivatedEIP(7928)) {
234
+ if (results.minerValue !== util_1.BIGINT_0) {
235
+ vm.evm.blockLevelAccessList.addBalanceChange(miner.toString(), minerAccount.balance, vm.evm.blockLevelAccessList.blockAccessIndex, minerOriginalBalance);
236
+ }
237
+ else {
238
+ // EIP-7928: If the COINBASE reward is zero, the COINBASE address
239
+ // MUST be included as a read (address only, no balance change)
240
+ vm.evm.blockLevelAccessList.addAddress(miner.toString());
241
+ }
242
+ }
243
+ // Store updated miner account
244
+ // Note: If balance remains zero, account is marked as "touched" and may be
245
+ // removed during cleanup for forks >= SpuriousDragon
246
+ await vm.evm.journal.putAccount(miner, minerAccount);
247
+ if (vm.DEBUG) {
248
+ debug(`tx update miner account (${miner}) balance (-> ${minerAccount.balance})`);
249
+ }
250
+ }
251
+ /**
252
+ * @ignore
253
+ */
254
+ async function runTx(vm, opts) {
255
+ if (vm['_opts'].profilerOpts?.reportAfterTx === true) {
256
+ enableProfiler = true;
257
+ }
258
+ if (enableProfiler) {
259
+ const title = `Profiler run - Tx ${(0, util_1.bytesToHex)(opts.tx.hash())}`;
260
+ // eslint-disable-next-line no-console
261
+ console.log(title);
262
+ // eslint-disable-next-line no-console
263
+ console.time(initLabel);
264
+ // eslint-disable-next-line no-console
265
+ console.time(entireTxLabel);
266
+ }
267
+ if (opts.skipHardForkValidation !== true && opts.block !== undefined) {
268
+ // If block and tx don't have a same hardfork, set tx hardfork to block
269
+ if (opts.tx.common.hardfork() !== opts.block.common.hardfork()) {
270
+ opts.tx.common.setHardfork(opts.block.common.hardfork());
271
+ }
272
+ if (opts.block.common.hardfork() !== vm.common.hardfork()) {
273
+ // Block and VM's hardfork should match as well
274
+ const msg = _errorMsg('block has a different hardfork than the vm', vm, opts.block, opts.tx);
275
+ throw (0, util_1.EthereumJSErrorWithoutCode)(msg);
276
+ }
277
+ }
278
+ const gasLimit = opts.block?.header.gasLimit ?? DEFAULT_HEADER.gasLimit;
279
+ if (opts.skipBlockGasLimitValidation !== true && gasLimit < opts.tx.gasLimit) {
280
+ const msg = _errorMsg('tx has a higher gas limit than the block', vm, opts.block, opts.tx);
281
+ throw (0, util_1.EthereumJSErrorWithoutCode)(msg);
282
+ }
283
+ // Ensure we start with a clear warmed accounts Map
284
+ await vm.evm.journal.cleanup();
285
+ if (opts.reportAccessList === true) {
286
+ vm.evm.journal.startReportingAccessList();
287
+ }
288
+ if (opts.reportPreimages === true) {
289
+ vm.evm.journal.startReportingPreimages();
290
+ }
291
+ await vm.evm.journal.checkpoint();
292
+ if (vm.DEBUG) {
293
+ debug('-'.repeat(100));
294
+ debug(`tx checkpoint`);
295
+ }
296
+ // Typed transaction specific setup tasks
297
+ if (opts.tx.supports(tx_1.Capability.EIP2718TypedTransaction) && vm.common.isActivatedEIP(2718)) {
298
+ // Is it an Access List transaction?
299
+ if (!vm.common.isActivatedEIP(2930)) {
300
+ await vm.evm.journal.revert();
301
+ const msg = _errorMsg('Cannot run transaction: EIP 2930 is not activated.', vm, opts.block, opts.tx);
302
+ throw (0, util_1.EthereumJSErrorWithoutCode)(msg);
303
+ }
304
+ if (opts.tx.supports(tx_1.Capability.EIP1559FeeMarket) && !vm.common.isActivatedEIP(1559)) {
305
+ await vm.evm.journal.revert();
306
+ const msg = _errorMsg('Cannot run transaction: EIP 1559 is not activated.', vm, opts.block, opts.tx);
307
+ throw (0, util_1.EthereumJSErrorWithoutCode)(msg);
308
+ }
309
+ if (!(0, tx_1.isFrameEIP8141Tx)(opts.tx)) {
310
+ const castedTx = opts.tx;
311
+ for (const accessListItem of castedTx.accessList) {
312
+ const [addressBytes, slotBytesList] = accessListItem;
313
+ // Using deprecated bytesToUnprefixedHex for performance: journal methods expect unprefixed hex strings for Map/Set lookups.
314
+ const address = (0, util_1.bytesToUnprefixedHex)(addressBytes);
315
+ vm.evm.journal.addAlwaysWarmAddress(address, true);
316
+ for (const storageKey of slotBytesList) {
317
+ vm.evm.journal.addAlwaysWarmSlot(address, (0, util_1.bytesToUnprefixedHex)(storageKey), true);
318
+ }
319
+ }
320
+ }
321
+ }
322
+ try {
323
+ const result = await _runTx(vm, opts);
324
+ await vm.evm.journal.commit();
325
+ if (vm.DEBUG) {
326
+ debug(`tx checkpoint committed`);
327
+ }
328
+ return result;
329
+ }
330
+ catch (e) {
331
+ await vm.evm.journal.revert();
332
+ if (vm.DEBUG) {
333
+ debug(`tx checkpoint reverted`);
334
+ }
335
+ throw e;
336
+ }
337
+ finally {
338
+ if (vm.common.isActivatedEIP(2929)) {
339
+ vm.evm.journal.cleanJournal();
340
+ }
341
+ vm.evm.stateManager.originalStorageCache.clear();
342
+ if (enableProfiler) {
343
+ // eslint-disable-next-line no-console
344
+ console.timeEnd(entireTxLabel);
345
+ const logs = vm.evm.getPerformanceLogs();
346
+ if (logs.precompiles.length === 0 && logs.opcodes.length === 0) {
347
+ // eslint-disable-next-line no-console
348
+ console.log('No precompile or opcode execution.');
349
+ }
350
+ (0, emitEVMProfile_ts_1.emitEVMProfile)(logs.precompiles, 'Precompile performance');
351
+ (0, emitEVMProfile_ts_1.emitEVMProfile)(logs.opcodes, 'Opcodes performance');
352
+ vm.evm.clearPerformanceLogs();
353
+ }
354
+ }
355
+ }
356
+ async function _runTx(vm, opts) {
357
+ const state = vm.stateManager;
358
+ // ===========================
359
+ // SETUP: Binary Tree Witness
360
+ // ===========================
361
+ let stateAccesses;
362
+ let txAccesses;
363
+ if (vm.common.isActivatedEIP(7864)) {
364
+ if (vm.evm.binaryTreeAccessWitness === undefined) {
365
+ throw Error(`Binary tree access witness needed for execution of binary tree blocks`);
366
+ }
367
+ // Check if statemanager is a BinaryTreeStateManager by checking for a method only on BinaryTreeStateManager API
368
+ if (!('verifyBinaryPostState' in vm.stateManager)) {
369
+ throw (0, util_1.EthereumJSErrorWithoutCode)(`Binary tree State Manager needed for execution of binary tree blocks`);
370
+ }
371
+ stateAccesses = vm.evm.binaryTreeAccessWitness;
372
+ txAccesses = new evm_1.BinaryTreeAccessWitness({
373
+ hashFunction: vm.evm.binaryTreeAccessWitness.hashFunction,
374
+ });
375
+ }
376
+ // ===========================
377
+ // SETUP: Transaction and Events
378
+ // ===========================
379
+ const { tx, block } = opts;
380
+ /** The `beforeTx` event - emits the Transaction that is about to be processed */
381
+ await vm._emit('beforeTx', tx);
382
+ const caller = tx.getSenderAddress();
383
+ if (vm.DEBUG) {
384
+ debug(`New tx run hash=${opts.tx.isSigned() ? (0, util_1.bytesToHex)(opts.tx.hash()) : 'unsigned'} sender=${caller}`);
385
+ }
386
+ // ===========================
387
+ // SETUP: Address Warming (EIP-2929)
388
+ // ===========================
389
+ if (vm.common.isActivatedEIP(2929)) {
390
+ // Add origin, precompiles, and relevant addresses to warm set
391
+ const activePrecompiles = vm.evm.precompiles;
392
+ for (const [addressStr] of activePrecompiles.entries()) {
393
+ vm.evm.journal.addAlwaysWarmAddress(addressStr);
394
+ }
395
+ vm.evm.journal.addAlwaysWarmAddress(caller.toString());
396
+ if (tx.to !== undefined) {
397
+ // Note: in case we create a contract, we do vm in EVMs `_executeCreate` (vm is also correct in inner calls, per the EIP)
398
+ // Using deprecated bytesToUnprefixedHex for performance: journal methods expect unprefixed hex strings.
399
+ vm.evm.journal.addAlwaysWarmAddress((0, util_1.bytesToUnprefixedHex)(tx.to.bytes));
400
+ }
401
+ if (vm.common.isActivatedEIP(3651)) {
402
+ const coinbase = block?.header.coinbase.bytes ?? DEFAULT_HEADER.coinbase.bytes;
403
+ // Using deprecated bytesToUnprefixedHex for performance: journal methods expect unprefixed hex strings.
404
+ vm.evm.journal.addAlwaysWarmAddress((0, util_1.bytesToUnprefixedHex)(coinbase));
405
+ }
406
+ }
407
+ // ===========================
408
+ // VALIDATION: Gas Limit and Fees
409
+ // ===========================
410
+ // Validate gas limit against tx base fee (DataFee + TxFee + Creation Fee)
411
+ const intrinsicGas = tx.getIntrinsicGas();
412
+ let floorCost = util_1.BIGINT_0;
413
+ // EIP-7623: Calculate floor cost for calldata
414
+ if (vm.common.isActivatedEIP(7623)) {
415
+ // Tx should at least cover the floor price for tx data
416
+ let tokens = 0;
417
+ for (let i = 0; i < tx.data.length; i++) {
418
+ tokens += tx.data[i] === 0 ? 1 : 4;
419
+ }
420
+ floorCost =
421
+ tx.common.param('txGas') + tx.common.param('totalCostFloorPerToken') * BigInt(tokens);
422
+ }
423
+ let gasLimit = tx.gasLimit;
424
+ const minGasLimit = (0, util_1.bigIntMax)(intrinsicGas, floorCost);
425
+ if (gasLimit < minGasLimit) {
426
+ const msg = _errorMsg(`tx gas limit ${Number(gasLimit)} is lower than the minimum gas limit of ${Number(minGasLimit)}`, vm, block, tx);
427
+ throw (0, util_1.EthereumJSErrorWithoutCode)(msg);
428
+ }
429
+ gasLimit -= intrinsicGas;
430
+ if (vm.DEBUG) {
431
+ debugGas(`Subtracting base fee (${intrinsicGas}) from gasLimit (-> ${gasLimit})`);
432
+ }
433
+ if (vm.common.isActivatedEIP(1559)) {
434
+ // EIP-1559 spec:
435
+ // Ensure that the user was willing to at least pay the base fee
436
+ // assert transaction.max_fee_per_gas >= block.base_fee_per_gas
437
+ const maxFeePerGas = 'maxFeePerGas' in tx ? tx.maxFeePerGas : tx.gasPrice;
438
+ const baseFeePerGas = block?.header.baseFeePerGas ?? DEFAULT_HEADER.baseFeePerGas;
439
+ if (maxFeePerGas < baseFeePerGas) {
440
+ const msg = _errorMsg(`Transaction's ${'maxFeePerGas' in tx ? 'maxFeePerGas' : 'gasPrice'} (${maxFeePerGas}) is less than the block's baseFeePerGas (${baseFeePerGas})`, vm, block, tx);
441
+ throw (0, util_1.EthereumJSErrorWithoutCode)(msg);
442
+ }
443
+ }
444
+ if (enableProfiler) {
445
+ // eslint-disable-next-line no-console
446
+ console.timeEnd(initLabel);
447
+ // eslint-disable-next-line no-console
448
+ console.time(balanceNonceLabel);
449
+ }
450
+ // ===========================
451
+ // VALIDATION: Sender Account
452
+ // ===========================
453
+ let fromAccount = await state.getAccount(caller);
454
+ if (fromAccount === undefined) {
455
+ fromAccount = new util_1.Account();
456
+ }
457
+ const { nonce, balance } = fromAccount;
458
+ if (vm.DEBUG) {
459
+ debug(`Sender's pre-tx balance is ${balance}`);
460
+ }
461
+ // EIP-3607: Reject transactions from senders with deployed code
462
+ // EIP-8141 frame transactions allow smart accounts (sender may have code)
463
+ if (!(0, util_1.equalsBytes)(fromAccount.codeHash, util_1.KECCAK256_NULL) && !(0, tx_1.isFrameEIP8141Tx)(tx)) {
464
+ const isActive7702 = vm.common.isActivatedEIP(7702);
465
+ switch (isActive7702) {
466
+ case true: {
467
+ const code = await state.getCode(caller);
468
+ // If the EOA is 7702-delegated, sending txs from this EOA is fine
469
+ if ((0, util_1.equalsBytes)(code.slice(0, 3), DELEGATION_7702_FLAG))
470
+ break;
471
+ // Trying to send TX from account with code (which is not 7702-delegated), falls through and throws
472
+ }
473
+ default: {
474
+ const msg = _errorMsg('invalid sender address, address is not EOA (EIP-3607)', vm, block, tx);
475
+ throw (0, util_1.EthereumJSErrorWithoutCode)(msg);
476
+ }
477
+ }
478
+ }
479
+ // Check balance against upfront tx cost
480
+ const baseFeePerGas = block?.header.baseFeePerGas ?? DEFAULT_HEADER.baseFeePerGas;
481
+ const upFrontCost = tx.getUpfrontCost(baseFeePerGas);
482
+ if (balance < upFrontCost) {
483
+ if (opts.skipBalance === true && fromAccount.balance < upFrontCost) {
484
+ if (tx.supports(tx_1.Capability.EIP1559FeeMarket) === false) {
485
+ // if skipBalance and not EIP1559 transaction, ensure caller balance is enough to run transaction
486
+ const originalBalance = fromAccount.balance;
487
+ fromAccount.balance = upFrontCost;
488
+ await vm.evm.journal.putAccount(caller, fromAccount);
489
+ if (vm.common.isActivatedEIP(7928)) {
490
+ vm.evm.blockLevelAccessList.addBalanceChange(caller.toString(), fromAccount.balance, vm.evm.blockLevelAccessList.blockAccessIndex, originalBalance);
491
+ }
492
+ }
493
+ }
494
+ else {
495
+ const msg = _errorMsg(`sender doesn't have enough funds to send tx. The upfront cost is: ${upFrontCost} and the sender's account (${caller}) only has: ${balance}`, vm, block, tx);
496
+ throw (0, util_1.EthereumJSErrorWithoutCode)(msg);
497
+ }
498
+ }
499
+ // Check balance against max potential cost (for EIP 1559 and 4844)
500
+ let maxCost = tx.value;
501
+ let blobGasPrice = util_1.BIGINT_0;
502
+ let totalblobGas = util_1.BIGINT_0;
503
+ if (tx.supports(tx_1.Capability.EIP1559FeeMarket)) {
504
+ // EIP-1559 spec:
505
+ // The signer must be able to afford the transaction
506
+ // `assert balance >= gas_limit * max_fee_per_gas`
507
+ maxCost += tx.gasLimit * tx.maxFeePerGas;
508
+ }
509
+ if ((0, tx_1.isBlob4844Tx)(tx)) {
510
+ if (!vm.common.isActivatedEIP(4844)) {
511
+ const msg = _errorMsg('blob transactions are only valid with EIP4844 active', vm, block, tx);
512
+ throw (0, util_1.EthereumJSErrorWithoutCode)(msg);
513
+ }
514
+ // EIP-4844 spec
515
+ // the signer must be able to afford the transaction
516
+ // assert signer(tx).balance >= tx.message.gas * tx.message.max_fee_per_gas + get_total_data_gas(tx) * tx.message.max_fee_per_data_gas
517
+ totalblobGas = vm.common.param('blobGasPerBlob') * BigInt(tx.numBlobs());
518
+ maxCost += totalblobGas * tx.maxFeePerBlobGas;
519
+ // 4844 minimum blobGas price check
520
+ blobGasPrice = opts.block?.header.getBlobGasPrice() ?? DEFAULT_HEADER.getBlobGasPrice();
521
+ if (tx.maxFeePerBlobGas < blobGasPrice) {
522
+ const msg = _errorMsg(`Transaction's maxFeePerBlobGas ${tx.maxFeePerBlobGas}) is less than block blobGasPrice (${blobGasPrice}).`, vm, block, tx);
523
+ throw (0, util_1.EthereumJSErrorWithoutCode)(msg);
524
+ }
525
+ }
526
+ if (fromAccount.balance < maxCost) {
527
+ if (opts.skipBalance === true && fromAccount.balance < maxCost) {
528
+ // if skipBalance, ensure caller balance is enough to run transaction
529
+ const originalBalance = fromAccount.balance;
530
+ fromAccount.balance = maxCost;
531
+ await vm.evm.journal.putAccount(caller, fromAccount);
532
+ if (vm.common.isActivatedEIP(7928)) {
533
+ vm.evm.blockLevelAccessList.addBalanceChange(caller.toString(), fromAccount.balance, vm.evm.blockLevelAccessList.blockAccessIndex, originalBalance);
534
+ }
535
+ }
536
+ else {
537
+ const msg = _errorMsg(`sender doesn't have enough funds to send tx. The max cost is: ${maxCost} and the sender's account (${caller}) only has: ${balance}`, vm, block, tx);
538
+ throw (0, util_1.EthereumJSErrorWithoutCode)(msg);
539
+ }
540
+ }
541
+ if (opts.skipNonce !== true) {
542
+ if (nonce !== tx.nonce) {
543
+ const msg = _errorMsg(`the tx doesn't have the correct nonce. account has nonce of: ${nonce} tx has nonce of: ${tx.nonce}`, vm, block, tx);
544
+ throw (0, util_1.EthereumJSErrorWithoutCode)(msg);
545
+ }
546
+ }
547
+ // ===========================
548
+ // CALCULATION: Gas Price
549
+ // ===========================
550
+ let gasPrice;
551
+ let inclusionFeePerGas;
552
+ if (tx.supports(tx_1.Capability.EIP1559FeeMarket)) {
553
+ // TODO make txs use the new getEffectivePriorityFee
554
+ const baseFee = block?.header.baseFeePerGas ?? DEFAULT_HEADER.baseFeePerGas;
555
+ inclusionFeePerGas = tx.getEffectivePriorityFee(baseFee);
556
+ gasPrice = inclusionFeePerGas + baseFee;
557
+ }
558
+ else {
559
+ // Have to cast as legacy tx since EIP1559 tx does not have gas price
560
+ gasPrice = tx.gasPrice;
561
+ if (vm.common.isActivatedEIP(1559)) {
562
+ const baseFee = block?.header.baseFeePerGas ?? DEFAULT_HEADER.baseFeePerGas;
563
+ inclusionFeePerGas = tx.gasPrice - baseFee;
564
+ }
565
+ }
566
+ // EIP-4844 tx
567
+ let blobVersionedHashes;
568
+ if ((0, tx_1.isBlob4844Tx)(tx)) {
569
+ blobVersionedHashes = tx.blobVersionedHashes;
570
+ }
571
+ // ===========================
572
+ // EIP-8141: Frame Transaction Execution (separate path)
573
+ // ===========================
574
+ let results;
575
+ let txCost;
576
+ let blobGasCost;
577
+ let gasRefund = util_1.BIGINT_0;
578
+ let executionTimerPrecise;
579
+ if ((0, tx_1.isFrameEIP8141Tx)(tx)) {
580
+ // Frame transactions: gas deduction and nonce increment happen inside APPROVE
581
+ txCost = util_1.BIGINT_0;
582
+ blobGasCost = util_1.BIGINT_0;
583
+ if (enableProfiler) {
584
+ // eslint-disable-next-line no-console
585
+ console.timeEnd(balanceNonceLabel);
586
+ executionTimerPrecise = performance.now();
587
+ }
588
+ results = await (0, runFrameTx_ts_1.runFrameTransaction)(vm, tx, gasPrice, blobGasPrice, intrinsicGas, block, opts);
589
+ }
590
+ else {
591
+ // ===========================
592
+ // STATE UPDATE: Deduct Costs (standard transactions)
593
+ // ===========================
594
+ txCost = tx.gasLimit * gasPrice;
595
+ blobGasCost = totalblobGas * blobGasPrice;
596
+ const senderOriginalBalance = fromAccount.balance;
597
+ fromAccount.balance -= txCost;
598
+ fromAccount.balance -= blobGasCost;
599
+ if (opts.skipBalance === true && fromAccount.balance < util_1.BIGINT_0) {
600
+ fromAccount.balance = util_1.BIGINT_0;
601
+ }
602
+ await vm.evm.journal.putAccount(caller, fromAccount);
603
+ if (vm.common.isActivatedEIP(7928)) {
604
+ vm.evm.blockLevelAccessList.addBalanceChange(caller.toString(), fromAccount.balance, vm.evm.blockLevelAccessList.blockAccessIndex, senderOriginalBalance);
605
+ }
606
+ // Process EIP-7702 authorization list (if applicable)
607
+ if (tx.supports(tx_1.Capability.EIP7702EOACode)) {
608
+ gasRefund = await processAuthorizationList(vm, tx, caller, gasRefund);
609
+ }
610
+ if (vm.DEBUG) {
611
+ debug(`Update fromAccount (caller) balance (-> ${fromAccount.balance}))`);
612
+ }
613
+ if (enableProfiler) {
614
+ // eslint-disable-next-line no-console
615
+ console.timeEnd(balanceNonceLabel);
616
+ executionTimerPrecise = performance.now();
617
+ }
618
+ // ===========================
619
+ // EXECUTION: Run EVM Call
620
+ // ===========================
621
+ const { value, data, to } = tx;
622
+ if (vm.DEBUG) {
623
+ debug(`Running tx=${tx.isSigned() ? (0, util_1.bytesToHex)(tx.hash()) : 'unsigned'} with caller=${caller} gasLimit=${gasLimit} to=${to?.toString() ?? 'none'} value=${value} data=${(0, util_1.short)(data)}`);
624
+ }
625
+ results = (await vm.evm.runCall({
626
+ block,
627
+ gasPrice,
628
+ caller,
629
+ gasLimit,
630
+ to,
631
+ value,
632
+ data,
633
+ blobVersionedHashes,
634
+ accessWitness: txAccesses,
635
+ }));
636
+ if (vm.common.isActivatedEIP(7864)) {
637
+ ;
638
+ stateAccesses?.merge(txAccesses);
639
+ }
640
+ }
641
+ if (enableProfiler) {
642
+ // eslint-disable-next-line no-console
643
+ console.log(`${executionLabel}: ${performance.now() - executionTimerPrecise}ms`);
644
+ // eslint-disable-next-line no-console
645
+ console.log('[ For execution details see table output ]');
646
+ // eslint-disable-next-line no-console
647
+ console.time(logsGasBalanceLabel);
648
+ }
649
+ if (vm.DEBUG) {
650
+ debug(`Update fromAccount (caller) nonce (-> ${fromAccount.nonce})`);
651
+ }
652
+ if (vm.DEBUG) {
653
+ const { executionGasUsed, exceptionError, returnValue } = results.execResult;
654
+ debug('-'.repeat(100));
655
+ debug(`Received tx execResult: [ executionGasUsed=${executionGasUsed} exceptionError=${exceptionError !== undefined ? `'${exceptionError.error}'` : 'none'} returnValue=${(0, util_1.short)(returnValue)} gasRefund=${results.gasRefund ?? 0} ]`);
656
+ }
657
+ // ===========================
658
+ // RESULTS: Gas and Balances
659
+ // ===========================
660
+ // Calculate tx gas used before refund processing
661
+ const totalGasSpentBeforeRefund = results.execResult.executionGasUsed + intrinsicGas;
662
+ results.totalGasSpent = totalGasSpentBeforeRefund;
663
+ if (vm.DEBUG) {
664
+ debugGas(`tx add baseFee ${intrinsicGas} to totalGasSpent (-> ${results.totalGasSpent})`);
665
+ }
666
+ // Add blob gas used to result
667
+ if ((0, tx_1.isBlob4844Tx)(tx)) {
668
+ results.blobGasUsed = totalblobGas;
669
+ }
670
+ // Process any gas refund
671
+ gasRefund += results.execResult.gasRefund ?? util_1.BIGINT_0;
672
+ results.gasRefund = gasRefund; // TODO: this field could now be incorrect with the introduction of 7623
673
+ const maxRefundQuotient = vm.common.param('maxRefundQuotient');
674
+ if (gasRefund !== util_1.BIGINT_0) {
675
+ const maxRefund = results.totalGasSpent / maxRefundQuotient;
676
+ gasRefund = gasRefund < maxRefund ? gasRefund : maxRefund;
677
+ results.totalGasSpent -= gasRefund;
678
+ if (vm.DEBUG) {
679
+ debug(`Subtract tx gasRefund (${gasRefund}) from totalGasSpent (-> ${results.totalGasSpent})`);
680
+ }
681
+ }
682
+ else {
683
+ if (vm.DEBUG) {
684
+ debug(`No tx gasRefund`);
685
+ }
686
+ }
687
+ if (vm.common.isActivatedEIP(7623)) {
688
+ if (results.totalGasSpent < floorCost) {
689
+ if (vm.DEBUG) {
690
+ debugGas(`tx floorCost ${floorCost} is higher than to total execution gas spent (-> ${results.totalGasSpent}), setting floor as gas paid`);
691
+ }
692
+ results.gasRefund = util_1.BIGINT_0;
693
+ results.totalGasSpent = floorCost;
694
+ }
695
+ }
696
+ // EIP-7778: block-level gas accounting does not subtract tx refunds.
697
+ // For pre-7778 forks this equals the amount paid by the sender.
698
+ results.blockGasSpent = vm.common.isActivatedEIP(7778)
699
+ ? (0, util_1.bigIntMax)(totalGasSpentBeforeRefund, floorCost)
700
+ : results.totalGasSpent;
701
+ results.amountSpent = results.totalGasSpent * gasPrice;
702
+ // Update sender/payer balance with gas refund
703
+ if ((0, tx_1.isFrameEIP8141Tx)(tx)) {
704
+ // EIP-8141: refund unused gas to the payer (set by APPROVE)
705
+ const frameTx = tx;
706
+ const payer = frameTx.getSenderAddress(); // For Example 1, payer == sender
707
+ let payerAccount = await state.getAccount(payer);
708
+ if (payerAccount === undefined)
709
+ payerAccount = new util_1.Account();
710
+ const gasCostCharged = frameTx.gasLimit * gasPrice;
711
+ const actualGasCost = results.totalGasSpent * gasPrice;
712
+ const payerRefund = gasCostCharged - actualGasCost;
713
+ if (payerRefund > util_1.BIGINT_0) {
714
+ payerAccount.balance += payerRefund;
715
+ await vm.evm.journal.putAccount(payer, payerAccount);
716
+ }
717
+ if (vm.DEBUG) {
718
+ debug(`EIP-8141: Refunded ${payerRefund} to payer ${payer} (charged=${gasCostCharged} actual=${actualGasCost})`);
719
+ }
720
+ }
721
+ else {
722
+ fromAccount = await state.getAccount(caller);
723
+ if (fromAccount === undefined) {
724
+ fromAccount = new util_1.Account();
725
+ }
726
+ const actualTxCost = results.totalGasSpent * gasPrice;
727
+ const txCostDiff = txCost - actualTxCost;
728
+ const originalBalance = fromAccount.balance;
729
+ fromAccount.balance += txCostDiff;
730
+ if (vm.common.isActivatedEIP(7928)) {
731
+ vm.evm.blockLevelAccessList.addBalanceChange(caller.toString(), fromAccount.balance, vm.evm.blockLevelAccessList.blockAccessIndex, originalBalance);
732
+ vm.evm.blockLevelAccessList.addNonceChange(caller.toString(), fromAccount.nonce, vm.evm.blockLevelAccessList.blockAccessIndex);
733
+ }
734
+ await vm.evm.journal.putAccount(caller, fromAccount);
735
+ if (vm.common.isActivatedEIP(7928) && txCostDiff > util_1.BIGINT_0) {
736
+ vm.evm.blockLevelAccessList.addBalanceChange(caller.toString(), fromAccount.balance, vm.evm.blockLevelAccessList.blockAccessIndex);
737
+ }
738
+ if (vm.DEBUG) {
739
+ debug(`Refunded txCostDiff (${txCostDiff}) to fromAccount (caller) balance (-> ${fromAccount.balance})`);
740
+ }
741
+ }
742
+ // Update miner's balance
743
+ await updateMinerBalance(vm, state, block, results, inclusionFeePerGas);
744
+ if (enableProfiler) {
745
+ // eslint-disable-next-line no-console
746
+ console.timeEnd(logsGasBalanceLabel);
747
+ // eslint-disable-next-line no-console
748
+ console.time(accountsCleanUpLabel);
749
+ }
750
+ // ===========================
751
+ // CLEANUP: Accounts and State
752
+ // ===========================
753
+ await processSelfdestructs(vm, results);
754
+ // Generate the bloom after selfdestruct finalization logs have been appended.
755
+ results.bloom = txLogsBloom(results.execResult.logs, vm.common);
756
+ if (vm.DEBUG) {
757
+ debug(`Generated tx bloom with logs=${results.execResult.logs?.length}`);
758
+ }
759
+ if (enableProfiler) {
760
+ // eslint-disable-next-line no-console
761
+ console.timeEnd(accountsCleanUpLabel);
762
+ // eslint-disable-next-line no-console
763
+ console.time(accessListLabel);
764
+ }
765
+ // Build access list result if requested
766
+ if (opts.reportAccessList === true && vm.common.isActivatedEIP(2930)) {
767
+ results.accessList = buildAccessListResult(vm);
768
+ }
769
+ if (enableProfiler) {
770
+ // eslint-disable-next-line no-console
771
+ console.timeEnd(accessListLabel);
772
+ // eslint-disable-next-line no-console
773
+ console.time(journalCacheCleanUpLabel);
774
+ }
775
+ // Collect preimages if requested
776
+ if (opts.reportPreimages === true && vm.evm.journal.preimages !== undefined) {
777
+ results.preimages = vm.evm.journal.preimages;
778
+ }
779
+ // Clear journal and caches
780
+ await vm.evm.journal.cleanup();
781
+ state.originalStorageCache.clear();
782
+ if (enableProfiler) {
783
+ // eslint-disable-next-line no-console
784
+ console.timeEnd(journalCacheCleanUpLabel);
785
+ // eslint-disable-next-line no-console
786
+ console.time(receiptsLabel);
787
+ }
788
+ // ===========================
789
+ // FINALIZE: Receipt and Events
790
+ // ===========================
791
+ const gasUsed = opts.blockGasUsed ?? block?.header.gasUsed ?? DEFAULT_HEADER.gasUsed;
792
+ const cumulativeGasUsed = gasUsed + results.totalGasSpent;
793
+ results.receipt = await generateTxReceipt(vm, tx, results, cumulativeGasUsed, totalblobGas, blobGasPrice);
794
+ if (enableProfiler) {
795
+ // eslint-disable-next-line no-console
796
+ console.timeEnd(receiptsLabel);
797
+ }
798
+ // EIP-7928: Clean up net-zero balance changes
799
+ // Per spec, if an account's balance changed during tx but final == pre-tx, don't record
800
+ if (vm.common.isActivatedEIP(7928)) {
801
+ vm.evm.blockLevelAccessList.cleanupNetZeroBalanceChanges();
802
+ }
803
+ /** The `afterTx` event - emits transaction results */
804
+ const event = { transaction: tx, ...results };
805
+ await vm._emit('afterTx', event);
806
+ if (vm.DEBUG) {
807
+ debug(`tx run finished hash=${opts.tx.isSigned() ? (0, util_1.bytesToHex)(opts.tx.hash()) : 'unsigned'} sender=${caller}`);
808
+ }
809
+ return results;
810
+ }
811
+ /**
812
+ * @method txLogsBloom
813
+ * @private
814
+ */
815
+ function txLogsBloom(logs, common) {
816
+ const bloom = new index_ts_1.Bloom(undefined, common);
817
+ if (logs) {
818
+ for (let i = 0; i < logs.length; i++) {
819
+ const log = logs[i];
820
+ // add the address
821
+ bloom.add(log[0]);
822
+ // add the topics
823
+ const topics = log[1];
824
+ for (let q = 0; q < topics.length; q++) {
825
+ bloom.add(topics[q]);
826
+ }
827
+ }
828
+ }
829
+ return bloom;
830
+ }
831
+ /**
832
+ * Returns the tx receipt.
833
+ * @param vm The vm instance
834
+ * @param tx The transaction
835
+ * @param txResult The tx result
836
+ * @param cumulativeGasUsed The gas used in the block including vm tx
837
+ * @param blobGasUsed The blob gas used in the tx
838
+ * @param blobGasPrice The blob gas price for the block including vm tx
839
+ */
840
+ async function generateTxReceipt(vm, tx, txResult, cumulativeGasUsed, blobGasUsed, blobGasPrice) {
841
+ const baseReceipt = {
842
+ cumulativeBlockGasUsed: cumulativeGasUsed,
843
+ bitvector: txResult.bloom.bitvector,
844
+ logs: txResult.execResult.logs ?? [],
845
+ };
846
+ let receipt;
847
+ if (vm.DEBUG) {
848
+ debug(`Generate tx receipt transactionType=${tx.type} cumulativeBlockGasUsed=${cumulativeGasUsed} bitvector=${(0, util_1.short)(baseReceipt.bitvector)} (${baseReceipt.bitvector.length} bytes) logs=${baseReceipt.logs.length}`);
849
+ }
850
+ if (!tx.supports(tx_1.Capability.EIP2718TypedTransaction)) {
851
+ // Legacy transaction
852
+ if (vm.common.gteHardfork(common_1.Hardfork.Byzantium)) {
853
+ // Post-Byzantium
854
+ receipt = {
855
+ status: txResult.execResult.exceptionError !== undefined ? 0 : 1, // Receipts have a 0 as status on error
856
+ ...baseReceipt,
857
+ };
858
+ }
859
+ else {
860
+ // Pre-Byzantium
861
+ const stateRoot = await vm.stateManager.getStateRoot();
862
+ receipt = {
863
+ stateRoot,
864
+ ...baseReceipt,
865
+ };
866
+ }
867
+ }
868
+ else {
869
+ // Typed EIP-2718 Transaction
870
+ if ((0, tx_1.isBlob4844Tx)(tx)) {
871
+ receipt = {
872
+ blobGasUsed,
873
+ blobGasPrice,
874
+ status: txResult.execResult.exceptionError ? 0 : 1,
875
+ ...baseReceipt,
876
+ };
877
+ }
878
+ else {
879
+ receipt = {
880
+ status: txResult.execResult.exceptionError ? 0 : 1,
881
+ ...baseReceipt,
882
+ };
883
+ }
884
+ }
885
+ return receipt;
886
+ }
887
+ /**
888
+ * Internal helper function to create an annotated error message
889
+ *
890
+ * @param msg Base error message
891
+ * @hidden
892
+ */
893
+ function _errorMsg(msg, vm, block, tx) {
894
+ const blockOrHeader = block ?? DEFAULT_HEADER;
895
+ const blockErrorStr = 'errorStr' in blockOrHeader ? blockOrHeader.errorStr() : 'block';
896
+ const txErrorStr = 'errorStr' in tx ? tx.errorStr() : 'tx';
897
+ const errorMsg = `${msg} (${vm.errorStr()} -> ${blockErrorStr} -> ${txErrorStr})`;
898
+ return errorMsg;
899
+ }
900
+ //# sourceMappingURL=runTx.js.map