UNPKG

@tevm/memory-client

Version:

MemoryClient for tevm is an in memory devnet for JavaScript

1,190 lines (1,168 loc) 47.1 kB
import { Common } from '@tevm/common'; import { Address, Abi, ContractFunctionName } from '@tevm/utils'; import * as viem from 'viem'; import { PublicRpcSchema, TestRpcSchema, Chain, ClientConfig, TransportConfig, Account, Client, PublicActions, WalletActions, TestActions, RpcSchema, Transport } from 'viem'; export { createClient, publicActions, testActions, walletActions } from 'viem'; import { Prettify } from 'viem/chains'; import { TevmActionsApi, JsonRpcSchemaTevm, EIP1193RequestFn } from '@tevm/decorators'; import * as _tevm_node from '@tevm/node'; import { TevmNode, TevmNodeOptions } from '@tevm/node'; import * as _tevm_actions from '@tevm/actions'; import { ContractParams, CallEvents, ContractResult } from '@tevm/actions'; /** * Provides powerful actions for interacting with the EVM using the TEVM API. * These actions allow for low-level access to the EVM, managing accounts, deploying contracts, and more. * * @see [Actions Guide](https://tevm.sh/learn/actions/) * @see [Viem Actions API](https://viem.sh/docs/actions/introduction) */ type TevmActions = { /** * Returns a promise that resolves when the TEVM is ready. * This is not needed to explicitly be called as all actions will wait for the TEVM to be ready. * @example * ```typescript * import { createMemoryClient } from 'tevm' * * const client = createMemoryClient() * * await client.tevmReady() * ``` * Same as calling `client.transport.tevm.ready()` */ tevmReady: () => Promise<true>; /** * A powerful low level API for executing calls and sending transactions. * See [CallParams](https://tevm.sh/reference/tevm/actions/type-aliases/callparams/) for options reference. * See [CallResult](https://tevm.sh/reference/tevm/actions/type-aliases/callresult/) for return values reference. * Remember, you must set `createTransaction: true` to send a transaction. Otherwise, it will be a call. You must also mine the transaction * before it updates the canonical head state. This can be avoided by setting mining mode to `auto` when using createMemoryClient. * @example * ```typescript * import { createMemoryClient } from 'tevm' * import { ERC20 } from 'tevm/contract' * * const client = createMemoryClient() * * const token = ERC20.withAddress(`0x${'0721'.repeat(10)}`) * * await client.setAccount(token) * * const balance = await client.tevmCall({ * to: token.address, * data: encodeFunctionData(token.read.balanceOf, [token.address]), * }) * ``` * In addition to making basic calls, you can also do advanced things like: * - Impersonate accounts via passing in `from`, `caller`, or `origin` * - Set the call depth via `depth` * - Create a trace or access list using `createTrace: true` or `createAccessList: true` * - Send as a transaction with `createTransaction: true` * For all options see [CallParams](https://tevm.sh/reference/tevm/actions/type-aliases/callparams/) */ tevmCall: TevmActionsApi['call']; /** * A powerful low level API for calling contracts. Similar to `tevmCall` but takes care of encoding and decoding data, revert messages, etc. * See [ContractParams](https://tevm.sh/reference/tevm/actions/type-aliases/contractparams/) for options reference. * See [ContractResult](https://tevm.sh/reference/tevm/actions/type-aliases/contractresult/) for return values reference. * Remember, you must set `createTransaction: true` to send a transaction. Otherwise, it will be a call. You must also mine the transaction * before it updates the canonical head state. This can be avoided by setting mining mode to `auto` when using createMemoryClient. * @example * ```typescript * import { createMemoryClient } from 'tevm' * import { ERC20 } from './MyERC721.sol' * * const client = createMemoryClient() * const token = ERC20.withAddress(`0x${'0721'.repeat(10)}`) * await client.setAccount(token) * const balance = await client.tevmContract({ * contract: token, * method: token.read.balanceOf, * args: [token.address], * }) * ``` * In addition to making basic calls, you can also do advanced things like: * - Impersonate accounts via passing in `from`, `caller`, or `origin` * - Set the call depth via `depth` * - Create a trace or access list using `createTrace: true` or `createAccessList: true` * - Send as a transaction with `createTransaction: true` * For all options see [ContractParams](https://tevm.sh/reference/tevm/actions/type-aliases/contractparams/) */ tevmContract: TevmActionsApi['contract']; /** * Deploys a contract to the EVM with encoded constructor arguments. Extends `tevmCall` so it supports all advanced options. * @see [DeployParams](https://tevm.sh/reference/tevm/actions/type-aliases/deployparams/) for options reference. * @see [DeployResult](https://tevm.sh/reference/tevm/actions/type-aliases/deployresult/) for return values reference. * Remember, you must set `createTransaction: true` to send a transaction. Otherwise, it will be a call. You must also mine the transaction * before it updates the canonical head state. This can be avoided by setting mining mode to `auto` when using createMemoryClient. * @example * ```typescript * import { createMemoryClient } from 'tevm' * import { ERC20 } from './MyERC721.sol' * * const client = createMemoryClient() * const token = ERC20.withAddress(`0x${'0721'.repeat(10)}`) * * const deploymentResult = await client.tevmDeploy({ * abi: token.abi, * bytecode: token.bytecode, * args: ['TokenName', 18, 'SYMBOL'], * }) * * console.log(deploymentResult.createdAddress) * ``` */ tevmDeploy: TevmActionsApi['deploy']; /** * Mines a new block with all pending transactions. In `manual` mode you must call this manually before the canonical head state is updated. * @example * ```typescript * import { createMemoryClient } from 'tevm' * * const client = createMemoryClient() * * await client.tevmMine() * ``` */ tevmMine: TevmActionsApi['mine']; /** * Loads a JSON serializable state into the EVM. This can be useful for persisting and restoring state between processes. * @example * ```typescript * import { createMemoryClient } from 'tevm' * import fs from 'fs' * * const client = createMemoryClient() * * const state = fs.readFileSync('state.json', 'utf8') * * await client.tevmLoadState(state) * ``` */ tevmLoadState: TevmActionsApi['loadState']; /** * Dumps a JSON serializable state from the EVM. This can be useful for persisting and restoring state between processes. * @example * ```typescript * import { createMemoryClient } from 'tevm' * import fs from 'fs' * const client = createMemoryClient() * const state = await client.tevmDumpState() * fs.writeFileSync('state.json', JSON.stringify(state)) * ``` */ tevmDumpState: TevmActionsApi['dumpState']; /** * Sets any property of an account including its balance, nonce, contract deployedBytecode, contract state, and more. * @see [SetAccountParams](https://tevm.sh/reference/tevm/actions/type-aliases/setaccountparams/) for options reference. * @see [SetAccountResult](https://tevm.sh/reference/tevm/actions/type-aliases/setaccountresult/) for return values reference. * @example * ```typescript * import { createMemoryClient, numberToHex } from 'tevm' * import { SimpleContract } from 'tevm/contract' * * const client = createMemoryClient() * * await client.tevmSetAccount({ * address: `0x${'0123'.repeat(10)}`, * balance: 100n, * nonce: 1n, * deployedBytecode: SimpleContract.deployedBytecode, * state: { * [`0x${'0'.repeat(64)}`]: numberToHex(420n), * } * }) * ``` */ tevmSetAccount: TevmActionsApi['setAccount']; /** * Gets the account state of an account. It does not return the storage state by default but can if `returnStorage` is set to `true`. * In forked mode, the storage is only the storage TEVM has cached and may not represent all the on-chain storage. * @see [GetAccountParams](https://tevm.sh/reference/tevm/actions/type-aliases/getaccountparams/) for options reference. * @see [GetAccountResult](https://tevm.sh/reference/tevm/actions/type-aliases/getaccountresult/) for return values reference. * @example * ```typescript * import { createMemoryClient } from 'tevm' * * const client = createMemoryClient() * * const account = await client.tevmGetAccount({ * address: `0x${'0000'.repeat(10)}`, * returnStorage: true, * }) * ``` */ tevmGetAccount: TevmActionsApi['getAccount']; /** * Sets the balance of an account to a specific amount of ETH or ERC20 tokens. * A convenience method over tevmSetAccount for quickly adjusting account balances. * @see [DealParams](https://tevm.sh/reference/tevm/actions/type-aliases/dealparams/) for options reference. * @see [DealResult](https://tevm.sh/reference/tevm/actions/type-aliases/dealresult/) for return values reference. * @example Deal native ETH * ```typescript * import { createMemoryClient } from 'tevm' * * const client = createMemoryClient() * * // Set ETH balance * await client.tevmDeal({ * account: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', * amount: 1000000000000000000n // 1 ETH * }) * ``` * * @example Deal ERC20 tokens * ```typescript * import { createMemoryClient } from 'tevm' * * const client = createMemoryClient() * * // Set ERC20 token balance * await client.tevmDeal({ * erc20: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', // USDC address * account: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', * amount: 1000000n // 1 USDC (6 decimals) * }) * ``` */ tevmDeal: TevmActionsApi['deal']; }; /** * The JSON-RPC schema for TEVM. * This type represents the JSON-RPC requests that the EIP-1193 client can handle when using TEVM. * It includes public, test, and TEVM-specific methods. * @example * ```typescript * import { createClient, http } from 'viem' * import { optimism } from 'tevm/common' * import { createTevmTransport } from 'tevm' * * const client = createClient({ * transport: createTevmTransport({ * fork: { transport: http('https://mainnet.optimism.io')({}) } * }), * chain: optimism, * }) * * async function example() { * const result = await client.request({ * method: 'tevm_call', * params: [{ to: '0x123...', data: '0x123...' }], * }) * console.log(result) * } * * example() * ``` * * @see [Tevm JSON-RPC guide](https://tevm.sh/learn/json-rpc/) * @see [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) * @see [Ethereum jsonrpc](https://ethereum.org/en/developers/docs/apis/json-rpc/) */ type TevmRpcSchema = [ ...PublicRpcSchema, ...TestRpcSchema<'anvil' | 'ganache' | 'hardhat'>, JsonRpcSchemaTevm['tevm_call'], JsonRpcSchemaTevm['tevm_dumpState'], JsonRpcSchemaTevm['tevm_loadState'], JsonRpcSchemaTevm['tevm_getAccount'], JsonRpcSchemaTevm['tevm_setAccount'] ]; /** * A type representing a custom TEVM Transport for viem. * * @template TName - The name of the transport. * @template TChain - The blockchain configuration. * * @param {TevmTransportConfig} config - Transport configuration options. * @returns {Object} The configured TEVM transport. * @returns {TransportConfig<TName>} config - The transport configuration. * @returns {EIP1193RequestFn} request - The EIP-1193 request function. * @returns {Object} value - The transport value. * @returns {TevmNode & { request: EIP1193RequestFn }} value.tevm - The TEVM base client with the EIP-1193 request function. */ type TevmTransport<TName extends string = string> = <TChain extends Chain | undefined = Chain>({ chain, pollingInterval, retryCount, timeout, }: { chain?: TChain | undefined; pollingInterval?: ClientConfig['pollingInterval'] | undefined; retryCount?: TransportConfig['retryCount'] | undefined; timeout?: TransportConfig['timeout'] | undefined; }) => { config: TransportConfig<TName>; request: EIP1193RequestFn; value: { tevm: TevmNode & { request: EIP1193RequestFn; }; }; }; /** * Represents a TEVM-enhanced viem client with an in-memory Ethereum client as its transport. * * The MemoryClient provides a complete in-memory Ethereum Virtual Machine implementation with * a full suite of capabilities: * * - Execute contract calls directly in JavaScript with full EVM compatibility * - Monitor EVM execution events (steps, messages, contract creation) * - Deploy and interact with contracts, including direct Solidity imports * - Set account states, balances, nonces, and contract storage * - Fork from existing networks and cache remote state as needed * - Mine blocks manually or automatically after transactions * - Persist and restore state across sessions * * The client implements multiple API styles: * - TEVM-specific methods for direct EVM interaction * - Standard Ethereum JSON-RPC methods * - Viem-compatible wallet, test, and public actions * * @type {import('./CreateMemoryClientFn.js').CreateMemoryClientFn} * * @example * ```typescript * import { createMemoryClient, http } from "tevm"; * import { optimism } from "tevm/common"; * import { parseEther } from "viem"; * * // Create a client forking from Optimism * const client = createMemoryClient({ * fork: { * transport: http("https://mainnet.optimism.io")({}), * }, * common: optimism, * }); * * // Wait for the client to be ready * await client.tevmReady(); * * // Set up account state * const address = "0x1234567890123456789012345678901234567890"; * await client.tevmSetAccount({ * address, * balance: parseEther("10") * }); * * // Deploy a contract with events tracking * const deployResult = await client.tevmDeploy({ * bytecode: "0x608060405234801561001057600080fd5b50610150806100206000396000f3fe...", * abi: [...], * onStep: (step, next) => { * console.log(`Executing ${step.opcode.name} at PC=${step.pc}`); * next(); * } * }); * * // Mine a block to confirm transactions * await client.mine({ blocks: 1 }); * * // Get the contract address from deployment * console.log(`Contract deployed at: ${deployResult.createdAddress}`); * ``` * * @see For creating a MemoryClient instance, see {@link createMemoryClient}. * @see [Client Guide](https://tevm.sh/learn/clients/) * @see [Actions Guide](https://tevm.sh/learn/actions/) * @see [Reference Docs](https://tevm.sh/reference/tevm/memory-client/functions/creatememoryclient/) * * ## Actions API * * MemoryClient supports the following viem actions: * * - [TEVM actions API](https://tevm.sh/reference/tevm/memory-client/type-aliases/tevmactions/) * ```typescript * import { createMemoryClient } from "tevm"; * * const tevm = createMemoryClient(); * await tevm.setAccount({ address: `0x${'01'.repeat(20)}`, balance: 100n }); * ``` * - [Viem public actions API](https://viem.sh/docs/actions/public/introduction) such as [getBlockNumber](https://viem.sh/docs/actions/public/getBlockNumber) * ```typescript * import { createMemoryClient } from "tevm"; * * const tevm = createMemoryClient(); * const bn = await tevm.getBlockNumber(); * ``` * - [Test actions](https://viem.sh/docs/actions/test/introduction) are included by default. * ```typescript * import { createMemoryClient } from "tevm"; * * const tevm = createMemoryClient(); * await tevm.setBalance({ address: `0x${'01'.repeat(20)}`, balance: 100n }); * ``` * * ## Forking * * To fork an existing network, pass an EIP-1193 transport to the `fork.transport` option with an optional block tag. * When you fork, TEVM will pin the block tag and lazily cache state from the fork transport. * It's highly recommended to pass in a `common` object that matches the chain. This will increase the performance of forking with known values. * * ```typescript * import { createMemoryClient, http } from "tevm"; * import { optimism } from "tevm/common"; * * const forkedClient = createMemoryClient({ * fork: { * transport: http("https://mainnet.optimism.io")({}), * blockTag: '0xa6a63cd70fbbe396321ca6fe79e1b6735760c03538208b50d7e3a5dac5226435', * }, * common: optimism, * }); * ``` * * The `common` object extends the viem chain interface with EVM-specific information. When using TEVM, you should also use `tevm/common` rather than `viem/chains` or use `createCommon` and pass in a viem chain. * * Viem clients, including MemoryClient, are themselves EIP-1193 transports. This means you can fork a client with another client. * * ## Mining Modes * * TEVM supports two mining modes: * - Manual: Using `tevm.mine()` * - Auto: Automatically mines a block after every transaction. * * TEVM state does not update until blocks are mined. * * ## Using TEVM over HTTP * * TEVM can be run as an HTTP server using `@tevm/server` to handle JSON-RPC requests. * * ```typescript * import { createServer } from "tevm/server"; * import { createMemoryClient } from "tevm"; * * const memoryClient = createMemoryClient(); * * const server = createServer({ * request: memoryClient.request, * }); * * server.listen(8545, () => console.log("listening on 8545")); * ``` * * This allows you to use any Ethereum client to communicate with it, including a viem public client. * * ```typescript * import { createPublicClient, http } from "viem"; * import { mainnet } from "viem/chains"; * * const publicClient = createPublicClient({ * chain: mainnet, * transport: http("https://localhost:8545"), * }); * * console.log(await publicClient.getChainId()); * ``` * * ## State Persistence (Experimental) * * It is possible to persist the TEVM client to a synchronous source using the `persister` option. * * ```typescript * import { createMemoryClient, createSyncPersister } from "tevm"; * import { createMemoryClient } from "tevm/sync-storage-persister"; * * // Client state will be hydrated and persisted from/to local storage * const clientWithLocalStoragePersistence = createMemoryClient({ * persister: createSyncPersister({ * storage: localStorage, * }), * }); * ``` * * ## Network Support * * TEVM guarantees support for the following networks: * - Ethereum mainnet * - Standard OP Stack chains * * Other EVM chains are likely to work but do not officially carry support. More official chain support will be added in the near future. * * Note: Optimism deposit transactions are not currently supported but will be in a future release. TEVM filters out these transactions from blocks. * * ## Network and Hardfork Support * * TEVM supports enabling and disabling different EIPs, but the following EIPs are always turned on: * - 1559 * - 4895 * - 4844 * - 4788 * * Currently, only EIP-1559 Fee Market transactions are supported. * * ## Tree Shakeable Actions * * TEVM supports tree-shakeable actions using `createTevmNode()` and the `tevm/actions` package. If you are building a UI, you should use tree-shakeable actions to optimize bundle size. These are described in detail in the [actions API guide](https://tevm.sh/learn/actions/). * * ## Composing with TEVM Contracts and Bundler * * MemoryClient can compose with TEVM contracts and the TEVM bundler. For more information, see the [TEVM contracts guide](https://tevm.sh/learn/contracts/) and the [TEVM Solidity imports guide](https://tevm.sh/learn/solidity-imports/). * * ```typescript * import { createMemoryClient } from "tevm"; * import { MyERC721 } from './MyERC721.sol'; * * const tevm = createMemoryClient({ * fork: { * transport: http("https://mainnet.optimism.io")({}), * }, * }); * * const address = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'; * * await tevm.runContractCall( * MyERC721.write.mint({ * caller: address, * }), * ); * * const balance = await tevm.runContractCall( * MyERC721.read.balanceOf({ * caller: address, * }), * ); * console.log(balance); // 1n * ``` */ type MemoryClient<TChain extends Chain | undefined = Chain | undefined, TAccountOrAddress extends Account | Address | undefined = Account | Address | undefined> = Prettify<Client<TevmTransport, TChain, TAccountOrAddress extends Account ? Account : undefined, TevmRpcSchema, TevmActions & PublicActions<TevmTransport, TChain, TAccountOrAddress extends Account ? Account : undefined> & WalletActions<TChain, TAccountOrAddress extends Account ? Account : undefined> & TestActions>>; /** * Configuration options for creating a {@link MemoryClient}. * * This type extends `TevmNodeOptions` and includes specific options for configuring the MemoryClient, * such as the transport type, account, polling interval, and caching behavior. It provides * a comprehensive set of parameters to customize the behavior of the in-memory Ethereum client. * * @template TCommon - The common chain configuration, extending both `Common` and `Chain`. * @template TAccountOrAddress - The account or address type for the client. * @template TRpcSchema - The RPC schema type, defaults to `TevmRpcSchema`. * * @example * ```typescript * import { createMemoryClient, http, type MemoryClientOptions } from "tevm"; * import { optimism } from "tevm/common"; * import { createSyncPersister } from "tevm/sync-storage-persister"; * * const options: MemoryClientOptions = { * // Fork configuration to pull state from a live network * fork: { * transport: http("https://mainnet.optimism.io")({}), * blockTag: '0xa6a63cd70fbbe396321ca6fe79e1b6735760c03538208b50d7e3a5dac5226435', * }, * // Chain configuration * common: optimism, * // Client identification * name: 'Optimism Memory Client', * key: 'optimism-memory', * // Mining configuration (auto mines blocks after transactions) * miningConfig: { * type: 'auto' * }, * // Client performance tuning * pollingInterval: 1000, * cacheTime: 60000, * // State persistence * persister: createSyncPersister({ * storage: localStorage, * key: 'tevm-state' * }), * // Enable unlimited contract sizes (for testing very large contracts) * allowUnlimitedContractSize: true, * // Logging level * loggingLevel: 'info' * }; * * const client = createMemoryClient(options); * ``` * * @see {@link MemoryClient} * @see {@link CreateMemoryClientFn} * @see {@link TevmNodeOptions} * * @property {string} [type] - The type of client (defaults to 'tevm'). * @property {string} [key] - The key for the client, used for caching and identification. * @property {string} [name] - The name of the client, used for logging and display purposes. * @property {TAccountOrAddress} [account] - The account associated with the client for signing transactions. * @property {number} [pollingInterval] - The interval (in milliseconds) at which the client polls for new data (for watchable actions). * @property {number} [cacheTime] - The time (in milliseconds) to cache data from requests. * @property {TCommon} [common] - The common chain configuration object that defines the chain and EVM parameters. * @property {Object} [fork] - The configuration for forking a network. * @property {Function} [fork.transport] - The transport function for connecting to the fork source network. * @property {string|number|bigint} [fork.blockTag] - The specific block tag to fork from (can be number, hash, or named tag like 'latest'). * @property {Object} [miningConfig] - Configuration for how blocks are mined. * @property {'manual'|'auto'|'interval'} [miningConfig.type] - The mining mode (manual requires calling mine(), auto mines after each tx, interval mines on a timer). * @property {number} [miningConfig.interval] - For interval mining, how often to mine blocks in milliseconds. * @property {import('@tevm/utils').SyncStoragePersister} [persister] - Handler for persisting and restoring state. * @property {boolean} [allowUnlimitedContractSize] - Whether to remove the EIP-170 contract size limit (default: false). * @property {'error'|'warn'|'info'|'debug'|'trace'} [loggingLevel] - Controls logging verbosity (default: 'info'). * @property {Object} [eips] - Configuration for specific Ethereum Improvement Proposals. * @throws {Error} When configuration options are incompatible or invalid. */ type MemoryClientOptions<TCommon extends Common & Chain = Common & Chain, TAccountOrAddress extends Account | Address | undefined = undefined, TRpcSchema extends RpcSchema | undefined = TevmRpcSchema> = TevmNodeOptions<TCommon> & Pick<ClientConfig<Transport, TCommon, TAccountOrAddress, TRpcSchema>, 'type' | 'key' | 'name' | 'account' | 'pollingInterval' | 'cacheTime'>; /** * Type definition for the function that creates a {@link MemoryClient}. * * This function type represents `createMemoryClient`, which initializes a complete in-memory Ethereum * virtual machine with a comprehensive API. The function supports extensive configuration options for: * * - Network forking from live Ethereum networks * - Custom chain settings and EVM parameters * - Mining behavior configuration * - State persistence * - Logging and debugging settings * - Custom account injection * * The returned client integrates with viem's action system while providing TEVM-specific * capabilities for more advanced EVM interaction. * * @template TCommon - The common chain configuration, extending both `Common` and `Chain`. * @template TAccountOrAddress - The account or address type for the client. * @template TRpcSchema - The RPC schema type, defaults to `TevmRpcSchema`. * * @param {MemoryClientOptions<TCommon, TAccountOrAddress, TRpcSchema>} [options] - The options to configure the MemoryClient. * @returns {MemoryClient<TCommon, TAccountOrAddress>} - A configured MemoryClient instance. * @throws {Error} When configuration is invalid or initialization fails. * * @example * ```typescript * import { createMemoryClient, http } from "tevm"; * import { optimism } from "tevm/common"; * import { parseEther } from "viem"; * * // Basic client with default settings * const basicClient = createMemoryClient(); * * // Advanced client with custom configuration * const client = createMemoryClient({ * // Fork from Optimism mainnet * fork: { * transport: http("https://mainnet.optimism.io")({}), * blockTag: 'latest', // Or specific block hash/number * }, * // Use Optimism chain configuration * common: optimism, * // Enable auto-mining (blocks mined after each transaction) * miningConfig: { * type: 'auto' * }, * // Set client metadata * name: 'Optimism Development Client', * // Configure performance * pollingInterval: 1000, * // Modify EVM behavior * allowUnlimitedContractSize: true, * // Set logging verbosity * loggingLevel: 'debug' * }); * * // Initialize and configure client * await client.tevmReady(); * * // Set up test account * await client.tevmSetAccount({ * address: '0x1234567890123456789012345678901234567890', * balance: parseEther('100'), * nonce: 0n * }); * * // Read from forked network * const balance = await client.getBalance({ * address: '0xd8da6bf26964af9d7eed9e03e53415d37aa96045' * }); * ``` * * @see {@link MemoryClient} - For the return type of this function * @see {@link MemoryClientOptions} - For detailed configuration options * @see [Client Guide](https://tevm.sh/learn/clients/) - Complete documentation */ type CreateMemoryClientFn = <TCommon extends Common & Chain = Common & Chain, TAccountOrAddress extends Account | Address | undefined = undefined, TRpcSchema extends RpcSchema | undefined = TevmRpcSchema>(options?: MemoryClientOptions<TCommon, TAccountOrAddress, TRpcSchema>) => MemoryClient<TCommon, TAccountOrAddress>; /** * Creates a {@link MemoryClient} - a fully-featured Ethereum development and testing environment. * * The MemoryClient is an all-in-one solution that combines: * - A complete in-memory Ethereum Virtual Machine implementation * - Full support for viem's wallet, test, and public actions * - TEVM-specific actions for advanced state manipulation and tracing * - Automatic handling of JSON-RPC requests through viem's client interface * * This provides an integrated environment for local Ethereum development with capabilities like: * - Executing and debugging smart contracts without deploying to a testnet * - Forking from existing networks with state caching for realistic testing * - Direct manipulation of blockchain state for complex test scenarios * - Complete control over mining and transaction processing * - Compatibility with standard Ethereum tooling and libraries * * @type {import('./CreateMemoryClientFn.js').CreateMemoryClientFn} * * @example * ```typescript * import { createMemoryClient, http } from "tevm"; * import { optimism } from "tevm/common"; * * // Create a memory client that forks from Optimism mainnet * const client = createMemoryClient({ * fork: { * transport: http("https://mainnet.optimism.io")({}), * }, * common: optimism, * mining: { auto: true }, // Automatically mine blocks after transactions * }); * * // Wait for fork initialization to complete * await client.tevmReady(); * * // Use standard viem actions * const blockNumber = await client.getBlockNumber(); * console.log(`Connected to Optimism block ${blockNumber}`); * * // Use TEVM-specific actions * await client.tevmSetAccount({ * address: "0x123...", * balance: 10000000000000000000n // 10 ETH * }); * ``` * * @see [Client Guide](https://tevm.sh/learn/clients/) * @see [Actions Guide](https://tevm.sh/learn/actions/) * @see [Reference Docs](https://tevm.sh/reference/tevm/memory-client/functions/creatememoryclient/) * @see [Viem client docs](https://viem.sh/docs/clients/introduction) * * ## Key Configuration Options * * ```typescript * createMemoryClient({ * // Chain configuration (use tevm/common chains, not viem/chains) * common: optimism, * * // Forking from an existing network * fork: { * transport: http("https://mainnet.optimism.io")({}), * blockTag: 'latest', // or specific block number/hash * }, * * // Mining configuration * mining: { * auto: true, // Auto-mine after each transaction * interval: 5000, // Mine blocks every 5 seconds (in ms) * }, * * // State persistence * persister: createSyncPersister({ * storage: localStorage, // or any synchronous storage * }), * * // Chain configuration * hardfork: 'prague', // Default and recommended * * // EVM execution logging * logging: { * logLevel: 'debug', * }, * }) * ``` * * ## Actions API * * MemoryClient combines multiple action types in a single interface: * * ### 1. TEVM-specific actions * ```typescript * // Account and state manipulation * await client.tevmSetAccount({ address: "0x123...", balance: 100n }); * await client.tevmGetAccount({ address: "0x123..." }); * * // Contract interactions * await client.tevmContract({ * abi: [...], * functionName: "transfer", * args: ["0x456...", 1000n] * }); * * // Contract deployment * const result = await client.tevmDeploy({ * abi: [...], * bytecode: "0x...", * args: ["Constructor", "Args"] * }); * * // Mining control * await client.tevmMine({ blockCount: 5, interval: 10 }); * * // State persistence * const state = await client.tevmDumpState(); * await client.tevmLoadState(state); * ``` * * ### 2. Viem public actions * ```typescript * const balance = await client.getBalance({ address: "0x123..." }); * const blockNumber = await client.getBlockNumber(); * const code = await client.getCode({ address: "0x123..." }); * const logs = await client.getLogs({ address: "0x123..." }); * ``` * * ### 3. Viem test actions * ```typescript * await client.setBalance({ address: "0x123...", value: 100n }); * await client.impersonateAccount({ address: "0x123..." }); * await client.mine({ blocks: 1 }); * await client.setStorageAt({ address, index, value }); * ``` * * ### 4. Viem wallet actions * ```typescript * const hash = await client.sendTransaction({ * from: "0x123...", * to: "0x456...", * value: 1000n * }); * ``` * * ## Forking Networks * * The MemoryClient can fork from any EVM-compatible network, creating a local copy that * lazily loads state from the remote network as needed: * * ```typescript * import { createMemoryClient, http } from "tevm"; * import { optimism } from "tevm/common"; * * const forkedClient = createMemoryClient({ * // Fork specification * fork: { * transport: http("https://mainnet.optimism.io")({}), * blockTag: '0xa6a63cd70fbbe396321ca6fe79e1b6735760c03538208b50d7e3a5dac5226435', * }, * // Always specify chain configuration for optimal performance * common: optimism, * }); * ``` * * The `common` object extends the viem chain interface with EVM-specific information. * Always use `tevm/common` chains rather than `viem/chains` when working with TEVM. * * ## Mining Modes * * TEVM supports three mining modes: * * ```typescript * // 1. Manual mining (default) * const client = createMemoryClient({ mining: { auto: false } }); * await client.sendTransaction(...); // Transaction is pending * await client.tevmMine(); // Now transaction is processed * * // 2. Auto-mining (mine after every transaction) * const autoClient = createMemoryClient({ mining: { auto: true } }); * await autoClient.sendTransaction(...); // Automatically mined * * // 3. Interval mining (mine periodically) * const intervalClient = createMemoryClient({ * mining: { interval: 5000 } // Mine every 5 seconds * }); * ``` * * ## Server Mode * * TEVM can be exposed as an HTTP JSON-RPC server with `@tevm/server`: * * ```typescript * import { createServer } from "tevm/server"; * import { createMemoryClient } from "tevm"; * * const memoryClient = createMemoryClient(); * const server = createServer({ * request: memoryClient.request, * }); * * server.listen(8545, () => console.log("TEVM running on port 8545")); * ``` * * This allows any Ethereum tool or library to connect to your TEVM instance: * * ```typescript * import { createPublicClient, http } from "viem"; * import { mainnet } from "viem/chains"; * * const publicClient = createPublicClient({ * chain: mainnet, * transport: http("http://localhost:8545"), * }); * * console.log(await publicClient.getChainId()); * ``` * * ## State Persistence * * TEVM state can be persisted between sessions with the `persister` option: * * ```typescript * import { createMemoryClient } from "tevm"; * import { createSyncPersister } from "tevm/sync-storage-persister"; * * // Browser example with localStorage * const browserClient = createMemoryClient({ * persister: createSyncPersister({ * storage: localStorage, * key: 'my-tevm-state' * }), * }); * * // Node.js example with file system * import { FileStorage } from "tevm/sync-storage-persister/node"; * const nodeClient = createMemoryClient({ * persister: createSyncPersister({ * storage: new FileStorage('./tevm-state.json'), * }), * }); * ``` * * ## Direct Solidity Imports * * When used with the TEVM bundler plugins, you can import Solidity files directly: * * ```typescript * import { createMemoryClient } from "tevm"; * import { MyERC721 } from './MyERC721.sol'; * * const client = createMemoryClient(); * * // Deploy the contract * const deployed = await client.tevmDeploy( * MyERC721.deploy("My NFT", "NFT") * ); * await client.tevmMine(); * * // Create contract instance with the deployed address * const nft = MyERC721.withAddress(deployed.createdAddress); * * // Call contract methods * await client.tevmContract({ * ...nft.write.mint('0x123...', 1), * from: '0x123...', * }); * * await client.tevmMine(); * * const balance = await client.tevmContract(nft.read.balanceOf('0x123...')); * console.log(balance); // 1n * ``` * * This requires setting up one of the TEVM bundler plugins (vite, webpack, esbuild, etc.). * * ## Network Support * * TEVM officially supports: * - Ethereum mainnet and testnets * - OP Stack chains (Optimism, Base, etc.) * * Other EVM chains are likely to work but not officially tested. Chain configuration * is available through `tevm/common`. * * ## Advanced EVM Features * * TEVM includes advanced EVM features, with the following enabled by default: * - EIP-1559 Fee Market * - EIP-4895 (Beacon chain withdrawals) * - EIP-4844 (Blob transactions) * - EIP-4788 (Beacon root in EVM) * * ## Optimizing Bundle Size * * For UI applications concerned with bundle size, use tree-shakeable actions with `createTevmNode()` * and individual actions from `tevm/actions`. See the [actions API guide](https://tevm.sh/learn/actions/) * for details. */ declare const createMemoryClient: CreateMemoryClientFn; declare function createTevmTransport(options?: _tevm_node.TevmNodeOptions): TevmTransport; /** * A type representing the handler for a TEVM contract procedure. * * This type reuses the viem `contractRead`/`contractWrite` API to encode ABI, function name, and arguments. * * @template TAbi - The ABI of the contract. * @template TFunctionName - The name of the contract function. * * @param {Client<TevmTransport<string>>} client - The viem client configured with TEVM transport. * @param {ContractParams<TAbi, TFunctionName>} params - Parameters for the contract method call, including ABI, function name, and arguments. * @returns {Promise<ContractResult<TAbi, TFunctionName>>} The result of the contract method call. * * @example * ```typescript * import { tevmContract } from 'tevm/actions' * import { createClient, http } from 'viem' * import { optimism } from 'tevm/common' * import { createTevmTransport } from 'tevm' * * const client = createClient({ * transport: createTevmTransport({ * fork: { transport: http('https://mainnet.optimism.io')({}) } * }), * chain: optimism, * }) * * async function example() { * const res = await tevmContract(client, { * abi: [...], * functionName: 'myFunction', * args: [...], * }) * console.log(res) * } * * example() * ``` * * @see [ContractParams](https://tevm.sh/reference/tevm/actions/type-aliases/contractparams/) for options reference. * @see [ContractResult](https://tevm.sh/reference/tevm/actions/type-aliases/contractresult/) for return values reference. * @see [BaseCallParams](https://tevm.sh/reference/tevm/actions/type-aliases/basecallparams-1/) for the base call parameters. */ type TevmContract = <TAbi extends Abi | readonly unknown[] = Abi, TFunctionName extends ContractFunctionName<TAbi> = ContractFunctionName<TAbi>>(client: Client<TevmTransport<string>>, params: ContractParams<TAbi, TFunctionName> & CallEvents) => Promise<ContractResult<TAbi, TFunctionName>>; /** * A custom [viem extension](https://viem.sh/docs/clients/custom#extending-with-actions-or-configuration) for adding powerful * Tevm specific actions to the client. These actions come preloaded with [MemoryClient](https://tevm.sh/reference/tevm/memory-client/type-aliases/memoryclient/) * To add these actions use the `extend` method on a TevmClient with the tevmViemActions() extension. * @example * ```typescript * import { createTevmClient, tevmViemActions } from 'tevm' * * const client = createTevmClient() * .extend(tevmViemActions()) * ``` */ type TevmViemActionsApi = { tevmReady: TevmNode['ready']; tevmCall: TevmActionsApi['call']; tevmContract: TevmActionsApi['contract']; tevmDeploy: TevmActionsApi['deploy']; tevmMine: TevmActionsApi['mine']; tevmLoadState: TevmActionsApi['loadState']; tevmDumpState: TevmActionsApi['dumpState']; tevmSetAccount: TevmActionsApi['setAccount']; tevmGetAccount: TevmActionsApi['getAccount']; tevmDeal: TevmActionsApi['deal']; }; declare function tevmCall(client: viem.Client<TevmTransport<string>>, params: _tevm_actions.CallParams): Promise<_tevm_actions.CallResult>; /** * A tree-shakeable version of the `tevmContract` action for viem. * Provides a high-level interface for contract interactions with automatic encoding/decoding and full type safety. * * While `tevmCall` offers a low-level interface for raw EVM execution, `tevmContract` provides a more convenient * developer experience for standard contract interactions by: * * - Automatically encoding function parameters based on the ABI * - Automatically decoding return values to the appropriate JavaScript types * - Properly handling and decoding revert messages from failed calls * - Maintaining full type safety with TypeScript when using properly typed ABIs * - Simplifying complex contract interactions with a cleaner interface * * Internally, `tevmContract` wraps the lower-level `tevmCall` action, handling all the ABI encoding/decoding * logic while providing access to the same advanced features like execution tracing and EVM customization. * * @type {import('./TevmContractType.js').TevmContract} * * @example * ```typescript * import { tevmContract } from 'tevm/actions' * import { createClient, http, parseAbi } from 'viem' * import { optimism } from 'tevm/common' * import { createTevmTransport } from 'tevm' * * const client = createClient({ * transport: createTevmTransport({ * fork: { transport: http('https://mainnet.optimism.io')({}) } * }), * chain: optimism, * }) * * async function example() { * // Define the contract ABI * const abi = parseAbi([ * 'function balanceOf(address owner) view returns (uint256)', * 'function transfer(address to, uint256 amount) returns (bool)' * ]) * * // Read from contract (view function) * const balance = await tevmContract(client, { * abi, * address: '0x4200000000000000000000000000000000000042', // OP token * functionName: 'balanceOf', * args: ['0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'], * }) * console.log(`Balance: ${balance}`) // Returns the decoded uint256 as a bigint * * // Write to contract (non-view function with impersonation) * const result = await tevmContract(client, { * abi, * address: '0x4200000000000000000000000000000000000042', // OP token * functionName: 'transfer', * args: ['0x1234567890123456789012345678901234567890', 100n], * from: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', // Impersonate this address * createTransaction: true, // Create actual transaction that needs mining * }) * * // Transaction needs to be mined to take effect * await client.mine() * * console.log(`Transfer result: ${result}`) // true * * // Optional: With execution tracing * const tracedResult = await tevmContract(client, { * abi, * address: '0x4200000000000000000000000000000000000042', * functionName: 'balanceOf', * args: ['0x1234567890123456789012345678901234567890'], * onStep: (step, next) => { * console.log(`Opcode: ${step.opcode.name}`) * next() * } * }) * } * * example() * ``` * * @example * ```typescript * // Using with TEVM contract imports from the bundler * import { tevmContract } from 'tevm/actions' * import { createClient } from 'viem' * import { createTevmTransport } from 'tevm' * import { MyToken } from './MyToken.sol' // Direct Solidity import * * const client = createClient({ * transport: createTevmTransport(), * chain: { * id: 1, * name: 'Local TEVM' * } * }) * * async function example() { * // Deploy the contract first * const deployResult = await tevmDeploy(client, MyToken.deploy("My Token", "MTK")) * await client.mine() * * // Now use tevmContract with the imported contract * const result = await tevmContract(client, { * ...MyToken.read.balanceOf(), // Spread the contract's read method * args: ['0x1234567890123456789012345678901234567890'], * address: deployResult.createdAddress, * }) * * console.log(`Balance: ${result}`) * } * ``` * * @see [ContractParams](https://tevm.sh/reference/tevm/actions/type-aliases/contractparams/) for options reference. * @see [ContractResult](https://tevm.sh/reference/tevm/actions/type-aliases/contractresult/) for return values reference. * @see [BaseCallParams](https://tevm.sh/reference/tevm/actions/type-aliases/basecallparams-1/) for the base call parameters. * @see [tevmCall](https://tevm.sh/reference/tevm/memory-client/functions/tevmcall/) for the lower-level call interface. * @see [TEVM Actions Guide](https://tevm.sh/learn/actions/) * @see [TEVM Contracts Guide](https://tevm.sh/learn/contracts/) * @throws Will throw if the contract call reverts. The error will contain the decoded revert reason when available. */ declare const tevmContract: TevmContract; declare function tevmDeploy(client: viem.Client<TevmTransport<string>>, params: _tevm_actions.DeployParams & _tevm_actions.CallEvents): Promise<_tevm_actions.DeployResult>; declare function tevmDumpState(client: viem.Client<TevmTransport<string>>): Promise<_tevm_actions.DumpStateResult>; declare function tevmGetAccount(client: viem.Client<TevmTransport<string>>, params: _tevm_actions.GetAccountParams): Promise<_tevm_actions.GetAccountResult>; declare function tevmLoadState(client: viem.Client<TevmTransport<string>>, params: _tevm_actions.LoadStateParams): Promise<_tevm_actions.LoadStateResult>; declare function tevmMine(client: viem.Client<TevmTransport<string>>, params?: _tevm_actions.MineParams): Promise<_tevm_actions.MineResult>; declare function tevmReady(client: viem.Client<TevmTransport<string>>): Promise<true>; declare function tevmSetAccount(client: viem.Client<TevmTransport<string>>, params: _tevm_actions.SetAccountParams): Promise<_tevm_actions.SetAccountResult>; declare function tevmViemActions(): (client: viem.Client<TevmTransport<string>>) => TevmViemActionsApi; export { type CreateMemoryClientFn, type MemoryClient, type MemoryClientOptions, type TevmActions, type TevmContract, type TevmRpcSchema, type TevmTransport, type TevmViemActionsApi, createMemoryClient, createTevmTransport, tevmCall, tevmContract, tevmDeploy, tevmDumpState, tevmGetAccount, tevmLoadState, tevmMine, tevmReady, tevmSetAccount, tevmViemActions };