Skip to content

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.

Written by

Faiz Kasman

Software engineer in Kuala Lumpur. Payments, multi-tenant SaaS, and inventory infrastructure. Currently building the Shell Malaysia ParkEasy app.

Keep reading