Blockchain

Chapter 7
Smart Contract and DApp Development

Bachelors Program in IT
Blockchain   •   Bachelor

Smart Contract and DApp Development

Introduction and Context

A smart contract is a program deployed to a blockchain whose state changes according to predefined code and transaction rules. A decentralized application (DApp) combines one or more smart contracts with a user interface, wallet integration, blockchain node access, and frequently decentralized storage. Unlike a conventional web application, a DApp does not rely entirely on a centrally controlled application server. Its trust model is distributed: contract execution is validated by a blockchain network, while users retain control of their signing keys.

This architecture creates new possibilities for payments, digital assets, governance, supply-chain records, identity, gaming, and financial services. It also changes software engineering practice. Developers must reason about irreversible deployment, transaction fees, adversarial inputs, public data, deterministic execution, and the separation between on-chain and off-chain components.

The examples in this chapter use Solidity and an Ethereum-compatible blockchain. The concepts apply, with suitable syntax changes, to equivalent smart-contract platforms.

Learning Objectives

After completing this chapter, a student should be able to:

  1. Explain smart-contract execution, accounts, transactions, gas, events, and contract state.
  2. Design a small contract using suitable data structures, access control, and error handling.
  3. Compile, test, deploy, and interact with a contract using a development framework, wallet, and RPC endpoint.
  4. Distinguish on-chain data from off-chain data and select an appropriate decentralized-storage approach.
  5. Build a basic DApp interface and analyse its security, usability, governance, and operational limitations.

1. The Smart-Contract Execution Model

An Ethereum-compatible blockchain contains accounts and contract storage. Externally owned accounts (EOAs) are controlled by private keys. Contract accounts are controlled by code. An EOA initiates a transaction; a contract cannot independently initiate a transaction, although it can call another contract during execution.

A transaction commonly specifies a destination address, input data, a value transfer, a gas limit, and a fee. The wallet signs the transaction locally and submits it through an RPC provider. Validators execute the transaction according to the current blockchain state. If execution succeeds, the resulting state transition becomes part of the ledger. If execution reverts, state changes are undone, although the transaction may still consume some gas.

A contract's persistent variables are stored in blockchain state. Functions marked view read state without requesting a state-changing transaction when called locally. Functions marked pure neither read nor modify contract state. A state-changing function requires a transaction and therefore a signature, network submission, and fee.

Gas and transaction cost

Gas measures computational and storage work. The fee paid by a transaction depends on gas used and the price per unit of gas:

Transaction fee = Gas used × Gas price

Gas used is measured in gas units; gas price is commonly expressed in gwei per gas unit; the resulting fee is denominated in the network's native currency. The equation assumes both quantities refer to the same transaction and that the gas price is expressed consistently. In a fee market with a base fee and a priority fee, a useful decomposition is:

Effective gas price = Base fee + Priority fee

The exact fee mechanism varies by network. Developers should not hard-code fee assumptions. A transaction's gas limit is a maximum allowance, not necessarily the amount consumed.

Determinism and transparency

Every validator must obtain the same result from the same transaction and prior state. Contracts therefore cannot safely depend on arbitrary local files, ordinary web requests, or nondeterministic system time. A contract may use blockchain-provided values, but these are not automatically secure sources of randomness. Most contract state and transaction history are publicly inspectable, so privacy must be designed rather than assumed.

2. Solidity Foundations

Solidity is a statically typed language designed for the Ethereum Virtual Machine (EVM). A contract generally contains state variables, functions, events, modifiers, errors, and constructors.

Important types include:

TypeTypical use
uint256Non-negative integers, balances, identifiers
addressAccount or contract address
boolTrue/false conditions
stringText, although costly for large on-chain values
bytes32Fixed-size hashes, identifiers, compact values
mappingKey-value lookup
structGrouped fields
enumRestricted states

Mappings provide efficient conceptual lookup but are not enumerable by themselves. Arrays support ordered collections but may become expensive when iterated over. Storage variables persist between transactions; memory variables exist temporarily during execution; calldata is read-only function input and is generally efficient for external calls.

A small contract

The following illustrative contract records a message and restricts updates to its owner.

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

contract MessageBoard {
    address public immutable owner;
    string private message;

    event MessageChanged(address indexed author, string newMessage);

    error NotOwner();

    constructor(string memory initialMessage) {
        owner = msg.sender;
        message = initialMessage;
    }

    function getMessage() external view returns (string memory) {
        return message;
    }

    function setMessage(string calldata newMessage) external {
        if (msg.sender != owner) revert NotOwner();
        message = newMessage;
        emit MessageChanged(msg.sender, newMessage);
    }
}

The constructor runs once during deployment. msg.sender identifies the immediate caller. immutable values are assigned once and cannot subsequently be changed. The event creates a searchable log for user interfaces and monitoring systems, but an event is not a substitute for state when later contract logic must read the value.

Checks, effects, and interactions

A widely used design pattern is:

  1. Checks: validate permissions and input conditions.
  2. Effects: update internal state.
  3. Interactions: call external contracts or send value.

This ordering reduces reentrancy risk. Reentrancy occurs when an external call transfers control to another contract, which calls back before the first operation has completed. The pattern is valuable but not sufficient by itself; developers should also use carefully designed withdrawal mechanisms, reentrancy guards when appropriate, and audited libraries.

3. Contract Design: State, Access, and Failure

A sound design begins with a state model. Identify entities, allowed transitions, invariants, and actors before writing code. An invariant is a condition that should remain true after every successful operation. For a simple escrow, an invariant might be that the recorded deposited amount equals the amount owed to the beneficiary.

Access control should be explicit. Common approaches include an owner, role-based permissions, or a multisignature administration account. An address stored in a contract is not automatically a human identity; it represents a key or another contract. Authentication proves control of a key, not legal authority or real-world identity.

Use custom errors for predictable failure conditions, because they can be more compact than long revert strings. Validate zero addresses, boundary values, duplicate actions, and state-transition order. Solidity 0.8 and later checks arithmetic overflow and underflow by default, but unchecked arithmetic may deliberately disable those checks and should be used only when the bounds are proven.

Example: a minimal escrow

The following example is intentionally simplified for teaching. It holds native cryptocurrency until the buyer confirms release or an authorised arbiter refunds the buyer.

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

contract SimpleEscrow {
    address public immutable buyer;
    address payable public immutable seller;
    address public immutable arbiter;
    uint256 public amount;
    bool public released;

    error Unauthorized();
    error InvalidState();
    error IncorrectValue();

    constructor(address payable seller_, address arbiter_) payable {
        if (seller_ == address(0) || arbiter_ == address(0)) revert InvalidState();
        buyer = msg.sender;
        seller = seller_;
        arbiter = arbiter_;
        amount = msg.value;
    }

    function release() external {
        if (msg.sender != buyer || released) revert Unauthorized();
        released = true;
        (bool ok, ) = seller.call{value: amount}("");
        if (!ok) revert InvalidState();
    }

    function refund() external {
        if (msg.sender != arbiter || released) revert Unauthorized();
        released = true;
        (bool ok, ) = payable(buyer).call{value: amount}("");
        if (!ok) revert InvalidState();
    }
}

A production escrow would need clearer dispute procedures, events, protection against incorrect deployment assumptions, handling of forced transfers, and independent security review. The example demonstrates role separation and a one-time state transition; it is not a complete commercial escrow service.

4. Development Frameworks and the Engineering Workflow

Frameworks such as Hardhat, Foundry, and Remix support compilation, local blockchain simulation, automated tests, deployment scripts, and debugging. A typical workflow is:

  1. Define requirements, actors, assets, trust assumptions, and invariants.
  2. Implement the smallest contract that satisfies the requirements.
  3. Compile with a pinned compiler version and inspect warnings.
  4. Test success paths, failure paths, access restrictions, boundary conditions, and event output.
  5. Run static analysis and, where appropriate, fuzz, invariant, and symbolic tests.
  6. Deploy to a local node or public test network.
  7. Verify the deployed bytecode and record addresses, compiler settings, and network identifiers.
  8. Integrate the front end through an RPC provider and wallet.
  9. Monitor events, errors, balances, and abnormal activity.
  10. Document limitations and establish an upgrade or migration policy before mainnet deployment.

Testing categories

Unit tests isolate individual functions. Integration tests examine interactions among contracts, wallets, and RPC services. End-to-end tests exercise the user interface and a realistic network. Fuzz tests generate many inputs to discover unexpected cases. Invariant tests repeatedly execute sequences of operations and check that specified properties remain true.

A test should not merely confirm that a transaction succeeds. It should verify that an unauthorised caller fails, invalid input reverts, balances change by the expected amount, events contain correct fields, and state cannot enter an illegal combination.

5. Wallets, RPC Interfaces, and DApp Architecture

A wallet manages keys and presents transaction information for user approval. A DApp should request only necessary permissions and should never ask users to disclose private keys or seed phrases. Wallet connection does not mean that a user has authorised every future transaction; each state-changing action should be understandable and explicitly approved.

RPC, or Remote Procedure Call, is the interface through which applications communicate with blockchain nodes. JSON-RPC methods commonly support reading chain identifiers, obtaining account data, estimating gas, submitting signed transactions, and querying logs. A provider may be hosted by a public service, operated by an organisation, or run locally. RPC providers can be unavailable, rate-limited, censored, or inconsistent in indexing, so important applications should consider redundancy and validate the expected chain ID.

A practical DApp has four layers:

  • Blockchain layer: contracts, state, events, and consensus.
  • Access layer: wallet and RPC provider.
  • Application layer: JavaScript or TypeScript logic that calls contracts and interprets results.
  • Presentation layer: web or mobile interface showing state and transaction progress.

The front end should distinguish a local read from a pending transaction. A submitted transaction is not necessarily final; the interface should show pending, confirmed, failed, and replaced states. It should also handle rejected signatures, wrong networks, insufficient funds, stale data, and chain reorganisations.

Contract interaction requires an ABI (Application Binary Interface). The ABI describes function names, parameter types, return types, events, and mutability. A read call can often be executed through an RPC provider without a wallet signature. A write operation requires a signer, transaction construction, user approval, submission, and confirmation.

6. Decentralized Storage and the On-Chain Boundary

Blockchains are replicated and comparatively expensive storage systems. Store on-chain only what must be independently verified, governed by consensus, or used by contract logic. Large documents, images, and media are generally better stored off-chain, with a content identifier or cryptographic digest recorded on-chain.

IPFS addresses content by a content identifier derived from the content and its associated metadata. If the content changes, its identifier changes. However, publishing a CID does not guarantee permanent availability: nodes must continue to provide the content. Pinning services and archival arrangements are therefore operational requirements. Other decentralized or content-addressed systems may offer different persistence and retrieval models.

A common pattern is:

  1. Encrypt or prepare a document off-chain.
  2. Store it in a content-addressed system.
  3. Record its identifier, owner, timestamp, or hash in a contract.
  4. Retrieve the content through a gateway or participating node.
  5. Verify that the retrieved content corresponds to the recorded identifier.

Encryption is essential when information is confidential. A blockchain record is not private merely because the user interface hides it. Metadata, transaction timing, wallet relationships, and access patterns may reveal sensitive information.

7. Applied Case: Certificate Verification DApp

Background

This illustrative case concerns a university that issues digital course certificates. Paper certificates are slow to verify, while centrally hosted digital records create dependence on a single database and may be altered without a transparent audit trail.

Problem and analysis

The university needs a verifiable record that employers can check without granting them access to an internal student database. The certificate document may contain personal information and should not be stored publicly in plain text. The system must also address correction, revocation, key compromise, and privacy regulations.

Approach and solution

The university creates a contract that records a certificate identifier, its issuing institution, an issuance timestamp, and a status such as valid or revoked. The certificate PDF is encrypted and stored in an appropriate off-chain repository. A digest or content identifier is recorded on-chain. An issuer role can revoke a certificate, but ordinary verifiers can only read the record.

The front end provides two workflows. An issuer connects a wallet, uploads the encrypted document through a controlled service, and submits the identifier. A verifier scans a QR code or enters the certificate identifier. The application reads the contract, retrieves the document, checks its digest, and displays whether the record is valid. It does not expose a student's unnecessary personal data.

Outcome

Verification becomes independent of a single employer-facing database, and the blockchain supplies a tamper-evident history of issuance and revocation. Nevertheless, the system does not prove that the underlying academic decision was correct. It proves only that an authorised key recorded a particular claim. Key management, role governance, storage availability, privacy compliance, and contract maintenance remain essential.

Learning points

The case illustrates the importance of minimising on-chain data, separating authentication from real-world identity, defining authority, and designing revocation before deployment. It also demonstrates that blockchain is one component of a socio-technical system, not a replacement for institutional governance.

8. Security, Governance, and Professional Practice

Common vulnerabilities include reentrancy, incorrect access control, unchecked external calls, denial of service through unbounded loops, oracle manipulation, signature replay, front-running, insecure randomness, precision errors, and upgrade-admin compromise. Developers should use established libraries, least privilege, explicit validation, pull rather than push payment patterns where suitable, and independent review for valuable systems.

Front-running occurs when an observer detects a pending transaction and submits a competing transaction with a higher fee or otherwise exploits ordering. Commit–reveal schemes can reduce exposure in selected applications, although they add complexity. Oracles introduce external data and therefore an additional trust and failure boundary. A contract cannot itself prove that an off-chain price, identity, or event is truthful.

Upgradeability can correct defects but adds governance and technical risk. Proxy contracts separate persistent storage from implementation logic; careless storage-layout changes can corrupt state. An upgrade policy should specify who can upgrade, under what conditions, with what delay, and how users can exit if governance becomes unacceptable.

Legal and ethical concerns include consumer protection, financial regulation, data-protection rights, sanctions compliance, accessibility, discrimination caused by automated rules, and the consequences of irreversible errors. Public blockchains may conflict with deletion or rectification requirements when personal data is recorded permanently. Professionals should obtain legal advice for regulated deployments and avoid treating technical immutability as a substitute for accountability.

9. Contemporary Directions and Best Practices

Current development increasingly involves account abstraction, improved wallet usability, layer-two networks, cross-chain messaging, zero-knowledge systems, formal verification, and modular data availability. These technologies can reduce costs or improve privacy, but they also introduce new assumptions, bridge risks, sequencer dependencies, and operational complexity.

AI tools can assist with code explanation, test generation, documentation, and vulnerability triage. They cannot establish that a contract is secure. Generated code may contain subtle access-control, economic, or logic errors and must be reviewed, tested, and audited. Sensitive keys, proprietary code, and personal data should not be submitted to unapproved AI services.

A professional release checklist should include:

  • pinned compiler and dependency versions;
  • documented assumptions, roles, invariants, and threat model;
  • unit, integration, fuzz, and failure-path tests;
  • static analysis and independent review proportional to risk;
  • verified source code and reproducible deployment records;
  • secure key storage and multisignature administration where appropriate;
  • clear user warnings, transaction previews, and recovery procedures;
  • monitoring for events, balances, failed transactions, and governance actions;
  • a documented incident-response, pause, migration, or sunset plan.

Chapter Summary

Smart contracts are deterministic programs whose state transitions are validated by a blockchain. DApps combine contracts with wallets, RPC providers, user interfaces, and often decentralized storage. Effective development requires clear state models, explicit access control, careful handling of external calls, meaningful events, and comprehensive testing. Gas connects execution work to transaction cost, while wallets and RPC interfaces connect users and applications to the network. On-chain storage should be minimised, and content-addressed storage must be paired with availability and privacy measures. Security, governance, legal compliance, usability, and key management are as important as Solidity syntax. A DApp is therefore an engineered socio-technical system rather than merely a web page connected to a contract.

Key Terms

Smart contract: A blockchain-resident program that executes according to encoded rules.

DApp: An application combining decentralized contracts with user-facing and infrastructure components.

EVM: The Ethereum Virtual Machine, which executes compatible contract bytecode.

Externally owned account: An account controlled by a private key.

Gas: A unit measuring computational and storage work in a transaction.

RPC: A protocol interface used to request data from or submit transactions to a node.

Wallet: Software or hardware that manages keys and signs transactions.

ABI: The interface specification used to encode contract calls and decode results.

State variable: Contract data that persists on the blockchain.

Event: An indexed log emitted during contract execution.

Modifier: Reusable function-entry logic, often used for access checks.

Reentrancy: Unexpected recursive entry into a contract during an external call.

Oracle: A mechanism that supplies external information to a blockchain.

Content identifier: A reference derived from content in a content-addressed storage system.

Invariant: A condition that should remain true after valid state transitions.

Fuzz testing: Testing with many automatically generated inputs.

Multisignature: A control arrangement requiring several authorised signatures.

Upgradeability: The ability to change contract implementation after deployment.

Layer-two network: A system that processes transactions away from a base blockchain while relying on it for specified guarantees.

Review Questions

Short Answer Questions

  1. Distinguish an externally owned account from a contract account.
  2. Why does a state-changing contract function normally require a transaction?
  3. What information does an ABI provide?
  4. Explain the checks–effects–interactions pattern.
  5. Why should large documents generally not be stored directly on-chain?

Long Answer Questions

  1. Explain the complete lifecycle of a DApp transaction from wallet approval to confirmation, including the roles of the front end, RPC provider, validators, and contract.
  2. Design a state model for a simple escrow contract. Identify its actors, state variables, valid transitions, and at least three invariants.
  3. Compare unit, integration, end-to-end, fuzz, and invariant testing in smart-contract development.
  4. Analyse the security and governance risks of an upgradeable contract and propose controls.
  5. Explain how decentralized storage, encryption, content identifiers, and on-chain hashes can be combined in a privacy-conscious certificate system.

Analytical / Application Questions

  1. A contract sends funds to an external address before marking a withdrawal as completed. Analyse the vulnerability and redesign the operation using an appropriate pattern.
  2. A verifier can read a certificate's blockchain record, but the referenced document is unavailable from all storage providers. Identify which guarantees still hold and propose an operational remedy.
  3. Design a deployment and monitoring plan for a DApp that manages valuable assets. Justify your choices concerning testing, key management, RPC redundancy, events, upgrades, and incident response.

Multiple Choice Questions with Answers

Question 1

Which operation normally requires a signed transaction and network fee?

A. Reading a public state variable through a local call

B. Executing a state-changing function

C. Decoding an ABI response

D. Displaying cached event data

Correct Answer: B. Executing a state-changing function

Question 2

If a transaction uses 50,000 gas at 20 gwei per gas, what is the fee in gwei?

A. 1,000 gwei

B. 2,500 gwei

C. 70,000 gwei

D. 1,000,000 gwei

Correct Answer: A. 1,000 gwei

Question 3

What is the primary purpose of an event in a Solidity contract?

A. To create a private database

B. To make contract code upgradeable

C. To emit searchable execution logs

D. To replace all persistent state

Correct Answer: C. To emit searchable execution logs

Question 4

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

A. External call, state update, validation

B. Validation, state update, external call

C. State update, validation, external call

D. Validation, external call, state update

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

Question 5

Why is a mapping not sufficient by itself for listing all stored records?

A. Mappings cannot store addresses

B. Mappings do not provide enumeration of their keys

C. Mappings are always temporary memory

D. Mappings cannot be read by contracts

Correct Answer: B. Mappings do not provide enumeration of their keys

Question 6

What does a wallet primarily provide in a DApp?

A. Consensus validation for the whole network

B. Private-key management and transaction signing

C. Permanent storage for all application files

D. Automatic correction of contract bugs

Correct Answer: B. Private-key management and transaction signing

Question 7

Which statement about IPFS-style content identifiers is correct?

A. The identifier normally remains unchanged when content changes

B. The identifier is derived from or linked to the addressed content

C. Publishing an identifier guarantees permanent availability

D. An identifier automatically encrypts confidential data

Correct Answer: B. The identifier is derived from or linked to the addressed content

Question 8

Which test is most directly intended to generate many varied inputs automatically?

A. Fuzz testing

B. Interface testing

C. Manual acceptance testing

D. Compilation

Correct Answer: A. Fuzz testing

Question 9

What is a major risk of recording personal information directly on a public blockchain?

A. The information becomes impossible for validators to process

B. The information may be permanently and publicly inspectable

C. The information is automatically encrypted for every user

D. The information cannot be referenced by a contract

Correct Answer: B. The information may be permanently and publicly inspectable

Question 10

Which statement best describes an oracle?

A. A tool that compiles Solidity into bytecode

B. A mechanism supplying external information to a blockchain

C. A wallet that stores several private keys

D. A contract function that never changes state

Correct Answer: B. A mechanism supplying external information to a blockchain

Blockchain
Chapter 7 of 15