Solidity, learning diary
Notes from a web2 engineer writing real smart contracts. Foundry, gas math, and the mental shift that actually matters.
· 4 min read
Signet Protocol was the excuse I needed to finally learn Solidity properly. Three smart contracts later, this is what nobody told me at the start.
Gas is the type system you did not sign up for
When I write TypeScript, I think about types. When I write Solidity, I think about gas. Every storage write is RM equivalent. Every extra require statement costs money on every call, forever, across every user.
This completely changes how you write control flow. Guard clauses at the top? Fine. A defensive-programming culture of three nested checks? Not fine. pure and view functions cost nothing to call off-chain, so you push computation there. Storage is sacred.
The mental shift that actually matters is not "learn new syntax". It is "your code runs in a world where every CPU instruction has a bill attached."
Foundry, not Hardhat
Hardhat was the default when I started looking. Foundry is what I ended up with. Rust-fast, test-first, and the Solidity feels like writing Solidity instead of writing a JavaScript bridge to Solidity.
forge test is instant. forge fuzz generates random inputs and finds edge cases I would not have thought of in an afternoon. forge snapshot gives me gas measurements per function — which is the only way you iterate on gas optimisation without going mad.
If you are starting today, start with Foundry.
Storage layout
The single most important Solidity concept for a web2 brain: storage slots. Every state variable maps to a 32-byte slot. Multiple small variables can pack into one slot if they fit. A uint256 followed by a uint8 wastes 31 bytes. A uint128 followed by a uint128 shares a slot.
I spent two days optimising SignetVault gas costs by reordering struct fields. The saving was ~8% on every stake operation. Real money at protocol scale.
The OpenZeppelin question
I use OpenZeppelin for anything non-trivial. Token standards, access control, reentrancy guards, upgradeable proxies. Writing these from scratch is a footgun I am not senior enough to hold.
That said, I read every OZ contract I import. Line by line. If a primitive is going to hold other people's money, understanding it is the minimum bar. This is the single biggest attitudinal shift from web2 work where you can reasonably npm install something and trust the ecosystem.
What broke first
Cross-chain anything. The SignetResolver contract was supposed to use LayerZero V2 for cross-chain verification. My first attempt at reading a LayerZero message silently failed on Arbitrum Sepolia because I had not enabled the right endpoint config. Three days of debugging later, the message path was fine. The test harness was wrong.
This is the thing nobody tells you about blockchain work: the tooling is 5 years behind web2 and you have to diagnose where your bug actually lives before you can fix it.
Would I go full crypto
No. I like shipping real products to real users who pay me in ringgit. But having Solidity in the toolkit changed how I think about economic incentives, escrow flows, and computational budgets. Skills that apply regardless of chain.
Keep reading
- sreobservability
Why Your Error Budget Is Lying to You (And How Observability Actually Fixes It)
Error budgets sound clean in theory but fail silently when you can't see where the unreliability actually is. Here's how to build observability that makes them real.
- databaseapi
When Your Team Stops Talking to the Database Directly — Why That Matters
Direct database access feels fast until someone deletes a year of records by accident. Here's what happens when you add an API layer, what it costs, and the exact warning signs your business has outgrown the shortcut.
- aiworkflow
Claude Code, but for backend people
Not a demo reel. How I actually use Claude Code for queue workers, schema migrations, and payment code where wrong answers cost RM.