⛓️

Solidity Notes

Solidity Resources

Overview

Solidity - High-level programming language to deploy smart contracts on EVM(Ethereum Virtual Machine). Inspired by C++, JavaScript and Python

Statically Typed, supports inheritance, libraries and complex user-defined types programming language.

Basic Syntax

pragma - tells the solidity compiler version to be used

Contract - Similar to a class

Variable Types

No full support for float/double in solidity

Variables

pragma solidity ^0.5.0;
contract SolidityTest {
   uint storedData; // State variable
   constructor() public {
      storedData = 10;   
   }
   function getResult() public view returns(uint){
      uint a = 1; // local variable
      uint b = 2;
      uint result = a + b;
      return storedData; //access the state variable
   }
		msg.sender // Global Variable
		msg.value // Global Variable
}

Storage is expensive on the blockchain

Local Variables do not use any gas because they are always on memory

Variables declared inside a function are stored in stack. They are called stack memory.

String by default is stored in storage. You have to explicitly mention it to be stored on memory

Variable Types

Constant and immutable uses lower gas units

Variable Scope

pragma solidity ^0.5.0;
contract C {
   uint public data = 30;
   uint internal iData= 10;
   
   function x() public returns (uint) {
      data = 3; // internal access
      return data;
   }
}
contract Caller {
   C c = new C();
   function f() public view returns (uint) {
      return c.data(); //external access
   }
}
contract D is C {
   function y() public returns (uint) {
      iData = 3; // internal access
      return iData;
   }
   function getResult() public view returns(uint){
      uint a = 1; // local variable
      uint b = 2;
      uint result = a + b;
      return storedData; //access the state variable
   }
}

Loops

Decision Making

Strings

bytes32 is usually preferred because it uses less gas

pragma solidity ^0.5.0;

contract SolidityTest {
   string data = "test";
   bytes32 new_data = "new_test";
}
// bytes32 - string conversion
bytes memory bstr = new bytes(10);
string message = string(bstr);

Arrays

Static Array - type arrayName [ arraySize ];

uint balance[10];

Dynamic Array - type[] arrayName;

Initializing Array

uint balance[3] = [1, 2, 3]; // Declare and intialize an array
uint balance[] = [1, 2, 3]; // Declare and initialize an array
balance[2] = 5; // set a value for an elemnt in an array

Fixed length array

// declaring a fixed-size array of type uint with 3 elements
uint[3] public numbers = [2, 3, 4];
 
// declaring fixed-size arrays of type bytes
bytes1 public b1;
bytes2 public b2;
bytes3 public b3;
//.. up to bytes32

Dynamic Length Array

contract DynamicArrays{
// dynamic array of type uint
uint[] public numbers;
 
// returning length
function getLength() public view returns(uint){
return numbers.length;
}
 
// appending a new element
function addElement(uint item) public{
numbers.push(item);
}
 
// returning an element at an index
function getElement(uint i) public view returns(uint){
if(i < numbers.length){
return numbers[i];
}
return 0;
}
 
 
// removing the last element of the array
function popElement() public{
numbers.pop();
}
 
function f() public{
// declaring a memory dynamic array
// it's not possible to resize memory arrays (push() and pop() are not available).
uint[] memory y = new uint[](3);
y[0] = 10;
y[1] = 20;
y[2] = 30;
numbers = y;
}
 
}

Members

pragma solidity ^0.5.0;

contract test {
   function testArray() public pure{
      uint len = 7; 
      
      //dynamic array
      uint[] memory a = new uint[](7);
      
      //bytes is same as byte[]
      bytes memory b = new bytes(len);
      
      assert(a.length == 7);
      assert(b.length == len);
      
      //access array variable
      a[6] = 8;
      
      //test array variable
      assert(a[6] == 8);
      
      //static array
      uint[3] memory c = [uint(1) , 2, 3];
      assert(c.length == 3);
   }
}

Strings vs Bytes

Constructor

contract Base {
   uint data;
   constructor(uint _data) public {
      data = _data;   
   }
}

Structs

struct Instructor{
    uint age;
    string name;
    address addr;
}
 
contract Academy{
    // declaring a state variabla of type Instructor
    Instructor public academyInstructor;
  
    // initializing the struct in the constructor
    constructor(uint _age, string memory _name){
        academyInstructor.age = _age;
        academyInstructor.name = _name;
        academyInstructor.addr = msg.sender;
    }
    
    // changing a struct state variable
    function changeInstructor(uint _age, string memory _name, address _addr) public{
        if (academyState == State.Open){
            Instructor memory myInstructor = Instructor({
                age: _age,
                name: _name,
                addr: _addr
            }
                );
            academyInstructor = myInstructor;
        }
    }
}

Enums

// declaring a new enum type
enum State {Open, Closed, Unknown}
    
// declaring and initializing a new state variable of type State
State public academyState = State.Open;

Mapping

contract Auction{
    
    // declaring a variable of type mapping
    // keys are of type address and values of type uint
    mapping(address => uint) public bids;
    
    // initializing the mapping variable
    // the key is the address of the account that calles the function
    // and the value the value of wei sent when calling the function
    function bid() payable public{
        bids[msg.sender] = msg.value;
    }
}

Storage vs Memory

Storage is call by reference. Memory is call by value

// Storage changes the state of the blockchain
contract A{
    string[] public crypto= ['BTC', 'ETH', 'BNB'];
    
    function myFunction() public{
        string[] storage s = crypto;
        s[2] = 'XMR';
    }

// Memory does not change the state of the blockchain
contract A{
    string[] public crypto= ['BTC', 'ETH', 'BNB'];
    
    function myFunction() public{
        string[] memory s = crypto;
        s[2] = 'XMR';
    }

Built in Global Variables

contract GlobalVars{
// the current time as a timestamp (seconds from 01 Jan 1970)
uint public this_moment = block.timestamp; // `now` is deprecated and is an alias to block.timestamp)
 
// the current block number
uint public block_number = block.number;
 
// the block difficulty
uint public difficulty = block.difficulty;
 
// the block gas limit
uint public gaslimit = block.gaslimit;
 
 
address public owner;
uint public sentValue;
 
constructor(){
// msg.sender is the address that interacts with the contract (deploys it in this case)
owner = msg.sender;
}
 
 
function changeOwner() public{
// msg.sender is the address that interacts with the contract (calls the function in this case)
owner = msg.sender;
}
 
function sendEther() public payable{ // must be payable to receive ETH with the transaction
// msg.value is the value of wei sent in this transaction (when calling the function)
sentValue = msg.value;
}
 
 
// returning the balance of the contract
function getBalance() public view returns(uint){
return address(this).balance;
}

Get ether to contract address

There are 2 ways to receive ETH to a contract address

contract Deposit{
    
    // either receive() or fallback() is mandatory for the contract to receive ETH by 
    // sending ETH to the contract's address
    
    // declaring the receive() function that is executed when sending ETH to the contract address
    // it was introduced in Solidity 0.6 and a contract can have only one receive function, 
    // declared with this syntax (without the function keyword and without arguments). 
    receive() external payable{
    }
   
 
    // declaring a fallback payable function that is called when msg.data is not empty or
    // when no other function matches
    fallback() external payable {
    }
    
    
    // ether can be send and received by the contract in the trasaction that calls this function as well
    function sendEther() public payable{
        
    }
 
   
    // returning the balance of the contract. 
    function getBalance() public view returns (uint) {
        // this is the current contract, explicitly convertible to address, 
        // and balance is a member of any variable of type address. 
        return address(this).balance;
    }

Sending ETH to an address from the contract

function transferEther(address payable recipient, uint amount) public returns(bool){
         // checking that only contract owner can send ether from the contract to another address
         require(owner == msg.sender, "Transfer failed, you are not the owner!!");
         
         if (amount <= getBalance()){
             // transfering the amount of wei from the contract to the recipient address
             // anyone who can call this function have access to the contract's funds
             recipient.transfer(amount);
             return true;
         }else{
             return false;
         }
     }

Variable and Function Visibility

Interface

Misc

Pure - The function does not read or modify state variables

View - These are read-only functions that do not modify the state variables

Lottery Smart Contract

pragma solidity >=0.5.0 <0.9.0;
 
contract Lottery{
    
    // declaring the state variables
    address payable[] public players; //dynamic array of type address payable
    address public manager; 
    
    
    // declaring the constructor
    constructor(){
        // initializing the owner to the address that deploys the contract
        manager = msg.sender; 
    }
    
    // declaring the receive() function that is necessary to receive ETH
    receive () payable external{
        // each player sends exactly 0.1 ETH 
        require(msg.value == 0.1 ether);
        // appending the player to the players array
        players.push(payable(msg.sender));
    }
    
    // returning the contract's balance in wei
    function getBalance() public view returns(uint){
        // only the manager is allowed to call it
        require(msg.sender == manager);
        return address(this).balance;
    }
    
    // helper function that returns a big random integer
    function random() internal view returns(uint){
       return uint(keccak256(abi.encodePacked(block.difficulty, block.timestamp, players.length)));
    }
    
    
    // selecting the winner
    function pickWinner() public{
        // only the manager can pick a winner if there are at least 3 players in the lottery
        require(msg.sender == manager);
        require (players.length >= 3);
        
        uint r = random();
        address payable winner;
        
        // computing a random index of the array
        uint index = r % players.length;
    
        winner = players[index]; // this is the winner
        
        // transferring the entire contract's balance to the winner
        winner.transfer(getBalance());
        
        // resetting the lottery for the next round
        players = new address payable[](0);
    }
 
}