πŸ›οΈ Solidity Fundamentals

🎯 What is Solidity?

Solidity is an object-oriented, high-level language for implementing smart contracts on Ethereum and EVM-compatible blockchains. Created by Gavin Wood (Ethereum co-founder) and developed by the Solidity team, it's statically typed, supports inheritance, libraries, and complex user-defined types. Solidity compiles to bytecode executed on the Ethereum Virtual Machine (EVM).

πŸ”¬ Core Concepts

  • Smart Contracts: Self-executing code with state variables and functions.
  • EVM (Ethereum Virtual Machine): Deterministic execution environment.
  • Gas Model: Every operation costs gas (measured in wei/gwei).
  • ABI (Application Binary Interface): Interface for interacting with contracts.
  • Version Pragma: Specifies compiler version (e.g., ^0.8.0).
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract FirstContract { string public greeting = "Hello, Web3!"; function setGreeting(string memory _newGreeting) public { greeting = _newGreeting; } }

πŸ”’ Value Types & Variables

🎯 Primitive Types

bool, int/uint (8-256 bits), address, bytes1..bytes32, string. Variables have state (storage), local (memory), or constant/immutable.

  • uint256: Unsigned integer (default for gas optimization)
  • address: 20-byte Ethereum address (payable address variant)
  • bytes32: Fixed-size byte array
  • enum: Custom user-defined type
pragma solidity ^0.8.0; contract TypesDemo { uint256 public balance = 1000 wei; address public owner = msg.sender; bool public isActive = true; bytes32 public hash = keccak256("Solidity"); enum Status { Pending, Active, Closed } Status public currentStatus = Status.Pending; }

βš™οΈ Functions & Modifiers

🎯 Function Visibility & State Mutability

public, private, internal, external. Modifiers: view (read-only), pure (no read/write), payable (receive ether). Custom modifiers for reusable checks.

contract ModifierExample { address public owner; constructor() { owner = msg.sender; } modifier onlyOwner() { require(msg.sender == owner, "Not owner"); _; } function changeOwner(address _newOwner) public onlyOwner { owner = _newOwner; } function getBalance() public view returns (uint256) { return address(this).balance; } }

πŸ”€ Control Structures

🎯 if-else, loops, conditional

Solidity supports if/else, for, while, do-while, break/continue. Loops should be used carefully to avoid gas limit issues.

function sumArray(uint[] memory arr) public pure returns (uint total) { for (uint i = 0; i < arr.length; i++) { total += arr[i]; } } function maxValue(uint a, uint b) public pure returns (uint) { return a > b ? a : b; }

πŸ“Š Arrays & Structs

🎯 Dynamic/Fixed Arrays + Custom Types

T[] dynamic array, T[5] fixed. Structs group data. Arrays have .push(), .length methods.

struct Player { string name; uint score; } contract Game { Player[] public players; function addPlayer(string memory _name) public { players.push(Player(_name, 0)); } function getPlayer(uint idx) public view returns (string memory, uint) { Player memory p = players[idx]; return (p.name, p.score); } }

πŸ—ΊοΈ Mappings & Enums

🎯 Key-Value Storage

Mappings: mapping(keyType => valueType) – hash tables for efficient lookups. Keys are not stored, only keccak256 hash. Enums create named constants.

mapping(address => uint) public balances; mapping(uint => address) public idToAddress; enum OrderState { Created, Shipped, Delivered } function setBalance(address _user, uint _amount) public { balances[_user] = _amount; }

πŸ’Ύ Storage / Memory / Calldata

🎯 Data Locations

storage (persistent on blockchain), memory (temporary), calldata (immutable function params). Gas costs vary drastically.

contract DataLocations { uint[] public storedArray; // storage function example(uint[] calldata _externalArr) external { uint[] memory tempArray = new uint[](3); // memory tempArray[0] = _externalArr[0]; storedArray = tempArray; // copy to storage } }

πŸ“’ Events & Logging

🎯 Emit logs for off-chain apps

Events are indexed EVM logs, gas-efficient way to track contract activity. Up to 3 indexed parameters for efficient filtering.

event Transfer(address indexed from, address indexed to, uint256 value); event Log(string message, uint timestamp); function sendToken(address _to, uint _amount) public { // transfer logic emit Transfer(msg.sender, _to, _amount); emit Log("Transfer executed", block.timestamp); }

🧬 Inheritance & Polymorphism

🎯 Multiple inheritance, virtual/override

Solidity supports multiple inheritance with linearization (C3). virtual and override keywords.

contract Base { function foo() public virtual pure returns (string memory) { return "Base"; } } contract Derived is Base { function foo() public override pure returns (string memory) { return "Derived"; } }

⚠️ Error Handling (require/revert)

🎯 Require, Assert, Revert

require(condition, "error") – refunds remaining gas. assert(condition) – consumes gas, used for invariants. revert() – manual rollback. Custom errors (Solidity 0.8.4+) reduce gas.

error Unauthorized(address caller); error InsufficientBalance(uint requested, uint available); function withdraw(uint amount) public { if (amount > balances[msg.sender]) { revert InsufficientBalance(amount, balances[msg.sender]); } require(msg.sender != address(0), "Zero address"); // logic }

πŸ”Œ Interfaces & Abstract Contracts

🎯 Define standards without implementation

interface – only external functions, no implementation. abstract contract – mix of implemented/unimplemented functions. ERC20/ERC721 are standard interfaces.

interface IERC20 { function totalSupply() external view returns (uint); function transfer(address to, uint amount) external returns (bool); } abstract contract Token is IERC20 { function totalSupply() public view virtual override returns (uint); // other logic }

πŸ“š Libraries & Using For

🎯 Reusable code, no storage

Libraries are deployed once and can be attached to types via using ... for .... Great for math, safe operations.

library SafeMath { function add(uint a, uint b) internal pure returns (uint) { uint c = a + b; require(c >= a, "overflow"); return c; } } contract Calculator { using SafeMath for uint; uint public value = 10; function addValue(uint x) public { value = value.add(x); } }

πŸ’° Payable & Ether Transfers

🎯 Receive Ether & Send

payable modifier allows functions to accept ether. Methods: transfer() (2300 gas), send(), call{value: }() (recommended). receive() and fallback() handle plain ether transfers.

contract Wallet { event Received(address sender, uint amount); receive() external payable { emit Received(msg.sender, msg.value); } function donate() public payable {} function withdraw(uint amount) public { payable(msg.sender).transfer(amount); } function callTransfer(address payable _to) public payable { (bool success, ) = _to.call{value: msg.value}(""); require(success, "Transfer failed"); } }

πŸ›‘οΈ Security Patterns (Reentrancy)

🎯 Smart Contract Security Best Practices

Reentrancy Guard (Checks-Effects-Interactions), Access Control, Timestamps manipulation, Integer overflow/underflow (Solidity 0.8+ built-in checks). Use OpenZeppelin contracts.

contract SecureWithdraw { mapping(address => uint) public balances; bool internal locked; modifier noReentrancy() { require(!locked, "Reentrancy"); locked = true; _; locked = false; } function withdraw(uint amount) public noReentrancy { require(balances[msg.sender] >= amount, "Insufficient"); balances[msg.sender] -= amount; // Effects (bool sent, ) = msg.sender.call{value: amount}(""); // Interaction require(sent, "Failed"); } }