1. Value types

1.1. INT / UINT

uint is short for unsigned integer, and you can choose the size from uint8 to uint256

  • uint8 starts from 0 to 2 ** 8 - 1
  • uint16 starts from 0 to 2 ** 16 - 1
    ...
  • uint256 starts from 0 to 2 ** 256 - 1

uint8 public u8 = 1;
uint256 public u256 = 456;
uint public u = 123; // uint is short hand for uint256

int is short for integer, and you can choose the size from int8 to int256

  • int8 starts from -2 ** 7 to 2 ** 7 - 1
  • int16 starts from -2 ** 15 to 2 ** 15 - 1 ...
  • int128 starts from -2 ** 127 to 2 ** 127 - 1
  • int256 starts from -2 ** 255 to 2 ** 255 - 1
int8 public i8 = -1;
int256 public i256 = 456;
int public i = -123; // int is short hand for int256

int and uint operators:

  • Comparisons: <=, <, ==, !=, >=, > (returns bool)
  • Bit operations: &, |, ^ (bitwise exclusive hoặc), ~ (bitwise negation)
  • Shifts: << (left shift), >> (right shift)
  • Addition, Subtraction and Multiplication: +, -, negative - (as in signed integer), *, /, % (modulo), ** (exponentiation)

For a type integer variable X, you can use type(X).min and type(X).max to access smallest and biggest value respectively for that type.

// minimum and maximum of int type: 
int public minInt = type(int).min;
int public maxInt = type(int).max;

// minimum and maximum of uint type:
uint public minUint = type(uint).min;
uint public maxUint = type(uint).max;

1.2. BOOL

bool means Boolean and has 2 possible values which are true and false

bool public trueVar = true;
bool public falseVar = false;

1.3. Operators:

  • ! (logical negation)
  • && (logical conjunction, “and”)
  • || (logical disjunction, “or”)
  • == (equality)
  • != (inequality)

The operators || and && apply the common short-circuiting rules. This means that in the expression f(x) || g(y), if f(x) evaluates to true, g(y) will not be evaluated even if it may have side-effects.

1.4. ADDRESS

  • address is a special data type in Solidity that allows storing 20 bytes (size) of the address of an Kaia account
  • address payable similar to address but adds 2 more methods transfer and send
address public exampleAddress = 0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c;
address payable public examplePayableAddress = 0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c;

1.5. BYTES

In Solidity, the byte data type represents a sequence of bytes. Solidity has two types of bytes:

  • Array byte with fixed size
  • Array byte with dynamically sized.

The word bytes in Solidity represents a dynamic Array of bytes. Basically stands for byte[].

bytes1 a = 0xb5; //  [10110101]
bytes1 b = 0x56; //  [01010110]
bytes c = "abc"; //  [01100001, 01100010, 01100011]

1.6. Default values

Declared variables without value assignment will have its default values.

bool public defaultBool; // false
uint public defaultUint; // 0
int public defaultInt; // 0
address public defaultAddr; // 0x0000000000000000000000000000000000000000
bytes1 public defaultByte; // 0x00

1.7. CONTRACT

contract is used to declare a contract in Solidity.

contract HelloWorld {}

contract can also inherit from another contract using the keyword is

contract Mercedes is Car {}

1.8. ENUM

Enums are one way to create a user-defined type in Solidity. They are explicitly convertible to and from all integer types but implicit conversion is not allowed. The explicit conversion from integer checks at runtime that the value lies inside the range of the enum and causes a Panic error otherwise. Enums require at least one member, and its default value when declared is the first member. Enums cannot have more than 256 members.

The data representation is the same as for enums in C: The options are represented by subsequent unsigned integer values starting from 0.

Using type(NameOfEnum).min and type(NameOfEnum).max you can get the smallest and respectively largest value of the given enum.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract Enum {
    // Enum representing shipping status
    enum Status {
        Pending,
        Shipped,
        Accepted,
        Rejected,
        Canceled
    }

    // Default value is the first element listed in
    // definition of the type, in this case "Pending"
    Status public status;

    // Returns uint
    // Pending  - 0
    // Shipped  - 1
    // Accepted - 2
    // Rejected - 3
    // Canceled - 4
    function get() public view returns (Status) {
        return status;
    }

    // Update status by passing uint into input
    function set(Status _status) public {
        status = _status;
    }

    // You can update to a specific enum like this
    function cancel() public {
        status = Status.Canceled;
    }

    // delete resets the enum to its first value, 0
    function reset() public {
        delete status;
    }
}

1.9. TYPE

A user-defined value type allows creating a zero cost abstraction over an elementary value type. This is similar to an alias, but with stricter type requirements.

A user-defined value type is defined using type C is V, where C is the name of the newly introduced type and V has to be a built-in value type (the “underlying type”)

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.8;

// Represent a 18 decimal, 256 bit wide fixed point type using a user-defined value type.
type UFixed256x18 is uint256;

1.10. FUNCTION

function keyword is used to declare a function in Solidity.

We can declare a function like below:

contract Counter {
    uint public count;

    // Function to view count variable
    function get() public view returns (uint) {
        return count;
    }
}

2. Reference types

2.1. Data storage locations

Variables are declared with the words storage, memory or calldata to specify the location to save the data.

  • storage - variable is state variable (stored on blockchain)
  • memory - Variables are in memory and only exist while the function is running
  • calldata - A special data store containing the data passed to the function
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract DataLocations {
    uint storage varStorage
    uint memory varMemory
    uint calldata varCallData
}

2.2. Array

Array is a combination of value elements in the same format, similar to list in python and array in Javascript.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract Array {
    // Several ways to initialize an array
    uint[] public arr;
    uint[] public arr2 = [1, 2, 3];
    // Fixed sized array, all elements initialize 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);
    }
}

2.3. Struct

Struct is a data format that programmers declare to gather many variables of different formats under one name for easy use in contract.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract Todos {
    struct Todo {
        string text;
        bool completed;
    }

    // An array of 'Todo' structs
    Todo[] public todos;

    function create(string calldata _text) public {
        // 3 ways to initialize a struct
        // - calling it like a function
        todos.push(Todo(_text, false));

        // key value mapping
        todos.push(Todo({text: _text, completed: false}));

        // initialize an empty struct and then update it
        Todo memory todo;
        todo.text = _text;
        // todo.completed initialized to false

        todos.push(todo);
    }

    // Solidity automatically created a getter for 'todos' so
    // you don't actually need this function.
    function get(uint _index) public view returns (string memory text, bool completed) {
        Todo storage todo = todos[_index];
        return (todo.text, todo.completed);
    }

    // update text
    function updateText(uint _index, string calldata _text) public {
        Todo storage todo = todos[_index];
        todo.text = _text;
    }

    // update completed
    function toggleCompleted(uint _index) public {
        Todo storage todo = todos[_index];
        todo.completed = !todo.completed;
    }
}

3. Mapping Types

Mapping

mapping can be used to create a hashmap (similar to dict in python) between 1 type to another type.

Note:
When you create a mapping, all keys exist at the same time. That means:
For example, you create mapping(address => uint256) addressToValue;. If you haven't set any key and value then all address that you input will return the default value of uint256 which is 0.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

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];
    }
}

4. Simple Storage

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

contract SimpleStorage {

    // Declare a variable to store the name of the maintainer
    string public maintainerName = "zxstim";
    // Declare the version of the contract
    uint8 public version = 1;
    // Declare an address to receive donation
    address public donationAddress = 0xe3d25540BA6CED36a0ED5ce899b99B5963f43d3F;

    // Declare a Person type to store information of a person
    struct Person {
        string name; // name    
        uint8 age; // age
        bool overEighteen; // Over eighteen?
        address uuid; // UUID
        uint256 assetValue; // asset value
        int256 debtValue; // debt value
    }

    Person[] private listOfPeople; // this syntax means creating an array to store Person named listOfPeople
    
    mapping(address => Person) uuidToPerson; // this syntax means creating a mapping from address to Person named uuidToPerson

    //  this function will store the information of a new person with name, age, overEighteen, assetValue, debtValue
    function storePerson(string memory _name, uint8 _age, bool _overEighteen, uint256 _assetValue, int256 _debtValue) public returns (Person memory person) {
        _assetValue *= 1e18; // Convert asset value to wei unit
        _debtValue *= 1e18; // Convert debt value to wei unit
        // Add information of the new person to the listOfPeople array
        listOfPeople.push(Person({name: _name, age: _age, overEighteen: _overEighteen, uuid: msg.sender, assetValue: _assetValue, debtValue: _debtValue}));
        // Add information of the new person to the uuidToPerson mapping
        uuidToPerson[msg.sender] = Person({name: _name, age: _age, overEighteen: _overEighteen, uuid: msg.sender, assetValue: _assetValue, debtValue: _debtValue});
        return Person({name: _name, age: _age, overEighteen: _overEighteen, uuid: msg.sender, assetValue: _assetValue, debtValue: _debtValue});
    }

    // this function will retrieve the information of a person based on the address
    function retrievePerson(address _address) public view returns (Person memory person) {
        return uuidToPerson[_address];
    }
}