Library
Mastering Ethereum: Building Smart Contracts and DApps · 8 of 15
Mastering Ethereum: Building Smart Contracts and DApps
blockchain HIGH

Rules of Thumb — Heuristics Across All Chapters

heuristics security gas solidity tokens oracles

Key Principle

On an immutable, adversarial platform, every rule below exists because a deployed bug is permanent, irreversible loss. "Complexity is the enemy of security" (Chapter 9): write less code, reuse audited code, and fail safe.

Why This Matters

These are the one-line imperatives the book repeats across every chapter. Each compresses a costly real-world failure into a check you can apply before deploying. Where dating matters it is flagged inline; the heuristics survive even where specific tooling moved on (Solidity 0.8+ checked math, EIP-1559 fees, EIP-6780 SELFDESTRUCT, The Merge).

Security

  • Authenticate with msg.sender, never tx.origintx.origin lets a malign intermediary contract inherit the victim's authority. (Chapter 9/13)
  • Apply checks-effects-interactions: make external calls the last operation, after every state change — the structural fix for reentrancy (the DAO). (Chapter 9)
  • Never send ether before updating your own state, or the recipient's fallback can re-enter and drain against a stale check; add a mutex if needed. (Chapter 9)
  • Route arithmetic through SafeMath pre-0.8 — EVM integers wrap silently with no revert: "Trying to store 256 into a uint8 will result in 0." (Chapter 9)
  • Never derive randomness from block variables (blockhash, timestamp, number) — Ethereum is deterministic with no entropy and miners can bias them; use commit–reveal, RANDAO, or a randomness oracle. (Chapter 9)
  • Never base logic on this.balance — an attacker can force ether in via selfdestruct; track deposits in your own payable-incremented variable. (Chapter 9)
  • Maximize reuse of trusted code and "Don't roll your own crypto." (Chapter 9)
  • Minimize complexity — "Every single line of code you add expands the attack surface." (Chapter 9/10)
  • Always declare explicit visibility on every function; never rely on the public default. (Chapter 9)
  • Never mark a variable private to hide a secret — all storage is public; visibility only controls callability. (Chapter 7)
  • Add a time-delay safeguard on high-value withdrawals — the DAO's 28-day delay was the only window any response could be built in. (Appendix A)

Keys & Wallets

  • Generate keys only from a CSPRNG with sufficient entropy; never write your own RNG. (Chapter 4)
  • Build any new wallet as HD + mnemonic — BIP-39 (words), BIP-32 (tree), BIP-44 — and avoid nondeterministic "JBOK" wallets. (Chapter 5)
  • Treat the mnemonic as the wallet: write it on paper, store it securely, "Never store it electronically." (Chapter 5)
  • Always harden the level-1 children of master keys — a leaked child key + parent chain code "can be used to deduce the parent private key." (Chapter 5)
  • Deploy only the xpub to online servers to generate receive addresses with zero spending capability; keep the xprv offline. (Chapter 5)
  • Plan a recovery path for any BIP-39 passphrase and never store it beside the seed — "there are no 'wrong' passphrases." (Chapter 5)
  • Validate addresses at the UI with EIP-55 checksums — raw Ethereum addresses carry no built-in checksum and a typo burns funds. (Chapter 4)
  • Never use real-money keys for testing. (Chapter 10)

Transactions & Gas

  • Always check the return value of send() and low-level call() — both return false on error instead of reverting. (Chapter 7/9)
  • Prefer transfer() over send()/call()transfer reverts on failure (fails safe). (Chapter 7/9) [DATED 2018: the 2300-gas stipend breaks with proxy/multisig recipients; modern practice prefers call + reentrancy guard.]
  • Always over-provision gasLimit for contract calls — gas "can be estimated but cannot be determined with accuracy." EOA-to-EOA transfer is fixed at 21,000 gas. (Chapter 7)
  • Minimize computation and bound loops/external calls — gas spent before an out-of-gas exception is charged and not refunded though state reverts. (Chapter 7/13)
  • Never submit a transaction whose own gas requirement exceeds the block gas limit — it can never be mined. (Chapter 13)
  • Use unit literals (0.1 ether, wei, 1 weeks) instead of raw numeric literals. (Chapter 7)
  • Always specify to explicitly; the protocol does no recipient validation, so a wrong address burns the ether. (Chapter 6)
  • Sign offline: separate signing from transmission so unlocked keys never sit on an internet-connected box. (Chapter 6)
  • Add EIP-155 replay protection (chain ID in the signed hash) so a tx can't be replayed on a fork/testnet. (Chapter 6 / Appendix B)

Contract Design (Solidity/Vyper)

  • Favor the withdrawal (pull) pattern over push payments — one failed transfer then corrupts only that user's interaction. (Chapter 9)
  • Never use an unbounded loop over a user-growable structure — gas can exceed the block gas limit and brick the function permanently. (Chapter 9)
  • Use require() to gate inputs and assert() to test invariants; use revert (with a message), not the obsolete throw. (Chapter 7)
  • Mark functions intended to receive ether payable; non-payable functions reject incoming payments. (Chapter 7)
  • Use the nameless constructor keyword — a function named after the contract that's mistyped silently becomes a public, seizable method (Rubixi). (Chapter 7/9)
  • Build stateless libraries with the library keyword to remove persistent storage and forbid selfdestruct (the Parity bricking). (Chapter 9)
  • Treat DELEGATECALL as a footgun — the callee runs against your storage slots by position, not variable name. (Chapter 9/13)
  • Multiply before you divide; do internal math in high-precision uint256 — integer division truncates silently. (Chapter 9)
  • Write access-control once as a function modifier and apply it consistently. (Chapter 7)
  • Pin the compiler version with a pragma to guard against silent mis-compilation. (Chapter 7)
  • Integration-test composed contracts, not just each inherited unit. (Appendix C/D)
  • Reconcile immutability with maintainability via the upgradeable proxy pattern. (Appendix C/D)

Tokens

  • Follow an existing standard — ERC-20 (fungible), ERC-721 (non-fungible) — so every wallet works "without any wallet upgrade or effort on your part." (Chapter 10 / Appendix B)
  • Use audited implementations (OpenZeppelin) and compose crowdsales from audited mixins (Refundable, Capped, Timed, Minted). (Chapter 10 / Appendix C/D)
  • Never reduce an existing approve allowance in one call — it is replaced, not diffed, and is front-runnable; set to 0 first or use increaseApproval/decreaseApproval. (Chapter 9)
  • Never transfer tokens to a contract with no token-receiving logic — "The MET sent to Faucet is stuck, forever"; pull via approve + transferFrom. (Chapter 10)
  • Reject ether in a token-handling contract with a reverting fallback. (Chapter 10)
  • Remember ERC-20 decimals changes display only, not storage — a mismatch silently shifts the order of magnitude. (Chapter 10)
  • Adopt a token only if the application genuinely needs it — never to disguise a securities offering: "If it walks like a duck and quacks like a duck, it's a duck." (Chapter 10)

Oracles & DApps

  • Never trust on-chain external data just because all nodes agree on it — "Such data simply cannot be trusted... we have just deferred the problem." (Chapter 11)
  • Design every oracle to minimize imported trust; every authentication scheme merely relocates trust. (Chapter 11)
  • Prefer decentralized oracles over a single trusted source — a single oracle is a single point of failure. (Chapter 11)
  • Always authenticate the oracle callback: require(msg.sender == oraclize_cbAddress()). (Chapter 11)
  • Choose the oracle pattern by data size/frequency/gas: immediate-read, publish–subscribe, or request–response. (Chapter 11)
  • Make a DApp as decentralized as its least-decentralized component — never serve "unstoppable" contracts behind a takedownable host. (Chapter 12)
  • Push bulky data off-chain to content-addressable storage (IPFS/Swarm) and attach an ENS content record. (Chapter 12)
  • For a true DApp, ship no privileged/specialized accounts. (Chapter 12)
  • Operate ENS on fixed 32-byte namehash nodes, never raw strings; normalize names with UTS #46. (Chapter 11)

Architecture & Consensus

  • Keep EVM execution totally deterministic — randomness and external data enter only via signed-transaction payloads. (Chapter 11)
  • Audit before you deploy — deployment is a one-way door: "in smart contracts, bugs literally cost money." (Chapter 7)
  • Reuse Bitcoin's battle-tested primitives (secp256k1, libsecp256k1) — don't reinvent crypto. (Chapter 4)
  • Keep SSTORE/storage writes minimal — persistent storage is the dominant gas cost because every node keeps it forever. (Chapter 13)
  • Rely on gas, not code inspection, to guarantee halting — the EVM is quasi-Turing-complete, capped by available gas. (Chapter 13)
  • Treat multisig as application-layer (a wallet contract) and as high-stakes code. (Chapter 6)
  • Never fork without whole-community consensus; distrust low-turnout, whale-weighted "community consensus." (Appendix A/B)
  • Interrogate any "better consensus" claim with five questions — who can change the past, the future, at what cost, how decentralized, who will know: "no algorithm can optimize across all dimensions of the problem of decentralized consensus." (Chapter 14)

Key Quotes

"Complexity is the enemy of security. Every single line of code you add expands the attack surface of your contract and could represent a vulnerability lying in wait." — Antonopoulos & Wood, Chapter 9

"It is good practice for any code that performs external calls to unknown addresses to be the last operation... known as the checks-effects-interactions pattern." — Antonopoulos & Wood, Chapter 9

"If you lose your private keys, you lose access to your funds and contracts. No one can help you regain access — your funds will be locked forever." — Antonopoulos & Wood, Chapter 2

Rules of Thumb

This entire file is the rules-of-thumb collection; see the grouped sections above.

Related References