Solidity Basics (Part 1) — Variables, Functions & Your First Real Contract
Source: Dev.to
Day 26 – Phase 3: Development
The 60‑Day Web3 Journey – Solidity Basics
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.
What We’ll Cover Today
- Understand the basic building blocks – variables and functions.
- Ship a tiny but real contract to Sepolia: Web3 Journey Logger.
Goal: Not to become a Solidity expert, but to grasp the fundamentals that let you store and manipulate data on‑chain.
1️⃣ Solidity at a Glance
Solidity is a high‑level, contract‑oriented programming language used to write smart contracts that run on Ethereum and other EVM‑compatible chains (Arbitrum, Optimism, Polygon, …).
- Compiled → bytecode that lives at a specific address on‑chain.
- 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.
Two crucial differences:
- You’re programming money and state, not just UI.
- Every operation costs gas, and the code you deploy is mostly permanent.
2️⃣ Variables – The Contract’s 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 needs to know exactly what will be stored where.
Three Levels of Variables
| Level | Scope | Persistence | Typical Use |
|---|---|---|---|
| State variables | Stored permanently in the contract’s storage on the blockchain. | Persist between function calls and transactions. | Owner address, token‑balance mapping, counters. |
| Local variables | Declared inside a function. | Exist only while that function is running, then disappear. | uint256 sum = a + b; inside a function. |
| Global variables | Built‑in helpers provided by the EVM. | Read‑only during execution. | msg.sender, block.timestamp, block.number. |
In this first step we focus on state variables, since they form your contract’s on‑chain memory.
Core Data Types (the ones you’ll see in almost every contract)
| Type | Description | Example |
|---|---|---|
bool | Stores true or false. | bool public completed; |
uint256 | Unsigned integer (0 and positive numbers). Common for amounts, IDs, counters, timestamps. | uint256 public dayNumber; |
address | Stores an Ethereum address (user or contract). | address public owner; |
string | Stores text data. More expensive than numbers in terms of gas, so use only when needed. | string public note; |
There are many more types (
int,bytes,mapping,struct, arrays, …) but these four are enough to get started.
3️⃣ Functions – The Doors & Buttons
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 (e.g., a dApp), the EVM executes the function’s logic step‑by‑step.
Basic Function Syntax
function setDay(uint256 _day) public {
dayNumber = _day;
}
setDay– function name.uint256 _day– input parameter.public– visibility specifier.- Body (
{ … }) updates thedayNumberstate variable.
Visibility Modifiers
| Modifier | Who can call it? |
|---|---|
public | Anyone, including other contracts. Automatically part of the contract’s external interface. |
external | Intended to be called from outside the contract. Slightly more gas‑efficient for pure external calls. |
internal / private | Used for helper functions; not covered here (will appear later). |
setEntry(in the example below) is markedpublicbut could beexternalfor a small gas saving.
State‑Interaction Modifiers
| Modifier | Behaviour |
|---|---|
view | Can read state variables but cannot modify them. No gas cost when called off‑chain (e.g., via Etherscan). |
pure | Cannot read or modify state. Works only with its inputs and local variables. |
Example Functions
function getDay() public view returns (uint256) {
return dayNumber;
}
function add(uint256 a, uint256 b) public pure returns (uint256) {
return a + b;
}
4️⃣ Project: Web3 Journey Logger
Instead of another generic “Simple Storage” example, we’ll build a Web3 Journey Logger contract that ties directly into this learning challenge.
What the Contract Does
- Store a name (your learner handle).
- Store the current learning day number.
- Store a short note about the day.
Core Behaviours
| Behaviour | Description |
|---|---|
| Owner | When deployed, it remembers who deployed it (owner). |
| Update | Allows updating dayNumber and note for the latest entry. |
| Read | Anyone can read the stored values (name, dayNumber, note). |
These map cleanly to a handful of state variables and two‑three functions.
5️⃣ Full Contract Code
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract Web3JourneyLogger {
// ── State variables ──
/// @notice Address of the person who deployed the contract
address public owner;
/// @notice Name or handle of the learner
string public name;
/// @notice Current day in the learning journey
uint256 public dayNumber;
/// @notice Short note for the current day
string public note;
// ── Constructor ──
/**
* @dev Sets the contract deployer as `owner` and initializes the learner's `name`.
* @param _name The learner's name or handle.
*/
constructor(string memory _name) {
owner = msg.sender;
name = _name;
}
// ── Core functions ──
/**
* @dev Updates the current day number and an optional note.
* Only the contract `owner` can call this function.
* @param _day New day number.
* @param _note Short note for the day.
*/
function setEntry(uint256 _day, string calldata _note) external {
require(msg.sender == owner, "Only owner can update");
dayNumber = _day;
note = _note;
}
/**
* @dev Returns the stored learner information.
* @return _name Learner's name.
* @return _day Current day number.
* @return _note Current note.
*/
function getEntry()
external
view
returns (string memory _name, uint256 _day, string memory _note)
{
return (name, dayNumber, note);
}
}
How It Works
| Function | Visibility | State Interaction | Who Can Call? |
|---|---|---|---|
constructor | public (executed once) | Writes owner & name | Deployer |
setEntry | external | Writes dayNumber & note (requires owner) | Owner only |
getEntry | external view | Reads all public variables | Anyone |
Deploy this contract to Sepolia (or any EVM‑compatible testnet) and you’ll have a personal on‑chain journal of your Web3 learning journey.
6️⃣ Next Steps
- Compile & Deploy the contract using Remix, Hardhat, or Foundry.
- Interact with
setEntryto log a new day and note. - Read the data via
getEntryor directly through the autogenerated getters (owner(),name(),dayNumber(),note()).
That’s it for Day 26! You now have a concrete example of how variables and functions combine to create a usable on‑chain application. Tomorrow we’ll dive deeper into more complex data structures (mappings, structs) and explore how to make your contracts safer and more gas‑efficient. Happy coding!
Web3 Journey Logger – A Minimal Solidity Contract
Below is a simple Solidity contract that records a daily entry (day number and a short note). It demonstrates:
- State variables and a constructor
- A state‑changing function with basic access control
- A view function that returns multiple values
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract Web3JourneyLogger {
address public owner;
string public name;
uint256 public dayNumber;
string public note;
// Constructor – runs once when the contract is deployed
constructor(string memory _name) {
owner = msg.sender;
name = _name;
}
// Update the current day and note (only the 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.
Deploying to Sepolia with Remix & MetaMask
Prerequisites
- MetaMask (or another Web3 wallet) installed in your browser.
- The wallet must be connected to the Sepolia test network.
- A small amount of test ETH from a Sepolia faucet to cover deployment gas.
Step‑by‑step Guide
- Open Remix – a browser‑based IDE for Solidity.
- Create a new file
- In the file explorer, click + and name the file
Web3JourneyLogger.sol.
- In the file explorer, click + and name the file
- Paste the contract code (the snippet above) into the file.
- Compile the contract
- Open the Solidity Compiler tab.
- Choose a compiler version that matches the pragma, e.g., 0.8.20.
- Click Compile Web3JourneyLogger.sol.
- Deploy to Sepolia
- Open the Deploy & Run Transactions tab.
- Set Environment to Injected Provider – MetaMask.
- Verify MetaMask is on the Sepolia network.
- Ensure
Web3JourneyLoggeris selected in the Contract dropdown. - In the Constructor Arguments field, type a name string, e.g.,
"Ribhav". - Click Deploy and confirm the transaction in MetaMask.
- Verify deployment
- After the transaction is mined, Remix shows a Deployed Contracts section with your contract instance.
- You can view the transaction on Sepolia Etherscan using the transaction hash.
- Interact with the contract
- Expand the deployed instance under Deployed Contracts.
- Call the autogenerated getters (
owner,name,dayNumber,note) to see current values. - Use
setEntryto store a new day number and note (only the owner can call). - Retrieve the data again via the individual getters or the
getEntryview function.
By repeating this daily, the contract becomes a tiny on‑chain journal of your Web3 learning journey.
Why This Matters
Every DeFi protocol, NFT marketplace, or DAO is ultimately 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
- How contracts are deployed to a network
is the foundation of secure smart‑contract development and auditing.
Later steps will expand to arrays, mappings, structs, access‑control patterns, and local development tools such as Hardhat and Foundry—all building on the core ideas presented 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
Stay Connected
- Follow the series on Medium, Twitter, and Future
- Join Web3ForHumans on Telegram – let’s brainstorm Web3 together!