Solidity Crash Course – Your First Smart Contract on Ethereum

5/5 - (1 vote)

This tutorial is based on our Finxter Academy course:

Feel free to check out the course with more advanced videos, codes, and explanations on Solidity.

Here’s a short video that may give you some motivation on why learning Solidity in the first place—it’s a great way to build your second income stream as a freelance Blockchain developer:

10 Solidity Freelancers Making $60/h on Upwork

Ready to learn? So, let’s get started!

Introduction to Remix

The Remix is an Integrated Development Environment (IDE) for smart contract development on Ethereum blockchains. It is a browser-based IDE and is available at https://remix.ethereum.org/.

Fig: Remix IDE

Remix offers quick development, deploy and test environments for smart contracts. It offers multiple environments to deploy the smart contracts, that include:

  1. Javascript VM: A local blockchain that does not use any external Ethereum tools. It offers fake or virtual ether for testing, which helps to debug smart contracts before deploying on the mainnet.
  2. Injected Web3: In this option, there is a need to use external wallets such as Metamask, then it can be used to connect to other Ethereum networks and mainnet.
  3. Web3 provider: Remix will connect to a remote node. You will need to provide the URL to the selected provider: geth, parity, or any Ethereum client.

Injected Web3 and Web3 provider, require the use of an external tool.

👉 Recommended Tutorial: Solidity Remix IDE Quickstart

Smart Contract Structure

A smart contract can be described as a program that runs on the blockchain when certain conditions are met. They execute automatically without any intermediary’s involvement.

A typical smart contract structure would be as below

pragma solidity ^0.8.7;

contract MyContract {
      string mystring = "Welcome to not-satoshi, blockchain education for everyone!"
}

It starts with the solidity compiler version. In this case, we use any compiler version >= 0.8.7, a contract is created using the keyword contract and name of the contract (here: MyContract). In the open and close curly brace, the logic for the contract is implemented.

Deployment

You need to deploy your smart contract in order for it to be available to users of an Ethereum network. To deploy a smart contract, you merely send an Ethereum transaction containing the code of the compiled smart contract without specifying any recipients. Deploy sends a transaction that deploys the selected contract.

Transaction

Transaction means sending any asset such as Ether or any cryptocurrency from source to destination. A transaction may also result from deploying a smart contract or setting state variables in the contract. A transaction causes a state change in the blockchain.

What Is Solidity?

Solidity is a programming language used for smart contract development. Solidity is influenced by Javascript, C++, and Python. It supports object-oriented programming for writing smart contracts. It is a statically typed language,  the code after compilation runs on the EVM (Ethereum Virtual Machine) of the Ethereum blockchain.

Function Definition

A function in Solidity is similar to the one defined in Javascript with some small modifications. It looks as below:

function function_name(params) {function visibility}{state mutability}returns(return type)
{
      // function block...
}

An example function definition may look like this: pragma solidity ^0.8.7;

contract nsContract {
       function ns_firstfunction(uint value) external pure returns(uint)
        {
            // function logic goes here...
        }
}

We will discuss function visibility, state mutability in more detail in the coming sections.

Function Visibility

Function visibility can be thought of as access specifiers that set the restrictions on the functions or the accessibility of the functions to other smart contracts. The function visibility can be either private, internal, external, or public.

1. Private:

It means the function will be called only inside the smart contract by other functions.

For example:

contract nsContract
{
	function ns_firstfunction(uint value) private pure returns (uint)
	{
      	// function logic
      }

   // this function can be private/public/internal
		function ns_secondfunction() private pure returns (bool)
	{
	      ns_firstfunction(10);
            return true;
      }
}

2. Internal:

It means the function will be called not only from inside the smart contract but also from smart contract inheritance.

We will discuss inheritance briefly at the end of Part II of the course.

3. External:

It means the function can be called only from outside of the smart contract and not from inside the smart contract. In other words, only another contract can call this function.

For example:

contract nsContract
{
      function ns_firstfunction(uint value) external pure returns (uint)
      {
            // function logic
      }

   // this function can be private/public/internal/external    // Will give compilation error as ns_firstfunction is not visible in the contract
      function ns_secondfunction() private pure returns (bool)
      {
            ns_firstfunction(10);   // Gives Error !
            return true;
      }
}
  • Public:

It means the function can be called from the outside of the smart contract and also from the inside the smart contract.

For example, calling from inside the smart contract:

contract nsContract
{
      function ns_firstfunction(uint value) public pure returns (uint)
      {
            // function logic
      }

   // this function can be private/public/internal/external
      function ns_secondfunction() private pure returns (bool)
      {
            ns_firstfunction(10);
            return true;
      }
}

State Mutability

The state mutability defines the limitations of using state variables (or storage variables) in the function definition. It can be view, pure or payable

1. View:

This means the storage or state variables in the function can be used as read-only.

There will be a compilation error if you try to modify the state variable, you can always modify local variables.

For example:

contract nsContract
{
    uint variable;  // state variable
    function ns_firstfunction(uint value) public view returns(uint)
    {
          // function logic
        uint  v = variable;   // OK
        variable = variable + 1;  // Error, not OK
   }
}

2. Pure:

This is even stricter than the view. It means the function cannot even read state or storage variables. It allows you to only modify local variables.

contract nsContract
{
    uint variable;  // state variable
    function ns_firstfunction(uint value) public pure returns(uint)
    {
          // function logic
        uint  v = variable;   //Error,  not OK
        variable = variable + 1;  // Error, not OK
   }
}

3. Payable:

This means the function can receive the cryptocurrency (ether).

Constructor

Is a special function that gets called on deployment or creation of the smart contract. It is used for initializing any state variables or any other initialization stuff to a known value. It can have parameters.

contract nsContract
{
    bytes32 name;

      constructor(bytes32 _name)
      {
            name = _name;  // gets called on deployment automatically.
      }
}

Memory Zones in Smart Contracts

A smart contract can be thought of as comprising the below memory zones.

Fig: Memory sections for smart contract

The zones can be Storage, Memory, or Call data.

  1. Storage:

It is permanent on the blockchain, memory is retained even after multiple transactions, between function calls. The data stored in this section is accessible to all the functions in the smart contract. It is always expensive to use this section of the memory as it alters the blockchain state.

mart contract. If the code modifies the state variable then you need to pay more gas (transaction fee).

2. Memory:

This is the temporary memory not stored, gets erased soon after the function exits. This section is used to save temporary variables for calculation during function execution, hence contents get discarded automatically. Not expensive like storage

For example, local variables used in the functions.

3. CallData:

Mostly applicable for external functions of the smart contract. The function arguments passed are part of call data. Like memory, it is nonpersistent.

4. Stack:

Stack is more related to EVM(blockchain), which maintains stack for loading variables and intermediate values.

An example contract with the memory zones allocated is as below:

contract ns_Contract
{
    uint variable = 10;  // state variable is part of storage by default
     // argument value is part of calldata
    function ns_firstfunction(uint value) public view returns (uint)
    {
      uint v = variable;
      return variable;
    }
   function ns_secondfunction() private view returns (int)
    {
      int x = 10;  // x,y and z are part of memory by default
      int y = 15;
      int z = x + y;

     ns_firstfunction(10);
     return z;
   }
}

Variable Types

The variables in Solidity can be divided into value types and reference types. Value types mostly include the simple known size-types like uint, int, bytes32, strings, etc. Reference types mostly include complex data types such as arrays, maps, structs, etc.

Value types can be fixed size or dynamic size and reference types are mostly dynamic sized.

Variable Memory Zones

The variables types declared in the contract occupy either storage or memory sections. Below is an example of variables and their memory zones.

contract nsContract
{
      // fixed size and dynamic size state variables -> storage bey default
      bool b;
      uint u;
      address src;

      function ns_function(uint u) public pure returns (bool)
      {
            //fixed size local variables -> memory by default
            uint local;
            address dest;
            bytes32 b32;
           
           // for dynamic types you need to specify storage or memory
           // during variable declaration
           uint [] memory accounts;
           bytes storage bs;
      }  
}

Visibility of Variables

Visibility is associated with only storage or state variables. Visibility can be private, internal, or public.

  1. Private: A private variable is visible only within the contract. All the functions in the smart contract can access a private variable. If no visibility is specified then the default is private.
  2. Internal: An internal variable is visible within the contract and also after inheritance.
  3. Public: A public variable is visible within and also outside the contract.

For example:

contract nsContract
{
     // Visibility valid for state variables only
      bool  b; //by default it is private
      uint public u; // a public variable
      bytes32 internal b32; // internal variable
      function ns_function(uint u) public pure returns (bool)
      {
     
      }  
}

Global or Built-in Variables

Solidity provides some built-in or global variables to use in the smart contract code. These variables are useful to collect some important information such as sender address, gas price of the transaction, block difficulty, and much more.

To know more global variables please follow the link:

Practical App: Simple Math Smart Contract

With the Solidity basics covered, we are in a position to write a simple math contract. The contract will perform the addition and multiplication of two numbers. Let us design the smart contract in steps.

Step 1: Constructor to initialize a and b

// SPDX-License-Identifier: GPL-3.0-or-later
pragma  solidity ^0.8.7;

contract ns_math {    // ns -> not-satoshi

    uint a;   // state variables or storage variables
    uint b;

    constructor (uint _a, uint _b)
    {
        a = _a;
        b = _b;
    }

Step 2: Setter function to set a and b

function ns_set(uint _a, uint _b) public
{
      a = _a;
      b = _b;
}

Step3: Implement add and multiply

function ns_add(uint _a, uint _b) private pure returns(uint)
{
     uint result = _a + _b;
     return result;
}

function ns_multiply(uint _a, uint _b) private pure returns(uint)
{
    uint result = _a * _b;
    return result;
}

Step 4: Getter functions for add and multiply

function ns_get_sum() private view returns(uint)
{
      uint sum = ns_add(a,b);
      return sum;
}
function ns_get_mult() private view returns(uint)
{
      uint mult = ns_multiply(a,b);
      return mult;
}

Finally, deploy the contract on Remix and test it with various inputs for a and b. You can use the setter function to set values for a and b and the getter function to get the sum and multiplication of a and b.

Conclusion

In Part-I of solidity and smart contracts, we discussed in detail Remix IDE, smart contract structure, and the solidity language. Intricate details on functions and variables were described. In the end, a simple math smart contract was designed that allowed us to convert the theory into practice.

In Part-II, more complex types and advanced solidity concepts will be introduced. Happy blockchaining 🙂

Smart Contract Developers Make $120,000 per Year on Upwork

Learn Solidity Course

Solidity is the programming language of the future.

It gives you the rare and sought-after superpower to program against the “Internet Computer”, i.e., against decentralized Blockchains such as Ethereum, Binance Smart Chain, Ethereum Classic, Tron, and Avalanche – to mention just a few Blockchain infrastructures that support Solidity.

In particular, Solidity allows you to create smart contracts, i.e., pieces of code that automatically execute on specific conditions in a completely decentralized environment. For example, smart contracts empower you to create your own decentralized autonomous organizations (DAOs) that run on Blockchains without being subject to centralized control.

NFTs, DeFi, DAOs, and Blockchain-based games are all based on smart contracts.

This course is a simple, low-friction introduction to creating your first smart contract using the Remix IDE on the Ethereum testnet – without fluff, significant upfront costs to purchase ETH, or unnecessary complexity.