Key Takeaways: Solidity Programming

Core Concepts

  1. Solidity is purpose-built for the EVM. Its design decisions — explicit data locations, gas-aware type sizes, 256-bit word alignment, visibility specifiers — all reflect the constraints of a deterministic virtual machine where every operation costs real money and deployed code is immutable.

  2. Data types map to EVM realities. uint256 is the native word size (single opcode). address is 20 bytes matching Ethereum's account model. mapping uses keccak256 hashing to scatter key-value pairs across the 2^256 storage space. There are no floating-point numbers because deterministic execution across all nodes requires exact arithmetic.

  3. Storage, memory, and calldata are not interchangeable. Storage persists on the blockchain and costs 5,000-20,000 gas per write. Memory is temporary scratch space at 3 gas per word. Calldata is read-only and cheapest of all. Choosing the wrong data location is the most common source of unexpected gas costs for new developers.

  4. Visibility is a security decision. public generates a getter and allows internal + external calls. external is for outside-only access (cheaper for calldata parameters). internal allows access from derived contracts. private restricts to the defining contract only — but does NOT hide data from the public blockchain.

  5. The ERC-20 standard is six functions and two events. totalSupply, balanceOf, transfer, allowance, approve, transferFrom, plus Transfer and Approval events. This minimal interface enabled a $100B+ token ecosystem by prioritizing composability over comprehensiveness.

  6. The approve/transferFrom pattern enables composability. It allows third-party contracts (DEXes, lending protocols, yield aggregators) to move tokens on behalf of users. This two-step pattern is the foundation of DeFi.

  7. Testing is the last defense before immutable deployment. Hardhat provides local blockchain testing, allowing you to deploy, interact, and verify contract behavior before committing to a public network. Every function should be tested for the happy path, edge cases, and failure conditions.

Critical Patterns

  • Checks-Effects-Interactions: Validate inputs first, update state second, make external calls last. Violating this order enables reentrancy attacks.
  • Custom modifiers for access control: Use onlyOwner and similar modifiers for repeated authorization checks, but keep complex validation as explicit require statements.
  • Events for off-chain communication: Events are cheaper than storage and are the standard mechanism for DApps to track contract activity. Emit events for every state change that external applications need to observe.
  • Custom errors over string messages: Since Solidity 0.8.4, custom errors (error InsufficientBalance(...)) save gas compared to require with string messages.

Common Pitfalls to Avoid

  • Forgetting that private does not mean "hidden" — all blockchain data is publicly readable
  • Using unbounded loops over dynamic arrays (denial-of-service risk)
  • Hard-coding private keys in configuration files or source code
  • Using transfer() or send() instead of the low-level call pattern for sending Ether
  • Not checking for the zero address in transfer and mint functions
  • Ignoring storage variable ordering (missing opportunities for slot packing)
  • Trusting that deployed contracts can be "fixed later" — they cannot

What You Built

  • HelloWorld.sol — A minimal contract demonstrating state variables, functions, and the pragma directive
  • DataTypes.sol — A reference contract showcasing every major Solidity data type
  • VotingToken.sol — A complete ERC-20 governance token with minting capability (progressive project)
  • SimpleVoting.sol — A governance contract with proposal creation, token-weighted voting, and execution (progressive project)
  • Hardhat test suite — Automated tests covering deployment, transfers, allowances, minting, and voting
  • Deployment script — A script for deploying both contracts to a testnet

Looking Ahead

Chapter 14 introduces advanced Solidity patterns that address the limitations of the contracts built here: reentrancy guards for safe external calls, proxy contracts for upgradeability, the factory pattern for deploying contracts from contracts, and gas optimization techniques that go beyond storage packing. The VotingToken and SimpleVoting contracts will be refactored to incorporate snapshot-based voting, quorum requirements, and time-locked execution.