1. Types có giá trị

1.1. INT / UINT

uint là viết tắt của unsigned integer, có nghĩa là số nguyên không âm và bạn có thể lựa chọn kích thước của nó từ uint8 đến uint256

  • uint8 bắt đầu từ 0 tới 2 ** 8 - 1
  • uint16 bắt đầu từ 0 tới 2 ** 16 - 1
    ...
  • uint256 bắt đầu từ 0 to 2 ** 256 - 1
uint8 public u8 = 1;
uint256 public u256 = 456;
uint public u = 123; // uint là viết tắt của uint256

int là viết tắt của integer, có nghĩa là số nguyên âm và không âm và bạn có thể lựa chọn kích thước của nó từ int8 đến int256

  • int8 bắt đầu từ -2 ** 7 tới 2 ** 7 - 1
  • int16 bắt đầu từ -2 ** 15 tới 2 ** 15 - 1 ...
  • int128 bắt đầu từ -2 ** 127 tới 2 ** 127 - 1
  • int256 bắt đầu từ -2 ** 255 tới 2 ** 255 - 1
int8 public i8 = -1;
int256 public i256 = 456;
int public i = -123; // int là viết tắt của int256

Toán tử của int và uint:

  • So sánh: <=, <, ==, !=, >=, > (trả về bool)
  • Toán tử Bit: &, |, ^ (bitwise exclusive hoặc), ~ (bitwise negation)
  • Toán tử Shift: << (left shift), >> (right shift)
  • Toán tử số học: +, -, dấu âm - (chỉ cho signed integer), *, /, % (phép toán modulo), ** (hàm mũ)

Cho một biến X type integer, bạn có thể dùng type(X).min và type(X).max để truy cập giá trị bé nhất và lớn nhất có thể diễn tả bằng 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 có nghĩa là Boolean và có 2 giá trị true và false

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

1.3. Toán tử:

  • ! (luận lý ngược, tương đương “not”)
  • && (luận lý gộp tương đương “and”)
  • || (luận lý hoặc tương đương “or”)
  • == (bằng)
  • != (không bằng)

Toán tử ||&& áp dụng các quy tắc short-circuit thông thường. Điều này có nghĩa là trong biểu thức f(x) || g(y), nếu f(x) đánh giá là true, g(y) sẽ không được đánh giá ngay cả khi nó có thể có tác động phụ.

1.4. ADDRESS

  • address là một kiểu dữ liệu đặc biệt trong Solidity cho phép lưu trữ 20 byte (kích cỡ) địa chỉ của một tài khoản Ethereum
  • address payable tương tự với address nhưng có thêm tính năng transfersend
address public exampleAddress = 0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c;
address payable public examplePayableAddress = 0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c;

1.5. BYTES

Trong Solidity, kiểu dữ liệu byte đại diện cho một chuỗi byte. Solidity có hai loại kiểu bytes:

  • Array byte có kích thước cố định
  • Array byte có kích thước động.

Từ bytes trong Solidity đại diện cho một Array động của byte. Căn bản là viết tắt của byte[].

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

1.6. Giá trị mặc định

Những biến được khai báo mà không gán giá trị thì sẽ có giá trị mặc định

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

1.7. CONTRACT

contract được dùng để khai báo 1 contract trong solidity

contract HelloWorld {}

contract cũng có thể thừa hưởng từ 1 contract khác với keyword is

contract Mercedes is Car {}

1.8. ENUM

Enum là một cách để tạo kiểu do người dùng xác định trong Solidity. Chúng có thể chuyển đổi rõ ràng sang và từ tất cả các loại số nguyên nhưng không được phép chuyển đổi ngầm. Việc chuyển đổi rõ ràng từ số nguyên sẽ kiểm tra trong thời gian chạy rằng giá trị nằm trong phạm vi của enum và gây ra Panic Error nếu không. Enums yêu cầu ít nhất một thành viên và giá trị mặc định của nó khi được khai báo là thành viên đầu tiên. Enums không thể có nhiều hơn 256 thành viên.

Cách biểu diễn dữ liệu giống như đối với enum trong C: Các tùy chọn được biểu thị bằng các giá trị số nguyên không dấu tiếp theo bắt đầu từ 0.

Sử dụng type(NameOfEnum).mintype(NameOfEnum).max bạn có thể nhận được giá trị nhỏ nhất và lớn nhất tương ứng của enum đã cho.

// 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

Loại giá trị do người dùng xác định cho phép tạo ra sự trừu tượng hóa chi phí bằng 0 đối với loại giá trị cơ bản. Điều này tương tự như bí danh nhưng có yêu cầu loại chặt chẽ hơn.

Loại giá trị do người dùng xác định được xác định bằng loại type C is V, trong đó C là tên của loại type mới được khai báo và V phải là loại giá trị tích hợp.

// 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 dùng để khai báo 1 hàm trong solidity.

Mình khai báo 1 function đầy đủ như bên dưới

contract Counter {
    uint public count;

    // Hàm để xem biến count
    function get() public view returns (uint) {
        return count;
    }
}

2. Reference types

2.1. Vị trí lưu Data

Các biến được khai báo cùng với các từ storage, memory hoặc calldata để chỉ định vị trí lưu dữ liệu.

  • storage - biến là state variable (lưu trên blockchain)
  • memory - biến ở trong memory và chỉ tồn tại khi function đang hoạt động
  • calldata - nơi lưu trữ dữ liệu đặc biệt có chứa dữ liệu truyền vào 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 là tổ hợp các phần tử giá trị giống định dạng của nhau, tương tự như list trong python và array trong 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 là định dạng dữ liệu mà lập trình viên khai báo để gom nhiều biến có định dạng khác nhau lại dưới 1 tên gọi để dễ dàng sử dụng trong 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 được sử dụng để tạo 1 hashmap (tương tự như dict trong python) giữa 1 type với 1 type khác.

Lưu ý:
Khi bạn tạo 1 mapping, tất cả các key đều tồn tại. Điều này nghĩa:
Ví dụ bạn tạo mapping(address => uint256) addressToValue;. Nếu bạn chưa thay đổi keyvalue nào thì hiện tại tất cả address bạn nhập vào sẽ trả về giá trị mặc định của uint256 là bằng 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];
    }
}