INSPIRATION
There are times where we want to look up basic solidity concepts or need some refreshers! What if we could jump-start this process?
OVERVIEW
Do you need a solidity-refresher or want your solidity-basic-muscles revitalized? This guide is intended to help beginners, developers (that seek a refresher) get familiar or acquainted with solidity syntax and smart contract in general.
SOLIDITY JUMPSTARTER-KIT
Solidity Definition
Simply put, Solidity is an object-oriented, high-level language for implementing smart contracts. It is quite similar to other object-oriented programming language like C++, Javascript, Python e.t.c. However, solidity is specifically designed to target the Ethereum Virtual Machine(EVM).
You might want to check out these links
So if you write one of the highlighted languages, you can easily pick up solidity.
Smart Contract Definition
Smart contracts are simply programs stored on a blockchain that run when predetermined conditions are met.
Contract Declaration in Solidity
A contract in this context is a collection of code(its function) and data (it’s state) which resides at a specific address on the Ethereum blockchain.
A Simple Smart Contract
Data Types
Solidity is a statically typed language, which means that the type of each variable (state and local) needs to be specified. The following explains useful data types to store variables.
Boolean
bool: true or false
Integer
Unsigned: uint8,uint16,uint32,uint64,uint128,uint256(uint)
Signed: int8, int16, int32, int64, int128, int256(int)
As stated above, solidity can have signed (a regular int) or unsigned (uint) integers.
int: means that the integer can either be positive or negative with a negative sign.
uint (unsigned integer): can hold only positive values along with zero.
For example, uint8 ranges from 0 to 255 while an int8 can hold from -128 to 127.
Address
address: Holds a 20 byte value (size of an Ethereum address).
address payable : Same as address, but includes additional members transfer and send
Methods:
balance
- .balance : balance of the Address in Wei
transfer and send
.transfer(uint256 amount): send given amount of Wei to Address, throws on failure.
.send(uint256 amount) returns (bool): send given amount of Wei to Address, returns false on failure.
Note: Using .send or .transfer has a gas limit of 2300 gas
Mappings
Mappings uses the syntax mapping(KeyType => ValueType) with its variable name in front.
Example Mapping(address => uint) addressToBalance
Note: The KeyType can be any built-in value type, bytes, string, or any contract or enum type. Other user-defined or complex types, such as mappings, structs or array types are not allowed. ValueType can be any type, including mappings, arrays and structs.
Struct
We can define new types using structs as provided by Solidity.
struct Funder {
address addr;
uint amount;
}
Funder funders;
Arrays
Arrays can be of fixed size, or they can have a dynamic size.
uint[] dynamicSizeArray;
uint[7] fixedSizeArray;
Array members
push: appends an item to an array, i.e will increase the array length by 1.
pop: removes the last element from an array i.e will decrease the array length by 1
length: is used to check the number of elements present in an array.
Data Locations - Storage, Memory and Calldata
Each variable declared and used in a contract has a data location. It specifies where the value of that variable should be stored.
Storage: A storage variable is stored in the state of a smart contract and is persistent between function calls. It is the permanent residence of data which makes it accessible by all functions within a contract.
Memory: The memory location stores data temporarily and is cheaper than the storage location. It is only accessible within the function in which it was declared. It is important to note that once the function gets executed, its values are discarded.
Calldata: Calldata is a non-persistent data location where all the passing values to the function are stored. However, unlike Storage and Memory that hold mutable data, Calldata is a non-modifiable storage location.. Also, Calldata is the default location of parameters (not return parameters) of external functions.
Function
Function are notated as follows:
function () {internal|external} [pure|view|payable] [returns ()].
Input Parameters: are declared just like variables.
function add(uint _x, uint _y) {}
Output Parameters: are declared after the return keyword
function add(uint _x, uint _y) returns (uint _sum) {
_sum = _x + _y;
}
Note: In contrast to the parameter types, the return types cannot be empty - if the function does not return anything, the returns () keyword need not be included in the function declaration.
Control Structure
Most of the control structures known from object-oriented programming languages are available in Solidity: The following are available in solidity:
if else
while
do
for
break
continue
return
Tenary operator( ? : )
Modifiers: Custom and Access Modifiers
Modifiers automatically check if certain pre-conditions have been met and restrict access of variables or functions to parties that satisfy the conditions
Custom Modifier
As well as built-in Access Modifiers, you can also create your own.
modifier onlyOwner {
require(msg.sender == owner);
_;
}
Access modifiers
public - can be accessed from this contract, inherited contracts and externally
private -can only be accessed from this contract
internal -can be accessed only from this contract and contracts inheriting from it
external - Cannot be accessed internally, only externally.
Interface
An interface is the description of all functions that an object must have in order for it to operate. It includes function signatures without the implementation of the function definition.
The following are the characteristics of an interface:
An interface can be created with the “interface” keyword.
All interface functions are implicitly virtual.
To easily identify an interface, it’s naming can start with an “I” e.g IERC20
Cannot have any functions implemented.
Cannot inherit other contracts or interfaces.
Cannot define constructor.
Cannot declare state variables
An interface function can be overridden.
Visibility of functions of an interface can be only of type external
Example
Inheritance
Inheritance is a way to extend functionality of a contract. Solidity supports both single as well as multiple inheritance.
Example
Multiple inheritance
Abstract Contract
Abstract Contract is one which contains at least one function without any implementation i.e in abstract contracts, at least one function lacks implementation. This type of contract cannot be compiled, but they can be used as base contracts.
Example
Events
Events allow the convenient usage of the EVM logging facilities. An event is emitted, and its arguments passed in transaction logs. Up to three parameters can receive the attribute indexed, which will cause the respective arguments to be searched for. All non-indexed arguments will be stored in the data part of the log.
Example
Libraries and Using-For
Libraries are similar to Contracts but are mainly intended for reuse. A Library contains functions which other contracts can call.
Characteristics of a Solidity Library
Library can not be destroyed as it is assumed to be stateless.
A Library cannot have state variables.
A Library cannot inherit any element.
A Library cannot be inherited.
Example
Using-For
using this for that; can be used to attach library functions to any type.
Example
Error Handling and Custom Errors
in Solidity, the assert(), require() and revert() functions are used to check for conditions. In cases when conditions are not met, they throw exceptions.
require(bool condition, string memory message): is used to validate inputs and conditions before execution.
assert(bool condition): is used to check for code that should never be false. Failing assertion probably means that there is a bug.
*** revert(): *** is used abort execution and revert state changes.
Example
Custom Errors
Starting from Solidity v0.8.4 , there is now an additional way to revert a transaction. With Custom Errors we can now revert transactions in a convenient and gas-efficient way and tell our users why a transaction failed. They are simply defined using the error
statement. Also have similar syntax as events do.
Example
Global Variables and functions
Global variables are special variables that are globally available and provide information about the blockchain.
block.blockhash(uint blockNumber) returns (bytes32): hash of the given block - only works for the 256 most recent blocks excluding current
block.coinbase (address): current block miner’s address
block.difficulty (uint): current block difficulty
block.gaslimit (uint): current block gaslimit
block.number (uint): current block number
block.timestamp (uint): current block timestamp as seconds since unix epoch
msg.data (bytes): complete calldata
msg.gas (uint): remaining gas
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)
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 at 2**256.
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 at 2**256.
keccak256(...) returns (bytes32): compute the Ethereum-SHA-3 (Keccak-256) hash of the (tightly packed) arguments
sha256(...) returns (bytes32): compute the SHA-256 hash of the (tightly packed) arguments
sha3(...) returns (bytes32): alias to keccak256
ripemd160(...) returns (bytes20): compute RIPEMD-160 hash of the (tightly packed) arguments
ecrecover(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 (example usage).
**this **(current contract’s type): the current contract, explicitly convertible to Address
selfdestruct(address recipient): destroy the current contract, sending its funds to the given Address.
Ether Units
1 wei == 1
1 gwei == 1e9
1 szabo == 1e12
1 finney == 1e15
1 ether == 1e18
Other Important Keywords
Virtual: A function marked as virtual allows a derived/inheriting contract to override its function behavior. This is suitable when you want to allow another contract that is inheriting from the current contract to override its functionality.
Override: A function that overrides a base function is marked as override. This occurs when you want to change the functionality of an inherited/derived contract function.
Example
Variable packing:
Whenever we declare a variable in our solidity code, the EVM stores it in a storage slot of 32 bytes (256 bits), which means there is a slot where the variable declared is stored. Simply put, whenever we declare a uint256 variable type, we have successfully filled a slot
Variable packing is literally checking the size of data types declared and fitting them sequentially. This is what is done under the hood by the EVM. In order to do this, we need to understand the size of each data type.
Uint256 is 32 bytes Uint128 is 16bytes Uint64 is 8bytes Address and address payable is 20bytes Bool is 1byte String is 1byte per character
This approach is pretty much cool for gas optimisation.
Example
Immutable: Immutable state variables can be declared using the immutable keyword. State variable declared as immutable can only be assigned during contract creation, but will remain constant throughout the life-time of a deployed contract.
uint256 immutable maxSupply = 6464;
new: The new keyword in Solidity deploys and creates a new contract instance.
Example
Constructor: Constructor is a special function declared using constructor keyword. This function is used to initialise state variables of a contract. Following are the key characteristics of a constructor.
A contract can have only one constructor.
A constructor executed during contract deployment and it is used to initialise contract’s state.
Example:
contract Sample {
uint num;
constructor(uint _num) {
data = _num;
}
}
Receive function: A contract can now have only one receive function that is declared with the syntax receive() external payable {…} (without the function keyword).
It executes on calls to the contract with no data (calldata), such as calls made via send() or transfer().
contract ReceiveSample {
event emitReceive(uint);
receive() external payable {
emit emitReceive(msg.value);
}
}
Fallback function: fallback function does not take any arguments and does not return anything.
Characteristics
Fallback function;
is triggered if function that does not exist is called.
is triggered if Ether is sent directly to a contract but receive() does not exist or msg.data is not empty.
can only be defined once per contract.
must be marked as external.
has a 2300 gas limit when called by transfer or send.
// SPDX-License-Identifier: Unlicensed
pragma solidity ^0.8.0;
contract Fallback {
event emitFallBack(uint, gasLeft);
fallback() external payable {
emit emitFallBack(msg.value, gasleft());
}
}
View functions: The mutability of functions can be declared as view if they do not contain any code/logic that potentially modifies the state, but can read from them.
function sub(uint a) public view returns (uint) {
return a - b; // where b is a storage variable
}
Pure functions: Functions can be declared pure if they neither read from nor modify the state
function mul(uint a, uint b) public pure returns (uint) {
return a * b; // where b is a storage variable
}
DEVELPER'S TOOL KITS AND IDE’s
Solidity: Most dominant Ethereum smart contracting language
Foundry: Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.
Hardhat: Hardhat is an Ethereum development environment. Compile your contracts and run them on a development network. Get Solidity stack traces, console.log and more.
Truffle: A world class development environment, testing framework and asset pipeline for blockchains using the Ethereum Virtual Machine (EVM), aiming to make life as a developer easier.
Ethers js: The ethers.js library aims to be a complete and compact library for interacting with the Ethereum Blockchain and its ecosystem.
Web3 js: web3.js is a collection of libraries that allow you to interact with a local or remote ethereum node using HTTP, IPC or WebSocket
ApeWorx: The Ethereum application development framework for Python Developers, Data Scientists, and Security Professionals.
Metamask: The leading crypto wallet available on browser extension and mobile. Trusted by over 30 million users to buy, store, send and swap crypto securely.
Etherscan: Etherscan is a Block Explorer and Analytics Platform for Ethereum, a decentralized smart contracts platform.
Openzeppelin: OpenZeppelin provides security products to build, automate, and operate decentralized applications.
Brownie: Brownie is a Python-based development and testing framework for smart contracts targeting the Ethereum Virtual Machine.
Ethereum Stack Exchange: Post and search questions to help your development life cycle.
Biconomy: Do gasless transactions in your dapp by enabling meta-transactions using simple to use SDK.
Radicle: Radicle is a new kind of code collab network built on Git. Radicle is a peer-to-peer stack for building software together.
IPFS: A peer-to-peer hypermedia protocol designed to preserve and grow humanity's knowledge by making the web upgradeable, resilient, and more open.
Arweave: Permanent decentralized data storage.
Moralis: Moralis provides a single workflow for building high performance dapps. Fully compatible with your favorite web3 tools and services.
Ankr: Access all the developer tools you need with remote node access, advanced APIs, and the largest multi-chain network of RPCs. Build with fast, reliable infrastructure.
Spheron: Decentralized web hosting which supports storage on Arweave, Skynet, IPFS & Filecoin.
Remix: Remix is a web IDE that allows developing, deploying and administering smart contracts for Ethereum like blockchains. It can also be used as a learning platform.
EthFiddle: This browser-based Solidity IDE lets you compile test code, compiles the smart contract with helpful error handling, and lets you share the fiddles with friends and coworkers effortlessly.
Vs Code: Visual Studio Code extension that adds support for Solidity
IntelliJ: Open-source plug-in for Jetbrains IntelliJ Idea IDE(free/commercial) with syntax highlighting, formatting, code completion etc.
CONCLUSION
This sole aim of this guide is to help beginners, developers (that seek a refresher) get familiar or acquainted with solidity syntax and smart contract in general. I hope it does that!!.
Thanks to all that have taken their time to review this content. WAGMI 🚀 🚀
For the love of the community, all forms of contribution are welcomed. You can find the Github Repo here 👇