Smart Contract Overview

·

Smart contracts are the foundation of decentralized applications on blockchain platforms like Ethereum. Written in programming languages such as Solidity, they enable self-executing agreements with transparent, tamper-proof logic. This guide walks you through the core concepts of smart contracts, from basic storage patterns to advanced Ethereum Virtual Machine (EVM) mechanics — all while maintaining clarity and real-world relevance.

Whether you're a developer exploring blockchain programming or a tech enthusiast seeking deeper understanding, this article delivers actionable insights into how smart contracts work under the hood.

👉 Discover how smart contracts power next-gen financial systems — start learning today.

A Simple Smart Contract

Let’s begin with a foundational example. Don’t worry if every detail isn’t immediately clear — we’ll unpack each component step by step.

Storage

pragma solidity ^0.4.0;
contract SimpleStorage {
    uint storedData;

    function set(uint x) public {
        storedData = x;
    }

    function get() public view returns (uint) {
        return storedData;
    }
}

The first line, pragma solidity ^0.4.0;, specifies that this code is compatible with Solidity version 0.4.0 and any newer version up to (but not including) 0.5.0. This ensures consistent behavior across compiler updates. The keyword pragma instructs the compiler on how to interpret the source code — similar to directives like #pragma once in C/C++.

In Solidity, a contract is a collection of code (functions) and data (state) residing at a specific address on the Ethereum blockchain. The line uint storedData; declares a state variable of type uint — an unsigned 256-bit integer. Think of it as a single slot in a decentralized database, accessible and modifiable only through functions defined within the contract.

Here, set() allows anyone to update the value, while get() retrieves it. Since Ethereum manages access via the owning contract, these functions control all interactions with storedData.

Notably, Solidity does not require prefixes like this. to access state variables — direct reference suffices.

While simple, this contract illustrates a key feature: immutability of history. Even if someone overwrites the stored number, prior values remain permanently recorded on the blockchain. Future sections will show how to restrict access so only authorized users can modify data.

Note: All identifiers — contract names, functions, variables — must use ASCII characters. However, UTF-8 encoded strings can be stored in string-type variables.

Warning: Be cautious with Unicode characters that appear identical but have different code points. These can result in distinct byte representations, leading to unexpected behavior or security risks.

Example: A Custom Cryptocurrency

Now let’s build something more practical — a basic cryptocurrency token.

pragma solidity ^0.4.0;

contract Coin {
    address public minter;
    mapping (address => uint) public balances;

    event Sent(address from, address to, uint amount);

    function Coin() public {
        minter = msg.sender;
    }

    function mint(address receiver, uint amount) public {
        if (msg.sender != minter) return;
        balances[receiver] += amount;
    }

    function send(address receiver, uint amount) public {
        if (balances[msg.sender] < amount) return;
        balances[msg.sender] -= amount;
        balances[receiver] += amount;
        Sent(msg.sender, receiver, amount);
    }
}

This contract introduces several essential concepts in smart contract development.

State Variables and Access Control

address public minter; declares a public state variable of type address — a 160-bit identifier used for Ethereum accounts or contracts. The public keyword automatically generates a getter function, allowing external access to the value.

Without public, the variable would be private to the contract. The compiler essentially creates:

function minter() public view returns (address) { return minter; }

Manually defining such a function would cause a naming conflict, but the compiler handles this transparently.

Mappings and Balances

mapping (address => uint) public balances; creates a public mapping from addresses to unsigned integers — effectively a balance sheet for our coin.

Mappings behave like hash tables: every possible key exists by default, initialized to zero. However, you cannot iterate over keys or values directly. You must track them separately if enumeration is needed.

The auto-generated getter function looks like:

function balances(address _account) public view returns (uint) {
    return balances[_account];
}

This allows external apps to query any user’s balance easily.

Events for Efficient Tracking

event Sent(address from, address to, uint amount); defines an event emitted during transfers. External applications — such as wallets or explorers — can listen for these events efficiently without scanning the entire blockchain.

For example:

Coin.Sent().watch({}, '', function(error, result) {
    if (!error) {
        console.log("Coin transfer: " + result.args.amount +
            " coins sent from " + result.args.from +
            " to " + result.args.to + ".");
    }
});

Events leverage bloom filters, enabling lightweight clients to detect relevant activity without downloading full block data.

Constructor and Message Origin

The Coin() function is a constructor — executed once when the contract is deployed. It sets msg.sender (the deployer) as the sole minter.

msg.sender is a global variable representing the caller’s address. Combined with tx and block, these “magic” variables provide context about the current transaction and blockchain state.

Core Functions: Minting and Sending

Importantly, these balances exist only within the contract’s storage. Standard blockchain explorers won’t display them unless they’re specifically configured to track this token — highlighting the importance of events and custom tooling.

👉 See how real-world tokens are built and traded on-chain — explore now.

Blockchain Basics

Understanding smart contracts requires grasping fundamental blockchain principles.

Transactions

A blockchain is a globally shared, transactional database. Everyone can read its contents, but changes require transactions — cryptographically signed operations that either succeed completely or fail entirely (atomicity).

Imagine a table tracking digital currency balances. If Alice sends 5 coins to Bob:

Moreover, every transaction is signed by the sender, ensuring only authorized changes occur — no usernames or passwords needed, just cryptographic keys.

Blocks

One challenge blockchains solve is the double-spend problem: what happens if two transactions try to spend the same funds?

The solution lies in ordering. Transactions are grouped into blocks, processed in sequence by network nodes. Conflicting transactions are resolved by inclusion order — the second one is rejected.

Blocks form a chronological chain — hence “blockchain.” On Ethereum, new blocks are added approximately every 17 seconds.

Occasionally, blocks may be reverted due to consensus rules (e.g., during network forks), but this becomes increasingly unlikely as more blocks are added on top. Thus, waiting longer increases transaction finality.

Ethereum Virtual Machine (EVM)

The EVM executes all smart contracts on Ethereum. It’s fully isolated — sandboxed code cannot access filesystems, networks, or external processes. Even inter-contract communication is tightly controlled.

Accounts

Ethereum has two account types sharing the same address space:

An external account’s address derives from its public key. A contract’s address is determined at creation, based on the creator’s address and transaction nonce.

Both types have:

The EVM treats both equally during execution.

Transactions

A transaction is a signed message from one account to another. It may include:

If the recipient has code, the payload triggers function execution. If sent to address 0, it creates a new contract.

Contract creation works uniquely:

  1. The transaction payload contains EVM bytecode.
  2. This code runs once upon deployment.
  3. Its output becomes the permanent contract code.

Thus, you don’t send final code directly — you send code that returns the final code.

Gas

Every transaction consumes gas — a unit measuring computational effort. Gas prevents abuse and compensates validators.

If gas runs out mid-execution, all state changes revert — preserving consistency.

Storage, Memory, and Stack

The EVM uses three data areas:

Instruction Set

The EVM features a minimal instruction set focused on arithmetic, bitwise operations, comparisons, and jumps. Contracts can also access block metadata like timestamp and number.

Message Calls

Contracts interact via message calls — similar to transactions but internal. They support:

Each call inherits context from its parent but can limit gas forwarding. Errors bubble up unless caught.

Calls are limited to 1024 levels deep — favor loops over recursion for complex logic.

Delegatecall / Callcode and Libraries

Delegatecall lets a contract execute code from another address in its own context:

This enables libraries — reusable code applied to calling contracts’ state (e.g., sorting algorithms or data structures).

Logs

Logs store indexed data linked to blocks. While contracts cannot read logs post-creation, external systems can efficiently query them via bloom filters — crucial for light clients and event-driven apps.

Create

Contracts can spawn others using the create opcode. Unlike zero-address transactions:

Self-destruct

Only selfdestruct(target) removes contract code from the chain:

Warning: Even without explicit selfdestruct, malicious use of delegatecall could trigger it indirectly.

Note: Archive nodes may retain deleted contract data indefinitely. Also, external accounts cannot be removed from state.


Frequently Asked Questions

Q: What is a smart contract?
A: A smart contract is self-executing code on a blockchain that enforces rules and automates actions without intermediaries.

Q: Can anyone modify a deployed smart contract?
A: No — once deployed, code is immutable unless designed with upgradeability features like proxy patterns.

Q: How do I interact with a smart contract?
A: Use wallet software or web3 libraries (e.g., Web3.js or Ethers.js) to send transactions calling its functions.

Q: Why does gas exist?
A: Gas prevents infinite loops and spam by charging for computation, ensuring network stability and fairness.

Q: Are mappings iterable in Solidity?
A: No — mappings cannot be looped through natively. You must maintain separate lists if iteration is required.

Q: Can a contract destroy itself?
A: Yes — using selfdestruct(address), which removes its code and sends remaining funds to a specified address.

👉 Start building your first smart contract securely — learn on OKX today.