Table of Contents
It is important to understand the structure of a contract because it determines how the contract will function and what it will be able to do. And that’s the topic we are going to explore in this blog.
A well-designed smart contract should be clear, concise, and easy to understand, and it should accurately reflect the terms of the agreement between the parties involved.
Here’s what a smart contract is made up of
When you’re writing your first smart contract, in this case with Solidity, here’s what the structure of it would look like and contain.
Inside your FirstContract.sol file, the following code will be included:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.0 <0.9.0;
contract FirstContract {
}
The first line is a license identifier, and the second line defines the pragma version so that Solidity, the programming language for the contract in the case, understands which version our contract is in. This means we are defining the version to be between 0.6.0 and 0.9.0 or equal to 0.6.0.
After defining the pragma version, we can define our contract using the “contract” keyword, followed by the contract name (in this case, “FirstContract”). This is similar to the “class” keyword in Python.
1. Variables
In Solidity, variables are used to store data that can be accessed and modified by functions within a smart contract. Solidity supports three types of variables: state variables, local variables, and global variables.
a. State variables
are permanently stored in contract storage and can be accessed and modified by functions within the contract. For example, the following code declares a state variable called count
of type uint256
and initializes it to 0:
pragma solidity >=0.6.0 <0.9.0; contract FirstContract { uint256 count = 0; // state variable }
b. Local variables
On the other hand, local variables are only present while a function is executing. These variables are only accessible within the function in which they are declared and are not permanently stored in contract storage.
c. Global variables
Lastly, global variables are special variables that exist in the global namespace and provide information about the blockchain. These variables can be accessed from anywhere within the contract and are useful for getting information such as the current block number or the address of the contract.
To make a state variable accessible outside the contract, we can use the public
keyword before the variable declaration. This allows the variable to be accessed from outside the contract using its name and the contract’s address. For example:
pragma solidity >=0.6.0 <0.9.0;
contract FirstContract {
public uint256 count = 0; // state variable accessible from outside the contract
}
In this code, the count
variable can be accessed from outside the contract using the contract’s address and the variable’s name. This allows other contracts or external programs to read and modify the value of the count
variable.
2. Structs
A Solidity struct is a user-defined data type that allows you to create complex data structures to store multiple values of different types in a single variable. This can be useful for organizing data and making it easier to access and manipulate.
To declare a struct in Solidity, you use the struct
keyword followed by the struct name and the variables it will contain inside curly braces {}
. For example, the following code defines a struct called Student
that contains the fields name
, age
, and grades
:
pragma solidity >=0.6.0 <0.9.0;
contract FirstContract {
struct Student {
string name;
uint age;
uint[] grades;
}
}
In this code, the Student
struct contains three fields: name
(a string), age
(an unsigned integer), and grades
(an array of unsigned integers).
Once a struct is defined, you can create variables of that struct type and initialize them with values. For example:
pragma solidity >=0.6.0 <0.9.0;
contract FirstContract {
struct Student {
string name;
uint age;
uint[] grades;
}
Student s1 = Student("John", 18, [90, 95, 80]); // create a Student struct variable and initialize it with values
}
In this code, we create a Student
struct variable called s1
and initialize it with the values “John” for the name
field, 18 for the age
field, and an array of grades [90, 95, 80] for the grades
field.
You can then access the individual fields of a struct variable using the .
operator, as shown below:
pragma solidity >=0.6.0 <0.9.0;
contract FirstContract {
struct Student {
string name;
uint age;
uint[] grades;
}
Student s1 = Student("John", 18, [90, 95, 80]);
function getName() public view returns (string) {
return s1.name; // access the name field of the s1 struct variable
}
}
In this code, we create a function called getName
that returns the name
field of the s1
struct variable. This allows us to access the name
field of the s1
struct from outside the contract.
Structs are a powerful tool for organizing data in smart contracts, and can be used to create complex data structures to store and manipulate data in them.
3. Arrays
An array is a data type that allows you to store a collection of values of the same type. Arrays can be declared using the type[]
syntax, where type
is the data type of the values that the array will store. For example:
pragma solidity >=0.6.0 <0.9.0; contract FirstContract { uint[] numbers; // declare an array of unsigned integers string[] names; // declare an array of strings }
In this code, we declare two arrays: numbers
, which will store unsigned integers, and names
, which will store strings.
Once an array is declared, you can initialize it with values and access individual elements using square brackets []
and the element’s index. For example:
pragma solidity >=0.6.0 <0.9.0; contract FirstContract { uint[] numbers = [1, 2, 3, 4, 5]; // initialize the numbers array with values function getNumber(uint index) public view returns (uint) { return numbers[index]; // return the element at the given index } }
In this code, we initialize the numbers
array with the values 1, 2, 3, 4, and 5. We also create a function called getNumber
that takes an index as an input and returns the element at that index in the numbers
array.
Additionally, Solidity also allows you to specify the length of the array when it is declared. For example:
pragma solidity >=0.6.0 <0.9.0;
contract FirstContract {
uint[5] numbers; // declare an array of length 5
}
In this code, we declare an array called numbers
that can store up to 5 elements.
Finally, arrays are a useful data type for storing and manipulating collections of values in your smart contracts.
4. Functions
A function is a block of code that performs a specific task and can be called from other parts of the smart contract. Functions allow you to organize and modularize your code, making it easier to write and maintain.
To declare a function in Solidity, you use the function
keyword followed by the function name, the input parameters (if any), and the function body inside curly braces {}
. For example:
pragma solidity >=0.6.0 <0.9.0;
contract FirstContract {
function greet() public { // declare a function called greet
// function body goes here
}
}
In this code, we declare a function called greet
that takes no input parameters. The function body, which contains the code that the function will execute, is left empty in this example.
To call a function from within the contract, you simply use the function name followed by the input parameters (if any) inside parentheses ()
. For example:
pragma solidity >=0.6.0 <0.9.0;
contract FirstContract {
function greet() public {
// function body goes here
}
function sayHello() public { // declare a function called sayHello
greet(); // call the greet function
}
}
In this code, we declare a second function called sayHello
that calls the greet
function when it is executed.
Functions can also return values using the return
keyword. For example:
pragma solidity >=0.6.0 <0.9.0;
contract FirstContract {
function add(uint a, uint b) public view returns (uint) { // declare a function called add
return a + b; // return the sum of a and b
}
}
In this code, we declare a function called add
that takes two unsigned integer inputs a
and b
and returns their sum using the return
keyword.
Overall, functions are an essential concept and are used to organize and modularize your code, making it easier to write and maintain.
5. Constructors
The constructor is a special function that is called when the contract is deployed to the blockchain. It is used to initialize the contract and set its initial state. The constructor typically takes any necessary arguments, such as the initial values for the contract’s variables.
For example:
constructor(string memory _name) public {
name = _name;
}
6. Events
These are used to emit log messages that can be used to track the contract’s execution and state. Events are typically used to provide a record of important actions that have taken place within the contract, such as the completion of a transaction. They can be used to trigger external functions or to provide information to users of the contract.
For example:
event Deposit(address indexed _from, uint256 _value);
event Withdrawal(address indexed _from, uint256 _value);
function deposit(uint256 _amount) public {
balance = balance.add(_amount);
emit Deposit(msg.sender, _amount);
}
function withdraw(uint256 _amount) public {
require(balance >= _amount, "Insufficient balance");
balance = balance.sub(_amount);
msg.sender.
7. Modifiers
These are used to modify the behavior of functions. They can be used to check for certain conditions before allowing a function to execute. For example, a modifier might be used to check that a certain requirement has been met before allowing a function to be called, or to ensure that a function can only be called by certain users.
For example:
modifier onlyOwner {
require(msg.sender == owner, "Only the owner can perform this action");
_;
}
function withdraw(uint256 _amount) public onlyOwner {
require(balance >= _amount, "Insufficient balance");
balance = balance.sub(_amount);
msg.sender.transfer(_amount);
}
8. The fallback function
This is a special function that is called when the contract receives an external call to an undefined function. It is used to handle situations where an external party tries to call a function that does not exist in the contract. The fallback function can be used to provide a default behavior for the contract, such as returning an error message or ignoring the call.
For example:
function() external {
revert("This function does not exist");
}
In conclusion, it is important to design and write smart contracts carefully to ensure that they accurately reflect the terms of the agreement and function as intended. And for that it is crucial to know what components make up the structure of a contract so that you fully understand and work with them.