EDITING BOARD
RO
EN
×
▼ BROWSE ISSUES ▼
Issue 108

A Crash Course in Solidity, the lingua franca of Ethereum Smart Contracts

Dan Sabadis
Team Lead @ SDL
PROGRAMMING

A Crash Course in Solidity, the lingua franca of Ethereum Smart Contracts

In this article we'll present the fundamentals of Solidity, the language of choice for developing Smart Contracts on Ethereum or Ethereum cloned blockchains.

The theory and examples presented in this article are inspired primarily from two books. The first one, "Mastering Ethereum", is written by two British computer scientists: Andreas M. Antonopoulos, a very known popularizer of Bitcoin and Gavin Wood, the creator of Solidity itself. The second book "Building Ethereum Dapps" written by Roberto Infante at Manning is my personal favorite: it is easy to understand but also tackles very complex aspects of Solidity and of the Ethereum ecosystem.

There are 3 other interesting programming languages of choice for writing Smart Contracts. The first one is LLL, which is inspired by Lisp. The other two are Viper and Serpent both being inspired by Python. Given the fact that these alternative languages are used in less than 20% of all Ethereum contracts we will keep the focus on Solidity which is by far the most popular for programming Ethereum Virtual Machines (EVM). Solidity is inspired from a variety of languages, but primarily from Java and Javascript (two of the most famous languages based on C).

There are not many books on this subject of Solidity programming language. It seems the big tech corporations don't want to spread too fast the knowledge of Smart Contracts and Decentralized Applications (Dapps) programming. For instance, if you do a search on the thousands of courses available on Pluralsight, you won't find even one Ethereum and Solidity programming course.

As developers we have two main alternatives to deploy the smart contracts: the first is through a local node that needs to be installed and fully synchronized with the whole blockchain; the second option consists in using a software tool that connects to a remote node and is performing a remote install. We will choose the second option as installing and configuring a full node locally can be cumbersome and synchronizing the local node with the full Ethereum block-chain can take even a few days for a slower internet connection.

The first option is used for full professional development when we interact with a local Ethereum node installation and where we use some testing environment like Javascript Mocha and a third party Smart Contract framework like Truffle or Ganache.

Again, we will keep the things as simple as possible by not using use complex programming environments for samples presented below. We will use a full online IDE (integrated development environment) called Remix.

In order to use the full development experience you will need to install in your browser of preference the Metamask add-on. This small application is one of the cornerstones of interacting with the blockchains. It uses in the background the web3 Javascript library, which will be the focus of the third article in this series. After you successfully installed Metamask, you will need to connect it to the Test version of Binance Smart Chain (BSC) as our samples will be deployed on this network. You can find a link with the instructions on how to connect to BSC Testnet blockchain in the footnotes. It is important to connect to the Testnet blockchain, as this is the one used by developers to test their smart contracts. The Mainnet is the main blockchain that uses the real coins and real money. You will also need to access a Fawcet site where you'll get fake coins (fake BNBs or fake stablecoins) to be used in the BSC Testnet. You'll have to specify your metamask address where the fake coins (BNB) will be instantly sent to you.

Le's perform now the configuration for the Remix online application.

Select Environment -> Injected Web3 option and Metamask will popup, requiring to allow the connection of Metamask with the https://remix.ethereum.org/ web site. Select Next/Allow and after that we are ready for the samples from this article!

Go to the File Menu and add a new File called SimpleCoin.sol

Then Go to the Solidity Compiler Menu and select the compiler version 0.4.26 as in the image below.

After that paste in the file SimpleCoin.sol created previously the following code snippet:

pragma solidity ^0.4.12;
contract SimpleCoin {
    mapping (address => uint256) public coinBalance;
    event Transfer(address indexed from,
      address indexed to, uint256 value);

    constructor() public {
      coinBalance[0x14723A09ACff6D2A60DcdF7aA4AFf308FD
    DC160C] = 10000;
    }
    function transfer(address to, uint256 amount) public {
        require(coinBalance[msg.sender] > amount);

      require(coinBalance[to] + amount >=     
    coinBalance[to]);

        coinBalance[msg.sender] -= amount;
        coinBalance[to] += amount;
        emit Transfer(msg.sender, to, amount);
    }
}

You should see the pasted code like in the image above. If you click the Compile SimpleCoin.sol (1), it will analyze the file, compile it and notify us that the compilation was successful (2). If you are truly curious about the internals of the Solidity, you can click the ABI option (3) and then paste from clipboard to a text file the produced output. The Application Binary Interface (ABI) is the enumeration of the functions from the contract that you can interact with and their signature (parameter names, their types and the expected return type). The ABI is displayed in JSON format. If you are truly an advanced professional, then you may be interested in the Bytecode option (3) which returns (again in a text format from clipboard) the machine byte code of the contract along with the operation codes that are compiled to the machine code. The Operation Codes (opcode) look very similar to the Assembly Language.

Value Types

Like most statically typed languages, Solidity requires you to explicitly declare the type of each variable, or at least needs the type to be inferred unequivocally by the compiler. Its data type system includes both value types and reference types. A value type variable is stored on the EVM stack, which allocates a single memory space to hold its value. When a value type variable is assigned to another variable or passed to a function as a parameter, its value is copied into a new and separate instance of the variable. Consequently, any change in the value of the assigned variable doesn't affect the value of the original variable. Value types include most native types and enums. We have the "classic" native types boolean, integers and enumeration, as you can see in the code snippet below (that we invite you to copy-paste in a new file in Remix and compile).

You can declare integer variables either as int (signed) or uint (unsigned). You also can specify an exact size, ranging from 8 to 256 bits, in multiples of 8. Address objects, which you generally declare using a literal containing up to 40 hexadecimal digits prefixed by 0x, hold 20 bytes. Variables declared as bool can have either a true or false value. An enum is a custom data type including a set of named values. You can then define an enum-based variable as below. The integer value of each enum item is implicitly determined by its position in the enum definition. In the previous example, the value of High is 0 and the value of Low is 2. You can retrieve the integer value of an enum type variable by explicitly converting the enum variable to an int variable. Implicit conversions aren't allowed. All the commented lines below are forbidden conversions that will fail at compile-time.

pragma solidity ^0.4.12;
contract ValueTypeIntConversions {
  enum InvestmentLevel {High, Medium, Low}
  bool isComplete = false;
  InvestmentLevel level = InvestmentLevel.Medium;
  int256 bigNumber = 150000000000;
  int32 mediumNegativeNumber = -450000;
  uint16 smallPositiveNumber = 15678;
  uint32 public newMediumNumber = 
         smallPositiveNumber;

  int256 public newBigNumber = 
       mediumNegativeNumber;

  int16 levelValue = int16(level);
  // int16 newSmallNumber = bigNumber;
  // uint64 newMediumPositiveNumber = 
      mediumNegativeNumber;

    // uint256 public newboolConverted = isComplete;
    // int16 levelValue2 = level;
}

Reference Types

Reference type variables are accessed through their reference (the location of their first item). You can store them in either of the following two data locations, which you can, in some cases, explicitly specify in their declaration:

The following listing shows various reference type variables declared in different data locations.

pragma solidity ^0.4.0;
contract ReferenceTypesSample {
    uint[] storageArray;
    function f(uint[] fArray) {}
    function g(uint[] storage gArray) internal {}
    function h(uint[] memory hArray) internal {}
}
pragma solidity ^0.4.0;

contract Voting {
    enum UserType {Voter, Admin, Owner}

    enum MajorityType {SimpleMajority, 
      AbsoluteMajority, SuperMajority, Unanimity}

    struct UserInfo {
        address account;
        string name;
        string surname;
        UserType uType;
    }

    struct Candidate {
        address account;
        string description;
    }

    struct VotingSession {
        uint sessionId;
        string description;
        MajorityType majorityType;
        uint8 majorityPercent;
        Candidate[] candidates;
        mapping (address => uint) votes;
    }

    uint numVotingSessions;
    mapping (uint => VotingSession) votingSessions;
}

Global namespace

The global namespace is a set of implicitly declared variables and functions that you can reference and use in your contract code directly.

The global namespace provides the following five variables:

The following two functions, available from the global namespace, throw an exception and revert the contract state if the associated condition isn't met. Although they work exactly the same way, their intention is slightly different:

You can also terminate the execution and revert the contract state explicitly by calling revert().

Access Level

The state variables hold the contract state. They have 3 possible access levels that you can specify when declaring them.

The code snippet below exemplifies various access levels:

pragma solidity ^0.4.0;
contract StateVariablesAccessibility {
    mapping (address => bool) private frozenAccounts;
    uint isContractLocked;
    mapping (address => bool) public tokenBalance;
}

Solidity also provides support for constant state variables as you can see in the following code snippet:

pragma solidity ^0.4.0;
contract ConstantStateVariables {
    uint constant maxTokenSupply = 10000000;
    string constant contractVersion ="2.1.5678";
}

Functions

You can specify function input and output parameters in various ways. In the code below you can see two approaches where the functions return a tuple:

pragma solidity ^0.4.0;
contract Functions {
    function calculate1(int _x, int _y, int _z, bool _flag)
        returns (int _alpha, int _beta, int _gamma) {
        _alpha = _x + _y;
        _beta = _y + _z;

        if (_flag)
            _gamma = _alpha / _beta;
        else
            _gamma = _z;
    }
  function calculate2(int _x, int _y, 
   int _z, bool _flag)
        returns (int, int, int) {
        int _alpha = _x + _y;
        int _beta = _y + _z;

        if (_flag)
            return (_alpha, _beta, _alpha / _beta);

        return (_alpha, _beta, _z);
    }
}

When invoking a function, you can pass the parameters in any order if you specify their name, as shown in the following listing:


pragma solidity ^0.4.0;

contract TaxCalculator {
  function calculateAlpha(int _x, int _y, int _z) 
    public returns (int _alpha) {
     _alpha = _x + this.calculateGamma({_z:_z, 
             _y:_y});
    }

  function calculateGamma(int _y, int _z) 
    public returns (int _gamma) {
        _gamma = _y *3 +7*_z;
    }
}

We provided only an introduction into the capabilities of Solidity. One of the most exiting parts of Solidity (for advanced programmers) is protecting against known security attacks like denial of service, which makes the contract unusable and front-running where is favored one transaction over the others, anticipating a price change. We invite you to read these books mentioned in the introduction.

VIDEO: ISSUE 97 LAUNCH EVENT

Sponsors

  • Accenture
  • Bosch
  • ntt data
  • Betfair
  • FlowTraders
  • MHP
  • Connatix
  • BoatyardX
  • metro.digital
  • Colors in projects