DEV Community

Cover image for Getting Started With Solidity
Kinanee Samson
Kinanee Samson

Posted on

Getting Started With Solidity

Solidity, a statically typed, high level, event driven, object oriented programming language that is used for coding smart contracts. Solidity was initially proposed by Gavin Wood in August of 2014 and was later developed by the Ethereum project's Solidity team. Solidity is designed to create smart contracts that run on the Ethereum Virtual Machine (EVM). It is by choice the best supported programming on Ethereum, Binance, Polygon network amongst others.

Solidity bears a similarity to ECMAScript, otherwise known as JavaScript, Solidity was actually inspired by ECMAScript and other curly braces language like Java, C++. Personally writing Solidity feels like writing JavaScript with a bit of Java syntax, this is because of the strictness in explicitly declaring the type of a variable, argument or return value from a function. Solidity as a programming language on the blockchain is a fully fledged Turing complete machine and this implies that it can simulate a universal Turing machine, in simple terms this means that we could use it for real world implementation of branching and conditional logic, we could also do iterations based on those conditions.

In this article we are going to look at what solidity is as a language, applications of solidity and we will write the most simple smart contract ever.

What is Solidity

Statically Typed

One of the most notable thing with solidity is the type declaration. Solidity is statically typed and as such it is required that we declare a type for every single variable or return value. This is to ensure that there is strictness when writing smart contracts, when programming, things can easily get out of hand and code executed on the blockchain is immutable so the compiler will check our code at run time to ensure that we trying to perform the right operation on the right type of value.

pragma solidity >= 0.7.0 <0.8.0;

string name = "my name";

bool isTrue = false;

uint age = 25;
Enter fullscreen mode Exit fullscreen mode

Object Oriented

Solidity is an object oriented programming language and supports all the principles of OOP, a class in solidity is called a contract. A typical class in solidity should look like this.

pragma solidity >= 0.7.0 <0.8.0;

contract OneKoin {
    bytes256 name;
    constructor(string _name) public {
        name = name;
    }
}
Enter fullscreen mode Exit fullscreen mode

We have just created a simple contract however as the need be we could use more advanced features of OOP, solidity also supports interfaces, we can create an interface using the interface keyword, and it functions just like in other languages. In solidity we can override methods defined on parent classes when inheriting from them allowing us to further customize or control the behaviour of a child class.

interface IERC20 {
 function totalSupply() public view returns(uint256);
}

// this contract is expected to conform to the above interface
contract ERC20 is IERC20 {
    // we can also override contract members
    uint256 totalSupply public override = 2000000;
}
Enter fullscreen mode Exit fullscreen mode

Event Driven

In solidity the concept of events exists to notify a consumer about what is currently going on with the blockchain, just like how events are fired in the DOM or just like how they are emitted in Nodejs, we can create an event and then emit it later when something tries to change the state of the blockchain.

// creating an event
pragma solidity ^0.4.21;

// Creating a contract
contract EventContract {

    // Declaring state variables
    uint256 public value = 0;

    // Declaring an event
    event Increment(address owner);   

    // Defining a function for logging event 
    function getValue(uint _a, uint _b) public returns(uint) {
        emit Increment(msg.sender); 
        value = _a + _b;
    }
}
Enter fullscreen mode Exit fullscreen mode

Solidity was designed to run on the EVM which powers the Ethereum blockchain, however due to the similiarity between the Ethereum blockchain and other blockchains like Binance, Polygon; solidity can be used to write smart contracts that can be deployed to any network that supports solidity and uses an EVM, this is to ensure that there is a smooth developer experience, you can expect your projects to behave in a predictable manner irrespective of the network you deploy to, as far as it supports an EVM.

Structure Of a Solidity file

The most basic solidity program is composed of a lisence declarations, which is followed by a pragma definition. Most smart contracts will only specify the pragma version, that is the version of compiler that should be used to execute our code. It is quite important that we do this to prevent our code from being compiled with a compiler that introduces breaking changes in the code base. We can specify things like the abi encoder and decoder, the abi encoder has a v2 that is used for encoding and decoding deeply nested arrays and objects. If it isn't already visible but it is required of us to put a semi-colon at the end of each line just like we do in most curly bracket language.

pragma solidity >= 0.7.0 <0.8.0; // pragma version

contract Coin { // contract alias for class

    // The keyword "public" makes variables
    // accessible from other contracts
    address public minter; // simple "value" types

    // "Reference" type
    mapping (address => uint) public balances;


    // Events allow clients to react to specific
    // contract changes you declare
    event Sent (address from, address to, uint amount);

    // Constructor code is only run when the contract
    // is created
    constructor() public {
        minter = msg.sender;
    }

    // Sends an amount of newly created coins to an address
    // Can only be called by the contract creator
    function mint(address receiver, uint amount) public {
        require(msg.sender == minter);
        require(amount < 1e60);
        balances[receiver] += amount;
    }

    // Sends an amount of existing coins
    // from any caller to an address
    function send(address receiver, uint amount) public {
        require(amount <= balances[msg.sender], "Insufficient balance.");
        balances[msg.sender] -= amount;
        balances[receiver] += amount;
        emit Sent (msg.sender, receiver, amount);
    }
}
Enter fullscreen mode Exit fullscreen mode

Solidity allows us to import code from other files, allowing us to modularize our code. The import syntax of solidity is similar to that of javascript, we will examine how we can import a module from another file.

import "path";
Enter fullscreen mode Exit fullscreen mode

This is a standard way to import a module from solidity. howerver this imports every contract defined in the module and it pollutes the workspace. So there exists a way to deal with this pollusion, we can use the import as syntax to avoid this.

import * as something from 'path'
Enter fullscreen mode Exit fullscreen mode

we can also use an alternative syntax, that still reads like the above.

import  "filename"  as  symbolName;
Enter fullscreen mode Exit fullscreen mode

This is just for conveinece, we can use destructuring syntax to get access to contracts declared in other modules.

import  {symbol1  as  alias,  symbol2}  from  "filename";
Enter fullscreen mode Exit fullscreen mode

The as allows us to rename an exported member from a file to avoid namespace collision.
comments in solidity are similar to comments in other programming languages. we use a double forward slashes // for a single line comment.

    // single line comments

    /** 
        use the normal synatx
        for comments spanning accross
        multiple lines
    **/
Enter fullscreen mode Exit fullscreen mode

Variables

Just like other programming languages we can create variables we can use to track the state of the smart contract.

pragma solidity  >=0.4.0  <0.9.0;

contract  Storage  {
  uint  stored;  // State variable
}
Enter fullscreen mode Exit fullscreen mode

We can annotate our variables with types, there are some built in types that comes baked with the solidity language however there is a distinction between the types solidity provides, types in solidity is classified into;

  • Value Type
  • Reference Type

Value Type

Any particular type which must be assigned a native value like a string or a boolean, they refer to the the simple types which serves as building blocks for more complicated structure of data, a value type is always assigned a value, they include.

  • uint which is used to represent unsigned integers, that is numbers that cannot have a minus sign, thus it will never be negative.
  • int used to represent numbers that may contain positive or negative values.
  • bool to represent Boolean values, can only be either true or focus.
  • address to represent ethereum addresses on the blockchain.
  • bytes/strings used to represent character sequences however bytes is advantageous because it cost less gas.
pragma solidity  >=0.4.0  <0.9.0;

contract  Example  {
  // integers
  int age = 23;  // alias for int256
  uint amount = 100000 // alias for uint256 or uint8

  bool isTrue = false;

  bytes32 name = 'bob';

  string fam = 'jack';
}
Enter fullscreen mode Exit fullscreen mode

There are also three other types of value types. We have;

  • address this is used to represent an ethereum address.
  • address payable this is also used to represent an address, the difference between this and address is that address payable is an Ethereum address we can send ether to. This type of address has some members like transfer, balance e.tc. You can easily convert adrress to payable by typecasting the address using the payable function.
pragma solidity  >=0.4.0  <0.9.0;

contract Example {
    address owner = "0xF...";

    address payable sender = msg.sender;

    address payable newAddr = payable(owner);

}
Enter fullscreen mode Exit fullscreen mode
  • Enums this is a fixed length of constant values, it is often used to avoid code duplication or to avoid hardcoding some values. Each item in an enum will return it's index when used.
pragma solidity  >=0.4.0  <0.9.0;

contract Example {
    enum MyChoices { stayAtHome, code, sleep, read };

    function goToSleep() public returns(uint) {
        return MyChoices.sleep
    }
}
Enter fullscreen mode Exit fullscreen mode

Reference Types

These are types that do not store the value of a variable, rather they store a reference to the location that contains the value, thus they are not really concerned with the value of the data.

  • mapping A mapping is a data structure used to store key-value pairs much like a dictionary or hash table, the key can be of any particular type, while the value can also be of any particular type.
  • struct This is a way of creating our own custom types in solidity. A struct is similar to a map in that it is also a key-value pair data structure, but a struct can contain both value types and reference types as members.
  • array An array is a bunch of variables of the same type. Each variable in the array has an index and can be accessed using that index, solidity allows for both dynamic and fixed sized arrays.
pragma solidity  >=0.4.0  <0.9.0;

contract Example {
       // array
    uint[5] arr = [1, 2, 3, 4, 5];

    struct SuperHero {   // struct
        string name;
        string alias;
    }

        // mapping
    mapping (address => SuperHero) superheroes;

    function makeSuperHero(string name, string val) public pure returns(SuperHero) {
        SuperHero Batman;
        Batman.name = name;
        Batman.alias = val;
        return Batman;
    }

    function getHero(address hero) public view returns(SuperHero) {
        return superheroes[address];
    }
}
Enter fullscreen mode Exit fullscreen mode

Scope

Variables in solidity are classified based on their scope and by this I mean, where can we get access to this variable from inside the smart contract? Think of it like scope in JavaScript. There are three types of variables in solidity;

  • State variable which are declared inside the smart contract body and can be used from anywhere inside the smart contract.
  • Local variables which are declared inside a function body and can only be used inside the function it was created in.
  • Global variables that are always available to every smart contract, basically the compiler supplies the value of this variables at compile time. They include information about the blockchain or the address which is trying to deploy the smart contract e.g msg.sender.
pragma solidity  >=0.4.0  <0.9.0;

contract Example {
    uint val = 100; // state variable

    address = msg.sender; // Global variable

    struct SuperHero {
        string name;
        string alias;
    }

    mapping (address => SuperHero) superheroes;

    function makeSuperHero(string name, string val) public pure returns(SuperHero) {
        SuperHero Batman; // local variable
        Batman.name = name;
        Batman.alias = val;
        superheroes[msg.sender] = Batman;
        return Batman;
    }

    function getHero(address hero) public view returns(SuperHero) {
        return superheroes[address];
    }
}
Enter fullscreen mode Exit fullscreen mode

Functions

No programming language would be complete without functions, solidity has functions for creating reusable blocks of code or to isolate logic in a smart contract. If a function returns a value it is required that we use the returns(type) keyword to specify the return type, however if the function does not return any value we can omit that statement. It is also necessary to specify the type of the arguments we pass to a function, to ensure that we are performing the right operation on the right data.

pragma solidity  >=0.4.0  < 0.9.0;

contract Example {
    uint val = 2000;
    function getVal() public view returns(uint) {
        return val;;
    }
}
Enter fullscreen mode Exit fullscreen mode

The above function is a very simple function that returns a state variable , to declare a function, we use the function keyword then the name of the function followed by parenthesis and we specify the argument we want to pass to the function if it accepts any, we can then specify a modifier for the function, we then we specify the type of function that we are creating then we use the returns keyword if there's any return value.

function name(type argument) modifier functionType returns(type){
    body;
}
Enter fullscreen mode Exit fullscreen mode

We can classify our functions based on the modifier we attach to the function;

  • public Functions that are tagged as public can be called from any where in the smart contract, by other smart contracts.

  • private Functions tagged as private are local to a contract or library, they can only be called in the contract that they are declared in.

  • external Functions tagged as external are similar to public functions, they can be called from transactions.

  • internal internal function can only be called inside the current contract they are defined in and cannot be used outside of this context.

 // can be called from anywhere
function sayHey() public {}
// can only be called inside the smart contract

function imPrivate() private {}

// can be called from outside the smart contract
function anyWhere() external {}

// only inside the smart contract
function notPublic() internal {}
Enter fullscreen mode Exit fullscreen mode

We can also classify functions based on the effect they will have on the smart contract;

  • view View functions do not modify the state of the smart contract, they only return a value;
  • pure Pure functions will not have any side effects on the smart contract, state variables cannot be modified, we cannot read from state variables inside pure functions. A pure function is only concerned with it's argument.
  • payable These are functions that allow smart contracts to receive some ethers.
  • non payable these function marks a smart contract as unable to receive some ether.
// return a value from the smart contract
function anyWhere() external view {
    return totalSupply;
}

// only work with arguments
function work(uint _val) external pure {
    return _val**
}

// allow a smart contract recieve ether
function payMe(uint amount) external payable {}
Enter fullscreen mode Exit fullscreen mode

So far we have looked at a lot! I think at this stage we have learned enough to code up our own basic smart contract, remember I said It would be the simplest smart contract.

pragma solidity  >=0.4.0  < 0.9.0;

contract Example {
    // a value we are going to read and write later
    uint val = 2000;

    // getting a reference to the address that will deploy this contract
    address owner = msg.sender;

    // get the value
    // anyone can do this.
    function get() external view returns(uint){
        return val;
    }

    // set the value
    // only the owner can do this.
    function set(uint _val) external {
        // if the caller is not the owner this will fail and revert.
        require(msg.sender == owner);
        val = _val;
        // emit an event that logs the sender and the value
        // each time we change it.
        emit ValueChanged(msg.sender, _val);
    }

    event ValueChanged(indexed caller, uint indexed val);


}
Enter fullscreen mode Exit fullscreen mode

That's it for this introduction, in subsequent articles we are going to look more into detail about solidity, hope you enjoyed this and you learned something new!

Discussion (0)