@tevm/actions
Version:
A typesafe library for writing forge scripts in typescript
1,408 lines (1,389 loc) • 223 kB
JavaScript
'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} */