पाठ 20 / 25
Hardhat, ethers.js and viem
Use the JavaScript and TypeScript tooling ecosystem for contracts and scripts.
The TypeScript side of the ecosystem
Hardhat is a JavaScript and TypeScript development environment for Ethereum. It compiles contracts, runs a local network (Hardhat Network) with console.log support in Solidity, runs tests written in TypeScript with Mocha and Chai (and recent versions also support Solidity tests), and offers plugins for verification, gas reporting, coverage and deployment (Hardhat Ignition for declarative deployments). Many teams combine tools: Foundry for fast Solidity tests and Hardhat or scripts for deployment and integration with front ends. Two main TypeScript libraries talk to Ethereum: ethers.js (v6), long the standard, with Provider, Signer and Contract objects; and viem, a newer, type-safe, modular library with public clients for reading and wallet clients for writing, used by wagmi for React apps. Both encode calls from the ABI, estimate gas, sign transactions and parse events. Use TypeChain or viem's ABI type inference for typed contract calls. Keep RPC URLs and private keys in environment variables or encrypted keystores, never in source control.
Reading and writing a contract with viem
A public client for reads, a wallet client for writes, and event watching.
import { createPublicClient, createWalletClient, http, parseEther, parseAbi } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { sepolia } from "viem/chains";
const abi = parseAbi([
"function deposit() payable",
"function unlockTime() view returns (uint256)",
"event Deposited(address indexed from, uint256 amount)",
]);
const bank = "0xYourPiggyBankAddress" as `0x${string}`;
const publicClient = createPublicClient({ chain: sepolia, transport: http(process.env.SEPOLIA_RPC_URL) });
const account = privateKeyToAccount(process.env.DEV_PRIVATE_KEY as `0x${string}`); // testnet-only key
const walletClient = createWalletClient({ account, chain: sepolia, transport: http(process.env.SEPOLIA_RPC_URL) });
const unlock = await publicClient.readContract({ address: bank, abi, functionName: "unlockTime" });
console.log("unlocks at", new Date(Number(unlock) * 1000).toISOString());
const { request } = await publicClient.simulateContract({
account, address: bank, abi, functionName: "deposit", value: parseEther("0.01"),
});
const hash = await walletClient.writeContract(request); // simulate first, then send
const receipt = await publicClient.waitForTransactionReceipt({ hash });
console.log("status:", receipt.status);
publicClient.watchContractEvent({
address: bank, abi, eventName: "Deposited",
onLogs: (logs) => logs.forEach((l) => console.log(l.args.from, l.args.amount)),
});Two workshops, one product
Foundry is a workshop where everything is built and tested in the same material (Solidity). Hardhat and viem are the showroom and delivery team that connect the product to web apps and customers in TypeScript.
त्वरित जाँच: Why call simulateContract before writeContract in viem?
- To pay less gas always
- To deploy the contract
- It is required for read calls
- To catch reverts and prepare a valid request before sending a real transaction
Answer
To catch reverts and prepare a valid request before sending a real transaction — Simulation runs the call against current state and surfaces errors without spending gas.