UNPKG

@tevm/actions

Version:

A typesafe library for writing forge scripts in typescript

1,408 lines (1,389 loc) 223 kB
'use strict'; var errors = require('@tevm/errors'); var zod = require('zod'); var zod$1 = require('abitype/zod'); var address = require('@tevm/address'); var viem = require('viem'); var node = require('@tevm/node'); var tx = require('@tevm/tx'); var utils = require('@tevm/utils'); var vm = require('@tevm/vm'); var blockchain = require('@tevm/blockchain'); var state = require('@tevm/state'); var evm = require('@tevm/evm'); var receiptManager = require('@tevm/receipt-manager'); var block = require('@tevm/block'); var contract = require('@tevm/contract'); var jsonrpc = require('@tevm/jsonrpc'); // src/BaseCall/validateBaseCallParams.js var zCallEvents = zod.z.object({ onStep: zod.z.function().optional().describe("Handler called on each EVM step (instruction execution)"), onNewContract: zod.z.function().optional().describe("Handler called when a new contract is created"), onBeforeMessage: zod.z.function().optional().describe("Handler called before a message (call) is processed"), onAfterMessage: zod.z.function().optional().describe("Handler called after a message (call) is processed") }).partial(); var zAddress = zod$1.Address.describe("A valid ethereum address"); var zBlockOverrideSet = zod.z.strictObject({ number: zod.z.bigint().gte(0n).optional(), time: zod.z.bigint().gte(0n).optional(), gasLimit: zod.z.bigint().gte(0n).optional(), coinbase: zAddress.optional(), baseFee: zod.z.bigint().gte(0n).optional(), blobBaseFee: zod.z.bigint().gte(0n).optional() }); var hexRegex = /^0x[0-9a-fA-F]*$/; var zHex = zod.z.string().transform((value, ctx) => { if (!hexRegex.test(value)) { ctx.addIssue({ code: zod.z.ZodIssueCode.custom, message: "value must be a hex string" }); } return ( /** @type {import('@tevm/utils').Hex}*/ value ); }).describe("A hex string"); // src/internal/zod/zBlockParam.js var zBlockParam = zod.z.union([ zod.z.literal("latest"), zod.z.literal("earliest"), zod.z.literal("pending"), zod.z.literal("safe"), zod.z.literal("finalized"), zod.z.bigint(), zod.z.number().transform((n) => BigInt(n)), // Add number support with transformation zHex ]); var zStateOverrideSet = zod.z.record( zAddress, zod.z.strictObject({ balance: zod.z.bigint().gte(0n).optional(), nonce: zod.z.bigint().gte(0n).optional(), code: zHex.optional(), state: zod.z.record(zHex, zHex).optional(), stateDiff: zod.z.record(zHex, zHex).optional() }) ); var zBaseParams = zod.z.object({ throwOnFail: zod.z.boolean().optional().describe( "If true the action handler will throw errors rather than returning errors an the `errors` property. Defaults to true." ) }).describe("Properties shared across actions"); // src/BaseCall/zBaseCallParams.js var zBaseCallParams = zBaseParams.extend({ ...zCallEvents.shape, createTrace: zod.z.boolean().optional().describe("If true, the call will also return a `trace` on the trace property"), createAccessList: zod.z.boolean().optional().describe("If true, the call will also return a `accessList` on the accessList property"), createTransaction: zod.z.union([ zod.z.boolean().optional().describe("If true, this call is a create transaction. Defaults to false."), zod.z.literal("on-success"), zod.z.literal("always"), zod.z.literal("never") ]), addToMempool: zod.z.union([ zod.z.boolean().optional().describe("If true, this call adds the transaction to the mempool. Defaults to false."), zod.z.literal("on-success"), zod.z.literal("always"), zod.z.literal("never") ]), addToBlockchain: zod.z.union([ zod.z.boolean().optional().describe("If true, this call adds the transaction to the blockchain. Defaults to false."), zod.z.literal("on-success"), zod.z.literal("always"), zod.z.literal("never") ]), skipBalance: zod.z.boolean().optional().describe("Set caller to msg.value of less than msg.value Defaults to false."), gasRefund: zod.z.bigint().nonnegative().optional().describe("Refund counter. Defaults to 0"), blockTag: zBlockParam.optional().describe('The block tag as a block number, block hash or one of "latest", "earliest", "pending" or "safe"'), gasPrice: zod.z.bigint().optional().describe("The gas price for the call. Defaults to `0`"), origin: zAddress.optional().describe("The address where the call originated from. Defaults to the zero address."), caller: zAddress.optional().describe("The address that ran this code (`msg.sender`). Defaults to the zero address."), gas: zod.z.bigint().nonnegative().optional().describe("The gas limit for the call. Defaults to `16777215` (`0xffffff`)"), value: zod.z.bigint().nonnegative().optional().describe("The value in ether that is being sent to `opts.address`. Defaults to `0`"), depth: zod.z.number().nonnegative().optional().describe("The call depth. Defaults to `0`"), selfdestruct: zod.z.set(zAddress).optional().describe("Addresses to selfdestruct. Defaults to the empty set."), to: zAddress.optional().describe( "The address of the account that is executing this code (`address(this)`). Defaults to the zero address." ), blobVersionedHashes: zod.z.array(zHex).optional().describe("Versioned hashes for each blob in a blob transaction"), stateOverrideSet: zStateOverrideSet.optional().describe("State override set for the call"), blockOverrideSet: zBlockOverrideSet.optional().describe("Block override set for the call"), maxFeePerGas: zod.z.bigint().optional().describe( "The maximum fee per gas for the call for an EIP-1559 tx. If not set it will be calculated based on the parent block." ), maxPriorityFeePerGas: zod.z.bigint().optional().describe("The maximum priority fee per gas for the call for an EIP-1559 tx.") }).refine( (params) => { if (params.addToMempool !== void 0 && params.addToBlockchain !== void 0) { return false; } return true; }, { message: "Cannot set both addToMempool and addToBlockchain simultaneously. Use one or the other." } ).describe("Properties shared across call-like actions"); // src/BaseCall/validateBaseCallParams.js var validateBaseCallParams = (action) => { const errors$1 = []; const parsedParams = zBaseCallParams.safeParse(action); if (parsedParams.success === false) { const formattedErrors = parsedParams.error.format(); formattedErrors._errors.forEach((error) => { errors$1.push(new errors.InvalidParamsError(error)); }); if (formattedErrors.skipBalance) { formattedErrors.skipBalance._errors.forEach((error) => { errors$1.push(new errors.InvalidSkipBalanceError(error)); }); } if (formattedErrors.gasRefund) { formattedErrors.gasRefund._errors.forEach((error) => { errors$1.push(new errors.InvalidGasRefundError(error)); }); } if (formattedErrors.blockTag) { formattedErrors.blockTag._errors.forEach((error) => { errors$1.push(new errors.InvalidBlockError(error)); }); } if (formattedErrors.gas) { formattedErrors.gas._errors.forEach((error) => { errors$1.push(new errors.InvalidGasPriceError(error)); }); } if (formattedErrors.origin) { formattedErrors.origin._errors.forEach((error) => { errors$1.push(new errors.InvalidOriginError(error)); }); } if (formattedErrors.caller) { formattedErrors.caller._errors.forEach((error) => { errors$1.push(new errors.InvalidCallerError(error)); }); } if (formattedErrors.gas) { formattedErrors.gas._errors.forEach((error) => { errors$1.push(new errors.InvalidGasPriceError(error)); }); } if (formattedErrors.value) { formattedErrors.value._errors.forEach((error) => { errors$1.push(new errors.InvalidValueError(error)); }); } if (formattedErrors.depth) { formattedErrors.depth._errors.forEach((error) => { errors$1.push(new errors.InvalidDepthError(error)); }); } if (formattedErrors.selfdestruct) { formattedErrors.selfdestruct._errors.forEach((error) => { errors$1.push(new errors.InvalidSelfdestructError(error)); }); } if (formattedErrors.to) { formattedErrors.to._errors.forEach((error) => { errors$1.push(new errors.InvalidToError(error)); }); } if (formattedErrors.blobVersionedHashes) { formattedErrors.blobVersionedHashes._errors.forEach((error) => { errors$1.push(new errors.InvalidBlobVersionedHashesError(error)); }); for (const [key, value] of Object.entries(formattedErrors.blobVersionedHashes)) { if (key === "_errors") continue; if ("_errors" in value) { value._errors.forEach((error) => { errors$1.push(new errors.InvalidBlobVersionedHashesError(error)); }); } } } if (formattedErrors.maxFeePerGas) { formattedErrors.maxFeePerGas._errors.forEach((error) => { errors$1.push(new errors.InvalidMaxFeePerGasError(error)); }); } if (formattedErrors.maxPriorityFeePerGas) { formattedErrors.maxPriorityFeePerGas._errors.forEach((error) => { errors$1.push(new errors.InvalidMaxPriorityFeePerGasError(error)); }); } if (formattedErrors.addToMempool) { formattedErrors.addToMempool._errors.forEach((error) => { errors$1.push(new errors.InvalidAddToMempoolError(error)); }); } if (formattedErrors.addToBlockchain) { formattedErrors.addToBlockchain._errors.forEach((error) => { errors$1.push(new errors.InvalidAddToBlockchainError(error)); }); } if (errors$1.length === 0 && parsedParams.success === false) { errors$1.push(new errors.InvalidParamsError(parsedParams.error.message)); } } return errors$1; }; var forkAndCacheBlock = async (client, block, executeBlock = false) => { client.logger.debug("Forking a new block based on block tag..."); const vm = await client.getVm().then((vm2) => vm2.deepCopy()); if (!client.forkTransport) { throw new errors.InternalError("Cannot forkAndCacheBlock without a fork url"); } vm.stateManager = state.createStateManager({ ...vm.evm.stateManager._baseState.options, fork: { transport: client.forkTransport, blockTag: block.header.number } }); vm.evm.stateManager = vm.stateManager; vm.blockchain = await blockchain.createChain({ fork: { transport: client.forkTransport, blockTag: block.header.number }, common: vm.common, // TODO silent not being in this type is a bug loggingLevel: ( /** @type {any}*/ client.logger.level ) }); vm.evm.blockchain = vm.blockchain; await Promise.all([vm.stateManager.ready(), vm.blockchain.ready()]); if (executeBlock) { const transactions = ( /** @type {import('@tevm/block').Block}*/ block.transactions ); client.logger.debug({ count: transactions.length }, "Processing transactions"); await Promise.all( transactions.map(async (tx, i) => { client.logger.debug({ txNumber: i, tx }, "Processing transaction"); await vm.evm.shallowCopy().runCall(tx); }) ); client.logger.debug("Finished processing block transactions and saving state root"); } vm.stateManager.saveStateRoot(block.header.stateRoot, await vm.stateManager.dumpCanonicalGenesis()); return vm; }; // src/Call/cloneVmWithBlock.js var cloneVmWithBlockTag = async (client, block) => { try { client.logger.debug("Preparing vm to execute a call with block..."); const originalVm = await client.getVm(); if (client.forkTransport && !await originalVm.stateManager.hasStateRoot(block.header.stateRoot)) { return await forkAndCacheBlock(client, block).catch((e) => { return new errors.ForkError(e instanceof Error ? e.message : "Unknown error", { cause: e }); }); } const vm = await originalVm.deepCopy(); await vm.stateManager.setStateRoot(block.header.stateRoot); return vm; } catch (e) { return new errors.InternalError(e instanceof Error ? e.message : "unknown error", { cause: ( /** @type {Error}*/ e ) }); } }; // src/internal/maybeThrowOnFail.js var maybeThrowOnFail = (throwOnFail, result) => { if (!throwOnFail) { return ( /** @type {any}*/ result ); } if ((result?.errors?.length ?? 0) === 1) { throw result.errors?.[0]; } if ((result?.errors?.length ?? 0) > 1) { throw new AggregateError(result?.errors ?? []); } return ( /** @type {any}*/ result ); }; var callHandler = async (handler, data, secondParam) => { if (typeof handler === "function") { let hasCalledNext = false; const next = () => { hasCalledNext = true; }; try { let result; if (secondParam !== void 0) { result = handler(data, secondParam, next); } else { result = handler(data, next); } if (result instanceof Promise) { await result; } if (!hasCalledNext) { } } catch (error) { console.error("Error in event handler:", error); } } }; var emitEvents = async (client, newBlocks, newReceipts, params = {}) => { const { onBlock, onReceipt, onLog } = params; for (const block of newBlocks) { client.emit("newBlock", block); await callHandler(onBlock, block); const blockHash = utils.bytesToHex(block.hash()); const receipts = newReceipts.get(blockHash); if (!receipts) { throw new Error( `InternalError: Receipts not found for block hash ${blockHash} in mineHandler. This indicates a bug in tevm.` ); } for (const receipt of receipts) { client.emit("newReceipt", receipt); await callHandler(onReceipt, receipt, blockHash); for (const log of receipt.logs) { client.emit("newLog", log); await callHandler(onLog, log, receipt); } } } }; var zMineParams = zBaseParams.extend({ blockCount: zod.z.number().int().gte(0).optional(), interval: zod.z.number().int().gte(0).optional(), onBlock: zod.z.function().optional(), onReceipt: zod.z.function().optional(), onLog: zod.z.function().optional() }); // src/Mine/validateMineParams.js var validateMineParams = (action) => { const errors$1 = []; const parsedParams = zMineParams.safeParse(action); if (parsedParams.success === false) { const formattedErrors = parsedParams.error.format(); formattedErrors._errors.forEach((error) => { errors$1.push(new errors.InvalidRequestError(error)); }); if (formattedErrors.blockCount) { formattedErrors.blockCount._errors.forEach((error) => { errors$1.push(new errors.InvalidAddressError(error)); }); } if (formattedErrors.interval) { formattedErrors.interval._errors.forEach((error) => { errors$1.push(new errors.InvalidNonceError(error)); }); } if (formattedErrors.throwOnFail) { formattedErrors.throwOnFail._errors.forEach((error) => { errors$1.push(new errors.InvalidBalanceError(error)); }); } } return errors$1; }; // src/Mine/mineHandler.js var mineHandler = (client, options = {}) => async ({ throwOnFail = options.throwOnFail ?? true, tx, ...params } = {}) => { switch (client.status) { case "MINING": { const err = new errors.MisconfiguredClientError("Mining is already in progress"); return maybeThrowOnFail(throwOnFail, { errors: [err] }); } case "INITIALIZING": { await client.ready(); client.status = "MINING"; break; } case "SYNCING": { const err = new errors.MisconfiguredClientError("Syncing not currently implemented"); return maybeThrowOnFail(throwOnFail, { errors: [err] }); } case "STOPPED": { const err = new errors.MisconfiguredClientError("Client is stopped"); return maybeThrowOnFail(throwOnFail, { errors: [err] }); } case "READY": { client.status = "MINING"; break; } default: { const err = new errors.UnreachableCodeError(client.status); return maybeThrowOnFail(throwOnFail, { errors: [err] }); } } try { client.logger.debug({ throwOnFail, ...params }, "mineHandler called with params"); const errors$1 = validateMineParams(params); if (errors$1.length > 0) { return maybeThrowOnFail(throwOnFail, { errors: errors$1 }); } const { interval = 1, blockCount = 1 } = params; const newBlocks = []; const newReceipts = /* @__PURE__ */ new Map(); client.logger.debug({ blockCount }, "processing txs"); const pool = await client.getTxPool(); const originalVm = await client.getVm(); const vm = await originalVm.deepCopy(); const receiptsManager = await client.getReceiptsManager(); for (let count = 0; count < blockCount; count++) { const parentBlock = await vm.blockchain.getCanonicalHeadBlock(); let timestamp = Math.max(Math.floor(Date.now() / 1e3), Number(parentBlock.header.timestamp)); timestamp = count === 0 ? timestamp : timestamp + interval; const blockBuilder = await vm.buildBlock({ parentBlock, headerData: { timestamp, number: parentBlock.header.number + 1n, // The following 2 are currently not supported // difficulty: undefined, // coinbase, gasLimit: parentBlock.header.gasLimit, baseFeePerGas: parentBlock.header.calcNextBaseFee() }, blockOpts: { // Proof of authority not currently supported // cliqueSigner, // proof of work not currently supported //calcDifficultyFromHeader, //ck freeze: false, setHardfork: false, putBlockIntoBlockchain: false, common: vm.common } }); const orderedTx = tx !== void 0 ? [ (() => { const mempoolTx = pool.getByHash(tx); pool.removeByHash(tx); return mempoolTx; })() ] : await pool.txsByPriceAndNonce({ baseFee: parentBlock.header.calcNextBaseFee() }); let index = 0; const blockFull = false; const receipts = []; while (index < orderedTx.length && !blockFull) { const nextTx = ( /** @type {import('@tevm/tx').TypedTransaction}*/ orderedTx[index] ); client.logger.debug(utils.bytesToHex(nextTx.hash()), "new tx added"); const txResult = await blockBuilder.addTransaction(nextTx, { skipBalance: true, skipNonce: true, skipHardForkValidation: true }); receipts.push(txResult.receipt); index++; } await vm.stateManager.checkpoint(); const createNewStateRoot = true; await vm.stateManager.commit(createNewStateRoot); const block = await blockBuilder.build(); await Promise.all([receiptsManager.saveReceipts(block, receipts), vm.blockchain.putBlock(block)]); pool.removeNewBlockTxs([block]); newBlocks.push(block); newReceipts.set(utils.bytesToHex(block.hash()), receipts); const value = vm.stateManager._baseState.stateRoots.get(utils.bytesToHex(block.header.stateRoot)); if (!value) { return maybeThrowOnFail(throwOnFail, { errors: [ new errors.InternalError( "InternalError: State root not found in mineHandler. This indicates a potential inconsistency in state management." ) ] }); } originalVm.stateManager.saveStateRoot(block.header.stateRoot, value); } originalVm.blockchain = vm.blockchain; originalVm.evm.blockchain = vm.evm.blockchain; receiptsManager.chain = vm.evm.blockchain; await originalVm.stateManager.setStateRoot(utils.hexToBytes(vm.stateManager._baseState.getCurrentStateRoot())); await emitEvents(client, newBlocks, newReceipts, params); return { blockHashes: newBlocks.map((b) => utils.bytesToHex(b.hash())) }; } catch (e) { return maybeThrowOnFail(throwOnFail, { errors: [new errors.InternalError( /** @type {Error} */ e.message, { cause: e } )] }); } finally { client.status = "READY"; } }; // src/internal/getPendingClient.js var getPendingClient = async (client) => { const pendingClient = await client.deepCopy(); const txPool = await pendingClient.getTxPool(); const blockHashes = []; while (txPool.txsInPool > 0) { const { errors, blockHashes: newBlockHashes } = await mineHandler(pendingClient)({ throwOnFail: false }); if (errors !== void 0) { return { errors }; } blockHashes.push(...newBlockHashes); } return { pendingClient, blockHashes }; }; var zGetAccountParams = zBaseParams.extend({ address: zAddress, blockTag: zBlockParam.optional().describe('Block tag to execute call on. defaults to "latest"'), returnStorage: zod.z.boolean().optional().describe("If true will return storage. Defaults to false. This can be expensive") }).describe("Params to create an account or contract"); // src/GetAccount/validateGetAccountParams.js var validateGetAccountParams = (action) => { const errors$1 = []; const parsedParams = zGetAccountParams.safeParse(action); if (parsedParams.success === false) { const formattedErrors = parsedParams.error.format(); if (formattedErrors.throwOnFail) { for (const err of formattedErrors.throwOnFail._errors) { errors$1.push( new errors.InvalidRequestError(`Invalid throwOnFail param. throwOnFail must be a boolean or not provided. ${err}`) ); } } if (formattedErrors.returnStorage) { for (const err of formattedErrors.returnStorage._errors) { errors$1.push( new errors.InvalidRequestError( `Invalid returnStorage param. returnStorage must be a boolean or not provided. ${err}` ) ); } } if (formattedErrors.address) { for (const err of formattedErrors.address._errors) { errors$1.push(new errors.InvalidAddressError(`Invalid address param. ${err}`)); } } if (formattedErrors.blockTag) { for (const err of formattedErrors.blockTag._errors) { errors$1.push(new errors.InvalidRequestError(`Invalid blockTag param. ${err}`)); } } formattedErrors._errors.forEach((error) => { errors$1.push(new errors.InvalidRequestError(error)); }); } return errors$1; }; // src/GetAccount/getAccountHandler.js var getAccountHandler = (client, options = {}) => async ({ throwOnFail = options.throwOnFail ?? true, ...params }) => { const vm = await client.getVm(); const errors$1 = validateGetAccountParams(params); if (errors$1.length > 0) { return maybeThrowOnFail(throwOnFail, { errors: errors$1, address: params.address, balance: 0n, /** * @type {`0x${string}`} */ storageRoot: "0x", nonce: 0n, /** * @type {`0x${string}`} */ deployedBytecode: "0x", /** * @type {`0x${string}`} */ codeHash: "0x", isContract: false, isEmpty: true }); } const address$1 = address.createAddress(params.address); try { if (params.blockTag === "pending") { const mineResult = await getPendingClient(client); if (mineResult.errors) { return maybeThrowOnFail(throwOnFail, { errors: mineResult.errors, address: params.address, balance: 0n, /** * @type {`0x${string}`} */ storageRoot: "0x", nonce: 0n, /** * @type {`0x${string}`} */ deployedBytecode: "0x", /** * @type {`0x${string}`} */ codeHash: "0x", isContract: false, isEmpty: true }); } return getAccountHandler(mineResult.pendingClient, options)({ throwOnFail, ...params, blockTag: "latest" }); } if (params.blockTag !== "latest" && params.blockTag !== void 0) { const block = await vm.blockchain.getBlockByTag(params.blockTag); const clonedVm = await cloneVmWithBlockTag(client, block); if (clonedVm instanceof errors.ForkError || clonedVm instanceof errors.InternalError) { return maybeThrowOnFail(throwOnFail, { errors: [clonedVm], address: params.address, balance: 0n, /** * @type {`0x${string}`} */ storageRoot: "0x", nonce: 0n, /** * @type {`0x${string}`} */ deployedBytecode: "0x", /** * @type {`0x${string}`} */ codeHash: "0x", isContract: false, isEmpty: true }); } return getAccountHandler( { ...client, getVm: () => Promise.resolve(clonedVm) }, options )({ throwOnFail, ...params, blockTag: "latest" }); } const res = await vm.stateManager.getAccount(address$1); if (!res) { return maybeThrowOnFail(throwOnFail, { address: params.address, balance: 0n, /** * @type {`0x${string}`} */ storageRoot: "0x", nonce: 0n, /** * @type {`0x${string}`} */ deployedBytecode: "0x", errors: [new errors.AccountNotFoundError(`account ${params.address} not found`)], /** * @type {`0x${string}`} */ codeHash: "0x", isContract: false, isEmpty: true }); } const code = res?.codeHash !== void 0 ? utils.bytesToHex(await vm.stateManager.getCode(address$1)) : "0x"; return { // TODO some of these fields are not in the api and should be added to @tevm/actions address: params.address, balance: res.balance, codeHash: utils.bytesToHex(res.codeHash), isContract: res.isContract(), isEmpty: res.isEmpty(), deployedBytecode: code, nonce: res.nonce, storageRoot: utils.bytesToHex(res.storageRoot), ...params.returnStorage ? { storage: Object.fromEntries( Object.entries(await vm.stateManager.dumpStorage(address$1)).map(([key, value]) => [ `0x${key}`, /** @type {import('../common/Hex.js').Hex}*/ value ]) ) } : {} }; } catch (e) { let err = e; if (typeof e !== "object" || e === null || !("_tag" in e)) { err = new errors.InternalError("UnexpectedError in getAccountHandler", { cause: ( /** @type {any}*/ e ) }); } errors$1.push( /** @type any*/ err ); return maybeThrowOnFail(throwOnFail, { errors: errors$1, address: params.address, balance: 0n, /** * @type {`0x${string}`} */ storageRoot: "0x", /** * @type {`0x${string}`} */ codeHash: "0x", nonce: 0n, /** * @type {`0x${string}`} */ deployedBytecode: "0x", isContract: false, isEmpty: true }); } }; // src/internal/zod/zBytecode.js var isValidEthereumBytecode = (bytecode) => { const rawBytecode = bytecode.slice(2); if (rawBytecode.length === 0 || rawBytecode.length % 2 !== 0) { return false; } return true; }; var zBytecode = zHex.refine(isValidEthereumBytecode, { message: "InvalidLength" }).describe("Valid bytecode"); var storageRootRegex = /^0x[0-9a-fA-F]{64}$/; var zStorageRoot = zod.z.string().transform((value, ctx) => { if (!storageRootRegex.test(value)) { ctx.addIssue({ code: zod.z.ZodIssueCode.custom, message: "Value must be a 32-byte hex string (64 hex characters with a 0x prefix)" }); } return value; }).describe("Valid ethereum storage root"); // src/SetAccount/zSetAccountParams.js var zSetAccountParams = zBaseParams.extend({ address: zAddress.describe("The ethereum address of the account"), balance: zod.z.bigint().nonnegative().optional().describe("The balance to give the account"), nonce: zod.z.bigint().nonnegative().optional().describe("The nonce to give the account"), deployedBytecode: zBytecode.optional().describe("The contract bytecode to set at the account address as a >0 byte hex string"), storageRoot: zStorageRoot.optional().describe("The storage root to set at the account address as a 32 byte hex strign"), state: zod.z.record(zHex, zHex).optional().describe("Overrides entire state with provided state"), stateDiff: zod.z.record(zHex, zHex).optional().describe("Patches the state with the provided state") }).refine( (data) => { if (data.state && data.stateDiff) { return false; } return true; }, { message: "Cannot have both state and stateDiff" } ).describe("Params to create an account or contract"); // src/SetAccount/validateSetAccountParams.js var validateSetAccountParams = (action) => { const errors$1 = []; const parsedParams = zSetAccountParams.safeParse(action); if (parsedParams.success === false) { const formattedErrors = parsedParams.error.format(); formattedErrors._errors.forEach((error) => { errors$1.push(new errors.InvalidRequestError(error)); }); if (formattedErrors.address) { formattedErrors.address._errors.forEach((error) => { errors$1.push(new errors.InvalidAddressError(error)); }); } if (formattedErrors.nonce) { formattedErrors.nonce._errors.forEach((error) => { errors$1.push(new errors.InvalidNonceError(error)); }); } if (formattedErrors.balance) { formattedErrors.balance._errors.forEach((error) => { errors$1.push(new errors.InvalidBalanceError(error)); }); } if (formattedErrors.deployedBytecode) { formattedErrors.deployedBytecode._errors.forEach((error) => { errors$1.push(new errors.InvalidDeployedBytecodeError(error)); }); } if (formattedErrors.storageRoot) { formattedErrors.storageRoot._errors.forEach((error) => { errors$1.push(new errors.InvalidStorageRootError(error)); }); } if (formattedErrors.state) { formattedErrors.state._errors.forEach((error) => { errors$1.push(new errors.InvalidRequestError(error)); }); } if (formattedErrors.stateDiff) { formattedErrors.stateDiff._errors.forEach((error) => { errors$1.push(new errors.InvalidRequestError(error)); }); } if (formattedErrors.throwOnFail) { formattedErrors.throwOnFail._errors.forEach((error) => { errors$1.push(new errors.InvalidRequestError(error)); }); } } return errors$1; }; // src/SetAccount/setAccountHandler.js var setAccountHandler = (client, options = {}) => async (params) => { const { throwOnFail = options.throwOnFail ?? true } = params; const errors$1 = validateSetAccountParams(params); if (errors$1.length > 0) { return maybeThrowOnFail(throwOnFail, { errors: errors$1 }); } const address$1 = address.createAddress(params.address); const promises = []; try { const vm = await client.getVm(); const account = await getAccountHandler(client)({ ...params, throwOnFail: false }); if (account.errors?.length && !(account.errors[0] instanceof errors.AccountNotFoundError)) { client.logger.error("there was an unexpected error getting account", account.errors); throw account.errors.length > 1 ? new AggregateError(account.errors) : account.errors[0]; } const accountData = { nonce: params.nonce ?? account?.nonce, balance: params.balance ?? account?.balance }; const storageRoot = (params.storageRoot && utils.hexToBytes(params.storageRoot)) ?? (account?.storageRoot !== void 0 && account?.storageRoot !== "0x" ? utils.hexToBytes(account.storageRoot) : void 0); const codeHash = (params.deployedBytecode && utils.hexToBytes(utils.keccak256(params.deployedBytecode))) ?? (account?.deployedBytecode !== void 0 ? utils.hexToBytes(utils.keccak256(account.deployedBytecode)) : void 0); if (storageRoot !== void 0) { accountData.storageRoot = storageRoot; } if (codeHash !== void 0) { accountData.codeHash = codeHash; } promises.push(vm.stateManager.putAccount(address$1, utils.createAccount(accountData))); if (params.deployedBytecode) { promises.push(vm.stateManager.putCode(address$1, utils.hexToBytes(params.deployedBytecode))); } if (params.state) { await vm.stateManager.clearStorage(address$1); } const state = params.state ?? params.stateDiff; if (state) { for (const [key, value] of Object.entries(state)) { promises.push( vm.stateManager.putStorage( address$1, utils.hexToBytes( /** @type {import('@tevm/utils').Hex}*/ key, { size: 32 } ), utils.hexToBytes(value) ) ); } } const results = await Promise.allSettled(promises); for (const result of results) { if (result.status === "rejected") { errors$1.push(new errors.InternalError("Unable to put storage", { cause: result.reason })); } } if (errors$1.length > 0) { return maybeThrowOnFail(throwOnFail, { errors: errors$1 }); } await vm.stateManager.checkpoint(); await vm.stateManager.commit(false); return {}; } catch (e) { errors$1.push(new errors.InternalError("Unexpected error setting account", { cause: e })); return maybeThrowOnFail(throwOnFail, { errors: errors$1 }); } }; // src/Contract/createScript.js var createScript = async (client, code, deployedBytecode, to) => { const scriptAddress = to ?? (() => { const randomBigInt = BigInt(Math.floor(Math.random() * 1e15)); return utils.getAddress(address.createContractAddress(address.createAddress(`0x${"6969".repeat(10)}`), randomBigInt).toString()); })(); const vm$1 = await client.getVm(); if (deployedBytecode) { const setAccountRes = await setAccountHandler(client)({ address: scriptAddress, deployedBytecode, throwOnFail: false }); if (setAccountRes.errors) { return { errors: setAccountRes.errors }; } return { address: scriptAddress }; } if (!code) { return { errors: [new errors.InternalError("Cannot create script without code or deployedBytecode")] }; } const parentBlock = await vm$1.blockchain.getCanonicalHeadBlock(); const priorityFee = 0n; const sender = address.createAddress( /** @type {import('@tevm/utils').Address}*/ node.prefundedAccounts[0] ); let _maxFeePerGas = parentBlock.header.calcNextBaseFee() + priorityFee; const baseFeePerGas = parentBlock.header.baseFeePerGas ?? 0n; if (_maxFeePerGas < baseFeePerGas) { _maxFeePerGas = baseFeePerGas; } const dataFee = (() => { let out = 0n; for (const entry of utils.hexToBytes(code) ?? []) { out += entry === 0 ? 4n : 16n; } return out; })(); const baseFee = (() => { let out = dataFee; const txFee = 21000n; out += txFee; if (vm$1.common.ethjsCommon.gteHardfork("homestead")) { const txCreationFee = 32000n; out += txCreationFee; } return out; })(); const minimumGasLimit = baseFee + BigInt(4294967295); const gasLimitWithExecutionBuffer = minimumGasLimit * 11n / 10n; try { const res = await vm.runTx(vm$1)({ block: parentBlock, tx: tx.createImpersonatedTx({ maxFeePerGas: _maxFeePerGas, maxPriorityFeePerGas: 0n, gasLimit: gasLimitWithExecutionBuffer, data: code, impersonatedAddress: sender }), skipNonce: true, skipBalance: true, skipBlockGasLimitValidation: true, skipHardForkValidation: true }); if (res.execResult.exceptionError?.error) { client.logger.error("Failed to create script because deployment of script bytecode failed"); throw new errors.InvalidBytecodeError(res.execResult.exceptionError.error, { cause: ( /** @type {any}*/ res.execResult.exceptionError ) }); } const deployedAddress = res.createdAddress; if (!deployedAddress) { return { errors: [new errors.InternalEvmError("Failed to create script")] }; } const account = await getAccountHandler(client)({ throwOnFail: false, address: ( /** @type {import('@tevm/utils').Address}*/ deployedAddress.toString() ), returnStorage: true }); if (account.errors) { return { errors: account.errors }; } const setAccountRes = await setAccountHandler(client)({ ...account, address: scriptAddress, throwOnFail: false, stateDiff: account.storage ?? {}, deployedBytecode: account.deployedBytecode }); if (setAccountRes.errors) { return { errors: setAccountRes.errors }; } await vm$1.stateManager.deleteAccount(deployedAddress); return { address: to ?? scriptAddress }; } catch (e) { return { errors: [ /** @type any*/ e ] }; } }; var abi = utils.parseAbi([ "function getL1GasUsed(bytes memory _data) public view returns (uint256)", "function getL1Fee(bytes memory _data) external view returns (uint256)", "function l1BaseFee() public view returns (uint256)", "function blobBaseFee() public view returns (uint256)" ]); var getL1FeeInformationOpStack = async (data, vm) => { const opstackChain = ( /** @type {any}*/ vm.common ); const serializedTx = utils.serializeTransaction({ chainId: opstackChain.id, data: utils.bytesToHex(data ?? new Uint8Array()), type: "eip1559" }); const to = address.createAddress(opstackChain.contracts.gasPriceOracle.address); const [l1GasUsed, l1Fee, l1BlobFee, l1BaseFee] = await Promise.all([ vm.evm.runCall({ to, data: utils.hexToBytes( viem.encodeFunctionData({ functionName: "getL1GasUsed", args: [serializedTx], abi }) ) }), vm.evm.runCall({ to, data: utils.hexToBytes( viem.encodeFunctionData({ functionName: "getL1Fee", args: [serializedTx], abi }) ) }), vm.evm.runCall({ to, data: utils.hexToBytes( viem.encodeFunctionData({ functionName: "blobBaseFee", args: [], abi }) ) }), vm.evm.runCall({ to, data: utils.hexToBytes( viem.encodeFunctionData({ functionName: "l1BaseFee", args: [], abi }) ) }) ]); return { l1GasUsed: viem.decodeFunctionResult({ abi, functionName: "getL1GasUsed", data: utils.bytesToHex(l1GasUsed.execResult.returnValue) }), l1Fee: viem.decodeFunctionResult({ abi, functionName: "getL1Fee", data: utils.bytesToHex(l1Fee.execResult.returnValue) }), l1BlobFee: viem.decodeFunctionResult({ abi, functionName: "blobBaseFee", data: utils.bytesToHex(l1BlobFee.execResult.returnValue) }), l1BaseFee: viem.decodeFunctionResult({ abi, functionName: "l1BaseFee", data: utils.bytesToHex(l1BaseFee.execResult.returnValue) }) }; }; var callHandlerOpts = async (client, params) => { const opts = {}; const vm = await client.getVm(); const block = await (async () => { try { if (params.blockTag === void 0) { return vm.blockchain.blocksByTag.get("latest"); } if (typeof params.blockTag === "bigint") { return await vm.blockchain.getBlock(params.blockTag); } if (typeof params.blockTag === "string" && params.blockTag.startsWith("0x")) { return await vm.blockchain.getBlock(utils.hexToBytes( /** @type {import('@tevm/utils').Hex}*/ params.blockTag )); } if (params.blockTag === "latest" || params.blockTag === "safe" || params.blockTag === "pending" || params.blockTag === "earliest" || params.blockTag === "finalized") { return vm.blockchain.blocksByTag.get( /** */ params.blockTag ); } return new errors.InvalidBlockError(`Unknown blocktag ${params.blockTag}`); } catch (e) { return new errors.UnknownBlockError(e instanceof Error ? e.message : `Unable to find block ${params.blockTag}`); } })(); if (block instanceof errors.UnknownBlockError || block instanceof errors.InvalidBlockError || block === void 0) { return { errors: [block ?? new errors.UnknownBlockError(`Unable to find block ${params.blockTag}`)] }; } client.logger.debug({ block: block.header }, "Using block"); opts.block = block; if (params.blockOverrideSet) { client.logger.debug(params.blockOverrideSet, "callHandlerOpts: Detected a block override set"); const { header } = await vm.blockchain.getCanonicalHeadBlock(); opts.block = { ...opts.block, header: { // this isn't in the type but it needs to be here or else block overrides will fail ...{ stateRoot: block.header.stateRoot }, coinbase: params.blockOverrideSet.coinbase !== void 0 ? address.createAddress(params.blockOverrideSet.coinbase) : header.coinbase, number: params.blockOverrideSet.number !== void 0 ? BigInt(params.blockOverrideSet.number) : header.number, difficulty: header.difficulty, prevRandao: header.prevRandao, gasLimit: params.blockOverrideSet.gasLimit !== void 0 ? BigInt(params.blockOverrideSet.gasLimit) : header.gasLimit, timestamp: params.blockOverrideSet.time !== void 0 ? BigInt(params.blockOverrideSet.time) : header.timestamp, baseFeePerGas: params.blockOverrideSet.baseFee !== void 0 ? BigInt(params.blockOverrideSet.baseFee) : header.baseFeePerGas ?? BigInt(0), getBlobGasPrice() { if (params.blockOverrideSet?.blobBaseFee !== void 0) { return BigInt(params.blockOverrideSet.blobBaseFee); } return header.getBlobGasPrice(); } } }; } if (params.to) { opts.to = address.createAddress(params.to); } if (params.data) { opts.data = utils.hexToBytes(params.data); } if (params.salt) { opts.salt = utils.hexToBytes(params.salt); } if (params.depth) { opts.depth = params.depth; } if (params.blobVersionedHashes) { opts.blobVersionedHashes = params.blobVersionedHashes; } if (params.selfdestruct) { opts.selfdestruct = params.selfdestruct; } if (params.gasRefund) { opts.gasRefund = BigInt(params.gasRefund); } if (params.gasPrice) { opts.gasPrice = BigInt(params.gasPrice); } if (params.value) { opts.value = BigInt(params.value); } const caller = params.caller || params.from || params.origin || (params.createTransaction || params.addToMempool || params.addToBlockchain ? "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" : `0x${"00".repeat(20)}`); if (caller) { opts.caller = address.createAddress(caller); } const origin = params.origin || params.from || params.caller || (params.createTransaction || params.addToMempool || params.addToBlockchain ? "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" : `0x${"00".repeat(20)}`); if (origin) { if (params.skipBalance !== void 0) { opts.skipBalance = Boolean(params.skipBalance); } else { opts.skipBalance = caller === `0x${"00".repeat(20)}` && (params.createTransaction ?? params.addToMempool ?? params.addToBlockchain ?? false) === false; } opts.origin = address.createAddress(origin); } if (params.gas) { opts.gasLimit = BigInt(params.gas); } if ((params.createTransaction || params.addToMempool || params.addToBlockchain) && opts.block !== await vm.blockchain.getCanonicalHeadBlock()) { return { errors: [new errors.InvalidParamsError("Creating transactions on past blocks is not currently supported")] }; } return { data: opts }; }; var createEvmError = (error) => { if (error instanceof errors.BaseError) { return ( /** @type {never}*/ error ); } const errorMessage = error?.error; switch (errorMessage) { case "stop": { return new errors.StopError(errorMessage, { cause: error }); } case "revert": { return new errors.RevertError(errorMessage, { cause: error }); } case "out of gas": { return new errors.OutOfGasError(errorMessage, { cause: error }); } case "invalid opcode": { return new errors.InvalidOpcodeError(errorMessage, { cause: error }); } case "stack overflow": { return new errors.StackOverflowError(errorMessage, { cause: error }); } case "stack underflow": { return new errors.StackUnderflowError(errorMessage, { cause: error }); } case "invalid JUMP": { return new errors.InvalidJumpError(errorMessage, { cause: error }); } case "value out of range": { return new errors.OutOfRangeError(errorMessage, { cause: error }); } case "kzg proof invalid": { return new errors.InvalidProofError(errorMessage, { cause: error }); } // @ts-expect-error - This error message is deprecated in ethereumjs v10 case "attempting to AUTHCALL without AUTH set": { return new errors.AuthCallUnsetError(errorMessage, { cause: error }); } case "internal error": { return new errors.InternalError(errorMessage, { cause: error }); } case "kzg inputs invalid": { return new errors.InvalidKzgInputsError(errorMessage, { cause: error }); } case "value overflow": { return new errors.ValueOverflowError(errorMessage, { cause: error }); } // @ts-expect-error - This error message is deprecated in ethereumjs v10 case "invalid JUMPSUB": { return new errors.InvalidJumpSubError(errorMessage, { cause: error }); } case "create collision": { return new errors.CreateCollisionError(errorMessage, { cause: error }); } // @ts-expect-error - This error message is deprecated in ethereumjs v10 case "invalid BEGINSUB": { return new errors.InvalidBeginSubError(errorMessage, { cause: error }); } case "refund exhausted": { return new errors.RefundExhaustedError(errorMessage, { cause: error }); } // @ts-expect-error - This error message is deprecated in ethereumjs v10 case "invalid RETURNSUB": { return new errors.InvalidReturnSubError(errorMessage, { cause: error }); } case "kzg commitment does not match versioned hash": { return new errors.InvalidCommitmentError(errorMessage, { cause: error }); } case "invalid EOF format": { return new errors.InvalidEofFormatError(errorMessage, { cause: error }); } case "static state change": { return new errors.StaticStateChangeError(errorMessage, { cause: error }); } case "code store out of gas": { return new errors.CodeStoreOutOfGasError(errorMessage, { cause: error }); } case "insufficient balance": { return new errors.InsufficientBalanceError(errorMessage, { cause: error }); } case "invalid input length": { return new errors.InvalidInputLengthError(errorMessage, { cause: error }); } case "input is empty": { return new errors.BLS12381InputEmptyError(errorMessage, { cause: error }); } case "initcode exceeds max initcode size": { return new errors.InitcodeSizeViolationError(errorMessage, { cause: error }); } case "invalid bytecode deployed": { return new errors.InvalidBytecodeResultError(errorMessage, { cause: error }); } case "code size to deposit exceeds maximum code size": { return new errors.CodeSizeExceedsMaximumError(errorMessage, { cause: error }); } case "fp point not in field": { return new errors.BLS12381FpNotInFieldError(errorMessage, { cause: error }); } case "point not on curve": { return new errors.BLS12381PointNotOnCurveError(errorMessage, { cause: error }); } default: { return new errors.InternalError(errorMessage || "Unknown error", { cause: error }); } } }; // src/Call/callHandlerResult.js var callHandlerResult = (evmResult, txHash, trace, accessList) => { const out = { rawData: utils.bytesToHex( /** @type {any} */ evmResult.execResult.returnValue ), executionGasUsed: ( /** @type {any} */ evmResult.execResult.executionGasUsed ) }; if (trace) { out.trace = trace; } if (evmResult.totalGasSpent) { out.totalGasSpent = evmResult.totalGasSpent; } if (evmResult.minerValue) { out.minerValue = evmResult.minerValue; } if (evmResult.blobGasUsed) { out.blobGasUsed = evmResult.blobGasUsed; } if (evmResult.amountSpent) { out.amountSpent = evmResult.amountSpent; } if (accessList && evmResult.preimages) { out.preimages = Object.fromEntries( [...evmResult.preimages.entries()].map(([key, value]) => [key, utils.bytesToHex(value)]) ); } if (accessList) { out.accessList = /** @type {Record<import('@tevm/utils').Address, Set<import('@tevm/utils').Hex>>} */ Object.fromEntries( [...accessList.entries()].map(([address, storageKeys]) => { const hexKeys = new Set([...storageKeys].map((key) => `0x${key}`)); return [`0x${address}`, hexKeys]; }) ); } if (txHash) { out.txHash = txHash; } if ( /** @type {any} */ evmResult.execResult.gasRefund ) { out.gasRefund = evmResult.gasRefund ?? /** @type {any} */ evmResult.execResult.gasRefund; } if ( /** @type {any} */ evmResult.execResult.selfdestruct ) { out.selfdestruct = new Set( [.../** @type {any} */ evmResult.execResult.selfdestruct].map((address) => utils.getAddress(address)) ); } if ( /** @type {any} */