1. Units

1.1. Common units

  • wei is the smallest subdenomination of Ether (1 wei == 1)
  • gwei is the commonly used subdenomination to describe gas price (1 gwei == 1e9)
  • ether unit (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. Time units

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

Example

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

2. Global Variables

2.1. Block and Transaction Properties

  • blockhash(uint blockNumber) returns (bytes32): hash of the given block when blocknumber is one of the 256 most recent blocks; otherwise returns zero
  • block.basefee (uint): current block’s base fee
  • block.chainid (uint): current chain id
  • block.coinbase (address payable): current block miner’s address
  • block.gaslimit (uint): current block gaslimit
  • block.number (uint): current block number
  • block.timestamp (uint): current block timestamp as seconds since unix epoch
  • gasleft() returns (uint256): remaining gas
  • msg.data (bytes calldata): complete calldata
  • msg.sender (address): sender of the message (current call)
  • msg.sig (bytes4): first four bytes of the calldata (i.e. function identifier)
  • msg.value (uint): number of wei sent with the message
  • tx.gasprice (uint): gas price of the transaction
  • tx.origin (address): sender of the transaction (full call chain)

2.2. Error handling

  • assert(bool condition): causes a Panic error and thus state change reversion if the condition is not met - to be used for internal errors.
  • require(bool condition): reverts if the condition is not met - to be used for errors in inputs or external components.
  • require(bool condition, string memory message): reverts if the condition is not met - to be used for errors in inputs or external components. Also provides an error message.
  • revert(): abort execution and revert state changes
  • revert(string memory reason): abort execution and revert state changes, providing an explanatory string

2.3. Members of Address Types

  • <address>.balance (uint256): balance of the Address in Wei
  • <address>.code (bytes memory): code at the Address (can be empty)
  • <address>.codehash (bytes32): the codehash of the Address
  • <address payable>.transfer(uint256 amount): send given amount of Wei to Address, reverts on failure, forwards 2300 gas stipend, not adjustable
  • <address payable>.send(uint256 amount) returns (bool): send given amount of Wei to Address, returns false on failure, forwards 2300 gas stipend, not adjustable
  • <address>.call(bytes memory) returns (bool, bytes memory): issue low-level CALL with the given payload, returns success condition and return data, forwards all available gas, adjustable
  • <address>.delegatecall(bytes memory) returns (bool, bytes memory): issue low-level DELEGATECALL with the given payload, returns success condition and return data, forwards all available gas, adjustable
  • <address>.staticcall(bytes memory) returns (bool, bytes memory): issue low-level STATICCALL with the given payload, returns success condition and return data, forwards all available gas, adjustable

2.4. Contract-related keywords

  • this: The current contract, explicitly convertible to Address
  • super: A contract one level higher in the inheritance hierarchy
  • selfdestruct(address payable recipient): Destroy the current contract, sending its funds to the given Address and end execution. Note that selfdestruct has some peculiarities inherited from the EVM:
    • the receiving contract’s receive function is not executed.
    • the contract is only really destroyed at the end of the transaction and revert s might “undo” the destruction.

3. Expressions and Control Structures

3.1. Supported keywords

There is: if, else, while, do, for, break, continue, return, try/catch with the usual semantics known from C or JavaScript.

3.2. Function Calls

We can call function of 1 contract from another contract. We have an example below with 2 contracts Caller and Callee.

// 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. Create new contract with keyword new

We can use keyword new to create a new contract. AdvancedStorage.sol example will explain this in more details.

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