09. Web2 to Web3: Solidity & Hardhat Testing
Overview
Section titled “Overview”This is the longest lesson in the module and the real starting line for writing contracts. It covers Solidity — the language the EVM’s bytecode is compiled from — and, just as importantly, how to test what you write. Because a deployed contract is immutable and can hold real money, testing is not an afterthought here; it is the discipline that makes deploying safe. Watch actively with an editor open and pause to try things.
Key concepts
Section titled “Key concepts”- Contract structure — a Solidity file starts with a license identifier and a
pragma solidityversion, then declares acontractthat holds state and functions. The contract is the unit that gets deployed to an address. - State variables — data stored permanently in the contract’s onchain storage. Writing
to storage costs gas; reading it in a transaction does too. Types you will use constantly:
uint256,address,bool,string, andmapping. mapping— a key→value store (e.g.mapping(address => uint256)for balances). It is the workhorse data structure of Solidity; there is no iteration over it, so you track keys yourself if you need to.- Functions and visibility —
public,external,internal,privatecontrol who can call a function;view/puremark functions that don’t change state (and are free to call off-chain). A function that changes state requires a transaction and gas. event— a log the contract emits (e.g.Transfer). Events are how off-chain apps and indexers learn what happened without reading storage directly; you will rely on them in Module 3 and 4.modifier— reusable pre-conditions attached to functions (e.g.onlyOwner) thatrequiresomething before the body runs. This is the standard pattern for access control.require/ revert — validate inputs and permissions; a failedrequirereverts the whole transaction (rolling back state, per Module 1’s gas lesson).- Testing with Hardhat — you write tests in JavaScript/TypeScript that deploy the contract to a local network, call its functions, and assert the results. A good test names the behavior, exercises both success and failure paths, and runs in seconds — a fast loop that lets you refactor without fear.
Common pitfalls
Section titled “Common pitfalls”- Forgetting that every storage write costs gas. Beginners store things onchain that could be derived or kept off-chain.
- Getting visibility wrong — a function meant to be internal left
publicis a classic vulnerability. Default to the least access that works. - Skipping the failure-path tests. The bugs that matter are usually “this should have reverted but didn’t,” which a happy-path-only suite never catches.
- Treating tests as busywork. On an immutable platform, the test suite is your safety net.
Check yourself
Section titled “Check yourself”- What is the difference between a
viewfunction and one that writes state, in terms of gas and how it’s called? - Why are
events important if the data is already in storage? - Name one thing a good test suite checks that a “does it work once” manual click does not.
Go deeper
Section titled “Go deeper”The notes above are enough to follow the lesson, but if you want a minimal, runnable contract for each Solidity building block, Solidity by Example (maintained by Cyfrin) is the best companion reference — every page is one small contract you can read in a minute.
Language building blocks
- Primitive data types —
bool,uint,int,address,bytes, and their default values. - Reading and writing state variables — the storage writes that cost gas.
- Functions and view / pure functions — which functions change state, which only read, and which cost gas.
- Mappings, arrays, structs, and enums — the everyday data structures.
- Data locations: storage, memory, calldata — where a variable lives, and why it matters.
- Errors —
require,revert, and custom errors. - Function modifiers and visibility — reusable guards, and
public/external/internal/private. - Events — logging state changes for a frontend to read.
- Constructor and inheritance — one-time setup and reusing base contracts.
Testing in Solidity
Solidity by Example also has a set of Foundry test examples — basic tests, expected reverts, emitted events, vm.store, and time control. Even while you write your Hardhat tests in JavaScript, skimming these shows the same testing ideas (arrange, act, assert; expect a revert; check an event) expressed in Solidity — which you’ll meet again with Foundry in Module 3.
Source: Solidity by Example, an open-source reference maintained by Cyfrin. Code belongs to its authors.
Watch the Video
Section titled “Watch the Video”Video credit: Austin Griffith · Duration: 1 hour 40 minutes
