Writing Your First Smart Contract
Blockchain & Web3 Fundamentals (Course 1) got you as far as knowing what a Contract Account is (Chapter 6) and roughly what the EVM, Solidity, and gas are for. This course builds directly on that foundation and gets hands-on: real Solidity syntax, a real worked contract, and exactly what happens on-chain when you deploy one.
Solidity's Real Origin
Solidity was proposed by Gavin Wood (one of Ethereum's own founding team members, per Course 1 Chapter 6) in August 2014, and subsequently developed further by Christian Reitwiessner, Alex Beregszaszi, and several other Ethereum core contributors. It's a statically-typed, contract-oriented language, purpose-built for writing code that runs on the EVM — compiling down to the actual bytecode the EVM executes, exactly as Course 1 Chapter 6 described. Its syntax deliberately draws on JavaScript, C++, and Python, making it comparatively approachable for developers coming from any of those backgrounds, while still enforcing static typing that plain JavaScript doesn't have.
The Anatomy of a Contract
Here's a real, complete, minimal Solidity contract — a simple counter that anyone can increment, with its current value publicly readable by anyone:
Breaking down each real piece:
-
pragma solidity ^0.8.0;— declares which compiler version(s) this contract is written for. The^means "this version or any later compatible 0.8.x version" — a real safeguard against a future compiler version silently changing how the code behaves. -
contract Counter { ... }— thecontractkeyword defines the contract's own scope, directly comparable to a class definition in an object-oriented language. -
uint public count;— a state variable. Unlike a local variable inside a function, a state variable's value is permanently stored as part of the Contract Account's own on-chain data (Course 1 Chapter 6), persisting between separate transactions. -
constructor() { ... }— code that runs exactly once, at the moment the contract is deployed, and never again afterward. -
function increment() public { ... }— a function anyone can call to change the contract's own state (here, increasingcountby one).
Function Visibility
Solidity has four real visibility keywords, controlling exactly who can call a given function:
| Keyword | Who Can Call It |
|---|---|
| public | Anyone — other contracts, external accounts, and code inside this same contract |
| external | Only from outside the contract — not callable directly from other functions inside the same contract |
| internal | Only from inside this contract, or a contract that inherits from it |
| private | Only from inside this exact contract — not even inheriting contracts can call it |
private only
restricts which other Solidity code can directly call a function or read a variable.
It does nothing to hide the underlying data itself — every transaction, every state
variable's own value, is stored on a public, replicated blockchain (Course 1, Chapters 1 and 3)
and can be read directly off the chain by anyone with the right tools, regardless of what
visibility keyword Solidity uses. "Private" is an access-control concept for other contract code,
not a real confidentiality guarantee.
view and pure: Promising Not to Change (or Even Touch) State
Two further real modifiers describe a function's relationship to the contract's own state:
-
view— the function can read state variables, but promises not to modify any of them.getCount()above is a real example. -
pure— the function promises not to even read state variables at all; it only operates on whatever arguments it's given.
This distinction has a genuine, practical payoff: because a view or pure
call doesn't change anything on-chain, it can typically be executed locally by a node without
needing to be broadcast as a real, gas-costing transaction at all — you can call
getCount() for free to simply read the current value, while calling
increment() genuinely requires a real, signed, gas-paying transaction, since it
actually changes the contract's own permanent state.
The address Type and msg.sender
Solidity has a dedicated address type representing an Ethereum account —
either an Externally Owned Account or a Contract Account (Course 1, Chapter 6). Inside any
function, the built-in variable msg.sender always holds the address that actually
called the currently-executing function — the real mechanism that lets a contract know
who is interacting with it, since a Contract Account has no private key of its own to
check a signature against directly the way an EOA does.
uint, address, and the boolean bool, Solidity's
mapping type is especially common — a key-value store, most
often written as mapping(address => uint), letting a contract associate a value
(like a token balance) with each address. Chapter 2's own gas coverage will show exactly why
reading and writing to a mapping's own storage has a real, measurable cost.
Deploying a Contract: Creating a Real Contract Account
Writing Solidity code doesn't put it on-chain by itself. Deploying a contract means compiling the source code down to real EVM bytecode, then sending a special transaction (signed with a real private key, exactly like any other transaction from Course 1 Chapter 2) whose destination is empty rather than an existing address. The network responds by creating a brand-new Contract Account — the exact account type Course 1 Chapter 6 introduced — at a new address, with the compiled bytecode permanently stored as that account's own code. From that point on, anyone can call the contract's own public functions by sending transactions to that new address.
Hands-On Exercises
Three exercises reinforcing Solidity's basic building blocks before Chapter 2 covers the EVM and gas in real depth.
Counter contract as a model, describe (in plain English,
no code required) what a decrement() function would need to do, and whether it
should be marked view, pure, or neither. Justify your answer.
private so nobody else can see the
secret value it stores." Using this chapter's own warning about what private
actually restricts, explain what's wrong with this reasoning.
Quick Reference
- Solidity — proposed by Gavin Wood in August 2014; statically-typed, compiles to EVM bytecode.
- Contract anatomy —
pragma(compiler version),contract(scope), state variables (persistent on-chain data),constructor(runs once, at deployment), functions. - Visibility —
public,external,internal,private;privaterestricts other Solidity code, not on-chain data visibility. view/pure— promise not to modify state / not to touch state at all; both can typically be called for free, without a real transaction.address/msg.sender— the type representing an account; the built-in variable holding whoever called the current function.- Deployment — a real, signed transaction with no destination address, which creates a brand-new Contract Account holding the compiled bytecode.