Intro to solidity II
Published: 2024-07-07 18:24:41

Continuing from previous article

Constructor
Well, constructors in solidity are similar to constructors in other languages. Some common features of constructor in solidity are :
- Executed only once
- Create only once constructor (even that is optional)
- A default contructor is created by compiler if there's no explicitly defined constructor
 
 Basic data types
 Integer
 - int8 (8 bit integer) to int256 (256 bit integer)
 - uint8 (8 bit integer) to uint256 (256 bit integer)
 - Ranges : int8 (range -128 to 127)
 	    int16 (range -32768 to 32767)
 	    uint8 (range 0 to 255)
 	    uint256 (range 0 to 65535)

Bool
 - Simple, stores only true or false
 - by default intialized as false
 
Address
 - stores hexadecimal address of type "0xBE4024Fa7461933F930DD3CEf5D1a01363E9f284"
  - It is a 160-bit value that does not allow any arithmetic operations.

Bytes
Bytes data type is used to store strings. Range - bytes1, bytes2, …..,bytes32.
 - It stores characters.
 - bytes3 public arr3="abc"; bytes1 public arr1="a"; bytes2 public arr2="ab";
 - Everything that will be stored in the bytes array will be in hexadecimal number.

These are the data types in solidity. To facilitate programming we have basic conditionnels (like if, if-else,  if-else if-else) and loops (for, while). However, we also have require as a conditionnel

Require
 - It has syntax : require(condition, "Error messaged")
 - If the condition is true the code below follows. If the condition is not satisfied we will see the "Error messaged" returned.
 
Modifier
Just like a middleware in Node.js we can have a modifier in solidity. Just write the modifier's name in function's declaration.
A Good example will be:

modifier onlytrue {
        require(false == true, "_a is not equal to true");
        _;
    }

function check1() public pure onlytrue returns (uint) {
        return 1;
    }

Visibility (from my best friend ChatGPT)
In Solidity, function and state variable visibility specifiers define the scope in which these elements can be accessed. There are four visibility specifiers: public, internal, external, and private. Each has specific rules governing where and how the function or state variable can be accessed.
Visibility Specifiers
1. public

    Functions: Can be called from within the contract, derived contracts, and externally via transactions.
    State Variables: Automatically creates a getter function, allowing external access to the variable.

Example:

solidity

pragma solidity ^0.8.0;

contract Example {
    uint public data; // Public state variable

    // Public function
    function setData(uint _data) public {
        data = _data;
    }
}

2. internal

    Functions: Can only be accessed within the contract they are defined in and in derived contracts (inheritance).
    State Variables: Similar to functions, internal state variables can only be accessed and modified within the contract and its derived contracts.

Example:

solidity

pragma solidity ^0.8.0;

contract Base {
    uint internal data; // Internal state variable

    // Internal function
    function setData(uint _data) internal {
        data = _data;
    }
}

contract Derived is Base {
    function updateData(uint _data) public {
        setData(_data); // Accessing internal function from derived contract
    }
}

3. external

    Functions: Can only be called from outside the contract (not from within the contract itself or derived contracts). Ideal for functions meant to be part of the public interface of the contract.
    State Variables: Cannot be marked as external.

Example:

solidity

pragma solidity ^0.8.0;

contract Example {
    // External function
    function setData(uint _data) external {
        data = _data;
    }

    uint private data;
}

contract Another {
    function updateData(Example example, uint _data) public {
        example.setData(_data); // Calling external function from another contract
    }
}

4. private

    Functions: Can only be accessed within the contract they are defined in. They are not accessible from derived contracts.
    State Variables: Only accessible and modifiable within the contract they are defined in.

Example:

solidity

pragma solidity ^0.8.0;

contract Example {
    uint private data; // Private state variable

    // Private function
    function setData(uint _data) private {
        data = _data;
    }

    function updateData(uint _data) public {
        setData(_data); // Can call private function within the same contract
    }
}

Summary of Visibility Specifiers
Visibility	Access Level
public	        Accessible from within the contract, derived   contracts, and externally. Automatically creates a getter for state variables.

internal	Accessible from within the contract and derived contracts.

external	Accessible only from outside the contract. Ideal for functions meant to be part of the public interface.

private	        Accessible only within the contract. Not accessible from derived contracts.

Choosing the Right Visibility

    public: Use for functions and variables that need to be accessed from outside the contract and should be part of the public interface.
    internal: Use for functions and variables that should be accessible within the contract and its derived contracts, but not externally.
    external: Use for functions that are meant to be called from outside the contract only, not from within the contract itself.
    private: Use for functions and variables that should be hidden from other contracts, including derived ones, and only accessible within the contract.

Selecting the appropriate visibility specifier enhances the security and clarity of your smart contracts, ensuring that only intended parties have access to certain functions and state variables.

# Good to know
- uint are integer data types but they dont accept negative values.
- Padding of 0 takes place if initialized characters are less than the byte size
- In bytes data types (eg bytes3 public arr3="abc", we can also access a character like arr3[0])
- Indexing in bytes is 0-based.
- When using require, if condition in require fails, the entire transaction is reverted, thus making transaction binary-type (that is either it happens completely or we reach back the stage before the transaction)
- We can have several modifiers in a function. The first modifier gets called first, second one after it and so in & in case the first one (or any previous ones fail) the entire transaction is reverted.


That's all folks.......
Stay tuned for more.