Solidity Interview Questions with Answers
Most Asked Solidity Interview Questions for Blockchain and Web3 Roles
Introduction
Solidity is the premier language for smart contracts on Ethereum and EVM‑compatible blockchains, combining object‑oriented and security‑focused design. This page compiles the most frequently asked Solidity interview questions – from basic syntax and data types to advanced design patterns, gas optimisation, and security best practices – essential for any blockchain or Web3 developer.
Why Solidity?
- Purpose‑built for Ethereum and EVM chains
- Turing‑complete – supports complex logic
- Security‑focused with built‑in modifiers and guards
- Used in DeFi, NFTs, DAOs, and Web3
- Gas‑efficient – optimised for blockchain resources
- Vibrant ecosystem with Hardhat, Foundry, and Truffle
- High demand in the growing blockchain industry
Most Asked Solidity Interview Questions
Solidity is a high-level, object-oriented programming language for implementing smart contracts on various blockchain platforms, most notably Ethereum.
- Contract-oriented: Designed for smart contracts
- EVM-based: Compiles to Ethereum Virtual Machine bytecode
- Statically typed: Type checking at compile time
- Inheritance: Supports multiple inheritance
- Security-focused: Built-in security features
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Hello World in Solidity
contract HelloWorld {
string public greeting = "Hello, World!";
function greet() public view returns (string memory) {
return greeting;
}
}Variables in Solidity are declared with explicit types. They can be state variables, local variables, or global variables.
- State variables: Stored on blockchain
- Local variables: Stored in memory
- Constants:
constantkeyword - Immutables:
immutablekeyword - Global variables:
msg.sender,block.number
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Variables in Solidity
contract Variables {
// State variables (stored on blockchain)
string public mutableVar = "Hello";
string public constant immutableVar = "World";
uint256 public inferred = 42;
// Data types
bool public isActive = true;
int256 public intNum = -10;
uint256 public uintNum = 100;
address public owner = msg.sender;
// Display
function getVariables() public view returns (string memory, string memory, uint256) {
return (mutableVar, immutableVar, inferred);
}
// Update variable
function setMutableVar(string memory _newValue) public {
mutableVar = _newValue;
}
}Solidity provides several data types including integers, booleans, addresses, bytes, strings, arrays, structs, and mappings.
- Integers:
int,uint(signed/unsigned) - Boolean:
bool - Address:
address,address payable - Bytes:
bytes,bytes1...bytes32 - String:
string - Array:
type[] - Struct:
struct - Mapping:
mapping
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Data Types in Solidity
contract DataTypes {
// Integer types
int8 public int8Num = -128;
int16 public int16Num = -32768;
int32 public int32Num = -2147483648;
int64 public int64Num = -9223372036854775808;
int256 public int256Num = -57896044618658097711785492504343953926634992332820282019728792003956564819968;
uint8 public uint8Num = 255;
uint16 public uint16Num = 65535;
uint32 public uint32Num = 4294967295;
uint64 public uint64Num = 18446744073709551615;
uint256 public uint256Num = 115792089237316195423570985008687907853269984665640564039457584007913129639935;
// Boolean
bool public isActive = true;
// Address
address public owner = 0x742d35Cc6634C0532925a3b844Bc454e4438f44e;
address payable public payableAddress = payable(0x742d35Cc6634C0532925a3b844Bc454e4438f44e);
// Fixed-size byte arrays
bytes1 public byte1 = 0x01;
bytes32 public bytes32 = 0x0000000000000000000000000000000000000000000000000000000000000001;
// Dynamic byte array
bytes public dynamicBytes = "Hello";
// String
string public str = "Hello Solidity";
// Array
uint256[] public uintArray = [1, 2, 3, 4, 5];
uint256[5] public fixedArray = [1, 2, 3, 4, 5];
// Struct
struct Person {
string name;
uint256 age;
}
Person public person = Person("Alice", 25);
// Mapping
mapping(address => uint256) public balances;
}Functions in Solidity are defined with the function keyword, visibility modifiers, and return types.
- Visibility:
public,private,internal,external - State mutability:
view,pure,payable - Parameters: Input parameters with types
- Returns:
returns (type) - Modifiers:
modifierfor custom logic
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Functions in Solidity
contract Functions {
// Basic function
function add(uint256 a, uint256 b) public pure returns (uint256) {
return a + b;
}
// Multiple return values
function divide(uint256 a, uint256 b) public pure returns (uint256 quotient, uint256 remainder) {
require(b != 0, "Division by zero");
quotient = a / b;
remainder = a % b;
}
// Function with default parameters (using overload)
function greet() public pure returns (string memory) {
return greet("Guest");
}
function greet(string memory name) public pure returns (string memory) {
return string(abi.encodePacked("Hello, ", name, "!"));
}
// View function (read-only)
function getBlockNumber() public view returns (uint256) {
return block.number;
}
// Pure function (no state access)
function pureAdd(uint256 a, uint256 b) public pure returns (uint256) {
return a + b;
}
// Payable function
function receivePayment() public payable {
// Receive ether
}
// Function with modifiers
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
address public owner;
function setOwner(address _owner) public onlyOwner {
owner = _owner;
}
}Arrays in Solidity can be fixed-size or dynamic. They store elements of the same type and support various operations.
- Fixed-size:
uint256[5] - Dynamic:
uint256[] - Push/Pop:
push(),pop() - Length:
array.length - Memory arrays:
new uint256[](size)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Arrays in Solidity
contract Arrays {
// Dynamic array
uint256[] public numbers;
string[] public strings = ["Apple", "Banana", "Orange"];
// Fixed array
uint256[5] public fixedNumbers = [1, 2, 3, 4, 5];
// Array operations
function addNumber(uint256 _num) public {
numbers.push(_num);
}
function getNumber(uint256 _index) public view returns (uint256) {
return numbers[_index];
}
function getLength() public view returns (uint256) {
return numbers.length;
}
function removeLast() public {
numbers.pop();
}
// Array iteration
function sumArray() public view returns (uint256) {
uint256 sum = 0;
for (uint256 i = 0; i < numbers.length; i++) {
sum += numbers[i];
}
return sum;
}
// Array methods
function getDoubled() public view returns (uint256[] memory) {
uint256[] memory doubled = new uint256[](numbers.length);
for (uint256 i = 0; i < numbers.length; i++) {
doubled[i] = numbers[i] * 2;
}
return doubled;
}
function getFiltered() public view returns (uint256[] memory) {
uint256 count = 0;
for (uint256 i = 0; i < numbers.length; i++) {
if (numbers[i] > 2) {
count++;
}
}
uint256[] memory filtered = new uint256[](count);
uint256 index = 0;
for (uint256 i = 0; i < numbers.length; i++) {
if (numbers[i] > 2) {
filtered[index] = numbers[i];
index++;
}
}
return filtered;
}
}Solidity provides arrays, mappings, and structs as collections for storing and organizing data.
- Arrays: Ordered list of elements
- Mappings: Key-value storage
- Structs: Custom data structures
- Nested collections: Arrays of structs, mappings to structs
- Operations: Iteration, access, modification
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Collections in Solidity
contract Collections {
// Arrays (List)
uint256[] public list = [1, 2, 3, 4, 5];
// Mapping (Dictionary)
mapping(address => uint256) public balances;
// Nested mapping
mapping(address => mapping(uint256 => bool)) public permissions;
// Struct with mapping
struct User {
string name;
uint256 age;
mapping(uint256 => bool) permissions;
}
mapping(address => User) public users;
// Array of structs
struct Item {
string name;
uint256 price;
}
Item[] public items;
// Collection operations
function addItem(string memory _name, uint256 _price) public {
items.push(Item(_name, _price));
}
function getItem(uint256 _index) public view returns (string memory, uint256) {
return (items[_index].name, items[_index].price);
}
function setBalance(address _user, uint256 _amount) public {
balances[_user] = _amount;
}
function getBalance(address _user) public view returns (uint256) {
return balances[_user];
}
// Mapping iteration (not directly possible, use array of keys)
address[] public userList;
function addUser(address _user) public {
if (balances[_user] == 0) {
userList.push(_user);
}
balances[_user]++;
}
function getAllUsers() public view returns (address[] memory) {
return userList;
}
}Structs are custom data types that group related variables. They are used to create complex data structures.
- Definition:
struct Person { string name; uint256 age; } - Creation:
Person("Alice", 25) - Access:
person.name - Storage: Can be stored in arrays and mappings
- Memory vs Storage:
memoryvsstoragekeyword
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Structs (Data Classes) in Solidity
contract DataClasses {
// Struct definition
struct Person {
string name;
uint256 age;
string city;
}
// Struct instance
Person public person1 = Person("Alice", 25, "NYC");
// Array of structs
Person[] public people;
// Mapping of structs
mapping(address => Person) public userProfiles;
// Constructor
constructor() {
people.push(Person("Alice", 25, "NYC"));
}
// Create person
function createPerson(string memory _name, uint256 _age, string memory _city) public {
Person memory newPerson = Person({
name: _name,
age: _age,
city: _city
});
people.push(newPerson);
}
// Update person
function updatePerson(uint256 _index, string memory _name, uint256 _age, string memory _city) public {
require(_index < people.length, "Index out of bounds");
people[_index] = Person(_name, _age, _city);
}
// Get person
function getPerson(uint256 _index) public view returns (string memory, uint256, string memory) {
require(_index < people.length, "Index out of bounds");
Person storage person = people[_index];
return (person.name, person.age, person.city);
}
// Get all people
function getAllPeople() public view returns (Person[] memory) {
return people;
}
}Enums define a set of named constants. They are used to represent a fixed set of states or options.
- Definition:
enum Status { Pending, Active, Inactive } - Usage:
Status public status = Status.Pending - Values:
uint8representation - Comparison:
status == Status.Active - Gas: Enums are gas efficient
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Enums in Solidity
contract Enums {
// Enum definition
enum Status {
Pending,
Active,
Inactive,
Suspended
}
enum Color {
Red,
Green,
Blue
}
// Enum variable
Status public status = Status.Pending;
Color public color = Color.Red;
// Enum in struct
struct User {
string name;
Status status;
}
User[] public users;
// Enum in mapping
mapping(address => Status) public userStatus;
// Set enum
function setStatus(Status _status) public {
status = _status;
}
// Get enum value
function getStatus() public view returns (Status) {
return status;
}
// Check enum
function isActive() public view returns (bool) {
return status == Status.Active;
}
// Enum in function
function setUserStatus(address _user, Status _status) public {
userStatus[_user] = _status;
}
// Create user with status
function createUser(string memory _name, Status _status) public {
users.push(User(_name, _status));
}
// Get user status
function getUserStatus(uint256 _index) public view returns (Status) {
return users[_index].status;
}
}Solidity doesn't have null values. Instead, it uses default values for uninitialized variables and custom patterns for optional values.
- Default values:
0,false,address(0) - Optional pattern: Struct with
isSetflag - Address check:
address != address(0) - Exists pattern: Mapping with existence check
- Require: Validate before use
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Null Safety in Solidity
contract NullSafety {
// Solidity doesn't have null, uses default values
// Use custom patterns for null-like behavior
// Default values
uint256 public defaultUint; // 0
bool public defaultBool; // false
address public defaultAddress; // 0x0
string public defaultString; // ""
// Custom null pattern using struct
struct OptionalUint {
bool isSet;
uint256 value;
}
OptionalUint public optionalValue;
// Set optional value
function setOptional(uint256 _value) public {
optionalValue = OptionalUint(true, _value);
}
// Clear optional value
function clearOptional() public {
optionalValue.isSet = false;
}
// Get optional value with default
function getOptional(uint256 _default) public view returns (uint256) {
if (optionalValue.isSet) {
return optionalValue.value;
}
return _default;
}
// Null check for address
function isValidAddress(address _addr) public pure returns (bool) {
return _addr != address(0);
}
// Using require for null checks
function transfer(address _to, uint256 _amount) public {
require(_to != address(0), "Invalid address");
// Transfer logic
}
// Using mapping with exists pattern
mapping(address => bool) public exists;
function addAddress(address _addr) public {
exists[_addr] = true;
}
function addressExists(address _addr) public view returns (bool) {
return exists[_addr];
}
}Solidity supports if-else, for loops, while loops, and ternary operators for control flow.
- If-else:
if (condition) { } else { } - For loop:
for (uint i = 0; i < n; i++) { } - While loop:
while (condition) { } - Do-while:
do { } while (condition) - Ternary:
condition ? value1 : value2
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Control Flow in Solidity
contract ControlFlow {
// If-else
function getStatus(uint256 _age) public pure returns (string memory) {
if (_age < 18) {
return "Minor";
} else {
return "Adult";
}
}
// If-else-if
function getGrade(uint8 _score) public pure returns (string memory) {
if (_score >= 90) {
return "A";
} else if (_score >= 80) {
return "B";
} else if (_score >= 70) {
return "C";
} else {
return "F";
}
}
// For loop
function sumArray(uint256[] memory _arr) public pure returns (uint256) {
uint256 sum = 0;
for (uint256 i = 0; i < _arr.length; i++) {
sum += _arr[i];
}
return sum;
}
// While loop
function countDown(uint256 _start) public pure returns (uint256[] memory) {
uint256[] memory result = new uint256[](_start);
uint256 i = 0;
while (_start > 0) {
result[i] = _start;
_start--;
i++;
}
return result;
}
// Do-while (using while with condition)
function doWhileExample(uint256 _n) public pure returns (uint256) {
uint256 sum = 0;
uint256 i = 0;
do {
sum += i;
i++;
} while (i <= _n);
return sum;
}
// Ternary operator
function isEven(uint256 _num) public pure returns (bool) {
return _num % 2 == 0 ? true : false;
}
}Inheritance allows contracts to derive from other contracts, inheriting their functions and state variables.
- Single inheritance:
contract Child is Parent - Multiple inheritance:
contract Child is Parent1, Parent2 - Constructor: Call parent constructor
- Override:
virtualandoverride - Interfaces: Define function signatures
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Inheritance in Solidity
// Base contract
contract Animal {
string public name;
constructor(string memory _name) {
name = _name;
}
function makeSound() public view virtual returns (string memory) {
return "Animal sound";
}
}
// Derived contract (single inheritance)
contract Dog is Animal {
string public breed;
constructor(string memory _name, string memory _breed) Animal(_name) {
breed = _breed;
}
function makeSound() public view override returns (string memory) {
return "Woof!";
}
}
// Multiple inheritance
contract Flyable {
function fly() public pure returns (string memory) {
return "Flying";
}
}
contract Swimmable {
function swim() public pure returns (string memory) {
return "Swimming";
}
}
contract Duck is Flyable, Swimmable {
// Inherits from both
}
// Abstract contract
abstract contract Vehicle {
function start() public virtual returns (string memory);
function stop() public pure returns (string memory) {
return "Stopped";
}
}
contract Car is Vehicle {
function start() public pure override returns (string memory) {
return "Car started";
}
}
// Interface
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
}Properties in Solidity are state variables with custom getters and setters implemented through functions.
- State variables: Stored on blockchain
- Getters:
viewfunctions - Setters: Functions with validation
- Computed:
viewfunctions that calculate values - Lazy: Cache computed results
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Properties in Solidity
contract Properties {
// State variables (properties)
string public name;
uint256 public age;
address public owner;
// Private variable with getter
uint256 private _balance;
// Constructor
constructor() {
owner = msg.sender;
_balance = 0;
}
// Getter (view function)
function getName() public view returns (string memory) {
return name;
}
// Setter with validation
function setName(string memory _name) public {
require(bytes(_name).length > 0, "Name cannot be empty");
name = _name;
}
// Setter with age validation
function setAge(uint256 _age) public {
require(_age >= 0 && _age <= 150, "Invalid age");
age = _age;
}
// Read-only property
function getBalance() public view returns (uint256) {
return _balance;
}
// Computed property
function getFullName() public view returns (string memory) {
return string(abi.encodePacked(name, " (Age: ", uint2str(age), ")"));
}
// Lazy property (cached computation)
uint256 private _expensiveData;
bool private _expensiveDataComputed;
function getExpensiveData() public returns (uint256) {
if (!_expensiveDataComputed) {
// Compute expensive data
_expensiveData = block.number * 1000;
_expensiveDataComputed = true;
}
return _expensiveData;
}
// Helper function
function uint2str(uint256 _i) internal pure returns (string memory) {
if (_i == 0) return "0";
uint256 j = _i;
uint256 len;
while (j != 0) {
len++;
j /= 10;
}
bytes memory bstr = new bytes(len);
uint256 k = len;
while (_i != 0) {
k = k - 1;
uint8 temp = (48 + uint8(_i - _i / 10 * 10));
bytes1 b1 = bytes1(temp);
bstr[k] = b1;
_i /= 10;
}
return string(bstr);
}
}Events emit data to the blockchain logs, and modifiers add reusable conditions to functions.
- Events:
event Transfer(address indexed from, address indexed to, uint256 amount) - Modifiers:
modifier onlyOwner() { require(msg.sender == owner); _; } - Multiple modifiers: Chain modifiers on functions
- Events logging: Emit events for off-chain tracking
- Gas: Events are gas efficient
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Events and Modifiers
contract EventsModifiers {
// Events
event ValueChanged(address indexed changer, uint256 oldValue, uint256 newValue);
event UserRegistered(address indexed user, string name);
event Transfer(address indexed from, address indexed to, uint256 amount);
// Modifiers
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
modifier validAddress(address _addr) {
require(_addr != address(0), "Invalid address");
_;
}
modifier validAmount(uint256 _amount) {
require(_amount > 0, "Amount must be greater than 0");
_;
}
// State variables
address public owner;
uint256 public value;
mapping(address => bool) public registered;
constructor() {
owner = msg.sender;
}
// Function with modifiers
function setValue(uint256 _newValue) public onlyOwner {
uint256 oldValue = value;
value = _newValue;
emit ValueChanged(msg.sender, oldValue, _newValue);
}
function registerUser(string memory _name) public {
require(!registered[msg.sender], "Already registered");
registered[msg.sender] = true;
emit UserRegistered(msg.sender, _name);
}
function transfer(address _to, uint256 _amount) public
validAddress(_to)
validAmount(_amount)
{
// Transfer logic
emit Transfer(msg.sender, _to, _amount);
}
// Multiple modifiers
function secureTransfer(address _to, uint256 _amount) public
onlyOwner
validAddress(_to)
validAmount(_amount)
{
// Transfer logic
}
// Modifier with parameters
modifier minimumBalance(uint256 _min) {
require(address(this).balance >= _min, "Insufficient balance");
_;
}
function withdraw(uint256 _amount) public onlyOwner minimumBalance(_amount) {
payable(msg.sender).transfer(_amount);
}
}Solidity handles errors using require, revert, assert, and custom errors.
- require:
require(condition, "message") - revert:
revert("message") - assert:
assert(condition) - Custom errors:
error InsufficientBalance(uint256 balance, uint256 requested) - Gas: Custom errors are more gas efficient
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Error Handling in Solidity
contract ErrorHandling {
// Custom errors (gas efficient)
error InsufficientBalance(uint256 balance, uint256 requested);
error Unauthorized(address caller);
error InvalidAmount(uint256 amount);
error UserNotFound(address user);
// State variables
mapping(address => uint256) public balances;
mapping(address => bool) public users;
// Require statements
function withdraw(uint256 _amount) public {
require(balances[msg.sender] >= _amount, "Insufficient balance");
require(_amount > 0, "Amount must be greater than 0");
balances[msg.sender] -= _amount;
payable(msg.sender).transfer(_amount);
}
// Custom error with require
function withdrawCustom(uint256 _amount) public {
if (balances[msg.sender] < _amount) {
revert InsufficientBalance({
balance: balances[msg.sender],
requested: _amount
});
}
if (_amount <= 0) {
revert InvalidAmount(_amount);
}
balances[msg.sender] -= _amount;
payable(msg.sender).transfer(_amount);
}
// Revert with custom error
function transfer(address _to, uint256 _amount) public {
if (balances[msg.sender] < _amount) {
revert InsufficientBalance(balances[msg.sender], _amount);
}
if (_to == address(0)) {
revert("Invalid address");
}
balances[msg.sender] -= _amount;
balances[_to] += _amount;
}
// Try-catch (only for external calls)
function safeCall(address _contract, uint256 _value) public {
(bool success, ) = _contract.call{value: _value}("");
if (!success) {
// Handle failure
revert("External call failed");
}
}
// Assert for internal errors (gas cost)
function assertExample(uint256 _a, uint256 _b) public pure returns (uint256) {
assert(_b != 0);
return _a / _b;
}
}Function types allow functions to be passed as parameters and returned, enabling higher-order functions.
- Function type:
function(uint256) pure returns (uint256) - Parameters: Pass functions as arguments
- Return: Return functions from functions
- Internal/External: Function visibility matters
- Usage:
operate(a, b, add)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Functions and Function Types
contract FunctionTypes {
// Function types
function add(uint256 a, uint256 b) public pure returns (uint256) {
return a + b;
}
function subtract(uint256 a, uint256 b) public pure returns (uint256) {
return a - b;
}
function multiply(uint256 a, uint256 b) public pure returns (uint256) {
return a * b;
}
// Function as parameter
function operate(
uint256 a,
uint256 b,
function(uint256, uint256) pure returns (uint256) operation
) public pure returns (uint256) {
return operation(a, b);
}
// Function returning function (using internal)
function getOperation(string memory _type) public pure returns (function(uint256, uint256) pure returns (uint256)) {
if (keccak256(abi.encodePacked(_type)) == keccak256(abi.encodePacked("add"))) {
return add;
} else if (keccak256(abi.encodePacked(_type)) == keccak256(abi.encodePacked("subtract"))) {
return subtract;
} else {
return multiply;
}
}
// Library-like functions
function square(uint256 x) public pure returns (uint256) {
return x * x;
}
function cube(uint256 x) public pure returns (uint256) {
return x * x * x;
}
// Function composition
function squareThenAdd(uint256 x, uint256 y) public pure returns (uint256) {
return square(x) + y;
}
// Using function as variable
function(uint256) pure returns (uint256) public squareFunc = square;
function callSquare(uint256 x) public view returns (uint256) {
return squareFunc(x);
}
}Libraries are reusable collections of functions that can be attached to types using using ... for ....
- Library:
library Math { function add(uint a, uint b) internal pure returns (uint) } - Using for:
using Math for uint256 - Pure functions: Libraries use pure/internal functions
- Gas: Libraries are gas efficient
- Deployment: Libraries can be deployed separately
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Libraries and Using For
library MathLib {
function add(uint256 a, uint256 b) internal pure returns (uint256) {
return a + b;
}
function subtract(uint256 a, uint256 b) internal pure returns (uint256) {
return a - b;
}
function multiply(uint256 a, uint256 b) internal pure returns (uint256) {
return a * b;
}
function divide(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0, "Division by zero");
return a / b;
}
}
library StringLib {
function concat(string memory a, string memory b) internal pure returns (string memory) {
return string(abi.encodePacked(a, b));
}
function length(string memory str) internal pure returns (uint256) {
return bytes(str).length;
}
}
contract UsingForExample {
using MathLib for uint256;
using StringLib for string;
function calculate(uint256 a, uint256 b) public pure returns (uint256) {
return a.add(b).multiply(2);
}
function combine(string memory a, string memory b) public pure returns (string memory) {
return a.concat(b);
}
function getLength(string memory str) public pure returns (uint256) {
return str.length();
}
}
// Library with events (not allowed, libraries can't have state)
library EventLib {
// Libraries cannot have state variables or events
}Custom modifiers add reusable conditions and logic to functions, improving code modularity.
- Definition:
modifier name() { _; } - Parameters: Modifiers can take parameters
- Multiple: Apply multiple modifiers
- Inheritance: Modifiers can be overridden
- Common:
onlyOwner,whenNotPaused
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Modifiers and Custom Modifiers
contract CustomModifiers {
// State variables
address public owner;
uint256 public value;
bool public paused;
// Events
event ValueChanged(uint256 oldValue, uint256 newValue);
event Paused(bool status);
constructor() {
owner = msg.sender;
paused = false;
}
// Basic modifier
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
// Modifier with parameter
modifier validValue(uint256 _value) {
require(_value > 0, "Value must be positive");
_;
}
// Modifier with condition
modifier whenNotPaused() {
require(!paused, "Contract is paused");
_;
}
// Modifier with multiple conditions
modifier validAddress(address _addr) {
require(_addr != address(0), "Invalid address");
_;
}
// Modifier with complex logic
modifier onlyWhen(bool _condition) {
require(_condition, "Condition failed");
_;
}
// Using modifiers
function setValue(uint256 _newValue) public
onlyOwner
validValue(_newValue)
whenNotPaused
{
uint256 oldValue = value;
value = _newValue;
emit ValueChanged(oldValue, _newValue);
}
function pause() public onlyOwner {
paused = true;
emit Paused(true);
}
function unpause() public onlyOwner {
paused = false;
emit Paused(false);
}
// Modifier inheritance
modifier overrideModifier() virtual {
_;
}
function overriddenFunction() public overrideModifier {
// Function logic
}
}
contract Child is CustomModifiers {
// Override modifier
modifier overrideModifier() override {
// Additional logic
_;
// More logic
}
}Events allow logging of contract activity to the blockchain, enabling off-chain applications to track state changes.
- Definition:
event Transfer(address indexed from, address indexed to, uint256 amount) - Emit:
emit Transfer(msg.sender, to, amount) - Indexed:
indexedfor filtering - Gas: Events are gas efficient
- Anonymous:
event Log(string message) anonymous
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Events in Solidity
contract EventsExample {
// Event definitions
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
event Log(string message, uint256 value);
event UserRegistered(address indexed user, string name, uint256 timestamp);
// State variables
mapping(address => uint256) public balances;
mapping(address => mapping(address => uint256)) public allowances;
// Transfer function with event
function transfer(address _to, uint256 _amount) public {
require(balances[msg.sender] >= _amount, "Insufficient balance");
require(_to != address(0), "Invalid address");
balances[msg.sender] -= _amount;
balances[_to] += _amount;
emit Transfer(msg.sender, _to, _amount);
}
// Approve function with event
function approve(address _spender, uint256 _amount) public {
allowances[msg.sender][_spender] = _amount;
emit Approval(msg.sender, _spender, _amount);
}
// Logging function
function logMessage(string memory _message, uint256 _value) public {
emit Log(_message, _value);
}
// Register user with event
function registerUser(string memory _name) public {
emit UserRegistered(msg.sender, _name, block.timestamp);
}
// Anonymous event (cheaper gas)
event AnonymousLog(string message) anonymous;
function anonymousLog(string memory _message) public {
emit AnonymousLog(_message);
}
}Interfaces define function signatures without implementations, enabling contract interaction and standards.
- Definition:
interface IERC20 { function totalSupply() external view returns (uint256); } - No implementation: Only function signatures
- External: Functions are external
- Standards: ERC-20, ERC-721, etc.
- Interaction: Call functions on external contracts
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Inheritance and Interfaces
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
}
// Implementing interface
contract MyToken is IERC20 {
string public name = "My Token";
string public symbol = "MTK";
uint8 public decimals = 18;
uint256 private _totalSupply;
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
constructor(uint256 initialSupply) {
_totalSupply = initialSupply * 10**uint256(decimals);
_balances[msg.sender] = _totalSupply;
}
function totalSupply() external view override returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) external view override returns (uint256) {
return _balances[account];
}
function transfer(address recipient, uint256 amount) external override returns (bool) {
require(recipient != address(0), "Invalid recipient");
require(_balances[msg.sender] >= amount, "Insufficient balance");
_balances[msg.sender] -= amount;
_balances[recipient] += amount;
emit Transfer(msg.sender, recipient, amount);
return true;
}
function approve(address spender, uint256 amount) external override returns (bool) {
_allowances[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transferFrom(address sender, address recipient, uint256 amount) external override returns (bool) {
require(sender != address(0), "Invalid sender");
require(recipient != address(0), "Invalid recipient");
require(_balances[sender] >= amount, "Insufficient balance");
require(_allowances[sender][msg.sender] >= amount, "Insufficient allowance");
_balances[sender] -= amount;
_balances[recipient] += amount;
_allowances[sender][msg.sender] -= amount;
emit Transfer(sender, recipient, amount);
return true;
}
}Higher-order functions take functions as parameters or return functions, enabling functional programming patterns.
- Function parameters:
function operate(uint a, uint b, function(uint,uint) pure returns(uint) op) - Returning functions:
function getMultiplier(uint factor) public pure returns (function(uint) pure returns(uint)) - Composition: Combine multiple functions
- Gas: Higher-order functions may cost more gas
- Limitations: Function types in Solidity have limitations
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Higher-Order Functions and Function Composition
contract HigherOrderFunctions {
// Function types
function add(uint256 a, uint256 b) public pure returns (uint256) {
return a + b;
}
function subtract(uint256 a, uint256 b) public pure returns (uint256) {
return a - b;
}
function multiply(uint256 a, uint256 b) public pure returns (uint256) {
return a * b;
}
// Function taking function as parameter
function applyOperation(
uint256 a,
uint256 b,
function(uint256, uint256) pure returns (uint256) operation
) public pure returns (uint256) {
return operation(a, b);
}
// Function returning function
function getMultiplier(uint256 factor) public pure returns (function(uint256) pure returns (uint256)) {
return function(uint256 x) pure returns (uint256) {
return x * factor;
};
}
// Function composition
function compose(
function(uint256) pure returns (uint256) f,
function(uint256) pure returns (uint256) g
) public pure returns (function(uint256) pure returns (uint256)) {
return function(uint256 x) pure returns (uint256) {
return f(g(x));
};
}
// Square function
function square(uint256 x) public pure returns (uint256) {
return x * x;
}
// Add ten function
function addTen(uint256 x) public pure returns (uint256) {
return x + 10;
}
// Compose example
function squareThenAddTen(uint256 x) public pure returns (uint256) {
function(uint256) pure returns (uint256) f = this.addTen;
function(uint256) pure returns (uint256) g = this.square;
function(uint256) pure returns (uint256) composed = compose(f, g);
return composed(x);
}
}Events are the primary logging mechanism in Solidity, allowing contracts to emit data for off-chain consumption.
- Events: Log contract activity
- Parameters: Can be indexed for filtering
- Gas: Events are gas efficient
- Off-chain: Used by dApps and monitoring tools
- Types: Regular and anonymous events
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Events and Logging
contract EventsLogging {
// Events
event Deposit(address indexed account, uint256 amount);
event Withdraw(address indexed account, uint256 amount);
event Transfer(address indexed from, address indexed to, uint256 amount);
event Log(string message, uint256 value);
event Error(string message);
// State
mapping(address => uint256) public balances;
// Deposit
function deposit() public payable {
balances[msg.sender] += msg.value;
emit Deposit(msg.sender, msg.value);
}
// Withdraw
function withdraw(uint256 _amount) public {
require(balances[msg.sender] >= _amount, "Insufficient balance");
balances[msg.sender] -= _amount;
payable(msg.sender).transfer(_amount);
emit Withdraw(msg.sender, _amount);
}
// Transfer
function transfer(address _to, uint256 _amount) public {
require(balances[msg.sender] >= _amount, "Insufficient balance");
require(_to != address(0), "Invalid address");
balances[msg.sender] -= _amount;
balances[_to] += _amount;
emit Transfer(msg.sender, _to, _amount);
}
// Logging
function logValue(uint256 _value) public {
emit Log("Value logged", _value);
}
// Error logging
function logError(string memory _message) public {
emit Error(_message);
}
}Modifiers provide reusable conditions for functions, implementing access control and validation patterns.
- Access control:
onlyOwner,onlyWhitelisted - State guards:
whenNotPaused - Rate limiting: Time-based modifiers
- Composition: Multiple modifiers can be combined
- Parameters: Modifiers can accept parameters
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Modifiers and Guards
contract ModifiersGuards {
// State
address public owner;
bool public paused;
mapping(address => bool) public whitelist;
mapping(address => uint256) public dailyLimit;
mapping(address => uint256) public lastTxTime;
// Events
event Paused(bool status);
event WhitelistUpdated(address indexed account, bool status);
constructor() {
owner = msg.sender;
paused = false;
}
// Owner modifier
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
// Pause modifier
modifier whenNotPaused() {
require(!paused, "Contract paused");
_;
}
// Whitelist modifier
modifier onlyWhitelisted() {
require(whitelist[msg.sender], "Not whitelisted");
_;
}
// Time-based modifier
modifier rateLimit(uint256 _limit) {
require(block.timestamp >= lastTxTime[msg.sender] + 1 hours, "Rate limited");
lastTxTime[msg.sender] = block.timestamp;
_;
}
// Balance modifier
modifier hasBalance(uint256 _amount) {
require(address(this).balance >= _amount, "Insufficient contract balance");
_;
}
// Combined modifiers
function transfer(address _to, uint256 _amount) public
whenNotPaused
onlyWhitelisted
rateLimit(1000)
hasBalance(_amount)
{
// Transfer logic
}
// Admin functions
function pause() public onlyOwner {
paused = true;
emit Paused(true);
}
function unpause() public onlyOwner {
paused = false;
emit Paused(false);
}
function updateWhitelist(address _account, bool _status) public onlyOwner {
whitelist[_account] = _status;
emit WhitelistUpdated(_account, _status);
}
}Libraries provide reusable functions that can be attached to types, improving code organization and gas efficiency.
- Libraries: Deploy once, use many times
- Internal functions: Inlined by compiler
- Using for: Attach to types
- Gas: More gas efficient than contracts
- Helpers: Utility functions for common operations
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Libraries and Helpers
library SafeMath {
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
return a - b;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: division by zero");
return a / b;
}
}
library AddressUtils {
function isContract(address account) internal view returns (bool) {
uint256 size;
assembly {
size := extcodesize(account)
}
return size > 0;
}
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value");
}
}
library StringUtils {
function concat(string memory a, string memory b) internal pure returns (string memory) {
return string(abi.encodePacked(a, b));
}
function compare(string memory a, string memory b) internal pure returns (bool) {
return keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b));
}
}
contract UsingLibraries {
using SafeMath for uint256;
using AddressUtils for address;
using StringUtils for string;
uint256 public value;
address public owner;
constructor() {
owner = msg.sender;
}
function addValue(uint256 _amount) public {
value = value.add(_amount);
}
function subtractValue(uint256 _amount) public {
value = value.sub(_amount);
}
function sendTo(address payable _to, uint256 _amount) public {
require(msg.sender == owner, "Not owner");
_to.sendValue(_amount);
}
function isContractAddress(address _addr) public view returns (bool) {
return _addr.isContract();
}
function concatStrings(string memory a, string memory b) public pure returns (string memory) {
return a.concat(b);
}
}Enums and structs are custom data types that help organize and structure contract data.
- Enums: Fixed set of constants
- Structs: Custom data containers
- Nested: Structs can contain enums
- Storage: Stored in arrays and mappings
- Gas: Efficient for representing states
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Enums and Structs
contract EnumsStructs {
// Enums
enum Status {
Pending,
Active,
Inactive,
Suspended
}
enum OrderStatus {
Created,
Confirmed,
Shipped,
Delivered,
Cancelled
}
// Structs
struct User {
string name;
uint256 age;
Status status;
address wallet;
}
struct Product {
uint256 id;
string name;
uint256 price;
bool inStock;
}
struct Order {
uint256 id;
address customer;
uint256[] productIds;
uint256 total;
OrderStatus status;
uint256 timestamp;
}
// State
User[] public users;
Product[] public products;
Order[] public orders;
mapping(address => User) public userMap;
// Events
event UserCreated(address indexed user, string name);
event OrderCreated(uint256 indexed orderId, address customer);
event OrderStatusChanged(uint256 indexed orderId, OrderStatus status);
// Create user
function createUser(string memory _name, uint256 _age) public {
User memory newUser = User({
name: _name,
age: _age,
status: Status.Active,
wallet: msg.sender
});
users.push(newUser);
userMap[msg.sender] = newUser;
emit UserCreated(msg.sender, _name);
}
// Create product
function createProduct(string memory _name, uint256 _price) public {
uint256 id = products.length;
products.push(Product({
id: id,
name: _name,
price: _price,
inStock: true
}));
}
// Create order
function createOrder(uint256[] memory _productIds) public {
uint256 total = 0;
for (uint256 i = 0; i < _productIds.length; i++) {
require(_productIds[i] < products.length, "Invalid product");
require(products[_productIds[i]].inStock, "Product out of stock");
total += products[_productIds[i]].price;
}
uint256 orderId = orders.length;
orders.push(Order({
id: orderId,
customer: msg.sender,
productIds: _productIds,
total: total,
status: OrderStatus.Created,
timestamp: block.timestamp
}));
emit OrderCreated(orderId, msg.sender);
}
// Update order status
function updateOrderStatus(uint256 _orderId, OrderStatus _status) public {
require(_orderId < orders.length, "Order not found");
require(orders[_orderId].customer == msg.sender, "Not owner");
orders[_orderId].status = _status;
emit OrderStatusChanged(_orderId, _status);
}
// Get user by address
function getUser(address _addr) public view returns (string memory, uint256, Status) {
User storage user = userMap[_addr];
return (user.name, user.age, user.status);
}
}Mappings provide key-value storage but cannot be directly iterated. Use separate arrays for iteration.
- Mapping:
mapping(address => uint256) balances - Keys: Store keys in separate array
- Iteration: Loop through keys array
- Gas: Mappings are gas efficient for lookups
- Nested:
mapping(address => mapping(uint256 => bool)) permissions
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Mappings and Iteration
contract MappingsIteration {
// Mappings
mapping(address => uint256) public balances;
mapping(address => bool) public exists;
mapping(address => string) public names;
mapping(uint256 => address) public idToAddress;
mapping(address => mapping(uint256 => bool)) public permissions;
// Arrays for iteration
address[] public users;
uint256[] public userIds;
// Events
event UserAdded(address indexed user, uint256 id);
// Add user with balance
function addUser(address _user, uint256 _balance) public {
require(!exists[_user], "User already exists");
exists[_user] = true;
balances[_user] = _balance;
users.push(_user);
userIds.push(users.length);
emit UserAdded(_user, users.length);
}
// Update balance
function updateBalance(address _user, uint256 _balance) public {
require(exists[_user], "User not found");
balances[_user] = _balance;
}
// Get all users
function getAllUsers() public view returns (address[] memory) {
return users;
}
// Get user count
function getUserCount() public view returns (uint256) {
return users.length;
}
// Get user at index
function getUserAtIndex(uint256 _index) public view returns (address) {
require(_index < users.length, "Index out of bounds");
return users[_index];
}
// Get all balances
function getAllBalances() public view returns (uint256[] memory) {
uint256[] memory allBalances = new uint256[](users.length);
for (uint256 i = 0; i < users.length; i++) {
allBalances[i] = balances[users[i]];
}
return allBalances;
}
// Nested mapping
function setPermission(address _user, uint256 _resource, bool _allowed) public {
permissions[_user][_resource] = _allowed;
}
function hasPermission(address _user, uint256 _resource) public view returns (bool) {
return permissions[_user][_resource];
}
}Inheritance allows contracts to extend functionality, and polymorphism enables flexible contract interactions.
- Inheritance:
contract Child is Parent - Polymorphism: Different behaviors for same interface
- Override:
virtualandoverride - Abstract: Contracts with unimplemented functions
- Interfaces: Standardized interactions
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Inheritance and Polymorphism
// Base contract
contract Token {
string public name;
string public symbol;
uint8 public decimals;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
event Transfer(address indexed from, address indexed to, uint256 amount);
constructor(string memory _name, string memory _symbol, uint8 _decimals, uint256 _supply) {
name = _name;
symbol = _symbol;
decimals = _decimals;
totalSupply = _supply;
balanceOf[msg.sender] = _supply;
}
function transfer(address _to, uint256 _amount) public virtual returns (bool) {
require(balanceOf[msg.sender] >= _amount, "Insufficient balance");
balanceOf[msg.sender] -= _amount;
balanceOf[_to] += _amount;
emit Transfer(msg.sender, _to, _amount);
return true;
}
}
// ERC20 Token (inheritance)
contract ERC20Token is Token {
mapping(address => mapping(address => uint256)) public allowance;
event Approval(address indexed owner, address indexed spender, uint256 amount);
constructor(string memory _name, string memory _symbol, uint8 _decimals, uint256 _supply)
Token(_name, _symbol, _decimals, _supply)
{}
function approve(address _spender, uint256 _amount) public returns (bool) {
allowance[msg.sender][_spender] = _amount;
emit Approval(msg.sender, _spender, _amount);
return true;
}
function transferFrom(address _from, address _to, uint256 _amount) public returns (bool) {
require(allowance[_from][msg.sender] >= _amount, "Insufficient allowance");
allowance[_from][msg.sender] -= _amount;
require(balanceOf[_from] >= _amount, "Insufficient balance");
balanceOf[_from] -= _amount;
balanceOf[_to] += _amount;
emit Transfer(_from, _to, _amount);
return true;
}
}
// Burnable Token (polymorphism)
contract BurnableToken is ERC20Token {
event Burn(address indexed from, uint256 amount);
constructor(string memory _name, string memory _symbol, uint8 _decimals, uint256 _supply)
ERC20Token(_name, _symbol, _decimals, _supply)
{}
function burn(uint256 _amount) public {
require(balanceOf[msg.sender] >= _amount, "Insufficient balance");
balanceOf[msg.sender] -= _amount;
totalSupply -= _amount;
emit Burn(msg.sender, _amount);
}
}Factory pattern creates new contract instances, enabling deployment of multiple copies with different parameters.
- Factory: Contract that creates other contracts
- Create: Use
newkeyword - Tracking: Store created instances
- Deployment: Deploy child contracts
- Gas: Factory pattern can be gas efficient
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Factory Pattern
contract TokenFactory {
// Events
event TokenCreated(address indexed token, string name, string symbol);
// Token contract (simple version)
contract SimpleToken {
string public name;
string public symbol;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
constructor(string memory _name, string memory _symbol, uint256 _supply) {
name = _name;
symbol = _symbol;
totalSupply = _supply;
balanceOf[msg.sender] = _supply;
}
function transfer(address _to, uint256 _amount) public returns (bool) {
require(balanceOf[msg.sender] >= _amount, "Insufficient balance");
balanceOf[msg.sender] -= _amount;
balanceOf[_to] += _amount;
return true;
}
}
// Create token
function createToken(string memory _name, string memory _symbol, uint256 _supply) public returns (address) {
SimpleToken token = new SimpleToken(_name, _symbol, _supply);
emit TokenCreated(address(token), _name, _symbol);
return address(token);
}
// Create token with salt (deterministic)
function createTokenWithSalt(string memory _name, string memory _symbol, uint256 _supply, bytes32 _salt) public returns (address) {
bytes memory bytecode = type(SimpleToken).creationCode;
bytes32 hash = keccak256(abi.encodePacked(bytecode, _salt));
address addr = address(uint160(uint256(hash)));
// This is a simplified version - in practice, use CREATE2
return addr;
}
}Strategy pattern allows switching algorithms at runtime using interfaces and dynamic dispatch.
- Interface: Define strategy interface
- Strategies: Different implementations
- Context: Uses current strategy
- Dynamic: Change strategy at runtime
- Flexibility: Swap algorithms without changing context
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Strategy Pattern
contract StrategyPattern {
// Strategy interface
interface IPaymentStrategy {
function pay(address payer, uint256 amount) external returns (bool);
}
// Concrete strategies
contract CreditCardStrategy is IPaymentStrategy {
function pay(address payer, uint256 amount) external pure override returns (bool) {
// Credit card payment logic
return true;
}
}
contract PayPalStrategy is IPaymentStrategy {
function pay(address payer, uint256 amount) external pure override returns (bool) {
// PayPal payment logic
return true;
}
}
contract CryptoStrategy is IPaymentStrategy {
function pay(address payer, uint256 amount) external pure override returns (bool) {
// Crypto payment logic
return true;
}
}
// Context
address public paymentStrategy;
function setStrategy(address _strategy) public {
require(_strategy != address(0), "Invalid strategy");
paymentStrategy = _strategy;
}
function executePayment(uint256 _amount) public returns (bool) {
require(paymentStrategy != address(0), "Strategy not set");
IPaymentStrategy strategy = IPaymentStrategy(paymentStrategy);
return strategy.pay(msg.sender, _amount);
}
}Observer pattern is implemented using events and listener contracts for notification-based communication.
- Events: Emit notifications
- Observers: Contracts that listen
- Registration: Add/remove observers
- Notification: Emit events on state change
- Decoupling: Loose coupling between components
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Observer Pattern (Events)
contract ObserverPattern {
// Events as observer notifications
event StateChanged(address indexed changer, uint256 oldValue, uint256 newValue);
event UserUpdated(address indexed user, string oldName, string newName);
event BalanceChanged(address indexed account, uint256 newBalance);
// State
uint256 public value;
mapping(address => string) public userNames;
mapping(address => uint256) public balances;
// Observers list (simplified)
address[] public observers;
mapping(address => bool) public isObserver;
// Add observer
function addObserver(address _observer) public {
require(!isObserver[_observer], "Already observer");
isObserver[_observer] = true;
observers.push(_observer);
}
// Remove observer
function removeObserver(address _observer) public {
require(isObserver[_observer], "Not observer");
isObserver[_observer] = false;
// Remove from array (simplified)
}
// Notify observers (simplified)
function notifyObservers(string memory _message) internal {
// In practice, observers would be contracts with callback functions
for (uint256 i = 0; i < observers.length; i++) {
if (isObserver[observers[i]]) {
// Call observer contract
}
}
}
// Update state with events
function setValue(uint256 _newValue) public {
uint256 oldValue = value;
value = _newValue;
emit StateChanged(msg.sender, oldValue, _newValue);
}
function updateUser(string memory _newName) public {
string memory oldName = userNames[msg.sender];
userNames[msg.sender] = _newName;
emit UserUpdated(msg.sender, oldName, _newName);
}
function updateBalance(uint256 _newBalance) public {
balances[msg.sender] = _newBalance;
emit BalanceChanged(msg.sender, _newBalance);
}
}State pattern manages contract behavior based on current state using enums and state-specific logic.
- States: Defined as enum
- Transitions: Change state with validation
- Behavior: Functions behave based on state
- Modifiers: Restrict to certain states
- Events: Emit on state changes
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// State Pattern
contract StatePattern {
// States
enum State {
Idle,
Processing,
Completed,
Cancelled
}
// State variables
State public currentState = State.Idle;
address public owner;
uint256 public value;
// Events
event StateTransition(State from, State to);
constructor() {
owner = msg.sender;
}
// State transitions
modifier onlyInState(State _state) {
require(currentState == _state, "Invalid state");
_;
}
function startProcessing() public onlyInState(State.Idle) {
currentState = State.Processing;
emit StateTransition(State.Idle, State.Processing);
}
function complete() public onlyInState(State.Processing) {
currentState = State.Completed;
emit StateTransition(State.Processing, State.Completed);
}
function cancel() public onlyInState(State.Idle) {
currentState = State.Cancelled;
emit StateTransition(State.Idle, State.Cancelled);
}
function reset() public {
require(msg.sender == owner, "Not owner");
currentState = State.Idle;
emit StateTransition(State.Completed, State.Idle);
}
// State-dependent behavior
function getStatus() public view returns (string memory) {
if (currentState == State.Idle) {
return "Idle - Ready to start";
} else if (currentState == State.Processing) {
return "Processing - Working on task";
} else if (currentState == State.Completed) {
return "Completed - Task finished";
} else {
return "Cancelled - Task cancelled";
}
}
}Reverse a string using bytes conversion and manual iteration.
- bytes conversion:
bytes(str) - Manual: Iterate from end to start
- Complexity: O(n) time
- Memory: Uses
memoryfor string
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Reverse a string in Solidity
contract StringReverse {
function reverseString(string memory _str) public pure returns (string memory) {
bytes memory strBytes = bytes(_str);
bytes memory reversed = new bytes(strBytes.length);
for (uint256 i = 0; i < strBytes.length; i++) {
reversed[i] = strBytes[strBytes.length - 1 - i];
}
return string(reversed);
}
}Check if a string is a palindrome using two-pointer approach on bytes.
- Two-pointer: Compare from both ends
- bytes conversion:
bytes(str) - Case sensitive: Solidity strings are case sensitive
- Complexity: O(n) time
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Check palindrome in Solidity
contract Palindrome {
function isPalindrome(string memory _str) public pure returns (bool) {
bytes memory strBytes = bytes(_str);
for (uint256 i = 0; i < strBytes.length / 2; i++) {
if (strBytes[i] != strBytes[strBytes.length - 1 - i]) {
return false;
}
}
return true;
}
}Find maximum value using manual iteration.
- Manual: Iterate and track max
- Empty array: Use
requirecheck - Complexity: O(n) time
- Return: Maximum value
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Find max in array in Solidity
contract ArrayMax {
function findMax(uint256[] memory _arr) public pure returns (uint256) {
require(_arr.length > 0, "Array is empty");
uint256 max = _arr[0];
for (uint256 i = 1; i < _arr.length; i++) {
if (_arr[i] > max) {
max = _arr[i];
}
}
return max;
}
}Remove duplicates using nested loops and temporary array.
- Nested loops: Check for duplicates
- Temporary array: Store unique elements
- Complexity: O(n²) time
- Return: Array without duplicates
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Remove duplicates in Solidity
contract RemoveDuplicates {
function removeDuplicates(uint256[] memory _arr) public pure returns (uint256[] memory) {
if (_arr.length <= 1) {
return _arr;
}
uint256 uniqueCount = 0;
for (uint256 i = 0; i < _arr.length; i++) {
bool isDuplicate = false;
for (uint256 j = 0; j < i; j++) {
if (_arr[i] == _arr[j]) {
isDuplicate = true;
break;
}
}
if (!isDuplicate) {
uniqueCount++;
}
}
uint256[] memory result = new uint256[](uniqueCount);
uint256 index = 0;
for (uint256 i = 0; i < _arr.length; i++) {
bool isDuplicate = false;
for (uint256 j = 0; j < i; j++) {
if (_arr[i] == _arr[j]) {
isDuplicate = true;
break;
}
}
if (!isDuplicate) {
result[index] = _arr[i];
index++;
}
}
return result;
}
}Merge arrays using a new array with combined length.
- New array: Create with combined length
- Loop: Copy elements from both arrays
- Complexity: O(n) time
- Return: Merged array
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Merge arrays in Solidity
contract ArrayMerge {
function mergeArrays(uint256[] memory _arr1, uint256[] memory _arr2) public pure returns (uint256[] memory) {
uint256[] memory result = new uint256[](_arr1.length + _arr2.length);
for (uint256 i = 0; i < _arr1.length; i++) {
result[i] = _arr1[i];
}
for (uint256 i = 0; i < _arr2.length; i++) {
result[_arr1.length + i] = _arr2[i];
}
return result;
}
}Convert string to number by iterating through bytes.
- bytes conversion:
bytes(str) - ASCII values: Check for valid digits
- Manual: Build number digit by digit
- Error handling: Revert on invalid input
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Convert string to number in Solidity
contract StringToNumber {
function stringToUint(string memory _str) public pure returns (uint256) {
bytes memory strBytes = bytes(_str);
uint256 result = 0;
for (uint256 i = 0; i < strBytes.length; i++) {
require(strBytes[i] >= 48 && strBytes[i] <= 57, "Invalid character");
result = result * 10 + uint256(uint8(strBytes[i] - 48));
}
return result;
}
}Loop through mapping using separate array for keys.
- Keys array: Store keys for iteration
- Mapping: Store values by key
- Manual: Iterate through keys array
- Return: Array of values
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Loop through mapping in Solidity
contract MappingIteration {
mapping(address => uint256) public balances;
address[] public users;
function addUser(address _user, uint256 _balance) public {
balances[_user] = _balance;
users.push(_user);
}
function getAllBalances() public view returns (address[] memory, uint256[] memory) {
uint256[] memory allBalances = new uint256[](users.length);
for (uint256 i = 0; i < users.length; i++) {
allBalances[i] = balances[users[i]];
}
return (users, allBalances);
}
}Delay execution using schedule pattern with timestamp checks.
- Schedule: Store call with execution time
- Execute: Check if time has passed
- State: Track execution status
- Events: Emit on schedule/execute
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Delay function execution in Solidity
contract DelayedExecution {
struct DelayedCall {
address target;
bytes data;
uint256 delay;
uint256 executeAt;
bool executed;
}
DelayedCall[] public delayedCalls;
event CallScheduled(uint256 indexed id, address target, uint256 delay);
event CallExecuted(uint256 indexed id);
function scheduleCall(address _target, bytes memory _data, uint256 _delay) public returns (uint256) {
uint256 id = delayedCalls.length;
delayedCalls.push(DelayedCall({
target: _target,
data: _data,
delay: _delay,
executeAt: block.timestamp + _delay,
executed: false
}));
emit CallScheduled(id, _target, _delay);
return id;
}
function executeCall(uint256 _id) public {
require(_id < delayedCalls.length, "Invalid id");
DelayedCall storage call = delayedCalls[_id];
require(!call.executed, "Already executed");
require(block.timestamp >= call.executeAt, "Not ready");
(bool success, ) = call.target.call(call.data);
require(success, "Call failed");
call.executed = true;
emit CallExecuted(_id);
}
}HTTP requests in Solidity are made through oracles like Chainlink.
- Oracles: Chainlink, API3
- External data: Fetch off-chain data
- Trust model: Oracle trust assumptions
- Implementation: Use oracle contracts
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// HTTP GET request (using Oracles)
// Note: Solidity cannot make HTTP requests directly
contract HttpRequest {
// This is a simplified example using Chainlink
// In practice, you would use Chainlink oracles
}Create a promise-like pattern using structs for deferred execution.
- Struct: Store resolved/rejected state
- Functions: resolve, reject, check status
- Events: Emit on resolution/rejection
- State: Track in contract storage
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Promise-like pattern in Solidity
contract DeferredPattern {
struct Deferred {
bool resolved;
bool rejected;
bytes result;
bytes error;
}
mapping(uint256 => Deferred) public deferreds;
uint256 public deferredCount;
event DeferredCreated(uint256 indexed id);
event DeferredResolved(uint256 indexed id, bytes result);
event DeferredRejected(uint256 indexed id, bytes error);
function createDeferred() public returns (uint256) {
uint256 id = deferredCount++;
deferreds[id] = Deferred(false, false, "", "");
emit DeferredCreated(id);
return id;
}
function resolve(uint256 _id, bytes memory _result) public {
require(!deferreds[_id].resolved && !deferreds[_id].rejected, "Already resolved");
deferreds[_id].resolved = true;
deferreds[_id].result = _result;
emit DeferredResolved(_id, _result);
}
function reject(uint256 _id, bytes memory _error) public {
require(!deferreds[_id].resolved && !deferreds[_id].rejected, "Already resolved");
deferreds[_id].rejected = true;
deferreds[_id].error = _error;
emit DeferredRejected(_id, _error);
}
function getStatus(uint256 _id) public view returns (bool resolved, bool rejected, bytes memory result, bytes memory error) {
Deferred storage d = deferreds[_id];
return (d.resolved, d.rejected, d.result, d.error);
}
}Calculate factorial using recursion.
- Recursive:
n * factorial(n-1) - Base case:
n <= 1 - Gas: Recursion can be gas intensive
- Edge cases: 0! = 1
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Factorial in Solidity
contract Factorial {
function factorial(uint256 n) public pure returns (uint256) {
if (n <= 1) {
return 1;
}
return n * factorial(n - 1);
}
}Calculate Fibonacci using recursion or iteration.
- Recursive:
fib(n-1) + fib(n-2) - Iterative: Loop with variables
- Gas: Iterative is more gas efficient
- Complexity: O(n) iterative
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Fibonacci in Solidity
contract Fibonacci {
function fibonacci(uint256 n) public pure returns (uint256) {
if (n <= 1) {
return n;
}
return fibonacci(n - 1) + fibonacci(n - 2);
}
function fibonacciIterative(uint256 n) public pure returns (uint256) {
if (n <= 1) {
return n;
}
uint256 a = 0;
uint256 b = 1;
for (uint256 i = 2; i <= n; i++) {
uint256 c = a + b;
a = b;
b = c;
}
return b;
}
}FizzBuzz using if-else with modulo operations.
- Modulo: Check divisibility by 3, 5, 15
- Order: Check 15 first
- Return: Array of strings
- Helper: Convert uint to string
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// FizzBuzz in Solidity
contract FizzBuzz {
function fizzbuzz(uint256 n) public pure returns (string[] memory) {
string[] memory result = new string[](n);
for (uint256 i = 1; i <= n; i++) {
if (i % 15 == 0) {
result[i-1] = "FizzBuzz";
} else if (i % 3 == 0) {
result[i-1] = "Fizz";
} else if (i % 5 == 0) {
result[i-1] = "Buzz";
} else {
result[i-1] = uint2str(i);
}
}
return result;
}
function uint2str(uint256 _i) internal pure returns (string memory) {
if (_i == 0) return "0";
uint256 j = _i;
uint256 len;
while (j != 0) {
len++;
j /= 10;
}
bytes memory bstr = new bytes(len);
uint256 k = len;
while (_i != 0) {
k = k - 1;
uint8 temp = (48 + uint8(_i - _i / 10 * 10));
bytes1 b1 = bytes1(temp);
bstr[k] = b1;
_i /= 10;
}
return string(bstr);
}
}Find missing number using formula n*(n+1)/2 - sum.
- Formula:
total - sum - Edge cases: Empty array, missing first or last
- Complexity: O(n) time
- Return: Missing number
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Find missing number in Solidity
contract MissingNumber {
function findMissing(uint256[] memory _arr) public pure returns (uint256) {
uint256 n = _arr.length + 1;
uint256 total = n * (n + 1) / 2;
uint256 sum = 0;
for (uint256 i = 0; i < _arr.length; i++) {
sum += _arr[i];
}
return total - sum;
}
}Find duplicates using nested loops.
- Nested loops: Compare each element
- Dedup: Avoid adding duplicates multiple times
- Complexity: O(n²) time
- Return: Array of duplicates
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Find duplicates in Solidity
contract FindDuplicates {
function findDuplicates(uint256[] memory _arr) public pure returns (uint256[] memory) {
uint256[] memory duplicates = new uint256[](_arr.length);
uint256 count = 0;
for (uint256 i = 0; i < _arr.length; i++) {
bool isDuplicate = false;
for (uint256 j = i + 1; j < _arr.length; j++) {
if (_arr[i] == _arr[j]) {
isDuplicate = true;
break;
}
}
if (isDuplicate) {
bool alreadyAdded = false;
for (uint256 k = 0; k < count; k++) {
if (duplicates[k] == _arr[i]) {
alreadyAdded = true;
break;
}
}
if (!alreadyAdded) {
duplicates[count] = _arr[i];
count++;
}
}
}
// Truncate array
uint256[] memory result = new uint256[](count);
for (uint256 i = 0; i < count; i++) {
result[i] = duplicates[i];
}
return result;
}
}Calculate sum using loop iteration.
- Loop: Iterate and accumulate
- Complexity: O(n) time
- Return: Sum value
- Gas: Memory vs storage consideration
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Sum of array in Solidity
contract SumArray {
function sumArray(uint256[] memory _arr) public pure returns (uint256) {
uint256 sum = 0;
for (uint256 i = 0; i < _arr.length; i++) {
sum += _arr[i];
}
return sum;
}
}Calculate average using sum divided by length.
- Method:
sum / length - Empty array: Use
requirecheck - Precision: Integer division truncates
- Return: Average value
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Average of array in Solidity
contract AverageArray {
function averageArray(uint256[] memory _arr) public pure returns (uint256) {
require(_arr.length > 0, "Array is empty");
uint256 sum = 0;
for (uint256 i = 0; i < _arr.length; i++) {
sum += _arr[i];
}
return sum / _arr.length;
}
}Sort ascending using bubble sort.
- Bubble sort: Compare adjacent, swap
- Complexity: O(n²) time
- In-place: Modifies array in memory
- Return: Sorted array
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Sort array ascending in Solidity
contract SortArray {
function sortAscending(uint256[] memory _arr) public pure returns (uint256[] memory) {
uint256[] memory sorted = new uint256[](_arr.length);
for (uint256 i = 0; i < _arr.length; i++) {
sorted[i] = _arr[i];
}
for (uint256 i = 0; i < sorted.length - 1; i++) {
for (uint256 j = 0; j < sorted.length - i - 1; j++) {
if (sorted[j] > sorted[j + 1]) {
uint256 temp = sorted[j];
sorted[j] = sorted[j + 1];
sorted[j + 1] = temp;
}
}
}
return sorted;
}
}Sort descending using bubble sort with reversed comparison.
- Bubble sort: Compare adjacent, swap if less
- Complexity: O(n²) time
- In-place: Modifies array in memory
- Return: Sorted array
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Sort array descending in Solidity
contract SortArrayDescending {
function sortDescending(uint256[] memory _arr) public pure returns (uint256[] memory) {
uint256[] memory sorted = new uint256[](_arr.length);
for (uint256 i = 0; i < _arr.length; i++) {
sorted[i] = _arr[i];
}
for (uint256 i = 0; i < sorted.length - 1; i++) {
for (uint256 j = 0; j < sorted.length - i - 1; j++) {
if (sorted[j] < sorted[j + 1]) {
uint256 temp = sorted[j];
sorted[j] = sorted[j + 1];
sorted[j + 1] = temp;
}
}
}
return sorted;
}
}Flatten a 2D array using nested loops.
- Nested loops: Iterate through sub-arrays
- Length: Calculate total length first
- Complexity: O(n) time
- Return: Flattened array
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Flatten nested array in Solidity
contract FlattenArray {
function flattenArray(uint256[][] memory _arr) public pure returns (uint256[] memory) {
uint256 totalLength = 0;
for (uint256 i = 0; i < _arr.length; i++) {
totalLength += _arr[i].length;
}
uint256[] memory result = new uint256[](totalLength);
uint256 index = 0;
for (uint256 i = 0; i < _arr.length; i++) {
for (uint256 j = 0; j < _arr[i].length; j++) {
result[index] = _arr[i][j];
index++;
}
}
return result;
}
}Split array into chunks of given size.
- Loop: Iterate with step size
- Slicing: Copy elements per chunk
- Edge case: Handle last chunk size
- Return: 2D array of chunks
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Chunk array in Solidity
contract ChunkArray {
function chunkArray(uint256[] memory _arr, uint256 _size) public pure returns (uint256[][] memory) {
require(_size > 0, "Size must be > 0");
uint256 chunks = (_arr.length + _size - 1) / _size;
uint256[][] memory result = new uint256[][](chunks);
for (uint256 i = 0; i < chunks; i++) {
uint256 start = i * _size;
uint256 end = start + _size;
if (end > _arr.length) {
end = _arr.length;
}
uint256[] memory chunk = new uint256[](end - start);
for (uint256 j = start; j < end; j++) {
chunk[j - start] = _arr[j];
}
result[i] = chunk;
}
return result;
}
}Binary search using while loop on sorted array.
- While loop: left <= right
- Mid calculation:
(left + right) / 2 - Requirement: Array must be sorted
- Return: Index or -1
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Binary search in Solidity
contract BinarySearch {
function binarySearch(uint256[] memory _arr, uint256 _target) public pure returns (int256) {
uint256 left = 0;
uint256 right = _arr.length - 1;
while (left <= right) {
uint256 mid = (left + right) / 2;
if (_arr[mid] == _target) {
return int256(mid);
} else if (_arr[mid] < _target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1;
}
}Quick sort using recursion and partitioning.
- Algorithm: Choose pivot, partition, recurse
- Time: O(n log n) average
- Memory: Creates new arrays
- Return: Sorted array
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Quick sort in Solidity
contract QuickSort {
function quickSort(uint256[] memory _arr) public pure returns (uint256[] memory) {
if (_arr.length <= 1) {
return _arr;
}
uint256 pivot = _arr[0];
uint256[] memory left = new uint256[](_arr.length);
uint256[] memory right = new uint256[](_arr.length);
uint256 leftCount = 0;
uint256 rightCount = 0;
for (uint256 i = 1; i < _arr.length; i++) {
if (_arr[i] < pivot) {
left[leftCount] = _arr[i];
leftCount++;
} else {
right[rightCount] = _arr[i];
rightCount++;
}
}
// Trim arrays
uint256[] memory leftTrimmed = new uint256[](leftCount);
for (uint256 i = 0; i < leftCount; i++) {
leftTrimmed[i] = left[i];
}
uint256[] memory rightTrimmed = new uint256[](rightCount);
for (uint256 i = 0; i < rightCount; i++) {
rightTrimmed[i] = right[i];
}
uint256[] memory leftSorted = quickSort(leftTrimmed);
uint256[] memory rightSorted = quickSort(rightTrimmed);
uint256[] memory result = new uint256[](leftCount + 1 + rightCount);
for (uint256 i = 0; i < leftCount; i++) {
result[i] = leftSorted[i];
}
result[leftCount] = pivot;
for (uint256 i = 0; i < rightCount; i++) {
result[leftCount + 1 + i] = rightSorted[i];
}
return result;
}
}Merge sort using divide-and-conquer and merging.
- Algorithm: Divide, sort, merge
- Time: O(n log n)
- Space: O(n) auxiliary space
- Return: Sorted array
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Merge sort in Solidity
contract MergeSort {
function mergeSort(uint256[] memory _arr) public pure returns (uint256[] memory) {
if (_arr.length <= 1) {
return _arr;
}
uint256 mid = _arr.length / 2;
uint256[] memory left = new uint256[](mid);
uint256[] memory right = new uint256[](_arr.length - mid);
for (uint256 i = 0; i < mid; i++) {
left[i] = _arr[i];
}
for (uint256 i = mid; i < _arr.length; i++) {
right[i - mid] = _arr[i];
}
uint256[] memory leftSorted = mergeSort(left);
uint256[] memory rightSorted = mergeSort(right);
return merge(leftSorted, rightSorted);
}
function merge(uint256[] memory _left, uint256[] memory _right) public pure returns (uint256[] memory) {
uint256[] memory result = new uint256[](_left.length + _right.length);
uint256 i = 0;
uint256 j = 0;
uint256 k = 0;
while (i < _left.length && j < _right.length) {
if (_left[i] <= _right[j]) {
result[k] = _left[i];
i++;
} else {
result[k] = _right[j];
j++;
}
k++;
}
while (i < _left.length) {
result[k] = _left[i];
i++;
k++;
}
while (j < _right.length) {
result[k] = _right[j];
j++;
k++;
}
return result;
}
}Bubble sort with early termination.
- Algorithm: Compare adjacent, swap
- Time: O(n²) worst case
- Optimization: Stop if no swaps
- Return: Sorted array
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Bubble sort in Solidity
contract BubbleSort {
function bubbleSort(uint256[] memory _arr) public pure returns (uint256[] memory) {
uint256[] memory sorted = new uint256[](_arr.length);
for (uint256 i = 0; i < _arr.length; i++) {
sorted[i] = _arr[i];
}
for (uint256 i = 0; i < sorted.length - 1; i++) {
bool swapped = false;
for (uint256 j = 0; j < sorted.length - i - 1; j++) {
if (sorted[j] > sorted[j + 1]) {
uint256 temp = sorted[j];
sorted[j] = sorted[j + 1];
sorted[j + 1] = temp;
swapped = true;
}
}
if (!swapped) {
break;
}
}
return sorted;
}
}Find common elements using nested loops.
- Nested loops: Compare elements
- Dedup: Avoid adding duplicates
- Complexity: O(n*m) time
- Return: Array of common elements
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Intersection of arrays in Solidity
contract ArrayIntersection {
function intersection(uint256[] memory _arr1, uint256[] memory _arr2) public pure returns (uint256[] memory) {
uint256[] memory temp = new uint256[](_arr1.length);
uint256 count = 0;
for (uint256 i = 0; i < _arr1.length; i++) {
bool found = false;
for (uint256 j = 0; j < _arr2.length; j++) {
if (_arr1[i] == _arr2[j]) {
found = true;
break;
}
}
if (found) {
bool duplicate = false;
for (uint256 k = 0; k < count; k++) {
if (temp[k] == _arr1[i]) {
duplicate = true;
break;
}
}
if (!duplicate) {
temp[count] = _arr1[i];
count++;
}
}
}
uint256[] memory result = new uint256[](count);
for (uint256 i = 0; i < count; i++) {
result[i] = temp[i];
}
return result;
}
}Combine arrays with unique elements.
- Merge: Combine both arrays
- Dedup: Remove duplicates
- Complexity: O(n*m) time
- Return: Array of unique elements
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Union of arrays in Solidity
contract ArrayUnion {
function union(uint256[] memory _arr1, uint256[] memory _arr2) public pure returns (uint256[] memory) {
uint256[] memory temp = new uint256[](_arr1.length + _arr2.length);
uint256 count = 0;
for (uint256 i = 0; i < _arr1.length; i++) {
bool duplicate = false;
for (uint256 j = 0; j < count; j++) {
if (temp[j] == _arr1[i]) {
duplicate = true;
break;
}
}
if (!duplicate) {
temp[count] = _arr1[i];
count++;
}
}
for (uint256 i = 0; i < _arr2.length; i++) {
bool duplicate = false;
for (uint256 j = 0; j < count; j++) {
if (temp[j] == _arr2[i]) {
duplicate = true;
break;
}
}
if (!duplicate) {
temp[count] = _arr2[i];
count++;
}
}
uint256[] memory result = new uint256[](count);
for (uint256 i = 0; i < count; i++) {
result[i] = temp[i];
}
return result;
}
}Find elements in first array not in second.
- Filter: Check if in second array
- Dedup: Avoid duplicates
- Complexity: O(n*m) time
- Return: Array of differences
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Difference of arrays in Solidity
contract ArrayDifference {
function difference(uint256[] memory _arr1, uint256[] memory _arr2) public pure returns (uint256[] memory) {
uint256[] memory temp = new uint256[](_arr1.length);
uint256 count = 0;
for (uint256 i = 0; i < _arr1.length; i++) {
bool found = false;
for (uint256 j = 0; j < _arr2.length; j++) {
if (_arr1[i] == _arr2[j]) {
found = true;
break;
}
}
if (!found) {
bool duplicate = false;
for (uint256 k = 0; k < count; k++) {
if (temp[k] == _arr1[i]) {
duplicate = true;
break;
}
}
if (!duplicate) {
temp[count] = _arr1[i];
count++;
}
}
}
uint256[] memory result = new uint256[](count);
for (uint256 i = 0; i < count; i++) {
result[i] = temp[i];
}
return result;
}
}Group items by type using structs and arrays.
- Structs: Define Item and Group
- Loop: Iterate and group by type
- Return: Array of groups
- Complexity: O(n²) time
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Group by property in Solidity
contract GroupByProperty {
struct Item {
string type_;
string name;
}
struct Group {
string type_;
Item[] items;
}
function groupByType(Item[] memory _items) public pure returns (Group[] memory) {
Group[] memory groups = new Group[](_items.length);
uint256 groupCount = 0;
for (uint256 i = 0; i < _items.length; i++) {
bool found = false;
for (uint256 j = 0; j < groupCount; j++) {
if (keccak256(abi.encodePacked(groups[j].type_)) == keccak256(abi.encodePacked(_items[i].type_))) {
groups[j].items.push(_items[i]);
found = true;
break;
}
}
if (!found) {
groups[groupCount].type_ = _items[i].type_;
groups[groupCount].items.push(_items[i]);
groupCount++;
}
}
Group[] memory result = new Group[](groupCount);
for (uint256 i = 0; i < groupCount; i++) {
result[i] = groups[i];
}
return result;
}
}Deep clone using struct copy with nested structs.
- Struct copy: Create new struct instance
- Nested: Copy nested struct fields
- Memory: Uses memory storage
- Return: Independent copy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Deep clone in Solidity
contract DeepClone {
struct Address {
string city;
string zip;
}
struct User {
string name;
Address address;
}
function cloneUser(User memory _user) public pure returns (User memory) {
return User({
name: _user.name,
address: Address({
city: _user.address.city,
zip: _user.address.zip
})
});
}
}Perform immutable update by copying struct and modifying.
- Copy: Create new struct from old
- Modify: Update field values
- Return: New struct instance
- Immutability: Original unchanged
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Immutable update in Solidity
contract ImmutableUpdate {
struct User {
string name;
uint256 age;
}
function updateUser(User memory _user, uint256 _newAge) public pure returns (User memory) {
User memory newUser = _user;
newUser.age = _newAge;
return newUser;
}
}Pipe composes functions from left to right.
- Composition: Chain function calls
- Direction: Left to right
- Implementation: Nested function calls
- Return: Final result
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Pipe function in Solidity
contract Pipe {
function double(uint256 x) public pure returns (uint256) {
return x * 2;
}
function addTen(uint256 x) public pure returns (uint256) {
return x + 10;
}
function square(uint256 x) public pure returns (uint256) {
return x * x;
}
function pipe(uint256 x) public pure returns (uint256) {
return square(addTen(double(x)));
}
}Compose functions from right to left.
- Composition: Chain function calls
- Direction: Right to left
- Implementation: Nested function calls
- Return: Final result
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Compose function in Solidity
contract Compose {
function double(uint256 x) public pure returns (uint256) {
return x * 2;
}
function addTen(uint256 x) public pure returns (uint256) {
return x + 10;
}
function square(uint256 x) public pure returns (uint256) {
return x * x;
}
function compose(uint256 x) public pure returns (uint256) {
return double(addTen(square(x)));
}
}Cache function results using mapping.
- Mapping: Store cached results
- Key: Function arguments
- Return: Cached or computed result
- Gas: Cache saves computation
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Memoization in Solidity
contract Memoization {
mapping(uint256 => uint256) public fibCache;
function fibonacci(uint256 n) public returns (uint256) {
if (n <= 1) {
return n;
}
if (fibCache[n] != 0) {
return fibCache[n];
}
uint256 result = fibonacci(n - 1) + fibonacci(n - 2);
fibCache[n] = result;
return result;
}
}Execute function only once using initialization flag.
- Flag: Track if initialized
- Result: Store computed value
- Guard: Check flag before execution
- Use case: One-time initialization
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Once function in Solidity
contract Once {
bool private initialized;
uint256 private result;
function initialize() public returns (uint256) {
if (!initialized) {
initialized = true;
result = 42;
return result;
}
return result;
}
}Debounce pattern using timestamp checks.
- Timestamp: Track last call time
- Leading edge: Execute immediately
- Delay: Wait before next execution
- Use case: Rate limiting
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Debounce pattern in Solidity
contract Debounce {
uint256 public lastCallTime;
uint256 public constant DEBOUNCE_DELAY = 1 hours;
event Executed();
function debouncedFunction() public {
require(block.timestamp >= lastCallTime + DEBOUNCE_DELAY, "Too soon");
lastCallTime = block.timestamp;
emit Executed();
// Function logic here
}
}Throttle pattern using timestamp tracking.
- Timestamp: Track last execution
- Leading edge: Execute if enough time passed
- Rate limiting: At most once per period
- Use case: Rate limiting, API calls
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Throttle pattern in Solidity
contract Throttle {
uint256 public lastCallTime;
uint256 public constant THROTTLE_DELAY = 1 minutes;
event Executed();
function throttledFunction() public {
require(block.timestamp >= lastCallTime + THROTTLE_DELAY, "Too many requests");
lastCallTime = block.timestamp;
emit Executed();
// Function logic here
}
}Deep equality comparison using struct field comparison.
- Struct comparison: Compare field by field
- String comparison: Compare bytes hash
- Return: Boolean result
- Complexity: O(n) time
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Deep equal in Solidity
contract DeepEqual {
struct Address {
string city;
string zip;
}
struct User {
string name;
Address address;
}
function deepEqual(User memory _a, User memory _b) public pure returns (bool) {
if (keccak256(abi.encodePacked(_a.name)) != keccak256(abi.encodePacked(_b.name))) {
return false;
}
if (keccak256(abi.encodePacked(_a.address.city)) != keccak256(abi.encodePacked(_b.address.city))) {
return false;
}
if (keccak256(abi.encodePacked(_a.address.zip)) != keccak256(abi.encodePacked(_b.address.zip))) {
return false;
}
return true;
}
}Observer pattern using events and observer list.
- Observers: List of observer addresses
- Events: Emit notifications
- Add/Remove: Manage observer list
- Notify: Emit event to all
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Observable pattern in Solidity
contract Observable {
address[] public observers;
mapping(address => bool) public isObserver;
event StateChanged(string state);
event ObserverAdded(address observer);
event ObserverRemoved(address observer);
function addObserver(address _observer) public {
require(!isObserver[_observer], "Already observer");
isObserver[_observer] = true;
observers.push(_observer);
emit ObserverAdded(_observer);
}
function removeObserver(address _observer) public {
require(isObserver[_observer], "Not observer");
isObserver[_observer] = false;
emit ObserverRemoved(_observer);
}
function notifyObservers(string memory _state) internal {
emit StateChanged(_state);
for (uint256 i = 0; i < observers.length; i++) {
if (isObserver[observers[i]]) {
// In practice, call observer callback
}
}
}
}Singleton pattern using contract address.
- Single instance: Contract itself is singleton
- Owner: Set owner in constructor
- State: Store state in contract
- Access: Control via modifiers
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Singleton pattern in Solidity
contract Singleton {
address public owner;
uint256 public value;
// Private constructor
constructor() {
owner = msg.sender;
}
// Singleton instance
function getInstance() public pure returns (Singleton) {
return Singleton(address(this));
}
function setValue(uint256 _value) public {
require(msg.sender == owner, "Not owner");
value = _value;
}
}Factory pattern using contract that creates new contracts.
- Factory: Contract that creates instances
- Create: Use
newkeyword - Store: Track created instances
- Return: Address of new contract
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Factory pattern in Solidity
contract UserFactory {
struct User {
string name;
address wallet;
}
User[] public users;
function createUser(string memory _name) public returns (User memory) {
User memory newUser = User({
name: _name,
wallet: msg.sender
});
users.push(newUser);
return newUser;
}
}Strategy pattern using interfaces and dynamic dispatch.
- Interface: Define strategy interface
- Strategies: Different implementations
- Context: Uses current strategy
- Switch: Change strategy at runtime
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Strategy pattern in Solidity
interface IStrategy {
function execute(uint256 _value) external returns (uint256);
}
contract StrategyA is IStrategy {
function execute(uint256 _value) external pure override returns (uint256) {
return _value * 2;
}
}
contract StrategyB is IStrategy {
function execute(uint256 _value) external pure override returns (uint256) {
return _value + 10;
}
}
contract StrategyContext {
address public strategy;
function setStrategy(address _strategy) public {
strategy = _strategy;
}
function execute(uint256 _value) public returns (uint256) {
require(strategy != address(0), "Strategy not set");
return IStrategy(strategy).execute(_value);
}
}Observer pattern using events and observer management.
- Subject: Maintains observers
- Events: Emit state changes
- Add/Remove: Manage observer list
- Notify: Emit event to all
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Observer pattern in Solidity
contract Subject {
address[] public observers;
mapping(address => bool) public isObserver;
event StateChanged(string state);
function addObserver(address _observer) public {
require(!isObserver[_observer], "Already observer");
isObserver[_observer] = true;
observers.push(_observer);
}
function removeObserver(address _observer) public {
require(isObserver[_observer], "Not observer");
isObserver[_observer] = false;
}
function setState(string memory _state) internal {
emit StateChanged(_state);
for (uint256 i = 0; i < observers.length; i++) {
if (isObserver[observers[i]]) {
// Notify observer
}
}
}
}Decorator pattern using wrapper contracts.
- Component: Base contract
- Decorator: Wraps component
- Chaining: Multiple decorators
- Benefits: Add behavior dynamically
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Decorator pattern in Solidity
contract Coffee {
function cost() public pure returns (uint256) {
return 5;
}
function description() public pure returns (string memory) {
return "Coffee";
}
}
contract MilkDecorator {
Coffee private _coffee;
constructor(Coffee coffee) {
_coffee = coffee;
}
function cost() public view returns (uint256) {
return _coffee.cost() + 2;
}
function description() public view returns (string memory) {
return string(abi.encodePacked(_coffee.description(), ", Milk"));
}
}Command pattern with execute and undo using interfaces.
- Interface: ICommand with execute/undo
- Command: Implements interface
- Receiver: Performs work
- Undo/Redo: Track history
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Command pattern in Solidity
interface ICommand {
function execute() external;
function undo() external;
}
contract AddCommand is ICommand {
uint256[] public data;
uint256 public value;
constructor(uint256[] memory _data, uint256 _value) {
data = _data;
value = _value;
}
function execute() external override {
data.push(value);
}
function undo() external override {
// Remove last element if it matches value
if (data.length > 0 && data[data.length - 1] == value) {
data.pop();
}
}
}Memento pattern using history of states.
- Originator: Creates/restores mementos
- History: Array of states
- Restore: Revert to previous state
- Undo: State history management
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Memento pattern in Solidity
contract Memento {
struct State {
string state;
uint256 timestamp;
}
State[] public history;
string public currentState;
function saveState() public {
history.push(State(currentState, block.timestamp));
}
function restoreState(uint256 _index) public {
require(_index < history.length, "Invalid index");
currentState = history[_index].state;
}
}Mediator pattern for centralized communication.
- Mediator: Central coordinator
- Participants: Register with mediator
- Messages: Send through mediator
- Events: Emit messages
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Mediator pattern in Solidity
contract Mediator {
address[] public participants;
mapping(address => bool) public isParticipant;
function register(address _participant) public {
require(!isParticipant[_participant], "Already registered");
isParticipant[_participant] = true;
participants.push(_participant);
}
function sendMessage(address _from, address _to, string memory _message) public {
require(isParticipant[_from] && isParticipant[_to], "Invalid participant");
// In practice, implement message passing
emit Message(_from, _to, _message);
}
event Message(address indexed from, address indexed to, string message);
}Chain of Responsibility using abstract contract.
- Handler: Abstract with next handler
- Chain: Link handlers together
- Process: Pass request along chain
- Events: Log processing
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Chain of Responsibility in Solidity
abstract contract Handler {
Handler public nextHandler;
function setNext(Handler _next) public {
nextHandler = _next;
}
function handle(uint256 _request) public virtual returns (bool) {
if (nextHandler != Handler(address(0))) {
return nextHandler.handle(_request);
}
return false;
}
}
contract AuthHandler is Handler {
function handle(uint256 _request) public override returns (bool) {
// Authentication logic
if (_request >= 100) {
return true;
}
return super.handle(_request);
}
}
contract LoggerHandler is Handler {
function handle(uint256 _request) public override returns (bool) {
// Logging logic
emit Logged(_request);
return super.handle(_request);
}
event Logged(uint256 request);
}State pattern using enum states.
- States: Define as enum
- Transitions: Move between states
- Validation: Check current state
- Events: Emit state changes
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// State pattern in Solidity
contract StateMachine {
enum State { Idle, Processing, Completed }
State public currentState = State.Idle;
function start() public {
require(currentState == State.Idle, "Invalid state");
currentState = State.Processing;
emit StateChanged("Processing");
}
function complete() public {
require(currentState == State.Processing, "Invalid state");
currentState = State.Completed;
emit StateChanged("Completed");
}
event StateChanged(string state);
}Proxy pattern for access control.
- Subject: Real implementation
- Proxy: Controls access
- Authorization: Check caller
- Delegation: Forward calls
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Proxy pattern in Solidity
contract RealSubject {
function request() public pure returns (string memory) {
return "RealSubject: Handling request";
}
}
contract Proxy {
RealSubject public realSubject;
address public owner;
constructor() {
owner = msg.sender;
realSubject = new RealSubject();
}
function request() public view returns (string memory) {
require(msg.sender == owner, "Not authorized");
return realSubject.request();
}
}Flyweight pattern using mapping for shared state.
- Flyweight: Shared state object
- Factory: Manages flyweights
- Cache: Store shared instances
- Memory: Optimize storage
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Flyweight pattern in Solidity
contract Flyweight {
struct SharedState {
string state;
}
mapping(string => SharedState) public flyweights;
function getFlyweight(string memory _state) public returns (SharedState memory) {
if (bytes(flyweights[_state].state).length == 0) {
flyweights[_state] = SharedState(_state);
}
return flyweights[_state];
}
}Bridge pattern for separating abstraction from implementation.
- Abstraction: High-level interface
- Implementation: Low-level operations
- Separation: Decouple interface/implementation
- Flexibility: Change implementation independently
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Bridge pattern in Solidity
interface IImplementation {
function operationImpl() external returns (string memory);
}
contract ImplementationA is IImplementation {
function operationImpl() external pure override returns (string memory) {
return "ImplementationA";
}
}
contract ImplementationB is IImplementation {
function operationImpl() external pure override returns (string memory) {
return "ImplementationB";
}
}
contract Abstraction {
IImplementation public impl;
function setImpl(address _impl) public {
impl = IImplementation(_impl);
}
function operation() public view returns (string memory) {
return impl.operationImpl();
}
}Adapter pattern for converting interfaces.
- Target: Expected interface
- Adaptee: Existing interface
- Adapter: Bridges interfaces
- Compatibility: Make incompatible classes work
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Adapter pattern in Solidity
interface ITarget {
function request() external returns (string memory);
}
contract Adaptee {
function specificRequest() public pure returns (string memory) {
return "Specific Request";
}
}
contract Adapter is ITarget {
Adaptee public adaptee;
constructor() {
adaptee = new Adaptee();
}
function request() external override returns (string memory) {
return adaptee.specificRequest();
}
}Facade pattern for simplifying complex subsystems.
- Facade: Simplified interface
- Subsystem: Complex components
- Simplification: Hide complexity
- Use case: Library APIs
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Facade pattern in Solidity
contract SubsystemA {
function operationA() public pure returns (string memory) {
return "SubsystemA";
}
}
contract SubsystemB {
function operationB() public pure returns (string memory) {
return "SubsystemB";
}
}
contract Facade {
SubsystemA public a;
SubsystemB public b;
constructor() {
a = new SubsystemA();
b = new SubsystemB();
}
function operation() public view returns (string memory) {
return string(abi.encodePacked(a.operationA(), " + ", b.operationB()));
}
}Composite pattern for tree structures.
- Component: Interface for all
- Leaf: Individual object
- Composite: Container
- Uniform: Treat leaf and composite uniformly
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Composite pattern in Solidity
interface IComponent {
function operation() external returns (string memory);
}
contract Leaf is IComponent {
string public name;
constructor(string memory _name) {
name = _name;
}
function operation() external override returns (string memory) {
return name;
}
}
contract Composite is IComponent {
IComponent[] public children;
string public name;
constructor(string memory _name) {
name = _name;
}
function add(IComponent _child) public {
children.push(_child);
}
function operation() external override returns (string memory) {
string memory result = name;
for (uint256 i = 0; i < children.length; i++) {
result = string(abi.encodePacked(result, " + ", children[i].operation()));
}
return result;
}
}Visitor pattern for adding operations without modifying elements.
- Visitor: Defines operations
- Element: Accepts visitors
- Extensibility: Add operations easily
- Double dispatch: Determine operation
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Visitor pattern in Solidity
interface IVisitor {
function visitElementA(address _element) external returns (string memory);
function visitElementB(address _element) external returns (string memory);
}
contract ElementA {
function accept(IVisitor _visitor) external returns (string memory) {
return _visitor.visitElementA(address(this));
}
}
contract ElementB {
function accept(IVisitor _visitor) external returns (string memory) {
return _visitor.visitElementB(address(this));
}
}
contract ConcreteVisitor is IVisitor {
function visitElementA(address _element) external pure override returns (string memory) {
return "Visiting ElementA";
}
function visitElementB(address _element) external pure override returns (string memory) {
return "Visiting ElementB";
}
}Iterator pattern for sequential access.
- Iterator: Traverses collection
- Index: Track current position
- hasNext: Check for more items
- next: Return next item
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Iterator pattern in Solidity
contract Iterator {
uint256[] public collection;
uint256 public index;
function add(uint256 _value) public {
collection.push(_value);
}
function next() public returns (uint256) {
require(hasNext(), "No more items");
uint256 value = collection[index];
index++;
return value;
}
function hasNext() public view returns (bool) {
return index < collection.length;
}
}Template Method for algorithm skeletons.
- AbstractClass: Defines template
- ConcreteClass: Implements steps
- Reuse: Code reuse
- Frameworks: Common in frameworks
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Template Method pattern in Solidity
abstract contract AbstractClass {
function templateMethod() public returns (string memory) {
string memory result = step1();
result = string(abi.encodePacked(result, step2()));
result = string(abi.encodePacked(result, step3()));
return result;
}
function step1() internal pure returns (string memory) {
return "Step1";
}
function step2() internal virtual returns (string memory);
function step3() internal pure returns (string memory) {
return "Step3";
}
}
contract ConcreteClass is AbstractClass {
function step2() internal pure override returns (string memory) {
return "ConcreteStep2";
}
}Builder pattern for constructing complex objects.
- Builder: Constructs parts
- Director: Orchestrates construction
- Product: Constructed object
- Step-by-step: Build incrementally
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Builder pattern in Solidity
contract Product {
string[] public parts;
function addPart(string memory _part) public {
parts.push(_part);
}
}
contract Builder {
Product public product;
constructor() {
product = new Product();
}
function buildStepA() public {
product.addPart("Part A");
}
function buildStepB() public {
product.addPart("Part B");
}
function getResult() public view returns (Product) {
return product;
}
}Prototype pattern for cloning objects.
- Prototype: Cloneable object
- Clone: Creates a copy
- Contract: New contract instance
- Performance: Object reuse
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Prototype pattern in Solidity
contract Prototype {
struct Data {
string name;
uint256 value;
}
Data public data;
constructor(string memory _name, uint256 _value) {
data = Data(_name, _value);
}
function clone() public returns (Prototype) {
return new Prototype(data.name, data.value);
}
}Custom errors for gas-efficient error handling.
- Custom errors: Define with
error - Revert: Use
revertwith custom error - Gas: More gas efficient than require
- Parameters: Can include parameters
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Error handling with custom errors
contract CustomErrors {
error InsufficientBalance(uint256 balance, uint256 requested);
error Unauthorized(address caller);
mapping(address => uint256) public balances;
function withdraw(uint256 _amount) public {
if (balances[msg.sender] < _amount) {
revert InsufficientBalance(balances[msg.sender], _amount);
}
if (msg.sender != address(this)) {
revert Unauthorized(msg.sender);
}
balances[msg.sender] -= _amount;
payable(msg.sender).transfer(_amount);
}
}Events for logging and off-chain communication.
- Events: Define with
event - Emit: Emit with
emit - Indexed:
indexedfor filtering - Gas: Events are gas efficient
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Events and logging
contract EventsLogging {
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
event Log(string message, uint256 value);
mapping(address => uint256) public balances;
mapping(address => mapping(address => uint256)) public allowances;
function transfer(address _to, uint256 _amount) public {
require(balances[msg.sender] >= _amount, "Insufficient balance");
balances[msg.sender] -= _amount;
balances[_to] += _amount;
emit Transfer(msg.sender, _to, _amount);
}
}Modifiers for reusable access control and validation.
- Modifiers: Define with
modifier - Access control:
onlyOwner - State checks:
whenNotPaused - Reuse: Apply to multiple functions
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Modifiers and guards
contract Modifiers {
address public owner;
bool public paused;
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
modifier whenNotPaused() {
require(!paused, "Paused");
_;
}
constructor() {
owner = msg.sender;
}
function pause() public onlyOwner {
paused = true;
}
function unpause() public onlyOwner {
paused = false;
}
function doSomething() public whenNotPaused {
// Logic
}
}Libraries for reusable utility functions.
- Libraries: Define with
library - Functions: Internal functions
- Using For: Attach library to types
- Gas: Libraries are gas efficient
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Libraries
library Math {
function add(uint256 a, uint256 b) internal pure returns (uint256) {
return a + b;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "Subtraction overflow");
return a - b;
}
}
contract UsingLibrary {
using Math for uint256;
function calculate(uint256 a, uint256 b) public pure returns (uint256) {
return a.add(b);
}
}Inheritance for code reuse and extension.
- Inheritance:
contract Child is Parent - Constructor: Call parent constructor
- Override:
virtualandoverride - Multiple: Multiple inheritance supported
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Inheritance
contract Parent {
string public name;
constructor(string memory _name) {
name = _name;
}
function getName() public view returns (string memory) {
return name;
}
}
contract Child is Parent {
uint256 public age;
constructor(string memory _name, uint256 _age) Parent(_name) {
age = _age;
}
function getInfo() public view returns (string memory, uint256) {
return (name, age);
}
}Interfaces for contract interaction and standardization.
- Interface: Define with
interface - Functions: External functions only
- Implementation: Contract implements interface
- Standards: ERC-20, ERC-721, etc.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Interfaces
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
}
contract MyToken is IERC20 {
uint256 private _totalSupply;
mapping(address => uint256) private _balances;
constructor(uint256 initialSupply) {
_totalSupply = initialSupply;
_balances[msg.sender] = initialSupply;
}
function totalSupply() external view override returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) external view override returns (uint256) {
return _balances[account];
}
function transfer(address recipient, uint256 amount) external override returns (bool) {
require(_balances[msg.sender] >= amount, "Insufficient balance");
_balances[msg.sender] -= amount;
_balances[recipient] += amount;
return true;
}
}Arrays and loops for data processing.
- Arrays: Dynamic and fixed size
- Loops:
forloops for iteration - Gas: Be mindful of gas costs
- Return: Processed data
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Arrays and loops
contract ArrayOperations {
uint256[] public numbers;
function add(uint256 _num) public {
numbers.push(_num);
}
function sum() public view returns (uint256) {
uint256 total = 0;
for (uint256 i = 0; i < numbers.length; i++) {
total += numbers[i];
}
return total;
}
function average() public view returns (uint256) {
require(numbers.length > 0, "Empty array");
return sum() / numbers.length;
}
}Mappings and structs for complex data storage.
- Mapping: Key-value storage
- Structs: Custom data types
- Combined: Mapping to struct
- Iteration: Use separate key array
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Mapping and structs
contract MappingStructs {
struct User {
string name;
uint256 age;
bool active;
}
mapping(address => User) public users;
address[] public userList;
function addUser(string memory _name, uint256 _age) public {
require(bytes(users[msg.sender].name).length == 0, "User exists");
users[msg.sender] = User(_name, _age, true);
userList.push(msg.sender);
}
function getUser(address _addr) public view returns (string memory, uint256, bool) {
User memory user = users[_addr];
return (user.name, user.age, user.active);
}
}Security best practices for smart contracts.
- Checks-effects-interactions: Order of operations
- Reentrancy: Protect against reentrancy attacks
- Pull over push: Withdraw pattern
- Access control: Use modifiers
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Security best practices
contract Security {
address public owner;
uint256 public value;
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
constructor() {
owner = msg.sender;
}
// Use checks-effects-interactions pattern
function withdraw(uint256 _amount) public onlyOwner {
require(_amount <= value, "Insufficient balance");
// Effects
value -= _amount;
// Interactions
payable(msg.sender).transfer(_amount);
}
// Use pull over push for payments
mapping(address => uint256) public pendingWithdrawals;
function requestWithdrawal(uint256 _amount) public {
pendingWithdrawals[msg.sender] += _amount;
}
function withdrawPending() public {
uint256 amount = pendingWithdrawals[msg.sender];
require(amount > 0, "No pending withdrawal");
pendingWithdrawals[msg.sender] = 0;
payable(msg.sender).transfer(amount);
}
}Gas optimization techniques for efficient contracts.
- uint256: Use efficient data types
- Mappings: Prefer mappings over arrays
- External: Use external over public
- Calldata: Use calldata for parameters
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Gas optimization
contract GasOptimization {
// Use uint256 for efficiency
uint256 public value;
// Use mappings over arrays when possible
mapping(address => uint256) public balances;
// Use external over public when possible
function getBalance(address _addr) external view returns (uint256) {
return balances[_addr];
}
// Use short-circuit evaluation
function check(address _addr, uint256 _amount) external view returns (bool) {
require(_addr != address(0), "Invalid address");
return balances[_addr] >= _amount;
}
// Use calldata over memory for parameters
function process(string calldata _data) external pure returns (string memory) {
return _data;
}
}