# Hardhat, ethers.js and viem — Blockchain & Smart Contracts (Solidity)

Source: https://www.geekswithgeeks.com/en/solidity/w-hardhat

> 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.

```typescript
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.

**Quiz:** Why call simulateContract before writeContract in viem?

- [ ] To pay less gas always
- [ ] To deploy the contract
- [ ] It is required for read calls
- [x] 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.
