Chapter 4: Solidity- Control Structures (Operators, Loops, Statements, Strings, Arrays, Enums, Structs, Mappings, Conversions)

Chapter 4: Solidity- Control Structures (Operators, Loops, Statements, Strings, Arrays, Enums, Structs, Mappings, Conversions)

In Solidity, manage structures are fundamental for implementing choice-making and iteration in clever contracts.Conditional Statements like if, else if, and else execute code blocks based on specific conditions. Loops such as for, while, and do-while facilitate repetitive actions, though they should be used cautiously to avoid high gas costs. Break and continue are used to control loop execution by terminating or skipping iterations. Solidity also supports inline assembly for low-level control. These structures enable the implementation of complex logic securely and efficiently.

Solidity – Operators

1. Arithmetic Operators

These are used for mathematical calculations.

OperatorDescriptionExample (a = 10, b = 5)Result
+Additiona + b15
Subtractiona – b5
*Multiplicationa * b50
/Divisiona / b2
%Modulus (remainder)a % b0

2. Comparison Operators

These compare two values and return true or false.

OperatorDescriptionExample (a = 10, b = 5)Result
==Equal toa == bfalse
!=Not equal toa != btrue
>Greater thana > btrue
<Less thana < bfalse
>=Greater or equala >= btrue
<=Less or equala <= bfalse

3. Logical Operators

These are used to combine multiple conditions.

OperatorDescriptionExample (x = true, y = false)Result
&&Logical ANDx && yfalse
||Logical ORx || ytrue
!Logical NOT!xfalse

4. Bitwise Operators

These perform operations on the binary representation of numbers.

OperatorDescriptionExample (a = 10, b = 4)Result (Binary)Result (Decimal)
&ANDa & b0000 01004
|ORa | b0000 111014
^XORa ^ b0000 101010
<<Left shifta << 10001 010020
>>Right shifta >> 10000 01015

5. Assignment Operators

These assign values to variables and can perform operations simultaneously.

OperatorDescriptionExample (a = 10)Result
=Assigna = 55
+=Add and assigna += 515
-=Subtract and assigna -= 55
*=Multiply and assigna *= 550
/=Divide and assigna /= 52
%=Modulus and assigna %= 50

6. Unary Operators

Operate on a single value.

OperatorDescriptionExample (a = 10)Result
++Increment (adds 1)a++11
Decrement (subtracts 1)a–9

7. Special Operators in Solidity

  • delete: Resets a variable to its default value.

    uint x = 10;
    delete x; // x becomes 0

  • Ternary Operator (condition ? ifTrue : ifFalse): A shorthand for if-else.

    uint a = 10;
    uint b = 20;
    uint result = (a > b) ? a : b; // result is 20

Solidity – Loops

In Solidity, loops are used to repeat a block of code multiple times. Solidity supports three types of loops:

  • for loop
  • while loop
  • do...while loop

1. for Loop

The for loop runs a block of code a specific number of times.

Syntax:

for (initialization; condition; update) {
    // Code to run
}

Example:

pragma solidity >=0.8.0 <0.9.0;
contract ForLoopExample {
    function sumNumbers() public pure returns (uint) {
        uint sum = 0;
        for (uint i = 1; i <= 5; i++) {
            sum += i; // Add i to sum
        }
        return sum; // Output: 15
    }
}

2. while Loop

The while loop runs a block of code as long as a condition is true.

Syntax:

while (condition) {
    // Code to run
}

Example:

pragma solidity >=0.8.0 <0.9.0; 
contract WhileLoopExample {
    function countNumbers() public pure returns (uint) {
        uint count = 0;
        uint i = 1;
        while (i <= 5) {
            count += i; // Add i to count
            i++;        // Increment i
        }
        return count; // Output: 15
    }
}

3. do...while Loop

The do...while loop runs the block of code at least once, then repeats it as long as the condition is true.

Syntax:

do {
    // Code to run
} while (condition);

Example:

pragma solidity >=0.8.0 <0.9.0;
contract DoWhileLoopExample {
    function sumNumbers() public pure returns (uint) {
        uint sum = 0;
        uint i = 1;
        do {
            sum += i; // Add i to sum
            i++;      // Increment i
        } while (i <= 5);
        return sum; // Output: 15
    }
}

Key Points to Remember

  • Gas Costs: Loops in Solidity can be expensive in terms of gas usage, especially if they run for many iterations. Avoid unbounded loops (e.g., loops that depend on user input or data size).
  • Break: Use break to exit a loop early if a condition is met.
for (uint i = 0; i < 10; i++) { if (i == 5) { break; // Exit loop when i is 5 } }

Continue: Use continue to skip the rest of the loop for the current iteration and move to the next one.v

 for (uint i = 0; i < 10; i++) { if (i % 2 == 0) { continue; // Skip even numbers } }

Summary

  • Use for loops when the number of iterations is known.
  • Use while loops when the condition needs to be checked before running the loop.
  • Use do...while loops when the block of code must run at least once.

Solidity – Decision Making Statements

In Solidity, decision-making statements are used to control the flow of execution based on conditions. They allow you to perform different actions based on whether certain conditions are true or false.

1. if Statement

The if statement runs a block of code only if a specified condition is true.

Syntax:

if (condition) {
    // Code to run if the condition is true
}

Example:

pragma solidity >=0.8.0 <0.9.0;
contract IfExample {
    function checkNumber(uint num) public pure returns (string memory) {
        if (num > 10) {
            return "Number is greater than 10";
        }
        return "Number is 10 or less";
    }
}

2. if...else Statement

The if...else statement runs one block of code if a condition is true and another block if it is false.

Syntax:

if (condition) {
    // Code to run if the condition is true
} else {
    // Code to run if the condition is false
}

Example:

pragma solidity >=0.8.0 <0.9.0;
contract IfElseExample {
    function checkEvenOdd(uint num) public pure returns (string memory) {
        if (num % 2 == 0) {
            return "Number is even";
        } else {
            return "Number is odd";
        }
    }
}

3. if...else if...else Statement

This statement checks multiple conditions. It runs the first block of code with a true condition; otherwise, it moves to the next condition.

Syntax:

if (condition1) {
    // Code to run if condition1 is true
} else if (condition2) {
    // Code to run if condition2 is true
} else {
    // Code to run if none of the conditions are true
}

Example:

pragma solidity >=0.8.0 <0.9.0;
contract ElseIfExample {
    function grade(uint marks) public pure returns (string memory) {
        if (marks >= 90) {
            return "A";
        } else if (marks >= 75) {
            return "B";
        } else if (marks >= 50) {
            return "C";
        } else {
            return "Fail";
        }
    }
}

4. require Statement

The require statement is used to check a condition. If the condition is false, it reverts the transaction with an error message.

Syntax:

require(condition, "Error message");

Example:

pragma solidity >=0.8.0 <0.9.0;
contract RequireExample {
    function transfer(uint amount) public pure {
        require(amount > 0, "Amount must be greater than zero");
        // Code to transfer funds
    }
}

5. assert Statement

The assert statement checks a condition and should only be used to detect bugs. If the condition is false, it reverts the transaction with an error and consumes all gas.

Syntax:

assert(condition);

Example:

pragma solidity >=0.8.0 <0.9.0;
contract AssertExample {
    function testAssert(uint num) public pure {
        assert(num != 0); // Ensure num is not zero
        // Code to execute if num is not zero
    }
}

6. revert Statement

The revert statement stops the execution and reverts the transaction with an optional error message. It is often used with complex conditions.

Syntax:

revert("Error message");

Example:

pragma solidity >=0.8.0 <0.9.0;
contract RevertExample {
    function withdraw(uint balance, uint amount) public pure {
        if (amount > balance) {
            revert("Insufficient balance");
        }
        // Code to withdraw funds
    }
}

Key Points

  • Gas Efficiency: Use decision-making statements judiciously as complex logic can increase gas costs.
  • require vs assert:
    • Use require for user input validation or conditions external to the contract.
    • Use assert for internal checks and invariants.
  • Fallback and Recovery: Always provide meaningful error messages to make debugging easier.

Summary

  • Use if statements for simple conditional checks.
  • Use require for input validation.
  • Use assert to verify internal logic and detect bugs.
  • Use revert to handle complex failure conditions.

Solidity – Strings

In Solidity, strings are used to handle text data. They represent a sequence of characters, such as "Hello, world!".

Key Features of Strings

  • Dynamic Size: Strings can store text of variable length (unlike fixed-size arrays).
  • Stored in Memory or Storage:
    • Memory: Temporary data used during function execution.
    • Storage: Persistent data stored on the blockchain.
  • Immutable by Default: Once deployed, string data cannot be changed directly; instead, you assign new values.

How to Use Strings

Declaring and Assigning

string public greeting = "Hello, Solidity!";

Reading the String

You can access the greeting value since it’s marked as public.

String Concatenation

Solidity does not directly support concatenation with operators like +. Instead, use libraries like Strings from OpenZeppelin or write your custom function.

Limitations

  • No Indexing: You can’t directly access characters like string[0].
  • Expensive Gas: Storing and manipulating strings on-chain is costly due to the high gas fees.

Common Use Cases

  • Store names, messages, or descriptions.
  • Use in off-chain applications where the string data is sent via events.

Example Contract


// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;
contract StringExample {
    string public message; // A public string variable

    // Function to set a new message
    function setMessage(string memory newMessage) public {
        message = newMessage;
    }

    // Function to get the length of the string
    function getMessageLength() public view returns (uint) {
        return bytes(message).length; // Convert to bytes to calculate length
    }
}
    

Key Notes

bytes vs. string:

  • bytes is a lower-level type, more efficient for certain operations.
  • Use bytes for fixed-length operations and string for user-readable text.

Solidity – Arrays

In Solidity, arrays are used to store multiple values of the same type. Arrays can be fixed-size or dynamic.

Key Features of Arrays

  • Fixed-Size Arrays: Size is defined during declaration and cannot change.
    Example: uint[3] fixedArray;
  • Dynamic Arrays: Can grow or shrink in size.
    Example: uint[] dynamicArray;
  • Array Storage:
    • Memory: Temporary during function execution.
    • Storage: Persistent on the blockchain.

How to Use Arrays

Declaring Arrays

// Fixed-size array
uint[3] numbers = [1, 2, 3];

// Dynamic array
uint[] numbers;

Accessing Elements

uint firstNumber = numbers[0]; // Accesses the first element

Adding Elements (Dynamic Arrays)

numbers.push(4); // Adds 4 to the array

Getting Length

uint length = numbers.length;

Example Contract


// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;
contract ArrayExample {
    uint[] public numbers; // A dynamic array

    // Add a number to the array
    function addNumber(uint num) public {
        numbers.push(num);
    }

    // Get a number at a specific index
    function getNumber(uint index) public view returns (uint) {
        require(index < numbers.length, "Index out of bounds");
        return numbers[index];
    }

    // Get the length of the array
    function getArrayLength() public view returns (uint) {
        return numbers.length;
    }
}
    

Limitations

  • Expensive Gas: Storing large arrays is costly.
  • No Built-in Remove: You can only remove by overwriting or shifting manually.

Solidity – Enums

In Solidity, enums are user-defined types used to create a list of named constants. They help make the code more readable and maintainable by replacing magic numbers with meaningful names.

Key Features of Enums

  • Fixed Values: Enums define a fixed set of possible values.
  • Internally Represented as Integers: The first value is assigned 0, the second is 1, and so on.
  • Useful for States: Often used to represent the state of a contract (e.g., Active, Inactive, Completed).

How to Use Enums

Declaring an Enum

enum Status { Pending, Shipped, Delivered }

Accessing Enum Values

Status public currentStatus = Status.Pending;

Updating Enum Values

currentStatus = Status.Shipped;

Getting the Integer Value of an Enum

uint statusIndex = uint(currentStatus); // Converts enum to integer

Example Contract


// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;
contract EnumExample {
    // Declare an enum
    enum Status { Pending, Shipped, Delivered }

    // Use the enum
    Status public currentStatus;

    // Set the initial status
    constructor() {
        currentStatus = Status.Pending;
    }

    // Update the status
    function setStatus(Status newStatus) public {
        currentStatus = newStatus;
    }

    // Get the status as an integer
    function getStatusIndex() public view returns (uint) {
        return uint(currentStatus);
    }
}
    

Benefits

  • Improves code readability.
  • Prevents invalid states (e.g., you can’t assign a value outside the defined enum options).

Limitations

  • Cannot Add New Values: Once deployed, enums cannot be modified.
  • No String Representation: Internally, enums are stored as integers.

Solidity – Structs

In Solidity, structs are custom data types used to group related variables. They allow you to create complex types that are easy to work with and maintain.

Key Features of Structs

  • Custom Data Types: Combine variables of different types into a single unit.
  • Flexibility: Useful for storing detailed data like user profiles or transaction records.
  • Defined Once: Define a struct once and reuse it multiple times.

How to Use Structs

Declaring a Struct

struct User {
    uint id;
    string name;
    bool isActive;
}

Initializing a Struct

// By setting each field
user1 = User(1, "Alice", true);

// Using named assignment
user1 = User({id: 1, name: "Alice", isActive: true});

Accessing Struct Fields

uint userId = user1.id; // Access the `id` field

Structs in Arrays

User[] public users;
users.push(User(1, "Alice", true));

Example Contract


// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;
contract StructExample {
    // Define a struct
    struct User {
        uint id;
        string name;
        bool isActive;
    }

    // Store users in an array
    User[] public users;

    // Add a new user
    function addUser(uint id, string memory name, bool isActive) public {
        users.push(User(id, name, isActive));
    }

    // Get a user by index
    function getUser(uint index) public view returns (uint, string memory, bool) {
        User memory user = users[index];
        return (user.id, user.name, user.isActive);
    }
}
    

Benefits

  • Simplifies code by grouping related data.
  • Useful for complex applications (e.g., tracking users, orders, or products).

Limitations

  • Structs cannot contain mappings or dynamic arrays directly if used in public state variables.
  • High gas costs when used in large quantities.

Solidity – Mappings

In Solidity, mappings are data structures that store key-value pairs. They are like hash tables in other programming languages and are very efficient for storing and retrieving data based on a key.

Key Features of Mappings

  • Key-Value Storage: A mapping links a unique key to a value.
  • Uninitialized Keys Return Default Values: If a key has not been assigned a value, it will return the default value of the value type (e.g., 0 for integers, false for booleans, and "" for strings).
  • Efficient Lookup: Optimized for constant-time retrieval of values.
  • Cannot Iterate: Mappings do not support iteration, so you cannot directly loop through them.

How to Use Mappings

Declaring a Mapping

mapping(address => uint) public balances;

Storing Data in a Mapping

balances[msg.sender] = 100;

Retrieving Data from a Mapping

uint balance = balances[msg.sender];

Deleting Data in a Mapping

delete balances[msg.sender];

Example Contract


// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;
contract MappingExample {
    // Define a mapping
    mapping(address => uint) public balances;

    // Set a balance for an address
    function setBalance(uint amount) public {
        balances[msg.sender] = amount;
    }

    // Get a balance for an address
    function getBalance(address account) public view returns (uint) {
        return balances[account];
    }

    // Delete a balance
    function deleteBalance() public {
        delete balances[msg.sender];
    }
}
    

Benefits

  • Efficient Data Access: Fast access to stored data.
  • Custom Keys: Keys can be of types like address, uint, etc.
  • Simplicity: Easy to implement and use.

Limitations

  • No Iteration: Mappings cannot be looped over to access all keys or values.
  • No Length Property: Unlike arrays, mappings do not have a size or length property.
  • One-Way Relationship: Cannot retrieve keys based on values.

Solidity – Conversions

In Solidity, conversions allow you to change data from one type to another. This includes conversions between integers, addresses, bytes, and strings.

Key Concepts

  • Implicit Conversion: Automatic conversion when no data loss is possible.
  • Explicit Conversion: Manual type casting required when data loss might occur.
  • Restrictions: Direct conversion between incompatible types (e.g., string to uint) is not allowed.

Examples

Implicit Conversion

uint8 smallNumber = 42;
uint256 largeNumber = smallNumber; // Implicit conversion

Explicit Conversion

uint256 bigNumber = 1000;
uint8 smallNumber = uint8(bigNumber); // Explicit conversion

Address and uint160 Conversion

address addr = msg.sender;
uint160 addrNumber = uint160(addr); // Address to uint160
address newAddr = address(addrNumber); // uint160 to address

String to Bytes

string memory str = "Hello, Solidity!";
bytes memory b = bytes(str); // Convert string to bytes

Bytes to String

bytes memory b = bytes("Hello");
string memory str = string(b); // Convert bytes to string

Example Contract


// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;
contract ConversionExample {
    function convertNumbers() public pure returns (uint8, uint256) {
        uint256 bigNumber = 1000;
        uint8 smallNumber = uint8(bigNumber); // Explicit conversion
        uint256 backToBig = smallNumber; // Implicit conversion
        return (smallNumber, backToBig);
    }

    function convertAddress() public view returns (address, uint160) {
        address addr = msg.sender;
        uint160 addrNumber = uint160(addr); // Address to uint160
        address newAddr = address(addrNumber); // uint160 to address
        return (newAddr, addrNumber);
    }

    function stringToBytes(string memory input) public pure returns (bytes memory) {
        return bytes(input);
    }

    function bytesToString(bytes memory input) public pure returns (string memory) {
        return string(input);
    }
}
    

For further details or examples, feel free to ask!

2 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *