Learn Solidity - A Handbook For Smart Contract Development
Learn Solidity - A Handbook For Smart Contract Development
Forum Donate
https://www.freecodecamp.org/news/learn-solidity-handbook/ 1/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
https://www.freecodecamp.org/news/learn-solidity-handbook/ 2/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
After 15 years in law and other roles, I’d experienced a number of jobs, countries, companies, and
career paths. None of them compared to the joy and thrill I get from coding.
The downside? Picking up new coding skills can be confusing, frustrating, and time consuming. And
it’s easy to forget some of the minute but important details.
So I wrote this handbook. It's intended to get you started on coding Solidity ASAP. It follows the
Pareto Principle (aka the 80/20 rule) by focusing on the 20% of the info that will cover 80% of your
needs.
I started assembling these concepts when I was learning Solidity, as part of my role at Chainlink
Labs. I applied many of the self-learning techniques that I learned when transitioning to coder at
the age of 38.
This is the resource I wish I had. It is designed to give beginner and intermediate developers solid
mental models to stack as you get deeper into the language (mental models massively accelerate
effective learning).
https://www.freecodecamp.org/news/learn-solidity-handbook/ 3/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
I will keep this handbook updated, but I could really use your help! Just tweet me @ZubinPratap to
let me know if I need to updateLearn
this to code — free 3,000-hour curriculum
handbook.
I’d like to acknowledge my amazing colleagues Kevin Ryu, Andrej Rakic, Patrick Collins, and Richard
Gottleber for the invaluable guidance and input on this handbook.
Table of Contents
1. Who is this handbook for?
3. What is Solidity?
https://www.freecodecamp.org/news/learn-solidity-handbook/ 4/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
This handbook is for people who are interested in exploring the vision behind “Web3”, and who
wish to acquire in-demand skills that are essential to realizing that vision.
Do not memorize it! Read it and then use it as a “desktop reference” companion. As you learn any
new language, you will find that concepts, idioms, and usage can get a bit confusing or your memory
fades with time. That’s ok! That’s what this handbook is designed to help you with.
Over time I may add some more advanced topics to this, or create a separate tutorial. But for now
this handbook will get you most of the outcomes you require to build your first several Solidity
dApps.
This handbook assumes you have at least a few months of experience with programming. By
programming I mean at the very least you’ve written in JavaScript or Python or some compiled
language (since HTML and CSS aren't actually "programming" languages, it won't be enough to
know only them).
The only other requirements are that you are curious, committed, and not putting arbitrary
deadlines on yourself.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 6/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
As long as you have a laptop, and a browser with an internet connection, you’ll be able to run the
Forum Donate
Solidity code. You can use Remix in your browser to write the code in this handbook. No other IDE
Learn to code — free 3,000-hour curriculum
required!
You are unlikely to need them to understand this handbook. But practically speaking, having a
browser wallet like Metamask and understanding the difference between Ethereum contract
accounts and externally owned accounts will help you get the most out of this handbook.
What is Solidity?
Now, let’s start with understanding what Solidity is. Solidity is an object-oriented programming
language influenced by C++, JavaScript and Python.
Solidity is designed to be compiled (converted from human readable to machine readable code) into
bytecode that runs on the Ethereum Virtual Machine (EVM). This is the runtime environment for
Solidity code, just like your browser is a runtime environment for JavaScript code.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 7/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
So, you write Smart Contract code in Solidity, and the compiler converts it into bytecode. Forum
Then that Donate
bytecode gets deployed and stored on Ethereum (and other EVM-compatible blockchains).
Learn to code — free 3,000-hour curriculum
You can get a basic introduction to the EVM and bytecode in this video I made.
Read it along with each comment to get a sense of what’s going on, and then move on to some key
learnings.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8.0;
contract HotFudgeSauce {
uint public qtyCups;
We will get to some of the details like what public and view mean shortly.
For now, take seven key learnings from the above example:
SPDX license identifiers are strongly recommended, though your code will compile without
it. Read more here. Also, you can add a comment or “comment out” (suppress) any line by
prefixing it with two forward slashes " // ".
2. The pragma directive must be the first line of code in any Solidity file. Pragma is a directive
that tells the compiler which compiler version it should use to convert the human-readable
Solidity code to machine readable bytecode.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 9/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Solidity is a new language and is frequently updated, so different versions of the compiler
Forum Donate
produce different results when compiling code. Some older solidity files will throw errors or
Learn to code — free 3,000-hour curriculum
warnings when compiled with a newer compiler version.
In larger projects, when you use tools like Hardhat, you may need to specify multiple
compiler versions because imported solidity files or libraries that you depend on were
written for older versions of solidity. Read more on Solidity’s pragma directive here.
3. The pragma directive follows Semantic Versioning (SemVer) - a system where each of the
numbers signifies the type and extent of changes contained in that version. If you want a
hands-on explanation of SemVer is check out this tutorial - it is very useful to understand
and it’s used widely in development (especially web dev) these days.
4. Semicolons are essential in Solidity. The compiler will fail if even a single one is missing.
Remix will alert you!
5. The keyword contract tells the compiler that you’re declaring a Smart Contract. If you’re
familiar with Object Oriented Programming, then you can think of Contracts as being like
Classes.
If you’re not familiar with OOP then think of contracts as being objects that hold data - both
variables and functions. You can combine smart contracts to give your blockchain app the
functionality it needs.
6. Functions are executable units of code that encapsulate single ideas, specific functionality,
tasks, and so on. In general we want functions to do one thing at a time.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 10/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
Functions are most often seen inside smart contracts, though they can be declared in the file
Learn to code — free 3,000-hour curriculum
outside the smart contract’s block of code. Functions may take 0 or more arguments and
they may return 0 or more values. Inputs and outputs are statically typed, which is a concept
you will learn about later in this handbook.
7. In the above example the variable qtyCups is called a “state variable”. It holds the contract’s
state - which is the technical term for data that the program needs to keep track of to
operate.
Unlike other programs, smart contract applications keep their state even when the program
is not running. The data is stored in the blockchain, along with the application, which means
that each node in the blockchain network maintains and synchronizes a local copy of the
data and smart contracts on the blockchain.
State variables are like database “storage” in a traditional application, but since blockchains
need to synchronize state across all nodes in the network, using storage can be quite
expensive! More on that later.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 11/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Let’s break down that HotFudgeSauce Smart Contract so we understand more about eachForum
little Donate
piece.
Learn to code — free 3,000-hour curriculum
The basic structure/syntax to defining things in Solidity is similar to other statically typed
languages. We give functions and variables a name.
But in typed languages we also need to specify the type of the data that is created, passed as input
or returned as output. You can jump down to the Typing Data section in this handbook if you need
to understand what typed data is.
Below, we see what declaring a “State Variable” looks like. We also see what declaring a function
looks like.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 12/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
The first snippet declares a State Variable (I’ll explain what this is soon, I promise) called qtyCups .
This can only store values that are of type uint which means unsigned integers. “Integer” refers to
https://www.freecodecamp.org/news/learn-solidity-handbook/ 13/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
all whole numbers below zero (negative) and above zero (positive). Forum Donate
In the second snippet, we see a familiar structure when we declare functions too. Most
importantly, we see that the functions have to specify a data type for the value that the function
returns.
In this example, since get() returns the value of the storage variable we just created, we can see
that the returned value must be a uint .
public is a visibility specifier. More on that later. view is a State-Mutability modifier. More on that
below too!
It’s worth noting here that state variables can also be of other types - constant and immutable .
They look like this:
Constants and immutable variables have their values assigned once, and only once. They cannot be
given another value after their first value is assigned.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 14/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
So if we made the qtyCups state variable either constant or immutable, we would not be able to
Learn to code
call the increment() or decrement() — freeon
functions 3,000-hour curriculum
it anymore (in fact, the code wouldn’t compile!).
Constants must have their values hardcoded in the code itself, whereas immutable variables can
have their values set once, generally by assignment in the constructor function (we’ll talk about
constructor functions very soon, I promise). You can read more in the docs here.
1. State Variables: store permanent data in the smart contract (referred to as persistent state)
by recording the values on the blockchain.
2. Local Variables: these are “transient” pieces of data that hold information for short periods
of time while running computations. These values are not stored permanently on the
blockchain.
3. Global variables: these variables and functions are “injected” into your code by Solidity, and
made available without the need to specifically create or import them from anywhere.
These provide information about the blockchain environment the code is running on and
also include utility functions for general use in the program.
Forum Donate
1. state variables are generally found inside the smart contract but outside of a function.
Learn to code — free 3,000-hour curriculum
2. local variables are found inside functions and cannot be accessed from outside that
function’s scope.
3. Global variables aren’t declared by you - they are “magically” available for you to use.
Here is our HotFudgeSauce example, slightly modified to show the different types of variables. We
give qtyCups a starting value and we dispense cups of fudge sauce to everyone except me (because
I’m on a diet).
https://www.freecodecamp.org/news/learn-solidity-handbook/ 16/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
https://www.freecodecamp.org/news/learn-solidity-handbook/ 17/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Visibility specifies the extent to which a variable, function, or contract can be accessed from
outside the region of code where it was defined. The scope of visibility can be adjusted depending
on which portions of the software system need to access it.
If you’re a JavaScript or NodeJS developer, you’re already familiar with visibility – any time you
export an object you’re making it visible outside the file where it is declared.
Types of Visibility
In Solidity there are 4 different types of visibility: public , external , internal and private .
Public functions and variables can be accessed inside the contract, outside it, from other smart
contracts, and from external accounts (the kind that sit in your Metamask wallet) - pretty much
from anywhere. It’s the broadest, most permissive visibility level.
When a storage variable is given public visibility, Solidity automatically creates an implicit getter
function for that variable’s value.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 18/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
So in our HotFudgeSauce smart contract, we don’t really need to have the get() method,Forum
because Donate
Solidity will implicitly provide us identical functionality, just by giving qtyCups a public visibility
Learn to code — free 3,000-hour curriculum
modifier.
Private functions and variables are only accessible within the smart contract that declares them.
But they cannot be accessed outside of the Smart Contract that encloses them. private is the
most restrictive of the four visibility specifiers.
Internal visibility is similar to private visibility, in that internal functions and variables can only be
accessed from within the contract that declares them. But functions and variables marked internal
can also be accessed from derived contracts (that is, child contracts that inherit from the declaring
contract) but not from outside the contract. We will talk about inheritance (and derived/child
contracts) later on.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 19/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
The 4 Solidity Visibility specifiers and where they can be accessed from
The external visibility specifier does not apply to variables - only functions can be specified as
external.
External functions cannot be called from inside the declaring contract or contracts that inherit
from the declaring contract. Thus, they can only be called from outside the enclosing contract.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 20/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
And that’s how they’re different from public functions – public functions can also be called from
Forum Donate
inside the contract that declare them, whereas an external function cannot.
Learn to code — free 3,000-hour curriculum
In the following example we have an explicit constructor and it accepts some data as a parameter.
This constructor parameter must be injected by you into your smart contract at the time you create
it.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 21/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
To understand when the constructor function gets called, it’s helpful to remember that a Smart
Learn to code — free 3,000-hour curriculum
Contract is created over a few phases:
it is compiled down to bytecode (you can read more about bytecode here). This phase is
called “compile time”.
it gets created (constructed) - this is when the constructor kicks into action. This can be
referred to as “construction time”.
The deployed smart contract bytecode gets run (executed) on the blockchain. This can be
considered “runtime”.
In Solidity, unlike other languages, the program (smart contract) is deployed only after the
constructor has done its work of creating the smart contract.
Interestingly, in Solidity, the finally deployed bytecode does not include the constructor code. This is
because in Solidity, the constructor code is part of the creation code (construction time) and not
part of the runtime code. It is used up when creating the smart contract, and since it’s only ever
called once it is not needed past this phase, and is excluded in the finally deployed bytecode.
So in our example, the constructor creates (constructs) one instance of the Person smart contract.
Our constructor expects us to pass a string value which is called _name to it.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 22/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
When the smart contract is being constructed, that value of _name will get stored in the state
Forum Donate
This is because we want contracts to be configurable or “parameterized”. Rather than hard code
values, we want the flexibility and reusability that comes with injecting data as and when we need.
In our example, let’s say that _name referred to the name of a given Ethereum network on which
the contract is going to be deployed (like Rinkeby, Goerli, Kovan, Mainnet, and so on).
How could we give that information to our smart contract? Putting all those values in it would be
wasteful. It would also mean we need to add extra code to work out which blockchain the contract
is running on. Then we'd have to pick the right network name from a hard-coded list which we store
in the contract, which takes up gas on deployment.
Instead, we can just inject it into the constructor, at the time we are deploying the smart contract to
the relevant blockchain network. This is how we write one contract that can work with any number
https://www.freecodecamp.org/news/learn-solidity-handbook/ 23/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
If you evaluate this in your head you’ll see it does nothing and that’s why it can be excluded (made
implicit) and the compiler will use the default constructor.
This is what makes smart contracts “composable” and why so many Defi protocols are referred to as
“money Legos” - you can write smart contracts that interact with other smart contracts that
interact with other smart contracts and so on…you get the idea.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 24/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum
So when you want your smart contract A to interact with another smart contract B, you need B’s Donate
interface. An interface gives you an to
Learn index
codeor
—menu of the various
free 3,000-hour functions available for you to call
curriculum
on a given Smart Contract.
An important feature of Interfaces is that they must not have any implementation (code logic) for
any of the functions defined. Interfaces are just a collection of function names and their expected
arguments and return types. They’re not unique to Solidity.
So an interface for our HotFudgeSauce Smart Contract would look like this (note that by
convention, solidity interfaces are named by prefixing the smart contract’s name with an “I”:
// SPDX-License-Identifier: MIT
interface IHotFudgeSauce {
function get() public view returns (uint);
function increment() public;
function decrement() public;
}
That’s it! Since HotFudgeSauce had only three functions, the interface shows only those.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 25/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
But there is an important and subtle point here: an interface does not need to include all the
Forum Donate
functions available to call in a smart contract. An interface can be shortened to include the function
Learn to code — free 3,000-hour curriculum
definitions for the functions that you intend to call!
So if you only wanted to use the decrement() method on HotFudgeSauce then you could absolutely
remove get() and increment() from your interface - but you would not be able to call those two
functions from your contract.
So what’s actually going on? Well, interfaces just give your smart contract a way of knowing what
functions can be called in your target smart contract, what parameters those functions accept (and
their data type), and what type of return data you can expect. In Solidity, that’s all you need to
interact with another smart contract.
In some situations, you can have an abstract contract which is similar to but different from an
interface.
An abstract contract is declared using the abstract keyword and is one where one or more of its
functions are declared but not implemented. This is another way of saying that at least one function
is declared but not implemented.
Flipping that around, an abstract contract can have implementations of its functions (unlike
interfaces which can have zero implemented functions), but as long as at least one function is
unimplemented, the contract must be marked as abstract:
https://www.freecodecamp.org/news/learn-solidity-handbook/ 26/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
// SPDX-License-Identifier: MIT
Forum Donate
pragma solidity ^0.8.7;
Learn to code — free 3,000-hour curriculum
// not implemented.
function utterance() public virtual returns (bytes32);
// implemented.
function setAge(int _age) public {
age = _age;
}
}
You may (legitimately) wonder what the point of this is. Well, abstract contracts cannot be
instantiated (created) directly. They can only be used by other contracts that inherit from them.
So abstract contracts are often used as a template or a “base contract” from which other smart
contracts can “inherit” so that the inheriting smart contracts are forced to implement certain
functions declared by the abstract (parent) contract. This enforces a defined structure across
related contracts which is often a useful design pattern.
This inheritance stuff will become a little clearer when we discuss Inheritance later. For now, just
remember that you can declare an abstract smart contract that does not implement all its functions
https://www.freecodecamp.org/news/learn-solidity-handbook/ 27/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
- but if you do, you cannot instantiate it, and future smart contracts that inherit it must doForum
the work Donate
of implementing those unimplemented functions.
Learn to code — free 3,000-hour curriculum
Some of the important differences between interfaces and abstract contracts are that:
Interfaces can have zero implementations, whereas abstract contracts can have any number
of implementations as long as at least one function is “abstract” (that is, not implemented).
All functions in an interface must be marked as “external” because they can only be called by
other contracts that implement that interface.
So start by reading the code and the comments below. You’ve already learned 99% of what you
need to understand the contract below, provided you read it carefully. Then move on to key
learnings from this contract.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 28/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
// SPDX-License-Identifier: MIT
Learn to code — free 3,000-hour curriculum
pragma solidity ^0.8.7;
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
contract PriceConsumerV3 {
AggregatorV3Interface internal priceFeed;
/**
* Network: Goerli
* Aggregator: ETH/USD
* Address: 0xD4a33860578De61DBAbDc8BFdb98FD742fA7028e
*/
constructor() {
priceFeed =
AggregatorV3Interface(0xD4a33860578De61DBAbDc8BFdb98FD742fA7028e);
}
/**
* Returns the latest price
*/
function getLatestPrice() public view returns (int) {
(
/*uint80 roundID*/,
int price,
/*uint startedAt*/,
/*uint timeStamp*/,
/*uint80 answeredInRound*/
) = priceFeed.latestRoundData();
https://www.freecodecamp.org/news/learn-solidity-handbook/ 29/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
return price;
Forum Donate
}
} Learn to code — free 3,000-hour curriculum
This smart contract gets the latest USD price of 1 Eth, from a live Chainlink price feed oracle (see
the oracle on etherscan). The example uses the Goerli network so you don’t end up spending real
money on the Ethereum mainnet.
1. Right after the pragma statement we have an import statement. This imports existing code
into our smart contract.
This is super cool because this is how we reuse and benefit from code that others have
written. You can check out the code that is imported on this GitHub link.
In effect, when we compile our smart contract, this imported code gets pulled in and
compiled into bytecode along with it. We will see why we need it in a second…
2. Previously you saw that single-line comments were marked with // . Now you're learning
about multiline comments. They may span one or more lines and use /* and */ to start and
end the comments.
we get to use the AggregatorV3Interface type because Chainlink defined it. Forum Donate
4. Take a look at the constructor function. This one doesn’t accept parameters, but we could
have just as easily passed the ETH/USD Price Feed’s oracle smart contract’s address
0xD4a33860578De61DBAbDc8BFdb98FD742fA7028e to it as a parameter of type address .
Instead, we are hard-coding the address inside the constructor.
But we are also creating a reference to the Price Feed Aggregator smart contract (using the
interface called AggregatorV3Interface ).
Now we can call all the methods available on the AggregatorV3Interface because the
priceFeed variable refers to that Smart Contract. In fact, we do that next…
5. Let's jump to the function getLatestPrice() . You’ll recognize its structure from our
discussion in HotFudgeSauce , but it’s doing some interesting things.
Inside this getLatestPrice() function we call the latestRoundData() function which exists
on the AggregatorV3Interface type. If you look at the source code of this method you’ll
notice that this latestRoundData() function returns 5 different types of integers!
https://www.freecodecamp.org/news/learn-solidity-handbook/ 31/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
In our smart contract, we are commenting out all 4 values that we don’t need. So this means that
Solidity functions can return multiple values (in this example we are returned 5 values), and we can
pick and choose which ones we want.
Another way of consuming the results of calling latestRoundData() would be: ( ,int price, , ,)
= priceFeed.latestRoundData() where we ignore 4 out of 5 returned values by not giving them a
https://www.freecodecamp.org/news/learn-solidity-handbook/ 32/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Since you’ve learned about interfaces, I recommend you take a look at Chainlink Labs’ GitHub repo
to examine the implemented latestRoundData() function in the Aggregator contract and how the
AggregatorV3Interface provides the interface to interact with the Aggregator contract.
“State” in computer science has a well-defined meaning. While it can get very confusing, the crux of
state is that it refers to all the information that is “remembered” by a program as it runs. This
information can change, update, be removed, created and so on. And if you were to take a snapshot
of it at various times, the information will be in different “states”.
So the state is just the current snapshot of the program, at a point in time during its execution -
what values do its variables hold, what are they doing, what objects have been created or removed,
and so on.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 33/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum
We have previously examined the three types of variables - State Variables, Local Variables, and Donate
2. what values the blockchain-related global variables have at that moment in time, and
These keywords are referred to as modifiers. But not all of them permit you to modify state. In fact
many of them expressly disallow modifications.
Here are Solidity modifiers you will see any real-world smart contract:
constant State variables Declared and given a value once, at the same time. Hard coded
into code. Its given value can never be changed.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 34/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
Used when we know a value is never meant to change - for
Learn to code — free
example 3,000-hour
if we curriculum
will never (ever) allow a user to buy more than 50
units of something, we can declare 50 as a constant value.
immutable State variables These are declared at the top of smart contracts, but given their
value (only once!) at construction time - i.e. via the constructor
function. Once they receive their value, they are (effectively)
constants. And their values are actually stored in the code itself
rather than in a storage slot (storage will be explained later).
view functions You’ll generally see this right after the visibility specifier. A view
modifier means that the function can only “view” (read from)
contract state, but cannot change it (cannot “write” to contract
state). This is effectively a read-only modifier. If the function
needs to use any value that is in the contract’s state, but not
modify that value, it will be a view function.
pure functions Functions that are pure are not allowed to write to (modify)
contract state, nor are they allowed to even read from it! They do
things that do not, in any way, interact with blockchain state.
Often these can be helper functions that do some calculation or
convert an input of one data type into another data type etc.
payable functions This keyword enables a function to receive Eth. Without this
keyword you cannot send Eth while calling a function.
Forum Donate
Note that this use of payable is a type conversion, and not the
same
Learn to code as the
— free function modifier,
3,000-hour though the same keyword is used.
curriculum
We will cover the address type later, but you can read about this
here.
virtual functions This is a slightly more advanced topic and is covered in detail in
the section on Inheritance. This modifier allows the function to be
“overridden” in a child contract that inherits from it. In other
words, a function with the keyword virtual can be “rewritten”
with different internal logic in another contract that inherits from
this one.
override functions This is the flip side to the virtual modifier. When a child
contract “rewrites” a function that was declared in a base contract
(parent contract) from which it inherits, it marks that rewritten
function with override to signal that its implementation overrides
the one given in the parent contract. If a parent’s virtual
function is not overridden by the child, the parent's
implementation will apply to the child.
indexed events We will cover events later in this handbook. They are small
bundles of data “emitted” by a smart contract typically in response
to noteworthy events happening. The indexed keyword indicates
that one of the pieces of data contained in an event should be
stored in the blockchain for retrieval and filtering later. This will
make more sense once we cover Events and Logging later in this
handbook.
anonymous events The docs say “Does not store event signature as topic” which
probably doesn’t mean a lot to you just yet. But the keyword
does indicate that it’s making some part of the event
“anonymous”. So this will make sense once we understand
events and topics later in this handbook.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 36/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
Data locations are part of the fundamental design and architecture of the EVM. When you see the
words “memory”, “storage” and “stack”, you should start thinking “data locations” - that is, where
can data be stored (written) to and retrieved (read) from.
Data location has an impact on how the code executes at run time. But it also has very important
impacts on how much gas gets used during deployment and running of the smart contract.
The use of gas requires a deeper understanding of the EVM and something called opcodes - we can
park that discussion for now. While useful, it is not strictly necessary for you to understand data
locations.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 37/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Though I’ve mentioned 3 data locations so far, there are 2 other ways in which data can beForum
stored Donate
and accessed in Smart Contracts: “calldata”, and “code”. But these are not data locations in the
Learn to code — free 3,000-hour curriculum
EVM’s design. They’re just subsets of the 3 data locations.
Let’s start with storage. In the EVM’s design, data that needs to be stored permanently on the
blockchain is placed in the relevant smart contract’s “storage” area. This includes any contract
“state variables”.
Once a contract is deployed and has its specific address, it also gets its own storage area, which you
can think of as a key-value store (like a hash table) where both the keys and the values are 256 bit
(32 byte) data “words”. And “words” has a specific meaning in computer architecture.
Because storage persists data on the blockchain permanently, all data needs to be synchronized
across all the nodes in the network, which is why nodes have to achieve consensus on data state.
This consensus makes storage expensive to use.
You’ve already seen examples of storage variables (aka contract state variables) but here is an
example taken from the Chainlink Verifiable Random Number Consumer smart contract
https://www.freecodecamp.org/news/learn-solidity-handbook/ 38/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
When the above contract is created and deployed, whatever address that is passed into the
contract’s constructor becomes permanently stored in the smart contract’s storage, and is
accessible using the variable vrfCoodinator . Since this state variable is marked as immutable , it
cannot be changed after this.
To refresh your memory from the previous section on keywords, where we last discussed
immutable and constant variables, these values are not put in storage. They become part of the
https://www.freecodecamp.org/news/learn-solidity-handbook/ 39/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
code itself when the contract is constructed, so these values don’t consume as much gas as storage
Forum Donate
variables.
Learn to code — free 3,000-hour curriculum
Now let’s move to memory . This is temporary storage where you can read and write data needed
during the running of the smart contract. This data is erased once the functions that use the data
are done executing.
The memory location space is like a temporary notepad, and a new one is made available in the
smart contract each time a function is triggered. That notepad is thrown away after the execution
completes.
When understanding the difference between storage and memory, you can think of storage as a
kind of hard disk in the traditional computing world, in the sense that it has “persistent” storage of
data. But memory is closer to RAM in traditional computing.
The stack is the data area where most of the EVM’s computations are performed. The EVM
follows a stack based computation model and not a register based computation model, which
means each operation to be carried out needs to be stored and accessed using a stack data
structure.
The stack’s depth - that is the total number of items it can hold - is 1024, and each item in the stack
can be 256 bits (32 bytes) long. This is the same as the size of each key and value in the storage data
location.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 40/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
You can read more about how the EVM controls access to the stack data storage area here.
Forum Donate
Messages and transactions are how smart contract functions are invoked, and they contain a
variety of data necessary for the execution of those functions. This message data is stored in a
read-only section of the memory called calldata , which holds things like the function name and
parameters.
This is relevant for externally callable functions, as internal and private functions don’t use calldata.
Only “incoming” function execution data and function parameters are stored in this location.
Remember, calldata is memory except that calldata is read-only. You cannot write data to it.
And finally, code is not a data location but instead refers to the smart contract's compiled bytecode
that is deployed and stored permanently on the blockchain. This bytecode is stored in an immutable
ROM (Read Only Memory), that is loaded with the bytecode of the smart contract to be executed.
Remember how we discussed the difference between immutable and constant variables in Solidity?
Immutable values get assigned their value once (usually in the constructor) and constant variables
have their values hard-coded into the smart contract code. Because they’re hardcoded, constant
https://www.freecodecamp.org/news/learn-solidity-handbook/ 41/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
values are compiled literally and embedded directly into the smart contract’s bytecode, and stored
Forum Donate
in this code / ROM data location.
Learn to code — free 3,000-hour curriculum
Like calldata , code is also read-only - if you understood the previous paragraph you’ll understand
why!
When a language has strict typing, it means that the language strictly defines what each piece of
data’s type is, and a variable that has a type cannot be given another type.
But in JavaScript, which is not typed, b=a would totally work - this makes JavaScript “dynamically
typed”.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 42/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
Similarly, in statically typed languages you cannot pass an integer into a function that expects a
Learn
string. But in JavaScript we can passtoanything
code — free
to a3,000-hour curriculum
function and the program will still compile but it
may throw an error when you execute the program.
function add(a,b){
return a + b
}
add(1, "2") // “2” is a string, not an integer, so the output becomes the string “12” (!?)
As you can imagine, this can produce some pretty hard-to-find bugs. The code compiles and can
even execute without failing, though it produces unexpected results.
But a strongly typed language would never let you pass the string “2” because the function would
insist on the types that it accepts.
Let’s take a look at how this function would be written in a strongly typed language like Go.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 43/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
Trying to pass a string (even if it represents a number) will prevent the program from even
compiling (building). You will see an error like this:
https://www.freecodecamp.org/news/learn-solidity-handbook/ 44/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
Go build failed.
So types are important because data that seems the same to a human can be perceived very
differently by a computer. This can cause some pretty weird bugs, errors, program crashes and even
big security vulnerabilities.
Types also give developers the ability to create their own custom types, which can then be
programmed with custom properties (attributes) and operations (behaviors).
Type systems exist so that humans can reason about the data by asking the question “what is this
data’s type, and what should it be able to do?” and the machines can do exactly what is intended.
Here is another example of how data that looks the same to you and me may be interpreted in
hugely different ways by a processor. Take the sequence of binary digits (that is the digits can only
have a value of 0 or 1, which is the binary system that processors work with) 1100001010100011 .
https://www.freecodecamp.org/news/learn-solidity-handbook/ 45/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
To a human, using the decimal system that looks like a very large number - perhaps 11 gazillion
Forumor Donate
something.
Learn to code — free 3,000-hour curriculum
But to a computer that is binary, so it’s not 11 anything. The computer sees this as a sequence 16
bits (short for binary digits) and in binary this could mean the positive number (unsigned integer)
49,827 or the signed integer -15,709 or the UTF-8 representation of the British Pound symbol £ or
something different!
A sequence of bits can be interpreted by a computer to have very different meanings (source)
So all this explanation is to say that types are important, and that types can be “inbuilt” into a
language even if the language does not strictly enforce types, like JavaScript.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 46/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
JavaScript already has inbuilt types like numbers, strings, booleans, objects, and arrays. But as we
saw, JavaScript does not insistLearn to code
on types — free
being 3,000-hour
stuck curriculum
to the way a statically typed language like Go
does.
Now back to Solidity. Solidity is very much a statically typed language. When you declare a variable
you must also declare its type. Going further, Solidity will simply refuse to compile if you try to pass
a string into a function that expects an integer.
In fact Solidity is very strict with types. For example, different types of integers may also fail
compilation like the following example where the function add() expects an unsigned integer
(positive) and will only add to that number thus always returning a positive integer. But the return
type is specified as an int which means it could be positive or negative!
So even though the input and output are 256-bit integers, the fact that the function only receives
unsigned integers makes the compiler complain that the unsigned integer type is not implicitly
convertible to the signed integer type.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 47/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
That’s pretty strict! The developer can force the conversion (called type casting) by rewriting the
Forum Donate
For now, just remember that Solidity is statically typed, which means that the type of each variable
must be expressly specified when declaring them in the code. You can combine types to form more
complex, composite types. Next, we can discuss some of these inbuilt types.
In JavaScript, for example, primitives are data that is not a JS object and has no methods or
properties. There are 7 primitive data types in JavaScript: string , number , bigint , boolean ,
undefined , symbol , and null .
Solidity also has its own primitive data types. Interestingly, Solidity does not have “undefined” or
“null”. Instead, when you declare a variable and its type, but do not assign a value to it, Solidity will
assign a Default Value to that type. What exactly that default value is depends on the data type .
https://www.freecodecamp.org/news/learn-solidity-handbook/ 48/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Many of Solidity’s primitive data types are variations of the same ‘base’ type. For exampleForum
the int Donate
type itself has subtypes based on the number of binary digits that the integer type can hold.
Learn to code — free 3,000-hour curriculum
If that confuses you a bit, don’t worry - it isn’t easy if you’re not familiar with bits and bytes, and I’ll
cover integers a bit more shortly.
Before we explore Solidity types, there is another very important concept that you must
understand - it is the source of many bugs, and “unexpected gotchas” in programming languages.
This is the difference between a value type and reference type, and the resulting distinction
between data in programs being “passed by value” vs “passed by reference”. I’ll go into a quick
summary below but you may also find it useful to watch this short video to strengthen your mental
model before proceeding.
There is also a piece of data that is often called a “pointer”. This pointer points to the memory
location (an “address” in the computer’s RAM) where that variable and its value can be found. So
https://www.freecodecamp.org/news/learn-solidity-handbook/ 49/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
the pointer effectively contains a reference to where the data can be found in the computer’s
Forum Donate
memory.
Learn to code — free 3,000-hour curriculum
So when you pass data around in a program (for example when you assign a value to a new variable
name, or when you pass inputs (parameters) into a function or method, the language’s compiler can
achieve this in two ways. It can pass a pointer to the data’s location in the computer’s memory, or it
can make a copy of the data itself, and pass the actual value.
The first approach is “pass by reference”. The second approach is “pass by value”.
Solidity’s data type primitives fall into two buckets - they’re either value types, or they’re reference
types.
In other words, in Solidity, when you pass data around, the type of the data will decide whether
you’re passing copies of the value or a reference to the value’s location in the computer’s memory.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 50/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
https://www.freecodecamp.org/news/learn-solidity-handbook/ 51/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
In Solidity’s “value types”, integers are of two categories - uint is unsigned (positive integers
Forumonly, Donate
so they have no plus or minus signs) and int is signed (could be positive or negative, and if you
Learn to code — free 3,000-hour curriculum
wrote them down, they’d have a plus or minus sign).
Integer types can also specify how many bits long they are - or how many bits are used to represent
the integer .
An uint8 is an integer represented by 8 binary digits (bits) and can store up to 256 different values
(2^8=256). Since uint is for unsigned (positive) integers, this means it can store values from 0 to
255 (not including 1 to 256).
However when you have signed integers, like an int8 , then one of the bits is used up to represent
whether it's a positive or negative number. That means we have only 7 bits left, and so we can only
represent up to 2^7 (128) different values, including 0. So an int8 can represent anything from
-127 to +127.
By extension, an int256 is 256 bits long and can store +/- (2^255) values.
The bit lengths are multiples of 8 (because 8 bits makes a byte) so you can have int8 , int16 ,
int24 etc all the way to 256 (32 bytes).
Addresses refer to the Ethereum account types - either a smart contract account or an externally
owned account (aka “EOA”. Your Metamask wallet represents an EOA). So an address is also a type
https://www.freecodecamp.org/news/learn-solidity-handbook/ 52/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Booleans represent either true or false values. Finally, we have fixed size byte arrays like bytes1 ,
bytes2 … bytes32 . These are arrays of fixed length that contain bytes. All these types of values
are copied when they’re passed around in the code.
For “reference types”, we have arrays, which can have a fixed size specified when they’re declared,
or dynamically sized arrays, which start off with a fixed size, but can be “resized” as the number of
data elements in the array grows.
Bytes are a low-level data type that refer to the data that is encoded into binary format. All data is
eventually reduced to binary form by the compiler so that the EVM (or, in traditional computing,
the processor) can work with it.
Storing and working with bytes is often faster and more efficient compared to other data types that
are more human readable.
You may be wondering why I’ve not referred to strings in either types of data in the picture above.
That’s because in Solidity, strings are actually dynamically-sized arrays, and the arrays store a
https://www.freecodecamp.org/news/learn-solidity-handbook/ 53/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
sequence of bytes (just binary numbers) that are encoded in the UTF-8 encoding format. Forum Donate
It is often more efficient to store a string as a bytes type in a smart contract, as converting
between strings and bytes is quite easy. It is therefore useful to store strings as bytes but
return them in functions as strings. You can see an example below:
// SPDX-License-Identifier: MIT
contract StringyBytes {
// Returns 0x5a7562696e when passed the string input “Zubin”
function stringIntoBytes(string memory input) public pure returns (bytes memory ){
return bytes(input);
}
https://www.freecodecamp.org/news/learn-solidity-handbook/ 54/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
Other than Solidity strings, the bytes data type is a dynamically sized byte array. Also, unlike its
Learn
fixed-size byte array cousin, it's to code —type.
a reference free 3,000-hour
The bytes curriculum
type in Solidity is a shorthand for
“array of bytes” and can be written in the program as bytes or byte[] .
The underlying gory details of strings and byte arrays are not too relevant for this handbook. The
important point for now is that some data types are passed by reference and others are passed by
copying their values.
Suffice it to say that Solidity strings and bytes without a size specified are reference types because
they’re both dynamically sized arrays.
Finally, among Solidity’s primitives, we have structs and mappings . Sometimes these are referred
to as “composite” data types because they’re composed from other primitives.
A struct will define a piece of data as having one or more properties or attributes, and specifying
each property’s data type and name. Structs give you the ability to define your own custom type so
that you can organize and collect pieces of data into one larger data type.
For example you could have struct that defines a Person as follows:
https://www.freecodecamp.org/news/learn-solidity-handbook/ 55/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
struct Job {
string employer;
string department;
boolean isRemote;
}
// Or in a key-value style
https://www.freecodecamp.org/news/learn-solidity-handbook/ 56/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Mappings are similar to hashtables, dictionaries or JavaScript objects and maps, but with a little
less functionality.
A mapping is also a key-value pair, and there are restrictions on the kinds of data types you can
have as keys, which you can read about here. The data types associated with a mapping’s keys can
be any of primitives, structs, and even other mappings.
Here is how mappings are declared, initialized, written to and read from - the below example is
from the Chainlink Link Token Smart Contract source code.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 57/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
https://www.freecodecamp.org/news/learn-solidity-handbook/ 58/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
If you try to access a value using a key that doesn’t exist in the mapping, it will return the default
Forum Donate
value of the type that is stored in the mapping.
Learn to code — free 3,000-hour curriculum
In the above example, the type of all values in the balances mapping is uint256 , which has a
default value of 0 . So if we called balanceOf() and passed in an address that does not have any
LINK tokens issued to it, we’d get a value of 0 back.
This is reasonable in this example, but it can be a bit tricky when we want to find out whether or not
a key exists in a mapping.
Currently there is no way to enumerate what keys exist in a mapping (that is, there is nothing
equivalent to JavaScript’s Object.keys() method). Retrieving using a key will only return the
default value associated with the data type, which does not clearly tell us whether or not the key
actually exists.
There is an interesting “gotcha” with mappings. Unlike other languages where you can pass key-
value data structures as an argument to function, Solidity does not support passing mappings as
arguments to functions except where the functions visibility is marked as internal . So you could
not write an externally or publicly callable function that would accept key-value pairs as an
argument.
Solidity comes with two flavors of arrays, so it is useful to understand the different ways in which
Forum Donate
they can be declared and initialized.
Learn to code — free 3,000-hour curriculum
The two main types of arrays in Solidity are the fixed-size array and the dynamic-sized array.
To refresh your memory, fixed-size arrays are passed by value (copied when passed around in the
code) and dynamic-sized arrays are passed by reference (a pointer to the memory address is passed
around in the code).
They’re also different in their syntax and their capacity (size), which then dictates when we would
use one versus the other.
Here’s what a fixed-size array looks like when declared and initialized. It has a fixed capacity of 6
elements, and this cannot be changed once declared. The memory space for an array of 6 elements
is allocated and cannot change.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 60/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
A fixed-size array can also be declared by just declaring a variable and the size of the array and the
Learn to code — free 3,000-hour curriculum
type of its elements with the following syntax:
// datatype arrayName[arraySize];
string myStrings[10]; // string array of size 10.
myStrings[0] = “chain.link”;
Contrast that with a dynamically-sized array that is declared and initialized as follows. Its capacity
is unspecific and you can add elements using the push() method:
uint[] dynamicArray;
Forum Donate
// The delete keyword resets the value at the index to its default value
Learn to code
delete dynamicArray[1]; — freeelement
// second 3,000-hour
is nocurriculum
longer 2 but 0.
You can also declare and initialize the value of an array in the same line of code.
These arrays are available in storage. But what if you needed only temporary in-memory arrays
inside a function? In that case there are two rules: only fixed size arrays are allowed, and you must
use the new keyword.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 62/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
return arr;
Forum Donate
}
Learn to code — free 3,000-hour curriculum
Clearly, there are several ways to declare and initialize arrays. When you’re wanting to optimize for
gas and computations you want to carefully consider which type of arrays are required, what their
capacity is, and if they’re likely to grow without an upper bound.
This also influences and is influenced by the design of your code - whether you need arrays in
storage or whether you need them only in memory.
For example, if you’re writing in pure JavaScript, you may want to check that your function receives
integers and not strings. If it’s on the backend you may want to check that the POST request
contained the right authentication headers and secrets.
In Solidity, we can perform these sort of validation steps by declaring a function-like block of code
called a modifier.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 63/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
A modifier is a snippet of code that can run automatically before or after you run the mainForum
function Donate
(that is, the function that has the modifier applied to it).
Learn to code — free 3,000-hour curriculum
Modifiers can be inherited from parent contracts too. It is generally used as a way to avoid
repeating your code, by extracting common functionality and putting it in a modifier that can be
reused throughout the codebase.
A modifier looks a lot like a function. The key thing to observe about a modifier is where the _
(underscore) shows up. That underscore is like a “placeholder” to indicate when the main function
will run. It reads as if we inserted the main function where the underscore is currently.
So in the modifier snippet below, we run the conditional check to make sure that the message
sender is the owner of the contract, and then we run the rest of the function that called this
modifier. Note that a single modifier can be used by any number of functions.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 64/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
How function modifiers are written, and the role of the underscore symbol
In this example, the require() statement runs before the underscore ( changeOwner() ) and that’s
the correct way to ensure that only the current owner can change who owns the contract.
If you switched the modifier’s lines and the require() statement came second, then the code in
changeOwner() would run first. Only after that would the require() statement run, and that
would be a pretty unfortunate bug!
https://www.freecodecamp.org/news/learn-solidity-handbook/ 65/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Modifiers can take inputs too - you’d just pass the type and name of the input into a modifier.
Forum Donate
Modifiers are a great way to package up snippets of logic that can be reused in various smart
contracts that together power your dApp. Reusing logic makes your code easier to read, maintain
and reason about – hence the principle DRY (Don’t Repeat Yourself).
https://www.freecodecamp.org/news/learn-solidity-handbook/ 66/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
The EVM will revert all changes to the blockchain’s state when there is an error In other words,
Forum Donate
when an exception is thrown, and it’s not caught in a try-catch block, the exception will “bubble up”
Learn to code — free 3,000-hour curriculum
the stack of methods that were called, and be returned to the user. All changes made to the
blockchain state in the current call (and its sub-calls) get reversed.
There are some exceptions, in low-level functions like delegatecall , send , call , and so on, where
an error will return the boolean false back to the caller, rather than bubble up an error.
As a developer there are three approaches you can take to handle and throw errors. You can use
require() , assert() or revert() .
A require statement evaluates a boolean condition you specify, and if false, it will throw an error
with no data, or with a string that you provide:
We use require() to validate inputs, validate return values, and check other conditions before we
proceed with our code logic.
In this example, if the function’s caller does not send at least 1 ether, the function will revert and
throw an error with a string message: “you must pay me at least 1 ether!”.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 67/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum
The error string that you want returned is the second argument to the require() function, but it is Donate
The good thing about a require() is that it will return gas that has not been used, but gas that was
used before the require() statement will be lost. That’s why we use require() as early as
possible.
An assert() function is quite similar to require() except that it throws an error with type
Panic(uint256) rather than Error(string) .
contract ThrowMe {
function assertExample() public pure {
assert(address(this).balance == 0);
// Do something.
}
}
An assert is also used in slightly different situations– where a different type of guarding is required.
Most often you use an assert to check an “invariant” piece of data. In software development, an
invariant is one or more pieces of data whose value never changes while the program is executing.
In the above code example, the contract is a tiny contract, and is not designed to receive or store
any ether. Its design is meant to ensure that it always has a contract balance of zero, which is the
https://www.freecodecamp.org/news/learn-solidity-handbook/ 68/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Just as require() does, an assert() will also revert all changes. Prior to Solidity’s v0.8, assert()
used to use up all remaining gas, which was different from require() .
A third approach is to use a revert() call. This is generally used in the same situation as a
require() but where your conditional logic is much more complex.
In addition, you can throw custom-defined errors when using revert() . Using custom errors can
often be cheaper in terms of gas used, and are generally more informative from a code and error
readability point of view.
Note how I improve the readability and traceability of my error by prefixing my custom error’s
name with the Contract name, so we know which contract threw the error.
contract ThrowMe {
// custom error
error ThrowMe_BadInput(string errorMsg, uint inputNum);
https://www.freecodecamp.org/news/learn-solidity-handbook/ 69/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
function revertExample(uint input) public pure {
if (input < 1000 ) Learn
{ to code — free 3,000-hour curriculum
revert ThrowMe_BadInput("Number must be an even number greater than 999", input);
}
if (input < 0) {
revert("Negative numbers not allowed");
}
}
}
In the above example, we use revert once with a custom error that takes two specific arguments,
and then we use revert another time with only a string error data. In either case, the blockchain
state is reverted and unused gas will be returned to the caller.
Inheritance in Solidity
Inheritance is a powerful concept in Object Oriented Programming (OOP). We won't go into the
details of what OOP is here. But the best way to reason about inheritance in programming is to
think of it as a way by which pieces of code “inherit” data and functions from other pieces of code by
importing and embedding them.
Inheritance in Solidity also allows a developer to access, use and modify the properties (data) and
functions (behaviour) of contracts that are inherited from.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 70/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum
The contract that receives this inherited material is called the derived contract, child contract or Donate
Inheritance facilitates convenient and extensive code reuse – imagine a chain of application code
that inherits from other code, and those in turn inherit from others and so on. Rather than typing
out the entire hierarchy of inheritance, we can just use a a few key words to “extend” the functions
and data captured by all the application code in the inheritance chain. That way child contract gets
the benefit of all parent contracts in its hierarchy, like genes that get inherited down each
generation.
Unlike some programming languages like Java, Solidity allows for multiple inheritance. Multiple
inheritance refers to the ability of a derived contract to inherit data and methods from more than
one parent contract. In other words, one child contract can have multiple parents.
You can spot a child contract and identify its parent contract by looking for the is keyword.
contract A {
string public constant A_NAME = "A";
https://www.freecodecamp.org/news/learn-solidity-handbook/ 71/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
contract B is A {
stringLearn to code
public — free
constant 3,000-hour
B_NAME = "B"; curriculum
}
If you were to deploy only Contract B using the in-browser Remix IDE you’d note that Contract B
has access to the getName() method even though it was not ever written as part of Contract B.
When you call that function, it returns “A” , which is data that is implemented in Contract A, not
contract B. Contract B has access to both storage variables A_NAME and B_NAME , and all functions in
Contract A.
This is how inheritance works. This is how Contract B reuses code already written in Contract A,
which could have been written by someone else.
Solidity lets developers change how a function in the parent contract is implemented in the derived
contract. Modifying or replacing the functionality of inherited code is referred to as “overriding”. To
understand it, let’s explore what happens when Contract B tries to implement its own getName()
function.
Modify the code by adding a getName() to Contract B. Make sure the function name and signature
is identical to what is in Contract A. A child contract’s implementation of logic in the getName()
function can be totally different from how it’s done in the parent contract, as long as the function
name and its signature are identical.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 72/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
contract B is A {
string public constant B_NAME = "B";
1. In Contract A, it will indicate that you are “trying to override non-virtual function” and prompt
you by asking if you forgot to add the virtual keyword.
2. In Contract B, it will complain that the getName() function is missing the override
specifier.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 73/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
This means that your new getName in Contract B is attempting to override a function by the same
Forum Donate
name in the parent contract, but the parent’s function is not marked as virtual – which means
Learn to code — free 3,000-hour curriculum
that it cannot be overridden.
You could change Contract A’s function and add virtual as follows:
Adding the keyword virtual does not change how the function operates in Contract A. And it
does not require that inheriting contracts must re-implement or override it. It simply means that
this function may be overridden by any derived contracts if the developer chooses.
Adding virtual fixes the compiler’s complaint for Contract A, but not for Contract B. This is
because getName in Contract B needs to also add the override keyword as follows:
We also add the pure keyword for Contract B’s getName() as this function does not change the
state of the blockchain, and reads from a constant (constants, you’ll remember, are hardcoded into
https://www.freecodecamp.org/news/learn-solidity-handbook/ 74/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
the bytecode at compile time and are not in the storage data location). Forum Donate
But what happens with functions that have identical names but different arguments? When this
happens it’s not an override, but an overload. And there is no conflict because the methods have
different arguments, and so there is enough information in their signatures to show the compiler
that they're different.
For example, in contract B we could have another getName() function that takes an argument,
which effectively gives the function a different “signature” compared to the parent Contract A’s
getName() implementation. Overloaded functions do not need any special keywords:
Don’t worry about the abi.encodepacked() method call. I’ll explain that later when we talk about
encoding and decoding. For now just understand that encodepacked() encodes the strings into
bytes and then concatenates them, and returns a bytes array.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 75/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
We discussed the relationship between Solidity strings and bytes in a previous section of Forum
this Donate
handbook (under Typing).
Learn to code — free 3,000-hour curriculum
Also, since you’ve already learned about function modifiers, this is a good place to add that
modifiers are also inheritable. Here’s how you’d do it:
contract A {
modifier X virtual {
// … some logic
}
}
contract B is A {
modifier X override {
// … logic that replaces X in Contract A
}
}
You might wonder which version of a function will be called if a function by the same name and
signature exists in a chain of inheritance.
For example, let's say there is a chain of inherited contracts like A → B → C → D → E and all of them
have a getName() that overrides a getName() in the previous parent contract.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 76/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Which getName() gets called? The answer is the last one – the “most derived” implementation
Forumin Donate
the contract hierarchy.
Learn to code — free 3,000-hour curriculum
State variables in child contracts cannot have the same name and type as their parent contracts.
For example, Contract B below will not compile because its state variable “shadows” that of the
parent Contract A. But note how Contract C correctly handles this:
contract A {
string public author = "Zubin";
https://www.freecodecamp.org/news/learn-solidity-handbook/ 77/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
}
Forum Donate
}
Learn to code — free 3,000-hour curriculum
It’s important to note that by passing a new value to the variable author in Contract C’s
constructor, we are effectively overriding the value in Contract A. And then calling the inherited
method C.getAuthor() will return ‘Hemingway’ and not ‘Zubin’!
It is also worth noting that when a contract inherits from one or more parent contract, only one
single (combined) contract is created on the blockchain. The compiler effectively compiles all the
other contracts and their parent contracts and so on up the entire hierarchy all into a single
compiled contract (which is referred to as a “flattened” contract).
If that smart contract is a parent contract, then its derived contracts must also pass arguments to
instantiate the parent contracts.
There are two ways to pass arguments to parent contracts - either in the statement that lists the
parent contracts, or directly in the constructor functions for each parent contract. You can see both
approaches below:
https://www.freecodecamp.org/news/learn-solidity-handbook/ 78/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
https://www.freecodecamp.org/news/learn-solidity-handbook/ 79/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
https://www.freecodecamp.org/news/learn-solidity-handbook/ 80/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
Learn to code
In Method 2 in the ChildTwo contract, — free
you’ll note3,000-hour curriculumpassed to the parent
that the arguments
contracts are first supplied to the child contract and then just passed up the inheritance chain.
This is not necessary, but is a very common pattern. The key point is that where parent contract
constructor functions expect data to be passed to them, we need to provide them when we
instantiate the child contract.
As we saw in our discussion on typed data, JavaScript can sometimes do strange things to data
because it is dynamically typed. But that’s also why it’s useful to introduce the concept of type
casting and type conversions generally.
var a = "1"
var b = a + 9 // we get the string '19'!!
typeof a // string
typeof b // string
https://www.freecodecamp.org/news/learn-solidity-handbook/ 81/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
There are two ways of converting the variable a into an integer. The first, called type casting, is
Learn to code — free 3,000-hour curriculum
done explicitly by the programmer and usually involves a constructor-like operator that uses () .
Now let's reset a to a string and do an implicit conversion, also known as a type conversion. This is
implicitly done by the compiler when the program is executed.
a = '1'
var b = a * 9 // Unlike addition, this doesn't concatenate but implicitly converts 'a' to a
number!
b // number 9, as expected!
typeof b // number
typeof a // still a string…
In Solidity, type casting (explicit conversion) is permissible between some types, and would look like
this:
https://www.freecodecamp.org/news/learn-solidity-handbook/ 82/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
uint256 a = 2022;
Learn to code — free 3,000-hour curriculum
bytes32 b = bytes32(a);
In this example we converted an integer with a size of 256 bits (since 8 bits makes 1 byte, this is 32
bytes) into a bytes array of size 32.
Since both the integer value of 2022 and the bytes value are of length 32 bytes, there was no “loss”
of information in the conversion.
But what would happen if you tried to convert 256 bits into 8 bits (1 byte)? Try running the
following in your browser-based Remix IDE:
contract Conversions {
function explicit256To8() public pure returns (uint8) {
uint256 a = 2022;
uint8 b = uint8(a);
return b; // 230.
}
}
https://www.freecodecamp.org/news/learn-solidity-handbook/ 83/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
Why does the integer 2022 get converted to 230? That’s clearly an undesirable and unexpected
Learn to code — free 3,000-hour curriculum
change in the value. A bug, right?
The reason is that an unsigned integer of size 256 bits will hold 256 binary digits (either 0 or 1). So
a holds the integer value ‘2022’ and that value, in bits, will have 256 digits, of which most will be 0,
except for the last 11 digits which will be... (see for yourself by converting 2022 from decimal
system to binary here).
The value of b on the other hand will have only 8 bits or digits, being 11100110. This binary
number, when converted to decimal (you can use the same converter - just fill in the other box!) is
230. Not 2022.
Oopsie.
So what happened? When we dropped the integer’s size from 256 bits to 8 bits we ended up
shaving the first three digits of data (11111100110) which totally changed the value in binary!
So when you’re explicitly casting, the compiler will let you do it in some cases. But you could lose
data, and the compiler will assume you know what you’re doing because you’re explicitly asking to
do it. This can be the source of many bugs, so make sure you test your code properly for expected
results and be careful when explicitly casting data to smaller sizes.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 84/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
Casting up to larger sizes does not result in data lost. Since 2022 needs only 11 bits to be
Learn to code — free 3,000-hour curriculum
represented, you could declare the variable a as type uint16 and then up-cast it to a variable b
of type uint256 without data loss.
The other kind of casting that is problematic is when you’re casting from unsigned integers to
signed integers. Play around with the following example:
contract Conversions {
function unsignedToSigned() public pure returns (int16, uint16) {
int16 a = -2022;
uint16 b = uint16(a);
// uint256 c = uint256(a); // Compiler will complain
return (a, b); // b is 63514
}
}
Note that a , being a signed integer of size 16 bits holds -2022 as a (negative integer) value. If we
explicitly type cast it to an unsigned integer (only positive) values, the compiler will let us do it.
But if you run the code, you’ll see that b is not -2022 but 63,514! Because uint cannot hold
information regarding the minus sign, it has lost that data, and the resulting binary gets converted
to a massive decimal (base 10) number - clearly undesirable, and a bug.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 85/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
If you go further, and un-comment the line that assigns the value of c , you’ll see the compiler
Forum Donate
complain with 'Explicit type conversion not allowed from "int16" to "uint256"'. Even though we are up-
Learn to code — free 3,000-hour curriculum
casting to a larger number of bits in uint256 , because c is an unsigned integer, it cannot hold
minus sign information.
So when explicitly casting, be sure to think through what the value will evaluate to after you’ve
forced the compiler to change the type of the data. It is the source of many bugs and code errors.
There is more to Solidity type conversions and type casting and you can go deep into some of the
nitty gritty in this article.
What’s going on here? 468 divided by 5 is 93.6, which seems a weird error, but that’s basically the
compiler saying it cannot handle floating point numbers.
Follow the suggestions of the error, and declare the variable’s type to be fixed or ufixed16x1 .
https://www.freecodecamp.org/news/learn-solidity-handbook/ 86/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
fixed floating = 93.6;
Learn to code — free 3,000-hour curriculum
So in Solidity, we get around this by converting the floating point number to a whole number (no
decimal points) by multiplying by 10 raised to the exponent of the number of decimal places to the
right of the decimal point.
In this case we multiply 93.6 by 10 to get 936 and we have to keep track of our factor (10) in a
variable somewhere. If the number was 93.2355 we would multiply it by 10 to the power 4 as we
need to shift the decimal 4 places to the right to make the number whole.
When working with ERC tokens we will note that the decimal places are often 10, 12, or 18.
For example, 1 Ether is 1*(10^18) wei, which is 1 followed by 18 zeros. If we wanted that expressed
with a floating point, we would need to divide 1000000000000000000 by 10^18 (which will give
us 1), but if it was 1500000000000000000 wei, then dividing by 10^18 will throw a compiler error
in Solidity, because it cannot handle the return value of 1.5.
In scientific notation, 10^18 is also expressed as 1e18, where 1e represents 10 and the number
after that represents the exponent that 1e is raised to.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 87/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
So the following code will produce a compiler error: “Return argument type rational_const 3Forum
/ 2 is not Donate
implicitly convertible to expected type…int256”:
Learn to code — free 3,000-hour curriculum
The result of the above division operation is 1.5, but that has a decimal point which Solidity does
not currently support. Thus Solidity smart contracts return very big numbers, often up to 18
decimal places, which is more than JavaScript can handle. So you'll need to handle that
appropriately in your front end using JavaScript libraries like Ethersjs that implement helper
functions for the BigNumber type.
While these can take some effort to wrap your head around, they’re quite fundamental to working
with cryptographic technology, and Ethereum in particular. They’re not complex in principle, but
can be a bit hard to grasp at first.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 88/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Let’s start with hashing. Using cryptographic math, you can convert any data into a (very large)
Forum Donate
unique integer. This operation is called hashing. There are some key properties to hashing
Learn to code — free 3,000-hour curriculum
algorithms:
1. They’re deterministic - identical inputs will always produce an identical output, each time
and every time. But the chance of producing the same output using different inputs is
extremely unlikely.
2. It is not possible (or computationally infeasible) to reverse engineer the input if you only
have the output. It is a one-way process.
3. The output’s size (length) is fixed - the algorithm will produce fixed-size outputs for all
inputs, regardless of the input size. In other words the outputs of a hashing algorithm will
always have a fixed number of bits, depending on the algorithm.
There are many algorithms that are industry-standard for hashing but you’ll likely see SHA256 and
Keccak256 most commonly. These are very similar. And the 256 refers to the size - the number of
bits in the hash that is produced.
For example, go to this site and copy and paste “FreeCodeCamp” into the text input. Using the
Keccak256 algorithm, the output will (always) be
796457686bfec5f60e84447d256aba53edb09fb2015bea86eb27f76e9102b67a .
This is a 64 character hexadecimal string, and since each character in a hex string represents 4 bits,
https://www.freecodecamp.org/news/learn-solidity-handbook/ 89/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
this hexadecimal string is 256 bits (32 bytes long). Forum Donate
Now paste back the “FreeCodeCamp” and change any character at all. You could make the “F”
lowercase. Or add a space. For each individual change you make, the hash hex string output
changes a lot, but the size is constant.
This is an important benefit from hashing algorithms. The slightest change changes the hash
substantially. Which means you can always test whether two things are identical (or have not been
tampered with at all) by comparing their hashes.
In Solidity, comparing hashes is much more efficient than comparing the primitive data types.
For example, comparing two strings is often done by comparing the hashes of their ABI-encoded
(bytes) form. A common helper function to compare two strings in Solidity would look like this:
https://www.freecodecamp.org/news/learn-solidity-handbook/ 90/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
return (keccak256(abi.encodePacked((str1))) ==
Forum Donate
keccak256(abi.encodePacked((str2))));
} Learn to code — free 3,000-hour curriculum
We’ll address what ABI encoding is in a moment, but note how the result of encodePacked() is a
bytes array which is then hashed using the keccak256 algorithm (this is the native hashing
algorithm used by Solidity). The hashed outputs (256 bit integers) are compared for equality.
Now let's turn to ABI encoding. First, we recall that ABI (Application Binary Interface) is the
interface that specifies how to interact with a deployed smart contract. ABI-encoding is the process
of converting a given element from the ABI into bytes so that the EVM can process it.
The EVM runs computation on bits and bytes. So encoding is the process of converting structured
input data into bytes so that a computer can operate on it. Decoding is the reverse process of
converting bytes back into structured data. Sometimes, encoding is also referred to as “serializing”.
You can read more about the solidity built-in methods provided with the global variable abi that
do different types of encoding and decoding here. Methods that encode data convert them to byte
arrays ( bytes data type). In reverse, methods that decode their inputs expect the bytes data type
as input and then convert that into the data types that were encoded.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 91/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
https://www.freecodecamp.org/news/learn-solidity-handbook/ 92/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
I ran the above in Remix, and used the following inputs for encode() : 1981, Forum Donate
0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC, [1,2,3,4].
Learn to code — free 3,000-hour curriculum
And the bytes I got returned were represented in hexadecimal form as the following:
0x00000000000000000000000000000000000000000000000000000000000007bd0000000000000000000000003c44c
I fed this as my input into the decode() function and got my original three arguments back.
Thus, the purpose of encoding is to convert data into the bytes data type that the EVM needs to
process data. And decoding brings it back into the human readable structured data that we
developers can work with.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 93/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Calling from your wallet via Remix is an example of the latter, as is using Metamask. You can also
Forum Donate
interact with smart contracts programmatically via libraries like EthersJS and Web3JS, the Hardhat
Learn to code — free 3,000-hour curriculum
and Truffle toolchains, and so on.
For the purposes of this Solidity handbook, we will use Solidity to interact with another contract.
There are two ways for a smart contract to call other smart contracts. The first way calls the target
contract directly, by using interfaces (which we discussed previously). Or, if the Target contract is
imported into the scope of the calling contract, it directly calls it.
contract Target {
int256 public count;
interface ITarget {
function decrement() external;
}
contract TargetCaller {
function callDecrementInterface(address _target) public {
https://www.freecodecamp.org/news/learn-solidity-handbook/ 94/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
In Remix you can deploy Target first, and call count() to see that the default value for the count
variable is 0 , as expected. This value will be decremented by 1 if you call the decrement() method.
Then you can deploy TargetCaller and there are two methods you can call, both of which will
decrement the value of count in Target .
Note that each of these two methods access the Target contract using a slightly different syntax.
When interacting by using the ITarget interface, the first method takes in Target ’s address
whereas the second method treats Target as a custom type.
This second approach is only possible when the Target contract is declared in, or imported into,
the same file as the TargetCaller . Most often, you will interact with smart contracts deployed by
third parties, for which they publish ABI interfaces.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 95/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Each time you call these methods, the value of count in Target will decrease by 1 . This Forum
is a very Donate
common way to interact with other smart contracts.
Learn to code — free 3,000-hour curriculum
The second way to do it is by using the “low level” call syntax that Solidity provides. You use this
when you also want to send some ether (value) to the target contract. Will talk about sending value
in the next section, but for now just replace the code in Remix with the following:
contract Target {
int256 public count;
interface ITarget {
function decrement(int num) external payable;
}
contract TargetCaller {
function callDecrementLowLevel(address _target) public {
ITarget target = ITarget(_target);
target.decrement{value:0}(5);
https://www.freecodecamp.org/news/learn-solidity-handbook/ 96/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
You’ll note that decrement() now takes an argument, and the interface and the Target contract
are updated with this new input data.
Next note that TargetCaller implements a new function that calls decrement() with a new
syntax, explained below.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 97/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum to
In the next section we will see examples of these low level ways of calling a target smart contract Donate
send Ether to it. Learn to code — free 3,000-hour curriculum
But what happens when you call a contract and it doesn’t actually have the function you tried to
call?
This can happen maliciously to exploit how Solidity works on the EVM. Or, more commonly, it can
happen accidentally. This occurs when, for example, there is an error in the Interface and the
compiler cannot match the function and parameters you sent with any that are actually contained
in the contract. What happens then?
For these situations, many contracts employ a special function called the fallback function. The
function looks like a normal function but it doesn’t need the function keyword. If you want it to
also handle situations where your contract is sent some ether, you must also mark it payable . But
this is not the recommended way to enable your contract to receive payments.
Let’s take a look by repurposing our previous Target , ITarget and TargetCaller and adding a
fallback function as follows:
contract Target {
int256 public count;
https://www.freecodecamp.org/news/learn-solidity-handbook/ 98/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
}
Forum Donate
Learn to code
fallback() — freepayable
external 3,000-hour
{ curriculum
count++;
}
}
interface ITarget {
function decrement(int num) external payable;
function nonExistentFunction() external;
}
contract TargetCaller {
function callFallback(address _target) public {
ITarget target = ITarget(_target);
target.nonExistentFunction();
}
}
Once we deploy a fresh instance of Target , we can call count() and see that it is set to the default
value of zero.
Next we can deploy TargetCaller and call the callFallback() method which internally calls
nonExistentFunction() .
It’s worth noting that the interface says that nonExistentFunction() is available but the actual
Target contract does not have any such function. This is why the Target fallback function gets
https://www.freecodecamp.org/news/learn-solidity-handbook/ 99/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
transfer will throw an exception when it fails, and send and call will return a boolean that you
must check before proceeding. Of these three, transfer and send are no longer recommended
for security reasons, though you can still use them and they'll work.
Forcibly when the calling contract calls selfdestruct and forces a target contract to accept
all its remaining ether. The calling contract is then deleted from the blockchain. This is a
separate topic and is often used maliciously by exploiters.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 100/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
It is generally recommended that you use a receive() function if you want your smart contract
Forum to Donate
receive Ether. You can get away by just making your fallback function payable , but recommended
Learn to code — free 3,000-hour curriculum
practice is to use a receive() function instead.
If you rely only on the fallback function, your compiler will grumble at you with the following
message: “Warning: This contract has a payable fallback function, but no receive ether function. Consider
adding a receive ether function.”
If you have both receive and fallback , you may legitimately wonder how Solidity decides which
function to receive Ether with. This design decision also tells you what these functions are designed
to do.
Receive is meant to receive ether. And fallback is meant to handle situations where the contract has
been called but, as we discussed in the previous section, there is no matching method in the
contract that can handle the call.
Solidity matches the method that was intended to be called by checking the msg.data field in the
transaction sent by the caller. If that field is a non-empty value, and that value does not match any
other function declared in the called contract, then the fallback method is triggered.
If msg.data is empty, then it will check if there is a receive function that has been implemented. If
so, it will use that to accept the Ether. If no receive exists, it will default to the fallback function. The
fallback is therefore the...fallback (default) method when nothing else makes sense.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 101/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
The receive function is the better way to enable your contract to receive Ether. You canForum
use the Donate
fallback function for any scenario where your smart contract is called but there is nothing to
Learn to code — free 3,000-hour curriculum
“handle” that call.
Here is a super handy logic tree that shows what receive and fallback are intended to handle.
send Ether
|
msg.data is empty?
/ \
yes no
/ \
receive() exists? fallback()
/ \
yes no
/ \
receive() fallback()
Going back to our example where we explored the fallback function, we can add a receive
function to Target as follows:
https://www.freecodecamp.org/news/learn-solidity-handbook/ 102/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
interface ITarget {
function decrement(int num) external payable;
function nonExistentFunction() external;
}
contract TargetCaller {
function callFallback(address _target) public {
ITarget target = ITarget(_target);
target.nonExistentFunction();
}
}
https://www.freecodecamp.org/news/learn-solidity-handbook/ 103/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
We already saw how callFallback will change the count value in Target . But if we deploy a fresh
Forum Donate
instance of Target , we can now send it 10 wei, as shown below, because it now has a payable
Learn to code — free 3,000-hour curriculum
receive function. Prior to sending 10 wei (or any other amount) Target has a balance of zero, as
shown below.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 104/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
https://www.freecodecamp.org/news/learn-solidity-handbook/ 105/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
https://www.freecodecamp.org/news/learn-solidity-handbook/ 106/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
Hitting the Transact button with empty calldata ( msg.data ) will change the balance as shown in the
image below. We can check count to see that it is incremented by 5, which is the logic in the
receive function.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 107/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
https://www.freecodecamp.org/news/learn-solidity-handbook/ 108/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
Sending Wei to the
Learn toTarget
code Contract and observing
— free 3,000-hour the updated balance
curriculum
If we call callFallback and give it the address of the new Target instance, we will note that it
increments only by 1 . If we include some wei, that will increase the balance of Target as well.
So any transfer of Ether to a smart contract would require the receiving smart contract to have
payable functions that can receive it. At the very minimum, the receiving smart contract would
need a payable fallback function, though a payable receive function is the better approach for
receiving Ether payments.
Solidity Libraries
In any programming language, a library refers to a collection of helper and utility functions that are
designed to be reusable across multiple code bases. These functions solve specific, recurring
programming problems.
In Solidity, libraries serve the same purpose, but have some special attributes.
First, they are stateless - that is, they don't store data (other than constants because these do not
change the blockchain’s state). They also can't receive value (which means they cannot have
https://www.freecodecamp.org/news/learn-solidity-handbook/ 109/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
All functions declared in a library must not be abstract - that is, they must all have concrete
implementations.
Since Solidity libraries are stateless, none of the methods in them can modify the blockchain’s state.
This means all methods inside libraries are pure or view functions.
Another interesting attribute of Solidity libraries is that they don't need to be imported into your
smart contract. They can be deployed as standalone contracts and then called via their interface in
all consuming smart contracts - just as you would an API service in the traditional engineering
world.
However, this is true only where the library contains public or external methods. Then that library
can be deployed as a standalone contract with its own Ethereum address, and becomes callable to
all consuming smart contracts.
If the libraries contain only internal methods, then the EVM simply “embeds” the library code into
the smart contract that uses the library (because internal functions cannot be accessed from other
smart contracts).
https://www.freecodecamp.org/news/learn-solidity-handbook/ 110/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Libraries in Solidity have advantages that go beyond code reuse. Deploying a library one time
Forumon Donate
the blockchain can save on future gas costs by avoiding repeated deployment or importing of the
Learn to code — free 3,000-hour curriculum
library's code.
Let's look at a simple library and then dissect the code to understand how to use the library’s code.
library WeirdMath {
int private constant factor = 100;
This library has two methods that operate on the int data type. The first argument is called self
for reasons that will become clear shortly. One method takes a number and then multiplies it by a
constant value that is stored in the library’s code. The second method takes in two numbers and
adds them.
Now let’s see how we can use this in a consuming smart contract.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 111/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
contract StrangeMath {
// Method 1 - using Library name with dot notation
function multiplyWithFactor(int num) public pure returns (int) {
return WeirdMath.applyFactor(num);
}
The first thing to note is that there are two ways of using the WeirdMath library.
1. Invoking the library’s name followed by the function you want to call, or
2. calling the function directly on the data type you want the function to operate on. This data
type must be identical to the type of the self parameter in the library’s function.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 112/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
The first approach is demonstrated bycode
Learn to method 1 in
— free the code curriculum
3,000-hour snippet where we invoke the library
with WeirdMath.add(num1, num2); .
The second approach uses the Solidity using keyword. The expression return num1.add(num2);
applies the WeirdMath library’s add function to the num1 variable. This is the same as passing it in
as self , which is the first argument to the add function.
Events cannot be consumed by other smart contracts. Instead, they are stored on the blockchain as
logs, and can be retrieved via the APIs that read from the blockchain.
This means that your application (most commonly your front end application) can “read” logs that
contain the event’s data from the blockchain. In this way your user interface can respond to events
on the blockchain.
This is how application user interfaces are updated to respond to on-chain events. Since these logs
on the blockchain can be queried, logs are a cheap form of storage, as discussed previously in the
discussion on storage areas.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 113/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Events emitted by a Smart Contract can be inspected using the relevant blockchain explorer,
Forum Donate
because everything on a public blockchain is publicly viewable. But if the smart contract’s bytecode
Learn to code — free 3,000-hour curriculum
has not been verified, the event data may not be human readable (it will be encoded). The events of
verified smart contracts will be human readable.
Nodes and other blockchain clients can listen for (subscribe to) specific events. At the heart of it,
this is how Chainlink Oracles work - decentralized oracle nodes listen to events from smart
contracts, and then respond accordingly. They can even pull data off from events, run complex and
resource-intensive computations off-chain, and then submit cryptographically verifiable
computation results back onto the blockchain.
Other network APIs and indexing services like subgraphs are made possible because of the ability
to query blockchain data via the events emitted by smart contracts.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
contract SimpleStorage {
uint256 favoriteNumber;
event storedNumber(
uint256 indexed oldNumber, // up to 3 indexed params allowed
uint256 indexed newNumber,
https://www.freecodecamp.org/news/learn-solidity-handbook/ 114/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
uint256 addedNumber,
Forum Donate
address sender
); Learn to code — free 3,000-hour curriculum
An event is first declared, and its arguments and their data types are specified. Any piece of data
that has the indexed keyword is indexed by the EVM so that queries on blockchain logs can use the
indexed param as a filter. This makes retrieving logs faster.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 115/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Most events are non-anonymous, meaning that they include data on the name and arguments
Forumof Donate
the Event.
Learn to code — free 3,000-hour curriculum
Non-anonymous events allow the developer to specify only 3 topics because the first topic is
reserved to specify the Event signature in ABI-encoded hexadecimal form. You can read more
about anonymous and non-anonymous topics here.
You can also explore Events on the relevant blockchain explorer (like etherscan.io).
You can approach this from one of two entry points. You can look at the contract’s address directly
and then go to the Events Tab (which will show you events emitted by that contract only). Or you
can go to a transaction hash and examine all the events emitted by all contracts that were touched
by that transaction.
For example, below is a screenshot of the Chainlink VRF Coordinator smart contract’s events on
Ethereum mainnet.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 116/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
The contract tab has a green tick mark which means the contract is verified, and so the Event name
and arguments are human readable. Take a moment to study this image as it contains a lot of
information! If you’d like to study it directly on etherscan, click here.
This Chainlink VRF Coordinator contract responds to requests for cryptographically verifiable
random numbers, and supplies the asking smart contract with random numbers (referred to as
https://www.freecodecamp.org/news/learn-solidity-handbook/ 117/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
When the VRF Coordinator contract fulfills the request for random numbers, it emits a
RandomWordsFulfilled event. That event contains 4 pieces of data, of which the first one,
requestID , is indexed.
2. The topics (indexed event parameters used for filtering queries of the logs).
3. Non-indexed parameters, referred to as “data”, and they are ABI-encoded and represented
in hexadecimal. This data needs to be ABI-decoded, in the way described in the section on
ABI encoding and decoding.
When working in Remix you can also inspect the Events in the console as shown below:
https://www.freecodecamp.org/news/learn-solidity-handbook/ 118/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum Donate
https://www.freecodecamp.org/news/learn-solidity-handbook/ 119/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
You can also programmatically access the events using the contract receipts object in EthersJS.
Forum Donate
Using the code snippet we used above in the SimpleStorage contract, we can access the events
Learn to code — free 3,000-hour curriculum
using EthersJS and Hardhat using the following JavaScript:
console.log(receipt.events[0].args.newNumber.toString()) // 1981
You can also use libraries such as the EtherJs library in your front end application to listen to events
and filter historical events. Both of these are useful when your application needs to respond to
events on the blockchain.
The global variable block.timestamp refers to the time, in milliseconds, when the block was
generated and added to the blockchain. The milliseconds count refers to the number of
milliseconds that have passed since the beginning of the Unix Epoch (in computing, this is 1 January
1970).
https://www.freecodecamp.org/news/learn-solidity-handbook/ 120/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Unlike Web2 references to timestamps in milliseconds, the value may not increment withForum
every Donate
millisecond.
Learn to code — free 3,000-hour curriculum
A block often contains several transactions, and since block.timestamp refers to the time at which
the block was mined, all transactions in a mined block will have the same timestamp value. So the
timestamp really refers to the block’s time, and not so much as to when the caller may have
initiated the transaction.
Solidity supports directly referring to the following units of time: seconds, minutes, hours, days, and
weeks.
You can also use this to calculate future expiry dates, where, for example, you may want an
operation to be possible between now and next week. You could do this with uint
registrationDeadline = block.timestamp + 1 weeks; and then we could use the
registrationDeadline as a validation or guard in a function as follows:
https://www.freecodecamp.org/news/learn-solidity-handbook/ 121/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
In this function we register the voter only if the current block’s timestamp is not past the
registration deadline.
This logic is used extensively when we want to make sure certain operations are performed at the
right time, or within an interval.
This is also one of the ways in which Chainlink Automation, a decentralized way to automate the
execution of your smart contract, can be configured. The Chainlink decentralized oracle network
can be configured to automatically trigger your smart contracts, and you can run a wide variety of
automations by checking conditions, including time related conditions. These are used extensively
for airdrops, promotions, special rewards, earn-outs and so on.
From here on, it’s a question of practice, repetition and experience. As you go about building your
next awesome decentralized application, remember to revisit the basics and focus on security.
Security is especially important in the Web3 space.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 122/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Forum
You can get good information on best practices from OpenZeppelin’s blogs and the Trail of Bits Donate
You can also get more practical hands on experience by doing the full end-to-end full-stack
blockchain developer course that my colleague Patrick Collins published on freeCodeCamp (it’s
free!).
There are other resources like blockchain.education and freeCodeCamp’s own upcoming Web3
curriculum that can consolidate your learnings.
In any event, this handbook can be your “desktop companion” for that quick refresh on basic
concepts, regardless of what level of experience you’re at.
The important thing to remember is that Web3 technology is always evolving. There is a crying
need for developers willing to tackle complex challenges, learn new skills, and solve important
problems that come with decentralized architecture.
That could (and should) be you! So just follow your curiosity and don’t be afraid to struggle along
the way.
And once again, I intend to keep this handbook updated. So if you see anything that’s not quite
right, is outdated, or unclear, just mention it in a tweet and tag me and freeCodeCamp - a big shout
out to you all for being part of keeping this handbook fresh.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 123/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Post Script
If you are serious about a career change to code, you can learn more about my journey from lawyer
to software engineer. Check out episode 53 of the freeCodeCamp podcast and also Episode 207 of
"Lessons from a Quitter". These provide the blueprint for my career change.
If you are interested in changing your career and becoming a professional coder, please reach out
here. You can also check out my free webinar on Career Change to Code if that is what you're
dreaming of.
Zubin Pratap
Recovering lawyer | recovering MBA type | founder | self taught coder| blogger | #TalkNerdyToMe I broke a lot of
"rules" , more as I got older, and all that did was encourage me to be bolder.
If you read this far, tweet to the author to show them you care. Tweet a thanks
https://www.freecodecamp.org/news/learn-solidity-handbook/ 124/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
Learn to code for free. freeCodeCamp's open source curriculum has helped more than 40,000
Forum Donate
people get jobs as developers. Get started
Learn to code — free 3,000-hour curriculum
ADVERTISEMENT
freeCodeCamp is a donor-supported tax-exempt 501(c)(3) charity organization (United States Federal Tax Identification Number: 82-
0779546)
Our mission: to help people learn to code for free. We accomplish this by creating thousands of videos, articles, and interactive coding lessons -
all freely available to the public. We also have thousands of freeCodeCamp study groups around the world.
Donations to freeCodeCamp go toward our education initiatives, and help pay for servers, services, and staff.
https://www.freecodecamp.org/news/learn-solidity-handbook/ 125/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
About Alumni Network Open Source Shop Support Sponsors Academic Honesty Code of Conduct Privacy Policy
https://www.freecodecamp.org/news/learn-solidity-handbook/ 126/127
6/15/23, 8:41 PM Learn Solidity – A Handbook for Smart Contract Development
https://www.freecodecamp.org/news/learn-solidity-handbook/ 127/127