Solidity Programming Language (Part 2)

In this Article, We will explain these topics:-

  • Reading and Writing to a State Variable
  • Ether and Wei
  • Gas and Gas Price
  • For and While Loop
  • Mapping
  • Array

Reading and Writing to a State Variable:

State variables: Variables in programming refer to storage locations that can contain values. These values can be changed during runtime. The variable can be used at multiple places within the code and they will all refer to the value stored within it. Solidity provides two types of variable-state and memory variables. In this section, we will introduce state variables.

One of the most important aspects of Solidity contracts is state variables. It is these state variables that are permanently stored in a blockchain/Ethereum ledger by miners. Variables declared in a contract that is not within any function are called state variables. State variables store the current values of the contract. The allocated memory for a state variable is static.

    • Variables whose values are permanently stored in contract storage.
    • To write or update a state variable you need to send a transaction.
    • You can read state variables, for free, without any transaction fee.
pragma solidity ^0.8.17;
contract SimpleStorage {
// State variable to store a number
    uint public num;
// You need to send a transaction to write to a state variable.
    function set(uint _num) public {
       num = _num;
    }
// You can read from a state variable without sending a transaction.
    function get() public view returns (uint) {
        return num;
   }
}

 Units in solidity :

There are two types of units in Solidity

1- Time Unit

Time units, on the other hand, are used to measure the duration of certain events in the blockchain, such as the amount of time that must pass before a certain action is allowed to occur. 

Solidity provides several time units that can be used in your code, including:

  • seconds (s).
  • minutes (min).
  • hours (h).
  • days (days).
  • weeks (weeks).

Example 1: Time unit can be used to specify a duration in the smart contract like this:

uint public lockPeriod = 1 week;

2- Ether & Wei

Ether units are used to represent value, such as the amount of money being transferred between accounts or the cost of a transaction. 

  • The smallest unit of ether is called a wei
  • Transactions are paid with ether.
  • Similar to how one dollar is equal to 100 cents, one ether is equal to 1018 wei.
pragma solidity ^0.8.17;
contract EtherUnits {
 // 1 wei is equal to 1boolpublicisOneWei =1wei==1;
    uint public oneWei =1wei;
// 1 ether is equal to 10^18 wei
uint public oneEther =1ether;
       bool public isOneEther =1ether==1e18;
   }

NOTE: Both Ether and Time units can be either local or global, with local units being accessible only within a specific function or contract, and global units being available throughout the entire program.

Gas and Gas Price

How much ether do you need to pay for a transaction?

You pay gas spent * gas price amount of ether, where

  • gas is a unit of computation
  • gas spent is the total amount of gas used in a transaction
  • gas price is how much ether you are willing to pay per gas

Transactions with higher gas price have higher priority to be included in a block.

Unspent gas will be refunded.

Gas Limit

There are 2 upper bounds to the amount of gas you can spend

  • gas limit (max amount of gas you’re willing to use for your transaction, set by you)
  • block gas limit (max amount of gas allowed in a block, set by the network)

There are a couple of major limitations when it comes to running smart contracts:

  1. Every deployed transaction, smart contract, or execution of a smart contract, must be run on every single full node on the Ethereum blockchain to guarantee validity. This is wildly inefficient (though newer blockchains are streamlining this)!
  2. Because smart contracts are Turing complete, they can potentially execute forever, locking up every single node on the blockchain.

Example: Calculating Gas Costs

For any given program, the total gas used is calculated as the sum of the gas for each operation executed by the Ethereum Virtual Machine. For example, adding two numbers in a smart contract costs 3 gas, whereas sending a transaction costs 21,000 gas.

The total cost of gas is found by taking the amount of gas used in a smart contract and multiplying it by the gas price, a value set by you, the transaction sender.

Setting a higher gas price for your transaction means it’s more likely to be confirmed on the blockchain, as the Ethereum blockchain can only confirm about 15 transactions a second. However, it also costs more Ether for the sender.

Another important value that can be set is the gas limit or the maximum amount of gas you’re willing to spend on your transaction.

By multiplying the gas price by the gas limit, you’ll get the maximum amount of Ether you’re allowing Ethereum to spend on gas fees for any particular transaction.

Learn about gas execution

When a smart contract call is made, the call (or transaction) will attempt to use the gas provided while the program is executed.

  • If the call succeeds, the unused gas will be returned to the sender.
  • If the call fails because it ran out of gas, the entire transaction will revert, undoing all changes to the blockchain. None of the gas will be returned since it will all have been used up during the computation process.

A portion of gas fees from a successful transaction will be burned (or removed from the total supply) according to the new EIP-1559 spec, and the rest will be sent to the miner that added your transaction to the blockchain.

For and While Loop:

Solidity supports forwhile, and do while loops.

Don’t write loops that are unbounded as this can hit the gas limit, causing your transaction to fail.

For the reason above, while and do while loops are rarely used.

For loop Syntax:

for (initialization; test condition; iteration statement) {
       statement or block of code to be executed if the condition is True
}

While loop Syntax:

while (condition) {
statement or block of code to be executed if the condition is True
}

do while loop Syntax:

do 
{
   block of statements to be executed
} while (condition);
pragma solidity ^0.8.17;
contract Loop {
function loop() public {
// for loop
for (uint i = 0; i < 10; i++) {
if (i == 3) {
// Skip to next iteration with continue
               continue;
          }
if (i == 5) {
               // Exit loop with break
               break;
           }
       }
// while loop
       uint j;
       while (j < 10) {
            j++;
// do while loop
do{
    j++;
   data.push(j);
   } while(j < 5) ;
}
   }
}

Mapping:

Mapping is a reference type as arrays and structs.

It acts like a hash table or dictionary in any other language. These are used to store the data in the form of key-value pairs.

Mappings are mostly used to associate the unique Ethereum address with the associated value type.

Syntax: 

mapping(keyType => valueType).

  • keyType: can be any built-in value type, bytes, string, or any contract.
  • valueType: can be any type including another mapping or an array.

NOTE: Mappings are not iterable

pragma solidity ^0.8.17;
contract Mapping {
   // Mapping from address to uint
    mapping(address => uint) public myMap;

function
get(address _addr) public view returns (uint) {

       // Mapping always returns a value.
       // If the value was never set, it will return the default value.
        return myMap[_addr];
   }

function
set(address _addr, uint _i) public {

       // Update the value at this address
       myMap[_addr] = _i;
   }

function
remove(address _addr) public {
// Reset the value to the default value.
       delete myMap[_addr];
   }
}

contract NestedMapping {
   // Nested mapping (mapping from address to another mapping)
    mapping(address => mapping(uint => bool)) public nested;

function
get(address _addr1, uint _i) public view returns (bool) {

       // You can get values from a nested mapping
       // even when it is not initialized
       return nested[_addr1][_i];
    }

function
set(address _addr1, uint _i, bool _boo) public {

        nested[_addr1][_i] = _boo;
}
function remove(address _addr1, uint _i) public {
      delete nested[_addr1][_i];
   }
}

Array:

Arrays are data structures that store the fixed collection of elements of the same data types in which each and every element has a specific location called index. Instead of creating numerous individual variables of the same type, we just declare one array of the required size and store the elements in the array which can be accessed using the index. In Solidity, an array can be of fixed size or dynamic size. Arrays have a continuous memory location, where the lowest index corresponds to the first element while the highest represents the last

Array can have a compile-time fixed size or a dynamic size.

Syntax:

<data type> <array name>[size] = <initialization>

 

pragma solidity ^0.8.17;

contract Array {

   // Several ways to initialize an array
   uint[] public arr;
    uint[] public arr2 = [1, 2, 3];

//Fixed-sized array, all elements initialized to 0

    uint[10] public myFixedSizeArr;

function
get(uint i)public view returns(uint) {

       return arr[i];
   }
   // Solidity can return the entire array.
   // But this function should be avoided for
   // arrays that can grow indefinitely in length.
    function getArr() public view returns (uint[] memory) {
       return arr;
    }
function push(uint i) public {
       // Append to array
       // This will increase the array length by 1.
       arr.push(i);
   }
function pop() public {
       // Remove last element from array
       // This will decrease the array length by 1
      arr.pop();
    }
function getLength() public view returns (uint) {
       return arr.length;
    }
function remove(uint index) public {
       // Delete does not change the array length.
       // It resets the value at index to it's default value,
       // in this case 0
       delete arr[index];
    }

function
examples() external {

       // create array in memory, only fixed size can be created
        uint[] memory a = new uint[](5);
}
}

Examples of removing array element

  • Remove array element by shifting elements from right to left
pragma solidity ^0.8.17; 
contract ArrayRemoveByShifting {
    // [1, 2, 3] -- remove(1) --> [1, 3, 3] --> [1, 3]
    // [1, 2, 3, 4, 5, 6] -- remove(2) --> [1, 2, 4, 5, 6, 6] --> [1, 2, 4, 5, 6]
    // [1, 2, 3, 4, 5, 6] -- remove(0) --> [2, 3, 4, 5, 6, 6] --> [2, 3, 4, 5, 6]
    // [1] -- remove(0) --> [1] --> []
uint[] public arr;
function remove(uint _index) public {
        require(_index < arr.length, "index out of bound");
for (uint i = _index; i < arr.length - 1; i++) {
            arr[i] = arr[i + 1];
        }
        arr.pop();
    }

function
test() external {
        arr = [1, 2, 3, 4, 5];
        remove(2);
        // [1, 2, 4, 5]
        assert(arr[0] == 1);
        assert(arr[1] == 2);
        assert(arr[2] == 4);
        assert(arr[3] == 5);
        assert(arr.length == 4);
arr = [1];
        remove(0);
        // []
        assert(arr.length == 0);
    }
}
  • Remove array element by copying the last element into the place to remove
pragma solidity ^0.8.17;
contract ArrayReplaceFromEnd {
   uint[] public arr;
   // Deleting an element creates a gap in the array.
   // One trick to keep the array compact is to
   // move the last element into the place to delete.
   function remove(uint index) public {
       // Move the last element into the place to delete
        arr[index] = arr[arr.length - 1];

// Remove the last element

       arr.pop();
    }
function test() public {
       arr = [1, 2, 3, 4];
      remove(1);
// [1, 4, 3]
       assert(arr.length == 3);
       assert(arr[0] == 1);
       assert(arr[1] == 4);
       assert(arr[2] == 3);
        remove(2);

// [1, 4]

       assert(arr.length == 2);
       assert(arr[0] == 1);
       assert(arr[1] == 4);
   }
}