The minimum Solidity you need
About 1776 wordsAbout 6 min
This is not a programming course
The goal is enough Solidity to read a simple contract and deploy one — not to make you a developer. If you have never written code, you can still complete this week. Everything you need is on this page.
If you already program, this will feel slow. Part 3 is where it gets hands-on.
AI assistance is explicitly allowed. You are not expected to write the Guestbook contract from scratch. You may use ChatGPT, Claude, Copilot or another coding assistant to explain unfamiliar Solidity, explain a line or structure, troubleshoot a Remix/compiler error, modify the provided example, or answer "what does this code do?"
The standard is not "Did you type the code yourself?" It is: Can you explain what you deployed? By the end of the week, you should be able to point to the major parts of this small contract, describe what changes on-chain when you interact with it, and recognise when you do not understand something AI produced.
Learning objectives
- Read a short Solidity contract and say what it does
- Identify state variables, functions, events and visibility
- Explain what
viewmeans and why a normal read does not charge a transaction fee - Explain what an ABI is and why applications need one
Core
Start with behaviour, not syntax
Before reading the full contract, look at the smallest version of the idea:
contract Guestbook {
string public message = "Hello";
function setMessage(string memory newMessage) public {
message = newMessage;
}
}Ask two questions:
- What does this program remember? A message.
- What can someone do? Call
setMessageto change that message.
That is the behaviour to understand first. The words and symbols are the programming language used to express it. Solidity calls the stored text a string, and calls the action that changes it a function.
The full version below uses calldata and external, which are more specific Solidity choices. You can ignore that distinction for now: the behaviour is unchanged — it still takes text and changes the message.
One contract, fully explained
Here is the complete, working Guestbook you will deploy tomorrow. It adds a visitor, a count and an event so that you can see what a real small contract remembers and announces.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract Guestbook {
string public message;
address public lastVisitor;
uint256 public visitCount;
event MessageChanged(address indexed visitor, string newMessage);
constructor() {
message = "Hello from Blockchain@NTU";
}
function setMessage(string calldata newMessage) external {
message = newMessage;
lastVisitor = msg.sender;
visitCount = visitCount + 1;
emit MessageChanged(msg.sender, newMessage);
}
function getMessage() external view returns (string memory) {
return message;
}
}constructor() runs once when the contract is deployed, setting its first message.
Read it once more now that you know its basic behaviour. The rest of this page names the important pieces one at a time.
Taken apart
string public message;
address public lastVisitor;
uint256 public visitCount;These are state variables — what the contract remembers, stored on-chain, persisting between calls.
| Type | Holds |
|---|---|
string | Text |
address | A 0x address, 20 bytes |
uint256 | A whole number, zero or positive |
bool | True or false |
public does something genuinely useful: Solidity automatically creates a read function for it. Declaring message public means anyone can call message() and get the value, without you writing that function.
function setMessage(string calldata newMessage) external {
message = newMessage;
lastVisitor = msg.sender;
visitCount = visitCount + 1;
emit MessageChanged(msg.sender, newMessage);
}A write function. It changes state, so it costs gas and needs a signed transaction.
msg.sender is the address that called this function. It is one of the most important things in Solidity — it is how a contract knows who is asking, and it is the basis of every permission check you will ever read.
function getMessage() external view returns (string memory) {
return message;
}A read function. view promises it does not change state — so a normal read through Remix or a wallet does not create an on-chain transaction or pay a transaction fee. It may still depend on a node response, as Week 2 Part 4 described.
event MessageChanged(address indexed visitor, string newMessage);When setMessage changes the message, the contract announces what happened in the transaction log. This announcement is an event. Applications watching from outside can use it, but the contract cannot read it back.
indexed makes a field searchable. Applications can ask "every MessageChanged where visitor was 0xABC…" cheaply. You get up to three indexed fields.
Events are emitted with emit, and they are far cheaper than storing the same data in state.
Who is allowed to call a function.
| Keyword | Callable by |
|---|---|
public | Anyone, inside or outside |
external | Designed to be called from outside the contract |
internal | Only this contract and ones inheriting it |
private | Only this contract |
private does not mean secret
It restricts which code can call it. All contract data is publicly readable on-chain regardless. Anyone can read a private variable's storage directly.
Never put a secret in a contract. There is no such thing.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;SPDX-License-Identifier declares the licence. The compiler warns if it is missing. MIT is a permissive open-source licence — the same one the Academy handbook's tooling uses.
pragma solidity ^0.8.20 says which compiler versions this code accepts. The ^ means "0.8.20 or any later 0.8.x". Version matters because compiler behaviour can change in ways that affect safety.
The ABI
After deployment, two different audiences need different information. The EVM needs the program itself; a wallet or website needs to know which functions it can call and what inputs to provide.
When Solidity compiles, it produces two useful outputs:
| Output | What it is |
|---|---|
| Bytecode | The actual program the EVM runs. Unreadable |
| ABI | A JSON description of the functions and events — their names, inputs and outputs |
The ABI (Application Binary Interface) is how anything outside knows how to talk to your contract. Your wallet, a website, Etherscan — they all need the ABI to turn "call setMessage with this text" into the bytes a transaction carries.
This is why "verified source code" matters
Publishing verified source lets Etherscan show human-readable code and a convenient Read/Write Contract interface matched against the deployed bytecode. Without verified source, the bytecode is still public, but the original human-readable code and interface are harder to inspect.
What you can safely skip for now
Real contracts contain more than this. You do not need it this week:
- Modifiers — reusable permission checks like
onlyOwner - Inheritance — building on contracts like OpenZeppelin's
require/ custom errors — rejecting invalid callspayable— functions that receive ETH- Libraries, interfaces, structs, enums
You will meet require and onlyOwner when you read a real token in Part 4. That is soon enough.
Landscape
- OpenZeppelin — reviewed standard implementations. Real projects reuse these rather than writing tokens from scratch; reuse reduces repeated mistakes but is not a guarantee
require/revert— reject a call and undo its state changes. The transaction can still consume gas for work done before the rejectiononlyOwner— a common permission pattern that lets one address call selected functions. That owner becomes an important trust assumption- Reentrancy guard — a safety check that stops a called contract from coming back in before the first action finishes. It helps prevent repeated withdrawals before the balance is updated
payable/receive— how a contract accepts ETH. If the contract has no safe way to handle incoming ETH, a transfer can fail- Storage vs memory vs calldata — three places Solidity can work with data.
storagepersists as contract state,memoryis temporary while code runs, andcalldatais read-only input to a call. Foundation only needs to recognise the names mapping— a lookup table such as address → balance; Week 3 Part 4 uses one for token balances. It lets a contract find a value by key- Foundry / Hardhat — professional toolkits for testing, compiling and deploying. They help developers repeat checks, but are not needed for Foundation
Worked example
Read this contract and answer three questions before scrolling.
contract Vault {
address public owner;
mapping(address => uint256) public deposits;
constructor() { owner = msg.sender; }
function deposit() external payable {
deposits[msg.sender] = deposits[msg.sender] + msg.value;
}
function drain() external {
payable(msg.sender).transfer(address(this).balance);
}
}- What does it remember?
- Who can call
drain()? - What is wrong with it?
Answers
1. Two things: owner, set once at deployment to whoever deployed it, and deposits, a table of how much each address has put in.
2. Anyone. drain() is external with no permission check at all. There is no require(msg.sender == owner). The name says "owner", the code does not.
3. Anyone can call drain() and send the entire balance to the caller. Worse, depositors cannot withdraw — there is no withdraw function, so their funds are stuck and deposits records balances the contract will never honour.
Two lessons in one twenty-line contract
A variable called owner does not create a permission. Only a check does. Reading contracts means reading what the code does, never what the names suggest.
And a missing function is a bug too. Nothing in this contract is technically broken — it compiles and runs. It simply cannot do the thing its users assume.
Further exploration — optional, not assessed
- Solidity by Example — short annotated contracts, the fastest way to build fluency
- Solidity documentation — the reference. Dense; use it to look things up, not to read through
- OpenZeppelin Contracts — read
ERC20.sol. It is the most-copied contract in the industry and it is genuinely readable
Sources and attribution
- Solidity documentation — Link, referenced only
- Solidity by Example — Link, referenced only
- ethereum.org — Anatomy of smart contracts — Reuse (CC BY 4.0), adapted
- OpenZeppelin Contracts — Reuse (MIT), referenced
Changelog
ff0f4-feat(academy): finalize Foundation learning experienceona931f-docs(foundation): finalize beginner learning path and handbook UXon80d94-feat(foundation): complete Weeks 3-4 and add the visual layeron2d4b4-docs(curriculum): finalize Week 0-2 Foundation revisiononaed71-Bootstrap Blockchain@NTU Academy Handbookon