Section VII: Ethereum Framework
April 9, 2026
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.
| 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.). |
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.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 costs gas; large code and heavy initialization are expensive.
msg.sender and msg.value are per-frame; gas forwarding is explicit.
msg.sender, not the original user. This is critical for access control.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.
pragma solidity ^0.8.26;).contract Name { ... }.uint, int (default 256-bit); smaller sizes available.bool - true/falseaddress (20 bytes) identifies accounts; address payable can receive ETH.enum for finite sets of named constants.1 ether (wei), time units (seconds, minutes), etc.bytes and string are dynamic.
bytes1…bytes32struct: custom composite types for grouping fields.mapping(Key => Value): hash table; not iterable.storage, memory, calldata control where data lives.public, external, internal, private.view (reads), pure (no reads/writes), default (writes allowed).payable functions can receive ETH; others reject attached ETH.external functionspragma 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;
}mint restricts access to minter via require. 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);
}
}indexed) create searchable topics; non-indexed fields go into the data blob.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.
keccak256("name(types)")[:4]receive handles plain ETH transfers; fallback handles unknown selectors or bad calldata.Occurs when an external call reenters before your function finishes.
Classics: withdraw pattern before balance update; ERC-777 hooks misused.
withdraw() before zeroing balance.The fix is disciplined ordering:
require)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:
ReentrancyGuard (mutex):
withdraw() before balance changed.delegatecall executes the logic contract’s code in the proxy’s storage context.
Without proxy (immutable)
With proxy (upgradeable)
Use the following smart contract lab to explore EVM:
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).

Smart Contracts: From Concept to Ethereum — Army Cyber Institute — April 9, 2026