1. Đơn vị

1.1. Những đơn vị thường sử dụng

  • wei đơn vị nhỏ nhất (1 wei == 1)
  • gwei đơn vị thường được sử dụng để diễn tả giá gas hiện tại (1 gwei == 1e9)
  • ether đơn vị (1 ether == 1e18)

Note:
In Solidity, we will use integers for calculations and the language does not support the float type. The float representation issue causes rounding errors (rounding) that create logical holes for attack.

1.2. Đơn vị thời gian

  • 1 == 1 seconds
  • 1 minutes == 60 seconds
  • 1 hours == 60 minutes
  • 1 days == 24 hours
  • 1 weeks == 7 days

Ví dụ

function f(uint start, uint daysAfter) public {
    if (block.timestamp >= start + daysAfter * 1 days) {
        // ...
    }
}

2. Biến toàn cục

2.1. Thuộc tính blockchain

  • blockhash(uint blockNumber) returns (bytes32): hash của khối đã cho khi số khối là một trong 256 khối gần đây nhất; nếu không thì trả về 0.
  • blobhash(uint index) returns (bytes32): hash được phiên bản của blob thứ chỉ mục được liên kết với giao dịch hiện tại. Hàm băm được phiên bản bao gồm một byte đơn biểu thị phiên bản (hiện tại là 0x01), theo sau là 31 byte cuối cùng của hàm băm SHA256 của cam kết KZG (EIP-4844).
  • block.basefee (uint): phí cơ bản của khối hiện tại (EIP-3198 và EIP-1559)
  • block.blobbasefee (uint): Phí cơ sở blob của khối hiện tại (EIP-7516 và EIP-4844)
  • block.chainid (uint): id chuỗi hiện tại
  • block.coinbase (address payable): địa chỉ của người khai thác khối hiện tại
  • block.difficulty (uint): độ khó khối hiện tại (EVM < Paris). Đối với các phiên bản EVM khác, nó hoạt động như một bí danh không được dùng nữa cho block.prevrandao (EIP-4399)
  • block.gaslimit (uint): gaslimit khối hiện tại
  • block.number (uint): số khối hiện tại
  • block.prevrandao (uint): số ngẫu nhiên được cung cấp bởi chuỗi beacon (EVM >= Paris)
  • block.timestamp (uint): dấu thời gian khối hiện tại tính bằng giây kể từ kỷ nguyên unix
  • gasleft() returns (uint256): khí còn lại
  • msg.data (bytes calldata): dữ liệu cuộc gọi hoàn chỉnh
  • msg.sender (address): người gửi tin nhắn (cuộc gọi hiện tại)
  • msg.sig (bytes4): bốn byte đầu tiên của calldata (tức là mã định danh hàm)
  • msg.value (uint): số wei gửi cùng tin nhắn
  • tx.gasprice (uint): giá gas của giao dịch
  • tx.origin (address): người gửi giao dịch (chuỗi cuộc gọi đầy đủ)

2.2. Xử lý Error

  • assert
  • require(bool condition)
  • require(bool condition, string memory message)
  • revert()
  • revert(string memory reason)

2.3. Những method sẵn của address

  • <address>.balance (uint256): số dư của Địa chỉ ở Wei.
  • <address>.code (bytes memory): mã tại Địa chỉ (có thể để trống).
  • <address>.codehash (bytes32): mã băm của Địa chỉ.
  • <address payable>.transfer(uint256 amount): gửi số lượng Wei nhất định đến Địa chỉ, hoàn nguyên nếu thất bại, chuyển tiếp 2300 tiền trợ cấp gas, không thể điều chỉnh.
  • <address payable>.send(uint256 amount) returns (bool): gửi số lượng Wei đã cho đến Địa chỉ, trả về sai nếu thất bại, chuyển tiếp 2300 tiền trợ cấp gas, không thể điều chỉnh.
  • <address>.call(bytes memory) returns (bool, bytes memory): đưa ra CALL ở mức độ thấp với tải trọng nhất định, trả về điều kiện thành công và trả về dữ liệu, chuyển tiếp tất cả khí có sẵn, có thể điều chỉnh.
  • <address>.delegatecall(bytes memory) returns (bool, bytes memory): đưa ra DELEGATECALL cấp thấp với tải trọng nhất định, trả về điều kiện thành công và trả về dữ liệu, chuyển tiếp tất cả gas có sẵn, có thể điều chỉnh
  • <address>.staticcall(bytes memory) returns (bool, bytes memory)

2.4. Keyword liên quan đến contract

  • this
  • super
  • selfdestruct(address payable recipient)

3. Expressions và Control Structures

3.1. Keyword hỗ trợ

Solidity có hỗ trợ if, else, while, do, for, break, continue, return, try/catch như C hoặc JavaScript.

3.2. Gọi function của contract khác

Chúng ta có thể gọi function của contract khác từ 1 contract. Có 1 ví dụ dưới đây với 2 contract CallerCallee.

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

contract Callee {
    uint public x;
    uint public value;

    function setX(uint _x) public returns (uint) {
        x = _x;
        return x;
    }

    function setXandSendEther(uint _x) public payable returns (uint, uint) {
        x = _x;
        value = msg.value;

        return (x, value);
    }
}

contract Caller {
    function setX(Callee _callee, uint _x) public {
        uint x = _callee.setX(_x);
    }

    function setXFromAddress(address _addr, uint _x) public {
        Callee callee = Callee(_addr);
        callee.setX(_x);
    }

    function setXandSendEther(Callee _callee, uint _x) public payable {
        (uint x, uint value) = _callee.setXandSendEther{value: msg.value}(_x);
    }
}

3.3. Tạo contract mới với new

Chúng ta có thể dùng keyword new để tạo contract mới từ 1 contract. Ví dụ AdvancedStorage.sol sẽ đi sâu vào phương pháp này.

4. Advanced Storage

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

contract AdvancedStorage {
    // Declare the address of a vault manager
    address public vaultManager;

    // Declare an error type for unauthorized access
    error OwnableUnauthorizedAccount(address account);

    // Constructor is a function that runs when the contract is initialized
    constructor() {
        // Assign the address of the deployer to the vault manager variable
        vaultManager = msg.sender;
    }

    // Declare the InvestmentVault Struct data type
    struct InvestmentVault {
        uint256 investmentDuration; // Thời gian đầu tư
        int256 returnOnInvestment; // % lãi suất trả về
        bool initialized; // Đã khởi tạo
        address identityCard; // Địa chỉ thẻ thông tin
    }
    // Declare a variable with the InvestmentVault type
    InvestmentVault private investmentVault;

    // This function initializes the investment vault
    function setInitialInvestmentVault(uint256 daysAfter, int256 _returnOnInvestment, address _vaultOwner) public {
        // We check if the initiator is the vaultManager
        if (msg.sender != vaultManager) {
            // This reverts all actions and reverts the transaction
            revert OwnableUnauthorizedAccount(msg.sender);
        }
        // Declare the investment duration
        uint256 _investmentDuration = block.timestamp + daysAfter * 1 days;

        // Create a new identity card for the customer
        CustomerIdentityCard customerIdentityCard = new CustomerIdentityCard(_vaultOwner);
        // Assign the address of the vault owner/customer to the mapping with the vault information
        investmentVault = InvestmentVault({investmentDuration: _investmentDuration, returnOnInvestment: _returnOnInvestment, initialized: true, identityCard: address(customerIdentityCard)});
    }

    // Function to change the return on investment
    function editReturnOnInvestment(int256 _newReturnOnInvestment) public {
        // require keyword works similarly to if and revert above
        require (msg.sender == vaultManager, "Unauthorized Manager");
        // Change the value of the interest rate
        investmentVault.returnOnInvestment = _newReturnOnInvestment;
    }

    // Function to return investmentVault information
    function retrieveInvestmentVault() public view returns (InvestmentVault memory _investmentVault) {
        return investmentVault;
    }

    // Function to return the address of the IdentityCard
    function retrieveCustomerInformation() public view returns (address) {
        return CustomerIdentityCard(investmentVault.identityCard).customer();
    }
}

// Contract that stores the address of the vault owner
contract CustomerIdentityCard {
    //  declares a variable to store the address of the customer
    address public customer;

    // initialize the contract and assign the address of the customer
    constructor(address _customer) {
        customer = _customer;
    }
}