Getting Started with Ethers.js
Installation
Install Dependencies
npm
npm install tevm ethers@latestCreate Tevm Client
import { createMemoryClient, http } from "tevm";
import { optimism } from "tevm/common";
import { requestEip1193 } from "tevm/decorators";
const client = createMemoryClient({
fork: {
transport: http("https://mainnet.optimism.io"),
common: optimism,
},
});
// Enable EIP-1193 compatibility for ethers
client.transport.tevm.extend(requestEip1193());
await client.tevmReady();Connect Ethers
import { BrowserProvider, Wallet } from "ethers";
const provider = new BrowserProvider(client.transport.tevm);
const signer = Wallet.createRandom().connect(provider);
await client.setBalance({
address: signer.address,
value: 1000000000000000000n, // 1 ETH
});Start Using Ethers
const blockNumber = await provider.getBlockNumber();
console.log(`Current block: ${blockNumber}`);
const balance = await provider.getBalance(signer.address);
console.log(`Wallet balance: ${balance.toString()}`);Complete Example
import { createMemoryClient, http, parseAbi } from "tevm";
import { optimism } from "tevm/common";
import { requestEip1193 } from "tevm/decorators";
import { BrowserProvider, Wallet, Contract, formatEther } from "ethers";
import { parseUnits } from "ethers/utils";
const client = createMemoryClient({
fork: {
transport: http("https://mainnet.optimism.io"),
common: optimism,
},
});
client.transport.tevm.extend(requestEip1193());
await client.tevmReady();
const provider = new BrowserProvider(client.transport.tevm);
const signer = Wallet.createRandom().connect(provider);
const blockNumber = await provider.getBlockNumber();
console.log(`Current block number: ${blockNumber}`);
const greeterContractAddress = "0x10ed0b176048c34d69ffc0712de06CbE95730748";
const greeterAbi = parseAbi([
"function greet() view returns (string)",
"function setGreeting(string memory _greeting) public",
]);
const greeter = new Contract(greeterContractAddress, greeterAbi, signer);
const getGreeting = async () => await greeter.greet();
const setGreeting = async (newGreeting) => {
const tx = await greeter.setGreeting(newGreeting);
return tx.hash;
};
await client.setBalance({
address: signer.address,
value: parseUnits("1.0", "ether"),
});
console.log(
`Wallet funded with: ${formatEther(await provider.getBalance(signer.address))} ETH`,
);
console.log(`Original greeting: ${await getGreeting()}`);
const txHash = await setGreeting("Hello from ethers.js and Tevm!");
console.log(`Transaction sent: ${txHash}`);
await client.mine({ blocks: 1 });
console.log(`Updated greeting: ${await getGreeting()}`);Understanding the Code
EIP-1193 Decorator — client.transport.tevm.extend(requestEip1193()) exposes the standard provider interface ethers expects.
Provider Creation — new BrowserProvider(client.transport.tevm) accepts the decorated Tevm node as a standard Ethereum provider.
Hybrid API — use Ethers for contract interaction (Contract, provider, signer) and Tevm for state control (client.setBalance(), client.mine()).
Mining Control — explicit client.mine({ blocks: 1 }) gives full timing control.
Common Patterns
Contract Deployment
import { createMemoryClient } from "tevm";
import { requestEip1193 } from "tevm/decorators";
import { BrowserProvider, Wallet, ContractFactory, formatEther } from "ethers";
const client = createMemoryClient();
client.transport.tevm.extend(requestEip1193());
await client.tevmReady();
const provider = new BrowserProvider(client.transport.tevm);
const signer = Wallet.createRandom().connect(provider);
await client.setBalance({
address: signer.address,
value: 10000000000000000000n, // 10 ETH
});
console.log(
`Wallet funded with: ${formatEther(await provider.getBalance(signer.address))} ETH`,
);
const counterAbi = [
"function count() view returns (uint256)",
"function increment() public",
];
const counterBytecode =
"0x608060405234801561001057600080fd5b5060f78061001f6000396000f3fe6080604052348015600f57600080fd5b5060043610603c5760003560e01c80633fb5c1cb1460415780638381f58a146053578063d09de08a14606d575b600080fd5b6051604c3660046083565b600055565b005b605b60005481565b60405190815260200160405180910390f35b6051600080549080607c83609b565b9190505550565b600060208284031215609457600080fd5b5035919050565b60006001820160ba57634e487b7160e01b600052601160045260246000fd5b506001019056fea2646970667358221220d5fb46adf6ce0cfd90fa4324ffd8c48b0fc6fb6c4cac9ca2c69c97e25f355c9d64736f6c63430008110033";
const counterFactory = new ContractFactory(counterAbi, counterBytecode, signer);
const counterDeploy = await counterFactory.deploy();
console.log(`Deployment transaction: ${counterDeploy.deploymentTransaction().hash}`);
await client.mine({ blocks: 1 });
const counter = await counterDeploy.waitForDeployment();
const counterAddress = await counter.getAddress();
console.log(`Counter deployed at: ${counterAddress}`);
console.log(`Initial count: ${await counter.count()}`);
const tx = await counter.increment();
console.log(`Increment transaction: ${tx.hash}`);
await client.mine({ blocks: 1 });
console.log(`New count: ${await counter.count()}`);Reading and Writing Contract Data
import { createMemoryClient, http, parseAbi } from "tevm";
import { mainnet } from "tevm/common";
import { requestEip1193 } from "tevm/decorators";
import {
BrowserProvider,
Wallet,
Contract,
formatEther,
formatUnits,
} from "ethers";
const client = createMemoryClient({
fork: {
transport: http("https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY"),
common: mainnet,
},
});
client.transport.tevm.extend(requestEip1193());
await client.tevmReady();
const provider = new BrowserProvider(client.transport.tevm);
const signer = Wallet.createRandom().connect(provider);
await client.setBalance({
address: signer.address,
value: 100000000000000000000n, // 100 ETH
});
const usdcAddress = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";
const usdcAbi = parseAbi([
"function name() view returns (string)",
"function symbol() view returns (string)",
"function decimals() view returns (uint8)",
"function totalSupply() view returns (uint256)",
"function balanceOf(address owner) view returns (uint256)",
]);
const usdc = new Contract(usdcAddress, usdcAbi, provider);
const [name, symbol, decimals, totalSupply] = await Promise.all([
usdc.name(),
usdc.symbol(),
usdc.decimals(),
usdc.totalSupply(),
]);
console.log(`Contract: ${name} (${symbol})`);
console.log(`Decimals: ${decimals}`);
console.log(`Total Supply: ${formatUnits(totalSupply, decimals)} ${symbol}`);
const daiAddress = "0x6B175474E89094C44Da98b954EedeAC495271d0F";
const daiUsdcBalance = await usdc.balanceOf(daiAddress);
console.log(
`DAI contract's USDC balance: ${formatUnits(daiUsdcBalance, decimals)} ${symbol}`,
);Working with Events
import { createMemoryClient } from "tevm";
import { requestEip1193 } from "tevm/decorators";
import { BrowserProvider, Wallet, Contract, formatUnits } from "ethers";
import { parseAbi } from "tevm";
const client = createMemoryClient();
client.transport.tevm.extend(requestEip1193());
await client.tevmReady();
const provider = new BrowserProvider(client.transport.tevm);
const signer = Wallet.createRandom().connect(provider);
await client.setBalance({
address: signer.address,
value: 10000000000000000000n,
});
const tokenAbi = parseAbi([
"constructor(string name, string symbol, uint8 decimals)",
"function transfer(address to, uint256 amount) returns (bool)",
"function balanceOf(address owner) view returns (uint256)",
"event Transfer(address indexed from, address indexed to, uint256 value)",
]);
const tokenBytecode =
"0x608060405234801561001057600080fd5b506040516107fa3803806107fa83398101604081905261002f91610215565b600380546001600160a01b031916331790558151610052906004906020850190610076565b5081516100669060059060208401906100fd565b506006805460ff191660ff929092169190911790555061030c565b828054610082906102c8565b90600052602060002090601f0160209004810192826100a4576000855561010a565b82601f106100bd57805160ff19168380011785556100ea565b828001600101855582156100ea579182015b828111156100ea5782518255916020019190600101906100cf565b506100f69291506100f6565b5090565b5b808211156100f657600081556001016100f7565b828054610109906102c8565b90600052602060002090601f01602090048101928261012b5760008552610167565b82601f1061014457805160ff1916838001178555610171565b82800160010185558215610171579182015b82811115610171578251825591602001919060010190610156565b5061017d9291506101bd565b5090565b5b8082111561017d576000815560010161017e565b6000602082840312156101a657600080fd5b81516001600160a01b03811681146101bd57600080fd5b9392505050565b5b8082111561017d57600081556001016101be565b80516001600160a01b03811681146101eb57600080fd5b919050565b600080600060608486031215610205578283fd5b833590925060208401359160408401359050509250925092565b60008060006060848603121561022a578081fd5b835160208501516040860151919450919290910181111581146102ad57634e487b7160e01b600052601160045260246000fd5b809150509250925092565b634e487b7160e01b600052602260045260246000fd5b600181811c908216806102dc57607f821691505b6020821081036102fc57634e487b7160e01b600052602260045260246000fd5b50919050565b6104df8061031b6000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806370a082311461004657806395d89b4114610079578063a9059cbb14610081575b600080fd5b610066610054366004610356565b6001600160a01b031660009081526001602052604090205490565b60405190815260200160405180910390f35b61007b610094565b005b61007b61008f366004610379565b610121565b6005805461009f906104a0565b80601f01602080910402602001604051908101604052809291908181526020018280546100cb906104a0565b80156101185780601f106100ed57610100808354040283529160200191610118565b820191906000526020600020905b8154815290600101906020018083116100fb57829003601f168201915b505050505081565b6001600160a01b03821660009081526001602052604081205461014a908363ffffffff61022916565b6001600160a01b038416600090815260016020526040812091909155610177908263ffffffff61024116565b6001600160a01b0383166000908152600160205260409020556101d0816040518060600160405280602381526020016104876023913960405180604001604052806029815260200161045e60299139600090339161025a565b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051610216918252602082015260400190565b60405180910390a35050565b600081831061023857506000610239565b5b92915050565b6000828201610239825b8351602g2b6020600020905b905090565b60408051808201909152602d8152600080516020610457833981519152602082015290565b9050919050565b841515610297576001600160e01b031916600052601160045260246000fd5b5063ffffffff1690565b6000602082840312156102b357600080fd5b81356001600160a01b03811681146102ca57600080fd5b9392505050565b6000602082840312156102e357600080fd5b813560fc81168114t12870be52600052602260045260246000d5b602082108a5734e487b7160q0b600052602260045260246000fd5b50919050565b6004f3fe68747470733a2f2f6769646c6572732e696f2f6a616d657368756768657369o97365722f7468696e67736e6f626f6479656c7365686173a2646970667358221220f3acb75fca24514561f12a53c4a92042a9a6c524895dc1b01f3c3d2cda5d8ff364736f6c634300080a0033";
const tokenFactory = new ContractFactory(tokenAbi, tokenBytecode, signer);
const token = await tokenFactory.deploy("Test Token", "TST", 18);
await client.mine({ blocks: 1 });
const tokenAddress = await token.getAddress();
console.log(`Token deployed at: ${tokenAddress}`);
token.on("Transfer", (from, to, value, event) => {
console.log(`Transfer detected in block ${event.blockNumber}:`);
console.log(` From: ${from}`);
console.log(` To: ${to}`);
console.log(` Value: ${formatUnits(value, 18)} TST`);
});
const recipient = Wallet.createRandom().address;
const tx = await token.transfer(recipient, 1000000000000000000n); // 1 TST
console.log(`Transfer transaction: ${tx.hash}`);
await client.mine({ blocks: 1 });
const balance = await token.balanceOf(recipient);
console.log(`Recipient balance: ${formatUnits(balance, 18)} TST`);
token.removeAllListeners();Key Differences from Standard Ethers Usage
- Transaction Flow — Real network: auto-mined. Tevm: call
client.mine(). - Account Management — Real: real funds and nonces. Tevm: fund on demand via
client.setBalance(). - Environment Control — Real: limited state manipulation. Tevm: full control.
- Performance — Real: network calls + confirmations. Tevm: local, near-instant.
- Debugging — Real: limited. Tevm: step-by-step EVM tracing.
- Determinism — Real: variable. Tevm: 100% deterministic.
Advanced Features
EVM State Control
await client.setBalance({
address: "0x123...",
value: parseUnits("1000", "ether"),
});
await client.setStorageAt({
address: contractAddress,
index: 0,
value: "0x1234...",
});
await client.setNextBlockTimestamp(Date.now());Contract Debugging
const tevmNode = await client.getTevmNode();
const vm = await tevmNode.getVm();
vm.evm.events.on("step", (data, next) => {
console.log(`EVM Step: ${data.opcode.name}`);
next();
});Fork Management
await client.reset({
fork: {
blockNumber: 42069n,
transport: http("https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY"),
},
});
