Skip to main content
Change page

Anatomy of smart contracts

Page last update: April 15, 2026

A smart contract is a program that runs at an address on Ethereum. They're made up of data and functions that can execute upon receiving a transaction. Here's an overview of what makes up a smart contract.

Prerequisites

Make sure you've read about smart contracts first. This document assumes you're already familiar with programming languages such as JavaScript or Python.

Data

Any contract data must be assigned to a location: either to storage or memory. It's costly to modify storage in a smart contract so you need to consider where your data should live.

Storage

Persistent data is referred to as storage and is represented by state variables. These values get stored permanently on the blockchain. You need to declare the type so that the contract can keep track of how much storage on the blockchain it needs when it compiles.

// Solidity example
contract SimpleStorage {
    uint storedData; // State variable
    // ...
}
# Vyper example
storedData: int128

If you've already programmed object-oriented languages, you'll likely be familiar with most types. However, address should be new to you if you're new to Ethereum development.

An address type can hold an Ethereum address which equates to 20 bytes or 160 bits. It returns in hexadecimal notation with a leading 0x.

Other types include:

  • boolean
  • integer
  • fixed point numbers
  • fixed-size byte arrays
  • dynamically sized byte arrays
  • rational and integer literals
  • string literals
  • hexadecimal literals
  • enums

For more explanation, take a look at the docs:

Memory

Values that are only stored for the lifetime of a contract function's execution are called memory variables. Since these are not stored permanently on the blockchain, they are much cheaper to use.

Learn more about how the EVM stores data (Storage, Memory, and the Stack) in the Solidity docs (opens in a new tab).

Environment variables

In addition to the variables you define on your contract, there are some special global variables. They are primarily used to provide information about the blockchain or current transaction.

Examples:

PropState variableDescription
block.timestampuint256Current block epoch timestamp
msg.senderaddressSender of the message (current call)

Functions

In the most simplistic terms, functions can get information or set information in response to incoming transactions.

There are two types of function calls:

  • internal – these don't create an EVM call
    • Internal functions and state variables can only be accessed internally (i.e., from within the current contract or contracts deriving from it)
  • external – these do create an EVM call
    • External functions are part of the contract interface, which means they can be called from other contracts and via transactions. An external function f cannot be called internally (i.e., f() does not work, but this.f() works).

They can also be public or private

  • public functions can be called internally from within the contract or externally via messages
  • private functions are only visible for the contract they are defined in and not in derived contracts

Both functions and state variables can be made public or private

Here's a function for updating a state variable on a contract:

// Solidity example
function update_name(string value) public {
    dapp_name = value;
}
  • The parameter value of type string is passed into the function: update_name
  • It's declared public, meaning anyone can access it
  • It's not declared view, so it can modify the contract state

View functions

These functions promise not to modify the state of the contract's data. Common examples are "getter" functions – you might use this to receive a user's balance for example.

// Solidity example
function balanceOf(address _owner) public view returns (uint256 _balance) {
    return ownerPizzaCount[_owner];
}
dappName: public(string)

@view
@public
def readName() -> string:
  return dappName

What is considered modifying state:

  1. Writing to state variables.
  2. Emitting events (opens in a new tab).
  3. Creating other contracts (opens in a new tab).
  4. Using selfdestruct.
  5. Sending ether via calls.
  6. Calling any function not marked view or pure.
  7. Using low-level calls.
  8. Using inline assembly that contains certain opcodes.

Constructor functions

constructor functions are only executed once when the contract is first deployed. Like constructor in many class-based programming languages, these functions often initialize state variables to their specified values.

# Vyper example

@external
def __init__(_beneficiary: address, _bidding_time: uint256):
    self.beneficiary = _beneficiary
    self.auctionStart = block.timestamp
    self.auctionEnd = self.auctionStart + _bidding_time

Built-in functions

In addition to the variables and functions you define on your contract, there are some special built-in functions. The most obvious example is:

  • address.send() – Solidity
  • send(address) – Vyper

These allow contracts to send ETH to other accounts.

Writing functions

Your function needs:

  • parameter variable and type (if it accepts parameters)
  • declaration of internal/external
  • declaration of pure/view/payable
  • returns type (if it returns a value)

A complete contract might look something like this. Here the constructor function provides an initial value for the dapp_name variable.

Events and logs

Events enable your smart contract to communicate with your frontend or other subscribing applications. Once a transaction is validated and added to a block, smart contracts can emit events and log information, which the frontend can then process and utilize.

Annotated examples

These are some examples written in Solidity. If you'd like to play with the code, you can interact with them in Remix (opens in a new tab).

Hello world

Token

Unique digital asset

Further reading

Check out Solidity and Vyper's documentation for a more complete overview of smart contracts:

Was this article helpful?