What is Solidity?
A high-level language for implementing smart contracts, designed to target the Ethereum Virtual Machine (EVM).
It is statically typed, allowing you to define the types of variables and functions.
Development in Remix
Remix is a web-based IDE for Ethereum smart contract development. It provides a user-friendly interface for writing, testing, and deploying smart contracts.
Go to Remix and create a new project.
Development in Foundry
Foundry manages your dependencies, compiles your project, runs tests, deploys, and lets you interact with the chain from the command-line and via Solidity scripts.
# Install Foundry
curl -L https://foundry.paradigm.xyz | bash
foundryup # installs forge, cast, anvil, and chisel
# Create a new project called Counter
forge init Counter && cd Counter
forge build # Compile your contracts
forge test # Run your test suiteStatic Types
Solidity is a statically typed language, meaning that the types of variables are known at compile time.
This applies to functions too. You must explicitly specify the argument type and the return type.
bool myBool = true; // Zero-value is false
// an Ethereum address (20-byte). Zero-value is 0x0000000000000000000000000000000000000000
address myAddress = 0x1234567890123456789012345678901234567890;
// an unsigned integer (256-bit). Zero-value is 0
uint256 myUint = 1234567890123456789012345678901234567890;
// [!] there are no floating point numbers in Solidity
// strings are arrays of bytes. Zero-value is empty string
string myString = "Hello, World!";
// arrays can be dynamic or fixed size. Zero-value is empty array
uint256[] myDynamicArray = [1, 2, 3];
uint256[3] myFixedArray = [1, 2, 3];
// structs are custom data types. Zero-value is empty struct
struct Person {
string name;
uint256 age;
}
Person myPerson = Person("John", 30);Arithmetic
uint256 sum = 2 + 3;
uint256 diff = 5 - 1;
uint256 product = 2 * 3;
uint256 quotient = 10 / 2;
uint256 remainder = 10 % 4; // remainder == 2
uint256 exp = 2 ** 3; // exp == 8
// Solidity does not have floats!!
// because floats aren't deterministic, and blockchains must be.
uint256 interest = 200 * 0.1; // fails
uint256 interest = 200 / 10; // works (integer division)
uint256 interest = 200 * 75 / 1000; // 7.5% interestOverflow and Underflow
Since version 0.8.0, Solidity does not underflow or overflow, it stops the execution and "throws" a revert error.
uint256 z = 2 - 5; // this will revert
// escape hatch: unchecked block disables overflow/underflow checks
// but you must be careful, as this can lead to undefined behavior
unchecked {
uint256 z = 2 - 5;
}Flow Control
Solidity supports the usual flow control statements: if, else, while, do, for, break, continue, return, and require.
// if/else statement
if (x % 10 == 0) {
return true;
} else {
return false;
}
// if/else/if statement
if (x % 10 == 0) {
return true;
} else if (x % 5 == 0) {
return false;
} else {
return false;
}
// for loop
uint256 sum = 0;
for (uint256 i = 0; i < 100; i++) {
sum = sum + i; // or sum += i
}
// while and do/while loops
// [!] rarely used to avoid infinite loops
while (x < 100) {
x = x + 1;
}
// do/while loop
do {
x = x + 1;
} while (x < 100);Arrays and Strings
// Arrays can be dynamic or fixed size
uint256[] myDynamicArray = [1, 2, 3];
uint256[3] myFixedArray = [1, 2, 3];
// Arrays are zero-indexed like every other language.
// Access elements using square brackets
uint256 firstElement = myDynamicArray[0];
uint256 secondElement = myFixedArray[1];
uint256 len = myDynamicArray.length;
myArray.push(newItem); // add a new item to the end of the array
myArray.pop(); // remove the last item from the array
// Sum of elements in an array
uint256 sum = 0;
for (uint256 i = 0; i < myDynamicArray.length; i++) {
sum += myDynamicArray[i];
}
// Product of elements in an array
uint256 prod = 1;
for (uint256 i = 0; i < myDynamicArray.length; i++) {
prod = prod * myDynamicArray[i];
}// Strings are arrays under the hood with some differences.
string myString = "Hello, World!";
string hi = string.concat("hello ", "alex"); // since v0.8.12
uint256 len = myString.length; // ERROR: Strings do not support .length
string char = hi[0]; // ERROR: Strings cannot be indexedStorage Variables
Variables declared outside of functions are storage variables.
They're stored in the contract's storage and keep their value after the transaction ends.
They look like “class variables” but don’t really behave like them. Think of them as a miniature database.
// in contract body, outside of functions...
// public:
// - current contract can read and write to it
// - other contracts can read but not modify (only via a function)
uint256 public myUint = 123;
string public myString = "Hello, World!";
address public myAddress = 0x1234567890123456789012345678901234567890;
// internal:
// - current contract can read and write to it (like public)
// - other contracts cannot read or write to it
uint256 internal myUint;
string internal myString;
address internal myAddress;
// private:
// - current contract can read and write to it (like public)
// - other contracts cannot read or write to it
uint256 private myUint;
string private myString;
address private myAddress;Mappings
Similar to Maps in JavaScript.
- Can only be declared as storage, you cannot declare them inside a function;
- Cannot be iterated over;
- Cannot be returned from a function;
// syntax: mapping(keyType => valueType) public mappingName;
mapping(address => uint256) public balances;
// create a new mapping
mapping(address => uint256) public balances = new mapping(address => uint256);
uint256 balance = balances[address]; // read a value
// If key not set, returns the “zero value” of the value datatype
balances[address] = value; // write a value
// mappings can have nested mappings
mapping(address => mapping(address => uint256)) public allowances;
allowances[owner][spender] = value; // write a value
uint256 allowance = allowances[owner][spender]; // read a valueGlobal Variables
available to all functions.
// msg
msg.sender // the address of the account that sent the transaction
msg.value // the amount of Ether sent with the transaction
msg.gas // the amount of gas provided with the transaction
msg.gasprice // the price of the gas
msg.block.number // the number of the block
msg.block.hash // the hash of the block
msg.block.timestamp // the timestamp of the block
msg.block.difficulty // the difficulty of the block// tx
tx.origin // the address of the account that sent the transaction
tx.gasprice // the price of the gas
tx.block.number // the number of the block
tx.block.hash // the hash of the block
tx.block.timestamp // the timestamp of the block
tx.block.difficulty // the difficulty of the blockConstructor
A special function that is executed only once, when the contract is deployed.
contract MyContract {
constructor() {
// initialize the contract
}
}
contract MyContract {
address public owner;
constructor(address _owner) {
owner = _owner;
}
}
contract MyContract {
string public name;
constructor(string memory _name) {
name = _name;
}
}