1. Units
1.1. Common units
weiis the smallest subdenomination of Ether (1wei== 1)gweiis the commonly used subdenomination to describe gas price (1gwei== 1e9)etherunit (1ether== 1e18)
Note:
In Solidity, we will use integers for calculations and the language does not support thefloattype. The float representation issue causes rounding errors (rounding) that create logical holes for attack.
1.2. Time units
- 1 == 1
seconds - 1
minutes== 60seconds - 1
hours== 60minutes - 1
days== 24hours - 1
weeks== 7days
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 whenblocknumberis one of the 256 most recent blocks; otherwise returns zeroblock.basefee (uint): current block’s base feeblock.chainid (uint): current chain idblock.coinbase (address payable): current block miner’s addressblock.gaslimit (uint): current block gaslimitblock.number (uint): current block numberblock.timestamp (uint): current block timestamp as seconds since unix epochgasleft() returns (uint256): remaining gasmsg.data (bytes calldata): complete calldatamsg.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 messagetx.gasprice (uint): gas price of the transactiontx.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 changesrevert(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 Addresssuper: A contract one level higher in the inheritance hierarchyselfdestruct(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;
}
}
Objectives
By the end of this lesson, you'll understand: