Writing a contract is only half the job; you also need to compile it, test it exhaustively, and deploy it — and because deployed contracts usually can't be changed and hold real money, the testing matters more here than almost anywhere in software. This lesson introduces the developer toolkit: Hardhat and Foundry, the two dominant frameworks, plus the local blockchain that lets you deploy and experiment instantly and for free. You'll see the develop-test-deploy loop that every serious contract goes through before it touches a real network.
- Describe what a smart-contract development framework does
- Compare Hardhat and Foundry at a practical level
- Explain why a local blockchain makes development fast and free
- Outline the write → test → deploy workflow and why testing is non-negotiable
What a framework gives you
You could compile and deploy a contract by hand, but no one does, because a development framework automates the whole cycle. A framework compiles your Solidity to EVM bytecode, runs your test suite against a simulated blockchain, deploys to whichever network you point it at, and helps you interact with the deployed contract. It also manages dependencies (like pulling in OpenZeppelin) and keeps configuration for different networks in one place. The two you'll hear about constantly are Hardhat and Foundry; both do these jobs well, and learning the workflow matters more than the choice between them.
Hardhat and Foundry
Hardhat is a JavaScript/TypeScript framework: you write your tests and deployment scripts in JavaScript, using libraries like ethers.js, which makes it a natural fit if you're also building a web frontend in the same language. It has a rich plugin ecosystem and a built-in local network with helpful features like console logging from inside Solidity. Foundry takes a different approach: you write your tests *in Solidity itself*, and it's known for being extremely fast and for powerful testing features like fuzzing (throwing many random inputs at your contract to find edge cases). Many teams use Foundry for its speed and testing depth and reach for Hardhat's JS ecosystem when integrating a frontend; some use both.
| Aspect | Hardhat | Foundry |
|---|---|---|
| Tests written in | JavaScript / TypeScript | Solidity |
| Best fit | JS/TS frontends, plugins | Speed, fuzzing, Solidity-native |
| Local chain | Hardhat Network | Anvil |
| Known for | Ecosystem and tooling | Very fast, strong testing |
The local blockchain
The single most useful tool is a local blockchain — a real EVM running on your own machine (Hardhat Network, or Foundry's Anvil). It starts instantly, gives you a set of accounts pre-funded with fake ether, and mines transactions the moment you send them. This means you can deploy your contract and call its functions hundreds of times a minute, with no waiting and no cost, resetting to a clean state whenever you like. Your tests run against this local chain, so a whole test suite that deploys contracts and exercises them finishes in seconds. Development happens here first; a public testnet comes later, and mainnet last of all.
# A typical Hardhat project, from scratch
npm init -y
npm install --save-dev hardhat
npx hardhat init # scaffold the project
npx hardhat compile # Solidity -> EVM bytecode
npx hardhat test # run tests on the built-in local chain
npx hardhat node # start a standalone local blockchainTesting is not optional
In ordinary software a bug means a patch. In smart contracts a deployed contract's code usually cannot be changed, and it may hold millions in value, so a bug can be an unfixable, catastrophic loss. That inverts the normal priorities: testing is the *main event*, not an afterthought. A good test suite deploys the contract and checks that every function behaves correctly, that permissions are enforced (a non-owner is rejected), that edge cases are handled (zero amounts, empty inputs), and that failures revert as expected. Frameworks make writing these tests straightforward, and you run them constantly as you develop.
// A Hardhat test (using ethers + chai) for the Counter contract
const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("Counter", function () {
it("starts at zero and increments", async function () {
const Counter = await ethers.getContractFactory("Counter");
const counter = await Counter.deploy(); // deploy to local chain
expect(await counter.count()).to.equal(0); // initial state
await counter.increment(); // send a transaction
expect(await counter.count()).to.equal(1); // state changed
});
});The develop-test-deploy loop
The whole workflow forms a loop you'll repeat constantly. Write or change the contract. Compile it to catch type errors. Test it against the local chain until everything passes and the edge cases are covered. Only then deploy — first to a public testnet (the previous stage's free network) to confirm it behaves the same against a real, shared chain, and finally, if it's meant for production, to mainnet, ideally after an audit. Each stage is a wider, more expensive net, so you catch problems as early and cheaply as possible. Never skip straight to mainnet; the frameworks make the earlier, safer stages nearly effortless.
- Write / edit the contract.
- Compile to catch type and syntax errors.
- Test on the local chain until all cases pass.
- Deploy to a public testnet and verify real-world behaviour.
- Deploy to mainnet last, after thorough testing (and an audit for real value).
A deployment script is code too — keep it in your project and version it, so deploying is reproducible rather than a series of manual clicks. The blockchain projects in this track use exactly this loop: write, test locally, deploy to a testnet.