After a short two‑day pause in the 60‑Day Web3 journey, it’s time to hit play again and kick off the next phase with our first steps into Solidity. For the last 25 days, we’ve treated smart contracts like mysterious vending machines: you put in a transaction, and something happens on-chain. Today we finally lift the lid and start reading the language those vending machines are written in: Solidity.
This is Day 26, the start of Phase 3: Development in the 60‑Day Web3 journey. The goal for today is not to become a Solidity expert. The goal is to understand the basic building blocks — variables and functions — and then ship a tiny but real contract to Sepolia: a Web3 Journey Logger.
1. What is Solidity, really?
Solidity is a high-level, contract‑oriented programming language used to write smart contracts that run on Ethereum and other EVM-compatible chains like Arbitrum, Optimism, and Polygon. Once compiled, your Solidity code becomes bytecode that lives at a specific address on-chain and is executed by every node that processes your transaction.
If you’ve seen JavaScript, C++, or TypeScript, Solidity will look familiar: curly braces, functions, variables, if conditions. But there are two crucial differences: you’re programming money and state, not just UI, and every operation costs gas while the code you deploy is mostly permanent.
That’s why starting with the basics — how data is stored and how users interact with that data — is so important.
2. Variables: the contract’s on-chain memory
Think of a smart contract as a tiny on-chain application with its own private database. Variables are the named boxes where this contract stores data.
In Solidity, you must always declare both the type and the name of each variable because the compiler wants to know exactly what will be stored where.
2.1 Types of variables
At a high level, there are three levels of variables:
-
State variables
- Stored permanently in the contract’s storage on the blockchain.
- Persist between function calls and transactions.
- Example: the contract’s owner address, a token balance mapping, or a counter.
-
Local variables
- Declared inside a function.
- Exist only while that function is running, then disappear.
- Example:
uint256 sum = a + b;inside a function.
-
Global variables
- Built-in helpers provided by the EVM.
- Examples:
msg.senderfor the caller,block.timestampfor the current block’s timestamp, andblock.numberfor the current block number.
In this first step, the focus is on state variables, since they form your contract’s on-chain memory.
2.2 Common data types you’ll actually use
Here are the basic types you’ll see in almost every contract:
-
bool- Stores
trueorfalse. - Example:
bool public completed;
- Stores
-
uint256- Unsigned integer (0 and positive numbers).
- Commonly used for amounts, IDs, counters, timestamps.
- Example:
uint256 public dayNumber;
-
address- Stores an Ethereum address, either a user or another contract.
- Example:
address public owner;
-
string- Stores text data.
- More expensive than numbers in terms of gas, so used only when needed.
- Example:
string public note;
There are many more types such as int, bytes, mapping, struct, and arrays, but these four are enough to get started.
3. Functions: how users talk to your contract
If variables are the contract’s memory, functions are the doors and buttons users can press to read or update that memory.
Every time someone calls a function via a transaction or a UI like a dApp, the EVM executes the function’s logic step by step.
3.1 Function anatomy
A typical Solidity function looks like this:
function setDay(uint256 _day) public {
dayNumber = _day;
}
In this example, setDay is the function name, uint256 _day is the input parameter, public is the visibility specifier, and the body between braces updates the dayNumber state variable.
3.2 Visibility (who can call this?)
Visibility controls who can call a function and from where:
-
public- Anyone, including other contracts, can call this function.
- A public function is part of the contract’s external interface automatically.
-
external- Intended to be called from outside the contract.
- Slightly more gas‑efficient for pure external calls.
Other options like internal and private are used for helper functions and will appear later as contracts become more modular.
Note: setEntry is marked public but usually called externally; external could be slightly cheaper.
3.3 State mutability (what does this function do to state?)
Solidity requires you to be explicit about how a function interacts with state:
-
view- Can read state variables but cannot modify them.
- Does not cost gas when called off-chain via tools like Etherscan or a dApp.
-
pure- Cannot read or modify state.
- Works only with its inputs and local variables.
Example:
function getDay() public view returns (uint256) {
return dayNumber;
}
function add(uint256 a, uint256 b) public pure returns (uint256) {
return a + b;
}
4. Your first real contract: Web3 Journey Logger
Instead of another generic “Simple Storage” example, this article builds a Web3 Journey Logger contract that ties directly into a learning challenge.
The contract will:
- Store a name.
- Store the current learning day number.
- Store a short note about the day.
This small contract is enough to show how variables and functions come together in a real, personal use case.
4.1 What the contract should do
The Web3 Journey Logger has a few core behaviors:
- When deployed, it remembers who deployed it as the
owner. - It allows updating the current
dayNumberandnotefor the latest entry. - It lets anyone read the stored values for name, day number, and note.
These requirements map cleanly to a handful of state variables and two or three functions.
4.2 Full contract code
Here is a complete version of the Web3 Journey Logger contract:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract Web3JourneyLogger {
// --- State variables ---
// Address of the person who deployed the contract
address public owner;
// Name or handle of the learner
string public name;
// Current day in the learning journey
uint256 public dayNumber;
// Short note for the current day
string public note;
// --- Constructor ---
constructor(string memory _name) {
owner = msg.sender;
name = _name;
}
// --- Core functions ---
// Update the current day and note (only owner can call)
function setEntry(uint256 _dayNumber, string calldata _note) public {
require(msg.sender == owner, "Not owner");
dayNumber = _dayNumber;
note = _note;
}
// Helper to read all data in one call
function getEntry()
public
view
returns (
string memory _name,
uint256 _day,
string memory _note
)
{
return (name, dayNumber, note);
}
}
This minimal contract demonstrates state variables, a constructor, a state‑changing function with a simple access control check, and a view function that returns multiple values.
5. Step‑by‑step: deploy to Sepolia with Remix
To make this more than theory, the contract is deployed on the Sepolia testnet using Remix and MetaMask.
5.1 Setup the wallet
A wallet such as MetaMask is needed to sign and send transactions:
- The wallet must be switched to the Sepolia test network.
- A small amount of test ETH can be requested from a Sepolia faucet to cover deployment gas.
This setup mirrors real mainnet deployment without risking real funds.
You can follow these steps!
5.2 Open Remix and add the contract
Remix is a browser-based IDE for Solidity:
- Visit
https://remix.ethereum.orgin a browser. - In the file explorer, create a new file named
Web3JourneyLogger.sol. - Paste the complete contract code into this file.
Remix automatically saves the file and makes it available to the Solidity compiler.
5.3 Compile the contract
Compiling turns Solidity into bytecode that can be deployed:
- Open the Solidity Compiler tab in Remix.
- Select a compiler version matching the pragma, such as
0.8.20. - Click Compile Web3JourneyLogger.sol.
If any errors appear, Remix highlights the problematic lines so they can be corrected before trying again.
5.4 Deploy to Sepolia
Once the contract compiles, it can be deployed to the testnet:
- Open the Deploy & Run Transactions tab.
- Set Environment to Injected Provider – MetaMask so Remix uses the wallet’s network.
- Confirm that MetaMask is on the Sepolia network.
- Ensure
Web3JourneyLoggeris selected in the contract dropdown. - In the constructor field, pass a name string, for example
"Ribhav". - Click Deploy and confirm the transaction in MetaMask.
After mining, Remix shows a deployed contract instance and the transaction can also be viewed on Sepolia Etherscan using the transaction hash.
5.5 Interact with the contract
The deployed contract can now be read and updated from Remix:
- Under Deployed Contracts, expand the
Web3JourneyLoggerinstance. - Call the generated getters like
owner,name,dayNumber, andnoteto see current values. - Use
setEntryto set a new day number and note, then read them back again using either the individual getters orgetEntry().
By repeating this daily, the contract becomes a small on-chain journal of a Web3 learning journey.
6. Why this matters for developers
Every DeFi protocol, NFT marketplace, or DAO is ultimately just a more complex arrangement of variables and functions like the ones introduced here.
Understanding how state is stored, how functions expose or protect that state, and how contracts are deployed to a network is the foundation of secure smart contract development and auditing.
In the next steps of the journey, these basics expand into arrays, mappings, structs, access control patterns, and local development tools like Hardhat and Foundry, but they all build on the same core ideas introduced in this first Solidity article.
Further reading
Solidity official docs – Introduction to Smart Contracts
Solidity by Example – Simple Storage and Basics
GeeksforGeeks – Solidity Basics of Contracts
GeeksforGeeks – Solidity Variables
GeeksforGeeks – Solidity Functions
Dapp University – Solidity for Beginners
- Follow the series on Medium | Twitter | Future
- Jump into Web3ForHumans on Telegram and we’ll brainstorm Web3 together.
Top comments (0)