The Ultimate Solidity Cheat Sheet for Beginners
Tags: web3, solidity, blockchain, beginners
Published: 2023-07-13 17:48:20

Welcome to the world of Solidity, the object-oriented, high-level language for implementing smart contracts on the Ethereum Blockchain.In this guide, we will dive into the fundamental concepts of Solidity and provide you with a detailed understanding of its syntax, features, and best practices. Whether you're a beginner or an experienced developer, this guide will serve as a valuable resource for mastering Solidity and building secure and efficient smart contracts.

___This guide is brought to you by [Hack Solidity](https://bento.me/hacksolidity), a platform dedicated to empowering individuals with blockchain knowledge. Our goal is to equip you with the necessary skills and insights to excel in the rapidly evolving blockchain industry.___

| Table of Contents               |
|--------------------------------|
| [What is Solidity](#what-is-solidity) |
| [Setting up your Development Environment](#setting-up-your-environment) |
| [Basic Syntax and Structure](#basic-syntax-and-structure) |
| [Data Types](#data-types) |
| [State Variables](#state-variables) |
| [Functions](#functions) |
| [Events](#events) |
| [Struct](#struct) |
| [Control Structures](#control-structures-in-solidity) |
| [Error Handling](#error-handling) |
| [Inheritance and Interfaces](#inheritance-and-interfaces) |
| [Import and Libraries](#import-and-libraries) | |
| [ABI](#abi) |
| [Cryptographic Functions](#cryptographic-functions) |
| [Solidity Version Pragma](#solidity-version-pragma) |
| [Conclusion](#conclusion) |


![Solidity Logo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o3pk09xm55g7tzoqi544.png)
## What is Solidity?

Solidity is a statically-typed programming language designed for developing smart contracts that run on the Ethereum Virtual Machine (EVM). Smart contracts are self-executing contracts with the terms of the agreement directly written into code. They allow trusted transactions and agreements to be carried out among disparate, anonymous parties without the need for a central authority, legal system, or external enforcement mechanism.

## Setting Up Your Environment
Before you start coding, you'll need to set up your development environment. The most common tools for Solidity development are:

**[Remix IDE](https://remix.ethereum.org/):** An online IDE specifically built for smart contract development with built-in static analysis, test blockchain, and debugging tools.

**[Truffle](https://trufflesuite.com/):** A popular development framework with built-in smart contract compilation, linking, deployment, and binary management.

**[Ganache](https://trufflesuite.com/ganache/):** A personal blockchain for Ethereum development you can use to deploy contracts, develop applications, and run tests.

To get started, head over to [Remix IDE](https://remix.ethereum.org/) on your browser.


## Basic Syntax and Structure
A Solidity smart contract is similar to a class in object-oriented languages. Each contract can contain declarations of State Variables, Functions, Function Modifiers, Events, and Struct Types. Here's a basic structure of a Solidity contract:

```solidity
pragma solidity ^0.8.0;

contract MyContract {
    // State variables
    uint256 public myVariable;
    
    // Events
    event ValueUpdated(uint256 newValue);

    // Constructor
    constructor() {
        myVariable = 0;
    }
    
    // Function modifiers
    modifier onlyOwner() {
        require(msg.sender == owner, "Only contract owner can call this function");
        _;
    }

    // Functions
    function setValue(uint256 newValue) public onlyOwner {
        myVariable = newValue;
        emit ValueUpdated(newValue);
    }
}
```

In the above example:
- The contract is declared using the `contract` keyword followed by the contract name `MyContract`.
- The `pragma` directive specifies the compiler version to be used (in this case, Solidity version 0.8.0 or higher).
- The `myVariable` state variable is declared as a publicly accessible `uint256`.
- The `ValueUpdated` event is defined, which will be emitted whenever the value of `myVariable` is updated.
- The constructor initializes `myVariable` to 0 when the contract is deployed.
- The `onlyOwner` modifier is defined to restrict access to certain functions, allowing only the contract owner to call them.
- The `setValue` function is defined, which updates the value of `myVariable` with the provided `newValue`. It can only be called by the contract owner due to the `onlyOwner` modifier.
- The `emit` keyword is used to trigger the `ValueUpdated` event and emit the new value.

**Note:** This example shows a simplified structure of a Solidity contract. In real-world scenarios, smart contracts may have more complex functionality, additional state variables, multiple functions, and interactions with other contracts or external systems.
 

## Data types

| Data Type  | Description                                             | Example                               |
|------------|---------------------------------------------------------|---------------------------------------|
| `bool`     | Boolean value (`true` or `false`)                        | `bool myBool = true;`                 |
| `uint`     | Unsigned integer                                       | `uint256 myUint = 42;`                |
| `int`      | Signed integer                                         | `int256 myInt = -10;`                 |
| `address`  | Ethereum address                                       | `address myAddress = 0x123...7890;`    |
| `string`   | Textual data                                           | `string myString = "Hello, Solidity!";`|
| `array`    | Collection of elements of the same type                 | `uint256[] myDynamicArray;`           |
| `mapping`  | Key-value pairs                                        | `mapping(address => uint256) balances;`|
| `struct`   | Custom data structure                                  | `struct Person { string name; uint256 age; } Person myPerson = Person("Alice", 25);`|
| `enum`     | Enumerated list of possible values                      | `enum Status { Active, Inactive, Suspended } Status myStatus = Status.Active;`|

```solidity
// Contract showcasing Solidity data types
pragma solidity ^0.8.0;

contract DataTypeExample {
    bool public myBool;
    uint256 public myUint;
    int256 public myInt;
    address public myAddress;
    string public myString;
    uint256[] public myDynamicArray;
    mapping(address => uint256) public balances;
    struct Person {
        string name;
        uint256 age;
    }
    Person public myPerson;
    enum Status { Active, Inactive, Suspended }
    Status public myStatus;

    constructor() {
        myBool = true;
        myUint = 42;
        myInt = -10;
        myAddress = 0x123...7890;
        myString = "Welcome to Hack Solidity!";
        myDynamicArray.push(1);
        balances[msg.sender] = 1000;
        myPerson = Person("Alice", 25);
        myStatus = Status.Active;
    }
}
```

## State Variables

State variables are an important concept in Solidity as they represent the persistent storage of data within a smart contract. These variables hold their values between function calls and are stored on the blockchain. Here's an extensive overview of state variables in Solidity:

1.**Declaration**: 

```solidity
contract MyContract {
    uint256 public myVariable; // Public state variable
    address private owner;     // Private state variable
    uint256 internal myInternalVariable; // Internal state variable
    string public constant myConstant = "Welcome to Hack Solidity!"; // Public constant state variable
}
```

2.**Visibility Modifiers**: The visibility modifier determines how the state variable can be accessed. 

- `public`: The variable is accessible from within the contract and externally.
- `private`: The variable is only accessible within the contract and not externally.
- `internal`: The variable is accessible within the contract and any derived contracts.
- `external`: The variable is only accessible externally and not within the contract.

3.**Accessing State Variables**: State variables can be accessed and modified by both internal and external functions within the contract. 

- Internal Access:
```solidity
function updateVariable(uint256 newValue) internal {
    myVariable = newValue;
}
```

- External Access:
```solidity
function getVariable() external view returns (uint256) {
    return myVariable;
}
```

4.**Constant State Variables**: Constant state variables hold values that remain the same throughout the execution of the contract. They can be used for storing fixed values or configuration data.

```solidity
string public constant GREETING = "Hello!";
uint256 public constant MAX_VALUE = 100;
```

Note that constant state variables can only be of types `bool`, `int`, `uint`, `address`, `string`, or arrays of these types.

5.**Default Values**: State variables are assigned default values if not explicitly initialized in the constructor or elsewhere.

- For value types (`bool`, `uint`, `int`, etc.), the default value is `0`.
- For `address`, the default value is `address(0)`.
- For `string`, the default value is an empty string (`""`).
- For reference types (`arrays`, `structs`, etc.), the default value is an empty instance.


## Functions

Functions in Solidity are an essential part of smart contracts. They define the behavior and operations that can be performed on a contract's data. Here's an overview of functions in Solidity:

1.**Function Declaration**:

```solidity
contract MyContract {
    // Public function without parameters and return value
    function myFunction() public {
        // Function body
    }

    // External function with parameters and return value
    function add(uint256 a, uint256 b) external pure returns (uint256) {
        // Function body
        return a + b;
    }
}
```

2.**Visibility Modifiers**: Functions can have visibility modifiers that control their accessibility.

- `public`: The function can be called internally from within the contract and externally from other contracts or accounts.
- `private`: The function is only accessible from within the contract.
- `internal`: The function is accessible from within the contract and any derived contracts.
- `external`: The function can be called externally, but not from within the contract itself.

3.**Function Parameters**:

```solidity
function myFunction(uint256 param1, address param2) public {
    // Function body
}
```

4.**Return Values**: Functions can return values using the `returns` keyword, followed by the return type. Multiple return values can be specified using parentheses.

```solidity
function getSumAndDifference(uint256 a, uint256 b) public pure returns (uint256, uint256) {
    uint256 sum = a + b;
    uint256 difference = a - b;
    return (sum, difference);
}
```

5.**Function Modifiers**: Modifiers are used to modify the behavior of functions. They are defined separately and can be applied to functions using the `modifier` keyword.

```solidity
modifier onlyOwner() {
    require(msg.sender == owner, "Only contract owner can call this function");
    _;
}

function myFunction() public onlyOwner {
    // Function body
}
```

6.**Function Overloading**: Solidity supports function overloading, which means you can have multiple functions with the same name but different parameter lists. The compiler determines which function to call based on the provided arguments.

```solidity
function process(uint256 data) public {
    // Function body
}

function process(uint256[] memory dataArray) public {
    // Function body
}
```

7.**Fallback and Receive Functions**: Solidity allows defining a fallback function and a receive function in a contract.

- Fallback Function: The fallback function is executed when a contract receives a message without any matching function or value transfer. It is declared without a function name using the `fallback` keyword.

```solidity
fallback() external {
    // Fallback function body
}
```

- Receive Function: The receive function is executed when a contract receives a plain Ether transfer without any data. It is declared without a function name using the `receive` keyword.

```solidity
receive() external payable {
    // Receive function body
}
```

## Events

Events allow external applications to listen to and react to specific occurrences within the contract. Here's an overview of events in Solidity:

1.**Event Declaration**:

```solidity
contract MyContract {
    // Event declaration with parameters
    event MyEvent(address indexed sender, uint256 value);
}
```

2.**Event Emission**: To emit an event and provide the associated data, you can use the `emit` keyword followed by the event name and the corresponding values.

```solidity
function myFunction() public {
    // Emitting an event
    emit MyEvent(msg.sender, 100);
}
```

3.**Event Parameters**: Events can include parameters that define the information to be emitted. These parameters can be indexed or non-indexed.

- Indexed Parameters: Indexed parameters enable efficient filtering and searching of events. Up to three parameters can be indexed per event.

```solidity
event MyEvent(address indexed sender, uint256 indexed id, string message);
```

- Non-indexed Parameters: Non-indexed parameters are included in the event but cannot be used for filtering.

```solidity
event MyEvent(address sender, uint256 value, string message);
```

4.**Event Subscription**: External applications or other contracts can subscribe to events emitted by a contract and listen for specific occurrences. They can do this by using the contract's address and the event's signature.

```solidity
// Event subscription example
MyContract myContract = MyContract(contractAddress);
myContract.MyEvent().listen(callbackFunction);
```

5.**Event Log**: When an event is emitted, a log entry is created on the blockchain, capturing the event's data. Event logs are stored in the transaction receipt and can be accessed using web3 libraries or blockchain explorers.

6.**Event Usage**: Events are commonly used for logging and providing information about specific state changes or significant occurrences within a contract. They serve as a means of communication between contracts and external systems, facilitating real-time updates and data synchronization.

```solidity
contract MyContract {
    event ValueUpdated(uint256 newValue);

    uint256 public myValue;

    function setValue(uint256 newValue) public {
        myValue = newValue;
        emit ValueUpdated(newValue);
    }
}
```

In the above example, the `ValueUpdated` event is emitted whenever the `setValue` function is called, allowing external applications to listen to the event and receive updates whenever `myValue` is changed.

## Struct

Structs in Solidity allow you to define custom data structures to group related variables. Here's a brief overview:

1.**Struct Declaration**: Structs are declared using the `struct` keyword within the contract scope.

```solidity
struct Person {
    string name;
    uint256 age;
}
```

2.**Struct Usage**: Structs can be used to create instances and access their variables.

```solidity
Person public myPerson;

function createPerson(string memory _name, uint256 _age) public {
    myPerson = Person(_name, _age);
}
```

3.**Struct Arrays**: Structs can be used to create arrays holding multiple instances.

```solidity
Person[] public people;

function addPerson(string memory _name, uint256 _age) public {
    Person memory newPerson = Person(_name, _age);
    people.push(newPerson);
}
```

4.**Nested Structs**: Structs can be nested within other structs.

```solidity
struct Person {
    string name;
    uint256 age;
    Address contactAddress;
}

struct Address {
    string street;
    string city;
}

Person public myPerson;

function createPerson(string memory _name, uint256 _age, string memory _street, string memory _city) public {
    Address memory newAddress = Address(_street, _city);
    myPerson = Person(_name, _age, newAddress);
}
```

## Control structures in Solidity 

1.**If-Else Statements:** If-else statements allow you to conditionally execute code based on a certain condition.

```solidity

function checkValue(uint256 value) public pure returns (string memory) {
    if (value > 10) {
        return "Value is greater than 10";
    } else if (value == 10) {
        return "Value is equal to 10";
    } else {
        return "Value is less than 10";
    }
}
```

2.**While Loop**: While loops execute a block of code repeatedly as long as a specified condition is true.

```solidity
function countDown(uint256 startValue) public pure returns (uint256) {
    while (startValue > 0) {
        startValue--;
    }
    return startValue;
}
```

3.**For Loop**: For loops allow you to execute a block of code repeatedly for a specific number of iterations.

```solidity
function sumArray(uint256[] memory numbers) public pure returns (uint256) {
    uint256 sum = 0;
    for (uint256 i = 0; i < numbers.length; i++) {
        sum += numbers[i];
    }
    return sum;
}
```

4.**Do-While Loop:** Do-while loops execute a block of code at least once and continue executing as long as a specified condition is true.

```solidity
function countUp(uint256 startValue) public pure returns (uint256) {
    do {
        startValue++;
    } while (startValue < 10);
    return startValue;
}
```

## Error Handling

1.**require Statement**: The `require` statement is used to validate conditions and revert the transaction if the condition evaluates to `false`. It is commonly used for input validation and contract pre-conditions.

```solidity
function deposit(uint256 amount) public {
    require(amount > 0, "Amount must be greater than zero");
    // Deposit logic
}
```

2.**assert Statement**: The `assert` statement is used to check for conditions that should never be false. If the condition evaluates to `false`, it indicates an internal error in the contract, and the transaction is reverted.

```solidity
function divide(uint256 numerator, uint256 denominator) public pure returns (uint256) {
    assert(denominator != 0);
    return numerator / denominator;
}
```

3.**revert Statement**: The `revert` statement allows you to explicitly revert the transaction and provide an optional error message. It is typically used for explicit error handling and can include custom error messages to provide more information.

```solidity
function withdraw(uint256 amount) public {
    if (amount > balance) {
        revert("Insufficient balance");
    }
    // Withdraw logic
}
```

4.**Error Propagation**: Errors can be propagated from one function to another by using the `revert` statement or by propagating them through return values or events. It is important to handle and propagate errors appropriately to ensure contract integrity.

```solidity
function transfer(address recipient, uint256 amount) public {
    require(balance >= amount, "Insufficient balance");
    // Transfer logic
    if (errorOccurred) {
        revert("Transfer failed");
    }
}
```

## Inheritance and Interfaces

1.**Inheritance**:
Inheritance allows a contract to inherit properties and functions from another contract, referred to as the base or parent contract. This promotes code reuse and enables contracts to build upon existing functionality. To inherit from a contract, the `is` keyword is used followed by the name of the parent contract.

```solidity
contract ParentContract {
    // Parent contract variables and functions
}

contract ChildContract is ParentContract {
    // Child contract variables and functions
}
```

The child contract `ChildContract` inherits all the variables and functions from the parent contract `ParentContract`. It can also override inherited functions or add new functions.

2.**Interfaces**:
Interfaces define a set of function signatures that must be implemented by any contract that adopts the interface. They provide a way to define standardized communication protocols between contracts. Interfaces do not contain any function bodies; they only specify the function names, input parameters, and return types.

```solidity
interface MyInterface {
    function myFunction(uint256 value) external;
    function myOtherFunction() external view returns (uint256);
}
```

Contracts that adopt an interface must implement all the functions defined in the interface. This ensures that contracts conform to a specific set of requirements and can interact with each other based on the interface's defined functions.

```solidity
contract MyContract is MyInterface {
    function myFunction(uint256 value) external {
        // Implementation of myFunction
    }

    function myOtherFunction() external view returns (uint256) {
        // Implementation of myOtherFunction
    }
}
```

In this example, `MyContract` implements the functions defined in `MyInterface`. It provides the necessary function implementations to comply with the interface's requirements.

## Global Variables 

| Global Variable  | Description                                                                                                                |
|------------------|----------------------------------------------------------------------------------------------------------------------------|
| `msg.sender`     | The address of the sender of the current message (current caller or contract).                                            |
| `msg.value`      | The amount of ether (in wei) sent with the current message.                                                                |
| `msg.data`       | The complete calldata of the current message.                                                                              |
| `block.number`   | The current block number.                                                                                                  |
| `block.timestamp`| The timestamp of the current block (in seconds since the Unix epoch).                                                      |
| `block.difficulty`| The difficulty of the current block.                                                                                       |
| `block.coinbase` | The address of the miner who mined the current block.                                                                      |
| `block.gaslimit` | The gas limit of the current block.                                                                                        |
| `tx.origin`      | The address of the sender of the transaction (originating external user).                                                  |
| `address(this)`  | The address of the current contract.                                                                                       |
| `address payable(this)` | The payable address of the current contract, allowing sending and receiving of ether.                                  |
| `this.balance`   | The balance (in wei) of the current contract.                                                                              |

## Imports and Libraries 

In Solidity, importing other contracts and using libraries are important mechanisms for code organization, modularity, and reusability. Here's an overview of import statements and libraries in Solidity:

1.**Import Statements**:
Import statements are used to include external Solidity files or libraries into your contract. They allow you to access the code and definitions from the imported files within your contract.

```solidity
import "./MyContract.sol"; // Importing a local file
import "github.com/username/MyLibrary.sol"; // Importing a file from a remote location
```

By using import statements, you can break your code into separate files, import external contracts, or include libraries to leverage existing functionality.

2.**Libraries**:
Libraries are reusable code components in Solidity that allow you to define and deploy shared utility functions. They are similar to contracts but cannot have any storage variables or receive Ether. Libraries are typically used to reduce code duplication and provide commonly used functionalities.

```solidity
library MathLibrary {
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        return a + b;
    }
}
```

To use a library, you can either deploy it as a separate contract or use the `using` keyword to attach it to a data type. The `using` keyword allows you to access library functions as if they were member functions of the data type.

```solidity
using MathLibrary for uint256;

function myFunction(uint256 a, uint256 b) public pure returns (uint256) {
    return a.add(b); // Accessing library function
}
```

In the above example, the `MathLibrary` provides the `add` function, which can be accessed using the `using` keyword on the `uint256` data type.

> It's worth noting that starting from Solidity version 0.8.0, external libraries are preferred over internal libraries. External libraries are deployed separately and linked to the contract during deployment, whereas internal libraries are included within the bytecode of the contract itself.

## ABI

| Function                                                  | Description                                                                                                                                                                                                                                                  |
|-----------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `abi.decode(bytes memory encodedData, (...)) returns (...)`             | ABI-decodes the provided data. The types are given in parentheses as the second argument. Example: `(uint256 a, uint256[2] memory b, bytes memory c) = abi.decode(data, (uint256, uint256[2], bytes))`                                                          |
| `abi.encode(...) returns (bytes memory)`                     | ABI-encodes the given arguments                                                                                                                                                                                                                               |
| `abi.encodePacked(...) returns (bytes memory)`                | Performs packed encoding of the given arguments. Note that this encoding can be ambiguous!                                                                                                                                                                  |
| `abi.encodeWithSelector(bytes4 selector, ...) returns (bytes memory)` | ABI-encodes the given arguments starting from the second and prepends the given four-byte selector                                                                                                                                                          |
| `abi.encodeCall(function functionPointer, (...)) returns (bytes memory)` | ABI-encodes a call to `functionPointer` with the arguments found in the tuple. Performs a full type-check, ensuring the types match the function signature. The result is equal to `abi.encodeWithSelector(functionPointer.selector, (...))`                   |
| `abi.encodeWithSignature(string memory signature, ...) returns (bytes memory)` | Equivalent to `abi.encodeWithSelector(bytes4(keccak256(bytes(signature))), ...)`                                                                                                               |

## Cryptographic Functions

| Function                                                      | Description                                                                                                           |
|---------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------|
| `keccak256(bytes memory) returns (bytes32)`                    | Compute the Keccak-256 hash of the input.                                                                             |
| `sha256(bytes memory) returns (bytes32)`                       | Compute the SHA-256 hash of the input.                                                                                |
| `ripemd160(bytes memory) returns (bytes20)`                    | Compute the RIPEMD-160 hash of the input.                                                                             |
| `ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)` | Recover the address associated with the public key from an elliptic curve signature. Returns zero on error.  |
| `addmod(uint256 x, uint256 y, uint256 k) returns (uint256)`     | Compute `(x + y) % k` where the addition is performed with arbitrary precision and does not wrap around at 2^256. Assert that `k != 0`. |
| `mulmod(uint256 x, uint256 y, uint256 k) returns (uint256)`     | Compute `(x * y) % k` where the multiplication is performed with arbitrary precision and does not wrap around at 2^256. Assert that `k != 0`. |

## Solidity Version Pragma

The Solidity version pragma is a statement used to specify the version of the Solidity compiler for contract compilation. It ensures compatibility and avoids potential issues due to breaking changes. Here are examples:

1. `pragma solidity ^0.8.0;`: Specifies compatibility with Solidity version 0.8.0 or higher (excluding 0.9.0).
2. `pragma solidity 0.8.7;`: Explicitly uses Solidity version 0.8.7.


## Conclusion
Congratulations on completing this comprehensive guide to Solidity! We've covered the essential aspects of Solidity, including its syntax, data types, state variables, functions, events, control structures, error handling, inheritance, interfaces, import statements, gas calculations, and cryptography.

Solidity offers immense potential for developing powerful smart contracts on the Ethereum blockchain. By leveraging the features and best practices discussed in this guide, you can create robust, secure, and efficient decentralized applications.

Remember to stay updated with the latest developments in Solidity by referring to the [official Solidity documentation](https://docs.soliditylang.org/) and actively participating in the vibrant blockchain community. Engage in discussions, explore open-source projects, and collaborate with fellow developers to expand your knowledge and network.

Thank you for joining us on this Solidity adventure. Happy coding and building innovative decentralized applications with Solidity!

Check out Hack Solidity [here](https://bento.me/hacksolidity)