Comments
You can write comments to document your code; Single-line comments (//) and multi-line comments (/…/) are possible.
// This is a single-line comment.
/*
This is a
multi-line comment.
*/
License
Since smart contract code is always open to anyone, solidity compiler encourages use of machine readable license identifiers to avoid legal problems. Every source file should start with a comment indicating its license:
// SPDX-License-Identifier: MIT
Version Pragma
The pragma keyword is used to enable certain compiler features or checks. Source files can (and should) be annotated with a version pragma to reject compilation with future compiler versions that might introduce incompatible changes.
pragma solidity ^0.8.2;
Imports
Solidity import statements allow code to be imported from another file. This helps break your project into modularise your code across multiple smaller files.
// global imports => import "filename";
import “Math.sol”
// named imports => import * as symbolName from "filename";
import * as formula from “Math.sol”
State Variables
State variables are variables whose values are permanently stored in contract storage.
contract SimpleStorage {
uint storedData1; // Unsinged integer; can be negative
int storedData2; // Signed integer; cannot be negative
bool storedData3, // Boolean value (true or false)
address storedData4; // 20 byte ethereum address
//complex data types
int[] storedData5; // a dynamic array of int
int[3] storedData6; // a fixed array of 3 int
string storedData7; // a literal string eg "Hello"
}
Structs
Structs are custom defined types that can group several variables
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;
contract Ballot {
// Struct
struct Voter {
uint weight;
bool voted;
address delegate;
uint vote;
}
}
Enums
Enums can be used to create custom types with a finite set of ‘constant values’
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;
contract Purchase {
enum State { Created, Locked, Inactive } // Enum
}
Variable visibility
The visibility of a variable dictates where in the code a variable can be accessed from. There are 3 possible visibility: private, internal (default) and public.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;
contract SimpleStorage {
// Visible only inside contract
private uint storedData1;
// Visible inside the contract and derived contracts (Default)
internal int storedData2;
// Like internal, but getter functions are automatically generated
public bool storedData3,
}
Functions
Functions are the executable units of code. Functions are usually defined inside a contract, but they can also be defined outside of contracts.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;
contract SimpleAuction {
function bid() public payable { // Function
// ...
}
}
// Helper function defined outside of a contract
function helper(uint x) pure returns (uint) {
return x * 2;
}
Function Modifiers
Modifiers can be used to change the behaviour of functions in a declarative way. For example, you can use a modifier to automatically ensure only sellers can list an NFT for auction.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;
contract Purchase {
address public seller;
modifier onlySeller() { // Modifier
require(
msg.sender == seller,
"Only seller can call this."
);
_;
}
function list() public view onlySeller { // Modifier usage
// ...
}
}
Internal Function Calls
Functions of the current contract can be called directly (“internally”), also recursively, as seen in this nonsensical example:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.22 <0.9.0;
// This will report a warning
contract C {
function g(uint a) public pure returns (uint ret) { return a + f(); }
function f() internal pure returns (uint ret) { return g(7) + f(); }
}
External Function Calls
Functions can also be called using the this.g(8);
and c.g(2);
notation, where c
is a contract instance and g
is a function belonging to c
.
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.2 <0.9.0;
contract InfoFeed {
function info() public payable returns (uint ret) { return 42; }
}
contract Consumer {
InfoFeed feed;
function setFeed(InfoFeed addr) public { feed = addr; }
function callFeed() public { feed.info{value: 10, gas: 800}(); }
}
Named Calls and Anonymous Function Parameters
Function call arguments can be given by name, in any order, if they are enclosed in { }
as can be seen in the following example.
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.9.0;
contract C {
mapping(uint => uint) data;
function f() public {
set({value: 2, key: 3});
}
function set(uint key, uint value) public {
data[key] = value;
}
}
Events
Event is an inheritable member of a contract. An event is emitted, it stores the arguments passed in transaction logs. Events are ways to communicate with a client application or front-end website that something has happened on the blockchain.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;
contract SimpleAuction {
event HighestBidIncreased(address bidder, uint amount); // Event
function bid() public payable {
// ...
emit HighestBidIncreased(msg.sender, msg.value); // Triggering event
}
}
Errors
Errors allow you to define descriptive names and data for failure situations. You can use NatSpec to describe the error to the user. They can be defined inside and outside of contracts.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;
/// Not enough funds for transfer. Requested `requested`,
/// but only `available` available.
error NotEnoughFunds(uint requested, uint available);
contract Token {
mapping(address => uint) balances;
function transfer(address to, uint amount) public {
uint balance = balances[msg.sender];
if (balance < amount)
revert NotEnoughFunds(amount, balance);
// ...
}
}
Operators
a+b // Addition
a-b // Subtraction
a*b // Multiplication
a/b // Division
a * (b + c) // grouping
person.age // member
person[age] // member
!(a == b) // logical not
a != b // not equal
a = b // assignment
a == b // equals
a != b // unequal
a === b // strict equal
a !== b // strict unequal
a < b a > b // less and greater than
a <= b a >= b // less or equal, greater or eq
a += b // a = a + b (works with - * %...)
a && b // logical and
a || b // logical or
Special Units
Ether Units
A literal number can take a suffix of wei, gwei or ether to specify a subdenomination of Ether, where Ether numbers without a postfix are assumed to be Wei.
Time Units
Suffixes like seconds
, minutes
, hours
, days
and weeks
after literal numbers can be used to specify units of time where seconds are the base unit and units are considered naively in the following way: – 1 == 1 seconds
– 1 minutes == 60 seconds
– 1 hours == 60 minutes
– 1 days == 24 hours
– 1 weeks == 7 days
Global functions and variables
There are special variables and functions which always exist in the global namespace
Block and Transaction Properties
blockhash(uint blockNumber) returns (bytes32)
: hash of the given block whenblocknumber
is one of the 256 most recent blocks; otherwise returns zeroblock.basefee
(uint
): current block’s base fee (EIP-3198 and EIP-1559)block.chainid
(uint
): current chain idblock.coinbase
(address payable
): current block miner’s addressblock.difficulty
(uint
): current block difficultyblock.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)
ABI Encoding and Decoding Functions
abi.decode(bytes memory encodedData, (...)) returns (...)
: ABI-decodes the given data, while the types are given in parentheses as second argument. Example:(uint a, uint[2] memory b, bytes memory c) = abi.decode(data, (uint, uint[2], bytes))
abi.encode(...) returns (bytes memory)
: ABI-encodes the given argumentsabi.encodePacked(...) returns (bytes memory)
: Performs packed encoding of the given arguments. Note that packed encoding can be ambiguous!abi.encodeWithSelector(bytes4 selector, ...) returns (bytes memory)
: ABI-encodes the given arguments starting from the second and prepends the given four-byte selectorabi.encodeWithSignature(string memory signature, ...) returns (bytes memory)
: Equivalent toabi.encodeWithSelector(bytes4(keccak256(bytes(signature))), ...)
abi.encodeCall(function functionPointer, (...)) returns (bytes memory)
: ABI-encodes a call tofunctionPointer
with the arguments found in the tuple. Performs a full type-check, ensuring the types match the function signature. Result equalsabi.encodeWithSelector(functionPointer.selector, (...))
Mathematical and Cryptographic Functions
addmod(uint x, uint y, uint k) returns (uint)
: compute(x + y) % k
where the addition is performed with arbitrary precision and does not wrap around at2**256
. Assert thatk != 0
starting from version 0.5.0.mulmod(uint x, uint y, uint k) returns (uint)
: compute(x * y) % k
where the multiplication is performed with arbitrary precision and does not wrap around at2**256
. Assert thatk != 0
starting from version 0.5.0.keccak256(bytes memory) returns (bytes32)
: compute the Keccak-256 hash of the inputsha256(bytes memory) returns (bytes32)
: compute the SHA-256 hash of the inputripemd160(bytes memory) returns (bytes20)
: compute RIPEMD-160 hash of the inputecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)
: recover the address associated with the public key from elliptic curve signature or return zero on error. The function parameters correspond to ECDSA values of the signature:r
= first 32 bytes of signatures
= second 32 bytes of signaturev
= final 1 byte of signatureecrecover
returns anaddress
, and not anaddress payable
. See address payable for conversion, in case you need to transfer funds to the recovered address.
Error Handling Functions
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
Modifiers Keywords
pure
for functions: Disallows modification or access of state.view
for functions: Disallows modification of state.payable
for functions: Allows them to receive Ether together with a call.constant
for state variables: Disallows assignment (except initialisation), does not occupy storage slot.immutable
for state variables: Allows exactly one assignment at construction time and is constant afterwards. Is stored in code.anonymous
for events: Does not store event signature as topic.indexed
for event parameters: Stores the parameter as topic.virtual
for functions and modifiers: Allows the function’s or modifier’s behaviour to be changed in derived contracts.override
: States that this function, modifier or public state variable changes the behaviour of a function or modifier in a base contract.
So cool thanks!