Smart Contracts: From Concept to Ethereum

Section VII: Ethereum Framework

Army Cyber Institute

April 9, 2026

Smart Contracts: Concept and Scope

  • In the last lesson we traced how transactions flow through the network. Now we look at what those transactions actually execute.

  • A smart contract is the combination of code and state that enforces rules autonomously on a blockchain.

  • Different from ‘legal contracts’: execution is automatic and deterministic, not interpretive.

  • We will briefly survey non-Ethereum smart contract models to situate the landscape.

From Bitcoin Script to Solidity

Feature Bitcoin Script Solidity (Ethereum)
Purpose Defines conditions to spend a specific UTXO. Defines programs that manage state and logic.
Language Type Stack-based, declarative, non–Turing complete. High-level, imperative, Turing complete.
State Model Stateless: no memory of prior executions. Stateful: contracts maintain persistent storage between calls.
Execution Context Runs only when spending an output. Invoked via transactions or calls; read/write storage, call contracts.
Control Flow No loops or recursion; limited branching. Full control flow (loops, branching, function calls, libraries).
Resource Control Bounded by design. Bounded by gas metering.
Security Model Simplicity and determinism reduce attack surface. Expressiveness increases flexibility and vulnerability (reentrancy, etc.).

Beyond Ethereum: Other Smart Contract Models

  • UTXO-plus models (e.g., Cardano eUTXO): richer logic while keeping UTXO structure.
    • Tradeoff: stronger formal verification, but stateless contracts are harder to program.
  • Account-centric but non-EVM (e.g., Solana): program-owned accounts with explicit parallelism.
    • Tradeoff: high throughput, but developers must declare all touched accounts upfront.
  • WebAssembly-based contracts (e.g., CosmWasm, Near): compile Rust/Go to a WASM sandbox.
    • Tradeoff: familiar languages and memory safety, but smaller ecosystem and tooling.
  • Private/permissioned chaincode (e.g., Hyperledger Fabric): enterprise settings with known participants.
    • Tradeoff: fine-grained access control, but sacrifices permissionless composability.

Ethereum’s Advantage for Smart Contracts

  • Largest and most mature ecosystem for contract development and deployment.
  • Clear execution environment (EVM) with deterministic outcomes and gas-metered computation.
  • Shared standards (ERC-20, ERC-721, ERC-1155, etc.) enable composability and predictable interfaces.
  • Rich tooling and libraries (Hardhat, Foundry, OpenZeppelin) lower barriers to safe experimentation.
  • Open participation model: anyone can verify, deploy, or compose with existing code.

Ethereum Accounts and Contract Review

  • Two account types: EOA (user, has private key) and Contract (code + storage).
    • Both share the same address format and can hold ETH, but only EOAs can sign transactions.
  • Contracts execute on message calls; they cannot self-initiate.
    • Every on-chain action traces back to an EOA signature, preserving explicit causality.
  • State lives in storage (persistent) vs memory (ephemeral).
  • Each call executes with msg.sender, msg.value, available gas, calldata (function inputs), block context, etc.
    • msg.sender is the immediate caller, not necessarily the original user. This matters for access control.

Contract Deployment

  • Deployment sends initcode that runs once to produce runtime bytecode.

  • Constructor logic initializes storage; only runtime code persists on chain.

  • Contract address is derived deterministically (nonce or CREATE2 with salt).

    • Deployment fails if a contract already exists at that address.
  • Deployment costs gas; large code and heavy initialization are expensive.

Calls and Control Flow in the EVM

  • External calls hand control to untrusted code; treat with care.
    • The callee can execute arbitrary logic, including calling back into your contract (reentrancy).
  • Reverts cascade unless caught; state changes roll back per frame.
    • If contract B reverts and A does not handle it (try/catch), A reverts too and the whole transaction unwinds.
  • msg.sender and msg.value are per-frame; gas forwarding is explicit.
    • When A calls B, B sees A as msg.sender, not the original user. This is critical for access control.

callflow user User (EOA) a Contract A msg.sender = User user->a tx: A.f(x) value = v a->user return / revert b Contract B msg.sender = A a->b external call B.g(y) b->a return / revert

Call frame: the EVM’s execution context for a single call. Each frame has its own msg.sender, msg.value, gas budget, memory, and return data. A revert undoes state changes within that frame only.

Solidity: File and Contract Structure

  • Version pragma: compiler compatibility (e.g., pragma solidity ^0.8.26;).
  • Contract declaration: contract Name { ... }.
  • State variables: persist in storage across transactions.
  • Functions: visibility, mutability (view/pure), and payable.
  • Constructor: one-time initialization at deployment.
  • Events: logs that external apps can subscribe to.

Types: Value Types

  • Integers: uint, int (default 256-bit); smaller sizes available.
  • bool - true/false
  • address (20 bytes) identifies accounts; address payable can receive ETH.
  • enum for finite sets of named constants.
  • Literals: 1 ether (wei), time units (seconds, minutes), etc.

Types: Reference Types

  • Arrays: fixed or dynamic; bytes and string are dynamic.
    • fixed-size example bytes1bytes32
    • fixed arrays fit into a single 32-byte EVM storage slot.
  • struct: custom composite types for grouping fields.
  • mapping(Key => Value): hash table; not iterable.
  • Data locations: storage, memory, calldata control where data lives.
  • Copy semantics differ by location; storage writes cost gas.

Visibility, Mutability, and Payable

  • Visibility: public, external, internal, private.
  • Function Mutability: view (reads), pure (no reads/writes), default (writes allowed).
  • payable functions can receive ETH; others reject attached ETH.
  • Public state variables get auto-generated getters.
  • External functions are cheaper to call from outside than public.

Data Locations: Storage, Memory, Calldata

  • Storage: persistent across transactions
    • Balances, owners, mappings
    • Most expensive to write (~20k gas per new slot)
  • Memory: ephemeral scratch pad per call
    • Local arrays, ABI encoding buffers
    • Zeroed between calls
  • Calldata: read-only transaction inputs
    • Function arguments from the caller
    • Cheapest; no copy needed for external functions
  • Wrong location choices waste gas or cause bugs.

dataloc cluster_contract Smart Contract tx Transaction calldata Calldata function args from caller (read-only) tx->calldata input bytes memory Memory local arrays, encoding buffers (cleared each call) calldata->memory copy if needed storage Storage balances, owners, mappings (persists forever) memory->storage SSTORE storage->memory SLOAD

Example: SimpleStorage (Code)

pragma solidity ^0.8.26;
contract SimpleStorage {
    uint256 private stored;
    event Updated(uint256 oldValue, uint256 newValue);

    function set(uint256 x) public {
        emit Updated(stored, x);
        stored = x;
    }

    function get() public view returns (uint256) {
        return stored;
    }
}
  • One state variable; one setter, one getter, one event.
  • Demonstrates state write vs view read.
  • Events record history clients can subscribe to.
  • Extend: add access control or input validation.

Example: Coin (Code, Part 1)

pragma solidity ^0.8.26;
contract Coin {
    address public minter;
    mapping(address => uint) public balances;
    event Sent(address from, address to, uint amount);
    constructor() { minter = msg.sender; }
    function mint(address receiver, uint amount) public {
        require(msg.sender == minter, "not minter");
        balances[receiver] += amount;
    }
  • Public state vars auto-generate getters.
  • Constructor initializes the minter to deployer.
  • mint restricts access to minter via require.
  • Mapping initializes to zero.

Example: Coin (Code, Part 2)

    error InsufficientBalance(uint requested, uint available);
    function send(address receiver, uint amount) public {
        require(amount <= balances[msg.sender], 
                InsufficientBalance(amount, balances[msg.sender]));
        balances[msg.sender] -= amount;
        balances[receiver] += amount;
        emit Sent(msg.sender, receiver, amount);
    }
}
  • Custom error improves gas vs string messages.
  • Transfers adjust balances and emit an event.
  • Pattern mirrors ERC‑20 internals (without allowances).
  • Extend: add burn/mint caps; pause; owner role.

Events and Logs

  • Contracts emit events, append-only records stored in the transaction’s receipt.
  • Each event creates a log entry with:
    • emitter address
    • topics (up to 4, including the event signature hash)
    • data (unindexed fields, ABI-encoded)
  • Logs stored in transaction but are not contract storage; they are not visible to smart contracts but are visible to off-chain systems (UIs, indexers, proofs).
  • Indexed fields (indexed) create searchable topics; non-indexed fields go into the data blob.
  • Events vanish if the transaction reverts.

Topic: a 32-byte indexed field in a log entry. Topic 0 is the keccak-256 hash of the event signature (e.g., Transfer(address,address,uint256)); topics 1–3 hold indexed parameter values. Clients filter logs by topic without scanning the full data blob.

ABI, Encoding, and Function Selectors

  • ABI (Application Binary Interface) defines how function calls, return values, and events are serialized into bytes.
  • Function call format:
    • 4-byte selector = keccak256("name(types)")[:4]
    • Followed by arguments, each encoded per ABI rules (32-byte words, offsets for dynamic types).
  • Return data and event logs follow ABI conventions.
  • receive handles plain ETH transfers; fallback handles unknown selectors or bad calldata.

Reentrancy: Anatomy and Defenses

  • Occurs when an external call reenters before your function finishes.

  • Classics: withdraw pattern before balance update; ERC-777 hooks misused.

reentrancy cluster_victim Victim Contract cluster_attacker Attacker Contract v1 1. check balance balance[attacker] = 10 v2 2. send ETH (balance NOT yet updated) v1->v2 v3 5. balance[attacker] = 0 (too late!) v2->v3 never reached a1 3. receive() triggered v2->a1 ETH transfer a2 4. call withdraw() again! a1->a2 a2->v1 re-enters (balance still 10!)

  • Defenses: Check-Effects-Interactions (CEI) ordering, ReentrancyGuard, pull over push payments.

Reentrancy: Vulnerable Pattern (Code)

mapping(address => uint) public balances;

function withdraw() public {
    uint amount = balances[msg.sender];
    require(amount > 0, "no funds");
    (bool ok,) = msg.sender.call{value: amount}("");
    require(ok, "send failed");
    balances[msg.sender] = 0;
}
  • External call before state update enables reentry.
  • Attacker’s fallback re-calls withdraw() before zeroing balance.
  • Result: multiple withdrawals from one balance.

Reentrancy: Checks-Effects-Interactions (Fix)

The fix is disciplined ordering:

  1. Check preconditions (require)
  2. Update state (effects) before yielding control
  3. External call last (interactions)

A reentrant call now sees zero balance and fails the check. The attack loop from the previous slide is broken.

For belt-and-suspenders safety, add a mutex via OpenZeppelin’s ReentrancyGuard: the nonReentrant modifier locks the function so recursive entry reverts immediately.

CEI pattern:

function withdraw() public {
    uint amount = balances[msg.sender];
    require(amount > 0, "no funds");   // CHECK
    balances[msg.sender] = 0;          // EFFECT
    (bool ok,) = payable(msg.sender)   // INTERACTION
        .call{value: amount}("");
    require(ok, "send failed");
}

 

ReentrancyGuard (mutex):

import "...utils/ReentrancyGuard.sol";

contract Vault is ReentrancyGuard {
    function withdraw() public nonReentrant {
        // CEI ordering + mutex lock
    }
}

Case Study: The DAO Hack (2016)

  • “The DAO,” a decentralized investment fund built on Ethereum, launched in 2016. It raised over 11 million ETH (≈ $150 million at the time, 14% of all ETH).
  • Vulnerability: contract sent ETH before updating balances.
    • Exploited on June 17, 2016: attacker’s fallback reentered withdraw() before balance changed.
    • Each loop drained more funds; state always one step behind reality.
  • Ethereum hard fork to revert chain, dissenters split off into Ethereum Classic (ETC)

Upgradeability and Proxy Patterns

  • Contracts on Ethereum are immutable once deployed.
    • No patching; bugs and all are permanent unless you plan ahead.
  • The proxy pattern separates storage (state) from logic (code) so new logic can be deployed without migrating data.
  • delegatecall executes the logic contract’s code in the proxy’s storage context.
    • The proxy address never changes; users and other contracts always call the same address.
  • Governance controls (multi-sig, timelocks) prevent malicious upgrades.

Without proxy (immutable)

noproxy user User v1 Contract v1 (code + state) user->v1 v2 Contract v2 (new deploy) user->v2 migrate?

With proxy (upgradeable)

proxy user User proxy Proxy (state lives here) user->proxy v1 Logic v1 proxy->v1 old v2 Logic v2 proxy->v2 delegatecall

Randomness, Oracles, and External Data

  • The EVM is deterministic: every node must compute the same result. That means contracts cannot reach outside the chain for data, generate true randomness, or call external APIs.
  • Naive on-chain “randomness” (block hash, timestamp) is manipulable by block proposers.
    • Safer approaches: commit-reveal schemes or Verifiable Random Functions (VRF) with cryptographic proofs.
  • External facts (prices, weather, sports scores) require oracles that post data on-chain.
    • Oracles introduce a trust assumption: who posts the data, how it is aggregated, and how manipulation is detected.
  • Defensive patterns for oracle failure:
    • Staleness checks (reject data older than N blocks)
    • Bounds/rate-of-change limits (reject implausible jumps)
    • Circuit breakers (pause critical operations if feeds go offline)
    • Multi-source aggregation (Chainlink, UMA, Pyth)

Micro Lab

Use the following smart contract lab to explore EVM:

/assets/labs/ether-smart-contract-micro/

Key Takeaways

  • A smart contract is code + persistent state that executes deterministically on every node.

  • Ethereum’s EVM provides the execution environment: accounts, gas metering, and a shared state trie.

  • External calls transfer control to untrusted code; the Checks-Effects-Interactions pattern and reentrancy guards are the primary defenses.

  • Contracts cannot reach outside the chain; oracles and VRFs bridge that gap, each with their own trust assumptions.

  • Immutability is the default; proxy patterns trade it for upgradeability at the cost of added governance complexity.

  • Next lesson: we apply these concepts to digital assets, tokenization, and NFTs (ERC-20, ERC-721, ERC-1155).

References

[1]
V. Buterin, “Ethereum: A Next-Generation Smart Contract and Decentralized Application Platform.” Ethereum.org, 2014. Available: https://ethereum.org/content/whitepaper/whitepaper-pdf/Ethereum_Whitepaper_-_Buterin_2014.pdf
[2]
Ethereum.org, “Smart ContractsDeveloper Docs.” 2025. Available: https://ethereum.org/en/developers/docs/smart-contracts/
[3]
S. Nakamoto, “Bitcoin: A Peer-to-Peer Electronic Cash System.” Satoshi Nakamoto Institute, Oct. 31, 2008. Accessed: Sep. 12, 2025. [Online]. Available: https://cdn.nakamotoinstitute.org/docs/bitcoin.pdf
[4]
G. Wood, “Ethereum: A Secure Decentralised Generalised Transaction Ledger, EIP-150 Revision.” 2016. Available: https://ethereum.github.io/yellowpaper/paper.pdf
[5]
Ethereum.org, “Accounts — Developer Docs.” 2025. Available: https://ethereum.org/en/developers/docs/accounts/
[6]
Solidity Team, “Solidity by Example.” 2025. Available: https://docs.soliditylang.org/en/latest/solidity-by-example.html
[7]
D. A. Siegel, “Understanding the DAO Attack.” 2016. Available: https://papers.ssrn.com/sol3/papers.cfm?abstract_id=3014782
[8]
Ethereum Foundation, “Critical update re: DAO vulnerability.” Accessed: Mar. 23, 2026. [Online]. Available: https://blog.ethereum.org/2016/06/17/critical-update-re-dao-vulnerability
[9]
S. Ellis, A. Juels, and S. Nazarov, ChainLink: A Decentralized Oracle Network.” Apr. 09, 2017. Accessed: Oct. 21, 2025. [Online]. Available: https://research.chain.link/whitepaper-v1.pdf
[10]
S. Eskandari, M. Salehi, W. C. Gu, and J. Clark, SoK: Oracles from the Ground Truth to Market Manipulation,” in Proceedings of the 3rd ACM Conference on Advances in Financial Technologies, Sep. 2021, pp. 127–141. doi: 10.1145/3479722.3480994.