Blockchain Fundamentals

Chapter 7
Smart Contracts and Decentralized Applications

Advance Diploma in Blockchain
Blockchain Fundamentals   •   Advance Diploma

Smart Contracts and Decentralized Applications

Introduction and Context

A blockchain becomes programmable when transactions can invoke persistent computational logic rather than merely transfer value. That logic is commonly implemented as a smart contract: a program deployed to a blockchain address whose execution is validated by the network and whose state is stored according to the platform’s rules. Smart contracts support token systems, lending markets, escrow, governance, games, insurance, identity credentials and many other applications.

The term “smart contract” can be misleading. A contract is not necessarily legally enforceable, intelligent or private. It is deterministic software that executes when a valid transaction or another contract call supplies the required conditions. Legal enforceability, business interpretation and external facts require additional mechanisms and governance.

A decentralized application (DApp) combines on-chain contracts with user interfaces, wallets, blockchain nodes, indexing services and, where necessary, external data providers. Its architecture must balance decentralization, usability, security, cost, performance and regulatory obligations.

Learning Objectives

By the end of this chapter, a learner should be able to:

  1. Explain how a smart contract is compiled, deployed and executed.
  2. Describe the role of a blockchain virtual machine, state, transactions, calls and gas.
  3. Distinguish storage, memory, calldata, logs and return data.
  4. Explain events, oracles and the architecture of a DApp.
  5. Develop and test a basic Solidity contract.
  6. Identify reentrancy, access-control, arithmetic, denial-of-service and oracle vulnerabilities.
  7. Evaluate upgradeability designs, audit requirements and governance risks.

1. The Smart-Contract Execution Model

On an Ethereum-like blockchain, a contract consists of bytecode stored at an address and persistent state associated with that address. Developers normally write Solidity or another high-level language. A compiler translates the source code into bytecode for the target virtual machine and generates an Application Binary Interface (ABI). The ABI describes callable functions, arguments, return values and events.

A transaction normally contains:

  • a sender address;
  • a recipient address, which may be a contract;
  • value transferred in the native asset;
  • input data encoding a function call and its arguments;
  • a gas limit; and
  • a fee specification.

A transaction is signed by the sender and propagated to the network. A validator includes it in a block. Each validating node independently executes the transaction against the previous state. If execution is valid, the resulting state transition becomes part of the canonical chain.

A useful abstract model is:

St+1 = F(St, Tt)

Here, St is the current blockchain state, Tt is a valid transaction, F is the deterministic state-transition function, and St+1 is the resulting state. This model assumes that every node has the same relevant inputs and deterministic execution rules. A contract must not depend directly on an uncertain local value, such as an operating-system clock or random number generator.

If execution fails, the transaction generally reverts the contract-state changes made during that call. However, transaction fees consumed before the failure are not normally returned, and a reverted transaction remains visible as a failed transaction. Some protocol-level effects, such as fee payment, are handled separately from application-state rollback.

Transactions, calls and internal messages

A user-signed transaction begins outside the virtual machine. A contract can then make an internal call to another contract. A read-only call made through a node’s query interface does not create a mined transaction and does not modify chain state. It is useful for displaying balances or estimating outcomes, but it does not prove that a future transaction will succeed because the state, fees or execution conditions may change.

A contract can receive native currency through a payable function or, subject to platform rules, a receive/fallback mechanism. Explicitly defining accepted payment paths is safer than relying on broad fallback behavior.

2. Virtual Machines and Determinism

The Ethereum Virtual Machine (EVM) is a stack-based, sandboxed execution environment. It provides instructions for arithmetic, comparisons, cryptographic hashing, reading and writing state, calling other contracts and emitting logs. The EVM has a word-oriented architecture, commonly using 256-bit stack words, although Solidity types may be narrower.

The virtual machine is not a general-purpose server. It cannot safely perform arbitrary network requests, open files or depend on local hardware. Every validator must be able to reproduce the same result. This requirement explains both the strengths and limitations of smart contracts.

Contract state and data locations

Solidity developers must understand where data is held:

LocationPersistenceTypical useCost implication
StoragePersists between transactionsBalances, ownership, configurationExpensive to modify
MemoryExists during one callTemporary arrays and calculationsUsually cheaper than storage
CalldataRead-only transaction inputExternal function argumentsEfficient for external inputs
StackTemporary VM computationLocal operationsLimited and transient
LogsIndexed event recordsOff-chain monitoring and interfacesNot contract-readable as state

Storage is organized as key-value slots at the virtual-machine level. Solidity maps and dynamic arrays use deterministic slot calculations. Developers should not assume that source-code declaration order alone explains every storage position, especially when using proxy upgrade patterns.

ABI encoding

A function call usually begins with a short selector derived from the function signature, followed by ABI-encoded arguments. The ABI also defines how addresses, integers, arrays, tuples and dynamic byte strings are represented. Interfaces and generated client libraries reduce encoding errors, but input validation remains necessary.

3. Gas, Fees and Resource Management

Gas is a unit measuring computational and state-access work. It prevents infinite execution and allocates scarce block resources. Each operation has a protocol-defined gas cost, and transactions specify a gas limit.

A simplified fee relationship is:

Fee = Gused × Pgas

where Fee is the transaction fee, Gused is gas actually consumed, and Pgas is the price per unit of gas. If a protocol separates a base fee and a priority fee, a commonly used expression is:

Peffective = Pbase + Ptip

and therefore:

Fee = Gused × Peffective

The actual fee rules depend on the network and transaction type. If gas is denominated in the native token’s smallest unit, the fee has that unit; conversion to a fiat currency requires an external exchange rate.

The gas limit is a maximum resource allowance, not necessarily the amount consumed. If execution uses less gas, the unused allowance is generally not charged. If it exceeds the limit, execution fails for out-of-gas, and state changes in that call are reverted. A transaction that performs an unbounded loop over a growing array can eventually become unusable because its gas requirement exceeds practical limits.

Gas creates important design incentives:

  • avoid unnecessary storage writes;
  • use bounded loops;
  • paginate large datasets;
  • prefer off-chain computation when trust assumptions permit;
  • avoid storing data that can be reconstructed from events or content hashes; and
  • measure gas with realistic tests rather than relying on intuition.

Gas optimization must not weaken security. A cheaper unchecked arithmetic operation is unacceptable if overflow is possible under the chosen compiler and code assumptions.

4. Events and Off-Chain Observability

An event is a structured log emitted during execution. Events commonly record transfers, deposits, withdrawals, administrative changes and important state transitions. An indexer, wallet or user interface can subscribe to these logs without repeatedly scanning every transaction’s internal execution.

Events are not a substitute for contract state. A contract cannot query its historical events as ordinary storage, and an event is not automatically proof that a promised off-chain action occurred. Events should be emitted after the relevant state transition has been validated, and their fields should be sufficient for reliable indexing.

For example, a withdrawal event might include the account, recipient, amount and a unique operation identifier. Sensitive information should not be assumed private merely because it is placed in an event; blockchain data is generally observable.

5. Oracles and the Blockchain Boundary

A blockchain cannot natively know a stock price, delivery status, temperature or election result. An oracle supplies external information to a contract. This creates an important trust boundary: the blockchain can verify that data was submitted according to a protocol, but it cannot independently establish that the real-world observation is true.

Oracle designs include:

  1. Single-source oracles, which are simple but create a single point of failure.
  2. Multi-source aggregation, where several providers contribute data and an aggregation rule reduces dependence on one source.
  3. Committee or decentralized oracle networks, which use multiple reporters, staking, signatures or reputation mechanisms.
  4. Optimistic systems, where a proposed value is accepted unless challenged during a dispute period.
  5. Authenticated data feeds, where a trusted provider signs observations.

An oracle design should specify freshness, timestamp tolerance, decimal precision, source selection, outlier handling, fallback behavior and circuit breakers. A price feed should not be accepted merely because it is signed; the contract must also validate the signer, data age, sequencing and plausible bounds.

Oracle manipulation is especially serious in lending and derivatives. An attacker may trade in a shallow market, influence a spot price, borrow against the inflated value and withdraw before the price normalizes. Time-weighted prices, deep liquidity, multiple markets, deviation limits and pause mechanisms can reduce—but not eliminate—this risk.

6. DApp Architecture

A DApp normally contains the following layers:

  1. User interface: web or mobile screens that explain actions and display state.
  2. Wallet and signing layer: manages user approval and cryptographic signatures. The application should never request or store a private key through an ordinary web form.
  3. Provider or node access: sends read requests and signed transactions to the network.
  4. Smart-contract layer: enforces ownership, accounting and settlement rules.
  5. Indexing and query layer: organizes events and historical data for efficient retrieval.
  6. External services: may provide metadata, notifications, storage or oracle data.
  7. Operations and governance: manages deployments, monitoring, upgrades, incident response and documentation.
User
  ↓
Interface ── read requests ──> Node / RPC provider ──> Blockchain state
  │                                  ↑
  └── signed transaction ────────────┘
                                     ↓
                           Smart contracts
                                     ↓
                         Events → Indexer → Interface
                                     ↑
                         Oracle / external data

The interface is not the security boundary. A malicious user can bypass it and call the contract directly. Therefore, authorization, limits and accounting must be enforced on-chain. Conversely, the interface remains important for informed consent: transaction previews should identify the contract, function, token amount, recipient and likely fee.

A robust DApp also handles chain reorganizations, delayed transactions, nonce conflicts, failed transactions, RPC outages and stale indexer data. Critical displays should distinguish confirmed state from optimistic, pending or indexed data.

7. Developing a Basic Contract

The following illustrative Solidity contract records deposits and permits each depositor to withdraw the balance. It is intentionally small so that its assumptions can be examined.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract SimpleVault {
    mapping(address => uint256) private balances;

    event Deposited(address indexed account, uint256 amount);
    event Withdrawn(address indexed account, uint256 amount);

    function deposit() external payable {
        require(msg.value > 0, "zero deposit");
        balances[msg.sender] += msg.value;
        emit Deposited(msg.sender, msg.value);
    }

    function withdraw(uint256 amount) external {
        require(amount > 0, "zero withdrawal");
        uint256 available = balances[msg.sender];
        require(available >= amount, "insufficient balance");

        balances[msg.sender] = available - amount;
        (bool sent, ) = payable(msg.sender).call{value: amount}("");
        require(sent, "transfer failed");

        emit Withdrawn(msg.sender, amount);
    }

    function balanceOf(address account) external view returns (uint256) {
        return balances[account];
    }
}

The contract applies the checks-effects-interactions pattern. It checks the request, updates internal accounting, and only then calls the recipient. Solidity version 0.8.x provides checked arithmetic by default, so subtraction reverts on underflow. This example is not automatically production-ready: it needs deployment controls, testing, monitoring, consideration of forced native-asset transfers, and review of recipient behavior and operational assumptions.

Testing strategy

A professional test suite should include:

  • a successful deposit and balance assertion;
  • rejection of zero deposits;
  • successful partial and full withdrawals;
  • rejection of excessive withdrawals;
  • rejection of zero withdrawals;
  • multiple independent depositors;
  • a recipient contract that attempts reentrancy;
  • failure of a recipient that rejects payment;
  • event contents and indexed fields;
  • conservation of accounted balances and contract balance under normal assumptions; and
  • fuzz or invariant tests with many sequences of deposits and withdrawals.

Tests should run against a local development chain and, where practical, a public test network. Static analyzers, compiler warnings and dependency checks should be part of continuous integration. A passing test suite demonstrates tested behavior; it does not prove absence of vulnerabilities.

8. Vulnerabilities and Secure Design

Reentrancy

Reentrancy occurs when a contract calls external code before completing its internal accounting, allowing the recipient to call back into the vulnerable function. The principal defenses are checks-effects-interactions, a carefully designed reentrancy guard, minimizing external calls and using pull-based withdrawals. A guard is not a replacement for correct accounting.

Access-control failure

Functions such as mint, pause, upgrade or withdraw-reserves require explicit authorization. Common errors include missing modifiers, using tx.origin for authentication, incorrect role initialization and failing to protect administrative keys. Roles should follow least privilege, and critical actions may require a multisignature wallet and a timelock.

Arithmetic and accounting errors

Overflow, underflow, unit confusion, rounding direction and fee miscalculation can create insolvency. Every monetary quantity needs a declared unit and precision. A protocol should specify whether rounding favors the user, pool or protocol and prove that liabilities cannot exceed assets under valid operations.

Denial of service

Unbounded loops, storage growth, reliance on one recipient accepting funds and adversarially large input arrays can make functions unusable. Designs should use bounded work, withdrawal queues, pagination and failure-tolerant processing.

Oracle and price manipulation

A contract that accepts an unvalidated price, stale update or thin-market spot price can be exploited. Use freshness checks, deviation limits, multiple sources and emergency responses appropriate to the value at risk.

Front-running and transaction ordering

Public pending transactions may reveal a user’s intended trade or claim. Mitigations include commit–reveal schemes, slippage limits, batch auctions, private transaction mechanisms and designs that do not assume a fair ordering. No mitigation is universally effective, and users must understand residual ordering risk.

Denial through unexpected token behavior

Not all token contracts behave identically. Some charge transfer fees, call hooks, return no Boolean value or impose transfer restrictions. Integrations should use well-reviewed libraries, measure balance changes where appropriate and define supported token standards explicitly.

9. Upgradeability and Governance

Immutable contracts can be difficult to correct after deployment; upgradeable contracts can introduce administrative compromise and change user expectations. Common proxy patterns separate:

  • a proxy holding user-facing address and usually storage;
  • an implementation containing logic; and
  • an administrator or upgrade authority.

A proxy delegates execution to the implementation while preserving the proxy’s storage context. This creates hazards: storage-layout collisions, uninitialized implementations, selector clashes, insecure upgrade authorization and accidental changes to critical invariants.

Upgrade governance should document who can upgrade, what delay applies, how users are notified, whether upgrades can be cancelled, and what emergency powers exist. A multisignature wallet reduces dependence on one key, but signers themselves require secure procedures. A timelock gives users and monitors time to react, although emergency response may require a narrowly scoped pause authority.

Before an upgrade, teams should compare storage layouts, run regression and invariant tests, verify the exact implementation bytecode, publish source code, and obtain independent review. Proxy standards and tooling reduce risk but do not remove the need for architectural analysis.

10. Audit and Assurance Requirements

An audit is a structured review, not a certification that a system is safe. Audit scope should include source code, compiler version, deployment scripts, configuration, oracle assumptions, economic model, upgrade system and dependencies.

A suitable assurance process includes:

  1. written specifications and threat models;
  2. documented trust assumptions and privileged roles;
  3. unit, integration, fuzz and invariant tests;
  4. static analysis and dependency scanning;
  5. manual review of authorization, accounting and external calls;
  6. adversarial testing and, when justified, formal verification of selected properties;
  7. independent audit and remediation verification;
  8. deployment and key-management review; and
  9. post-deployment monitoring, bug bounty and incident-response procedures.

Useful invariants for a vault include that a user cannot withdraw more than their recorded balance and that total recorded liabilities do not exceed assets under the defined model. Formal verification can establish properties under explicit assumptions, but it cannot verify an incorrect specification or guarantee truthful oracle data.

11. Illustrative Case: A Tokenized Escrow DApp

Background

Consider an illustrative freelance marketplace in which a client deposits a stable-value token into escrow and releases it when work is accepted. A dispute process can freeze release temporarily.

Problem

The marketplace needs transparent settlement, protection against unauthorized release and a usable interface. It must also handle a client who disappears, a freelancer who disputes rejection and a token that may not behave exactly like a simple transfer token.

Analysis

The core contract should store the client, freelancer, amount, status and deadlines. It should restrict creation to valid participants, prevent double release, emit state-transition events and avoid sending funds before updating status. A dispute arbiter introduces a governance assumption: the arbiter is not a decentralized fact but a privileged decision-maker.

Approach

The DApp uses a contract for escrow state, a wallet for signatures, an indexer for case history and a user interface for evidence submission. The contract applies role-based authorization, a state machine such as Funded → Submitted → Released/Disputed, deadline checks and a pause mechanism. Token transfers use a vetted library and verify the expected accounting behavior. The arbiter role is controlled by a multisignature wallet, and changes are announced through a timelock where operationally feasible.

Outcome

The architecture provides auditable settlement and reduces dependence on the marketplace database. It does not eliminate disputes, oracle-like judgments about work quality, legal obligations or the risk that the arbiter is compromised. Those limitations are documented in user terms and governance procedures.

Learning points

The contract should enforce only rules that can be expressed and verified on-chain. Human judgments need explicit governance. Events improve transparency but do not replace state. Security includes operational keys, interface warnings, token assumptions and dispute procedures—not only Solidity code.

12. Ethical, Legal and Professional Considerations

Smart-contract developers handle financial value and may affect people who cannot inspect code. Ethical practice requires clear disclosure of risks, accessible transaction explanations, responsible vulnerability reporting and avoidance of deceptive claims about decentralization or guaranteed returns.

Legal treatment varies by jurisdiction. A token or DApp may involve consumer protection, financial services, securities, payments, data protection, sanctions, taxation or intellectual-property obligations. Immutability can conflict with data-erasure requirements, so personal data should generally not be written directly to a public chain. A hash is not automatically anonymous if the underlying information can be inferred or linked.

Professional teams should maintain source-code provenance, reproducible builds, access logs, key-rotation procedures and an incident plan. AI-assisted coding can accelerate development, but generated code must be reviewed, tested and checked against the exact compiler and dependency versions. AI output is not an audit and may reproduce insecure patterns or misunderstand economic assumptions.

13. Contemporary Developments

Layer-2 networks and rollups execute transactions with different performance and fee characteristics while using another chain for settlement or data availability. Developers must understand message delays, withdrawal periods, bridge assumptions and chain-specific finality.

Account abstraction can support programmable authorization, sponsored fees and recovery mechanisms, but it adds new validation and paymaster risks. Zero-knowledge systems can provide scalable execution or selective privacy, yet circuits, proving systems and trusted setup assumptions require specialist assurance. Modular blockchain designs separate execution, consensus, settlement and data availability, increasing architectural choice while also increasing integration complexity.

Across these developments, the central engineering requirement remains unchanged: state transitions must be correctly specified, economically constrained, observable and governed.

Best-Practice Checklist

Before deploying a DApp, a team should be able to answer “yes” to the following questions:

  • Are all privileged functions identified and protected?
  • Are external calls minimized and made after state updates?
  • Are loops bounded and gas behavior tested?
  • Are units, decimals and rounding rules documented?
  • Are oracle freshness, source and failure behavior explicit?
  • Are events complete enough for indexing but not treated as state?
  • Are proxy storage layouts and upgrade authorities reviewed?
  • Are tests, fuzzing, static analysis and independent review complete?
  • Are keys protected by appropriate operational controls?
  • Can users understand what they are signing and what happens on failure?
  • Is there monitoring, a pause policy and an incident-response plan?

Chapter Summary

Smart contracts are deterministic programs that implement blockchain state transitions. Their execution depends on transactions, virtual-machine rules, persistent storage and gas limits. Events provide structured observability, while oracles connect contracts to external facts at a significant trust cost. A DApp combines contracts with wallets, interfaces, node access, indexers and governance.

Secure development requires explicit authorization, checks-effects-interactions, bounded computation, careful arithmetic, validated oracle data and robust testing. Upgradeability improves maintainability but introduces proxy, storage-layout and administrative risks. Audits are most effective when supported by specifications, threat models, invariants, independent review and post-deployment monitoring. Finally, decentralization does not remove legal, ethical or operational responsibilities.

Key Terms

Smart contract: Deterministic program deployed to and executed by a blockchain.

DApp: Application combining decentralized contracts with user and infrastructure components.

Virtual machine: Sandboxed execution environment that processes contract bytecode.

State transition: Deterministic transformation from one blockchain state to another.

ABI: Interface specification for encoding contract calls, returns and events.

Gas: Unit measuring computational and state-access resources.

Gas limit: Maximum gas a transaction is permitted to consume.

Storage: Persistent contract data retained between calls.

Calldata: Read-only input supplied with an external call.

Event: Structured log emitted during contract execution.

Oracle: Mechanism supplying external information to a blockchain.

Reentrancy: Vulnerability caused by an external call re-entering a function before completion.

Checks-effects-interactions: Pattern that validates, updates state and then performs external calls.

Access control: Rules determining which accounts may perform actions.

Proxy: Contract that delegates execution to another implementation.

Upgradeability: Ability to change contract logic after deployment.

Invariant: Property that should remain true across valid state transitions.

Indexer: Service that organizes blockchain data for efficient application queries.

Timelock: Delay imposed before an administrative action takes effect.

Multisignature wallet: Wallet requiring approval from multiple authorized signers.

Review Questions

Short Answer Questions

  1. What is the difference between a blockchain transaction and a read-only call?
  2. Why must smart-contract execution be deterministic?
  3. What information does an ABI provide?
  4. Why are events not a replacement for contract storage?
  5. What problem does gas solve?

Long Answer Questions

  1. Explain the lifecycle of a smart contract from source code to execution in a block.
  2. Compare storage, memory, calldata and logs, including their persistence and typical uses.
  3. Explain oracle trust risks and describe controls for a price-feed application.
  4. Discuss the security and governance trade-offs of upgradeable proxy contracts.
  5. Design a testing and audit plan for a contract that holds user funds.

Analytical / Application Questions

  1. A vault updates a user’s balance only after sending funds to the user. Identify the likely vulnerability, explain the attack sequence and recommend corrections.
  2. Design a DApp architecture for tokenized escrow. Identify the on-chain state, events, external services, privileged roles and failure-handling procedures.
  3. A lending protocol uses one exchange’s current price and accepts updates without a freshness check. Analyze possible attacks and propose a safer oracle and emergency-control design.

Multiple Choice Questions with Answers

Question 1

Which component primarily enforces the rules that cannot be bypassed by changing a DApp’s user interface?

A. The smart contract

B. The CSS stylesheet

C. The browser cache

D. The analytics dashboard

Correct Answer: A. The smart contract

Question 2

What is the main purpose of gas in an Ethereum-like virtual machine?

A. To encrypt all contract data

B. To prevent unlimited resource consumption and allocate execution resources

C. To guarantee that an oracle reports truthful data

D. To replace digital signatures

Correct Answer: B. To prevent unlimited resource consumption and allocate execution resources

Question 3

Which sequence best represents the checks-effects-interactions pattern?

A. External call, state update, validation

B. Validation, state update, external call

C. State update, external call, validation

D. Validation, external call, state update

Correct Answer: B. Validation, state update, external call

Question 4

Why can a blockchain not directly determine a real-world temperature?

A. Blockchains cannot store numbers

B. Validators cannot access external facts deterministically without a data-supply mechanism

C. Temperature is always encrypted

D. Smart contracts cannot emit events

Correct Answer: B. Validators cannot access external facts deterministically without a data-supply mechanism

Question 5

Which statement about events is correct?

A. Events are ordinary storage that contracts can query directly

B. Events provide structured logs useful to off-chain systems

C. Events automatically make private data confidential

D. Events eliminate the need for state variables

Correct Answer: B. Events provide structured logs useful to off-chain systems

Question 6

A transaction uses 50,000 gas units at a gas price of 20 gwei per unit. What is the fee in gwei?

A. 2,500 gwei

B. 50,020 gwei

C. 1,000,000 gwei

D. 1,000 gwei

Correct Answer: C. 1,000,000 gwei

Question 7

Which is a major risk specific to upgradeable proxy contracts?

A. The inability to emit any events

B. Storage-layout collision between implementations

C. The guaranteed elimination of administrative control

D. The prevention of all external calls

Correct Answer: B. Storage-layout collision between implementations

Question 8

Which test is most directly suited to checking that a vault never permits withdrawal above a user’s recorded balance across many transaction sequences?

A. A screenshot test

B. An invariant or fuzz test

C. A typography test

D. A DNS lookup

Correct Answer: B. An invariant or fuzz test

Question 9

What is the principal danger of using a single shallow-market spot price as a lending oracle?

A. It may be manipulated to inflate collateral value

B. It makes contract addresses shorter

C. It prevents ABI encoding

D. It guarantees stale data cannot occur

Correct Answer: A. It may be manipulated to inflate collateral value

Question 10

Which control most directly reduces the risk that one administrator can unilaterally upgrade a contract?

A. Removing all events

B. Using a multisignature authority, potentially with a timelock

C. Increasing the interface font size

D. Storing private keys in the browser source code

Correct Answer: B. Using a multisignature authority, potentially with a timelock

Blockchain Fundamentals
Chapter 7 of 14