Solidity is the primary programming language for writing smart contracts on Ethereum. It's a statically-typed, high-level language designed for implementing smart contracts on various blockchain platforms.
What is Solidity?
Solidity is influenced by C++, Python, and JavaScript, designed to target the Ethereum Virtual Machine (EVM).
Key Features
- Static typing
- Support for inheritance
- Libraries and complex user-defined types
- Contract-oriented programming
- Gas optimization features
Basic Structure
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract MyContract {
// State variables
uint256 public myValue;
// Functions
function setValue(uint256 _value) public {
myValue = _value;
}
}Value Types
Integers
// Signed integers
int8 smallNumber;
int256 largeNumber;
// Unsigned integers
uint8 counter;
uint256 amount;
// Operations
uint256 a = 5;
uint256 b = 10;
uint256 sum = a + b;Boolean
bool isActive;
bool public isRegistered = true;
// Operations
bool result = (a > b) && isActive;Address
address public owner;
address payable public wallet;
// Methods
address payable sender = payable(msg.sender);
uint256 balance = sender.balance;
// Transfer
sender.transfer(100);Bytes and Strings
// Fixed-size byte arrays
bytes32 public hash;
bytes1 public singleByte;
// Dynamic byte array
bytes public data;
// String
string public name = "My Contract";
// Conversion
bytes32 hash = keccak256(abi.encodePacked("hello"));Reference Types
Arrays
// Fixed-size array
uint256[5] public fixedArray;
// Dynamic array
uint256[] public dynamicArray;
// Array operations
dynamicArray.push(1);
dynamicArray.pop();
uint256 value = dynamicArray[0];
uint256 length = dynamicArray.length;
// Memory array
function getArray() public pure returns (uint256[] memory) {
uint256[] memory arr = new uint256[](3);
arr[0] = 1;
arr[1] = = 2;
arr[2] = 3;
return arr;
}Mappings
// Basic mapping
mapping(address => uint256) public balances;
// Nested mapping
mapping(address => mapping(address => uint256)) public allowances;
// Operations
balances[msg.sender] = 100;
uint256 balance = balances[msg.sender];Structs
struct User {
uint256 id;
string name;
uint256 balance;
}
User public user;
function createUser(uint256 _id, string memory _name) public {
user = User({
id: _id,
name: _name,
balance: 0
});
}Functions
Function Basics
function add(uint256 a, uint256 b) public pure returns (uint256) {
return a + b;
}Visibility Specifiers
// Public - can be called internally and externally
function publicFunc() public {}
// Internal - only within this contract
function internalFunc() internal {}
// Private - only within this contract, not derived
function privateFunc() private {}
// External - only from outside
function externalFunc() external {}State Mutability
// View - doesn't modify state
function getBalance() public view returns (uint256) {
return balance;
}
// Pure - doesn't read or modify state
function add(uint256 a, uint256 b) public pure returns (uint256) {
return a + b;
}
// Payable - can receive ETH
function deposit() public payable {}Modifiers
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
function sensitiveFunction() public onlyOwner {}Events
Events allow logging to the blockchain.
event Transfer(
address indexed from,
address indexed to,
uint256 value
);
function transfer(address to, uint256 amount) public {
balances[msg.sender] -= amount;
balances[to] += amount;
emit Transfer(msg.sender, to, amount);
}Error Handling
Require
function withdraw(uint256 amount) public {
require(balances[msg.sender] >= amount, "Insufficient balance");
balances[msg.sender] -= amount;
payable(msg.sender).transfer(amount);
}Revert
function criticalFunction() public {
if (condition) {
revert("Something went wrong");
}
}Assert
function internalLogic() public {
uint256 result = complexCalculation();
assert(result > 0); // Should never fail
}Custom Errors
error InsufficientBalance(uint256 requested, uint256 available);
function withdraw(uint256 amount) public {
uint256 balance = balances[msg.sender];
if (balance < amount) {
revert InsufficientBalance(amount, balance);
}
// ...
}Inheritance
Basic Inheritance
contract Animal {
function makeSound() public pure virtual returns (string memory) {
return "Some sound";
}
}
contract Dog is Animal {
function makeSound() public pure override returns (string memory) {
return "Bark";
}
}Multiple Inheritance
contract A {
function foo() public pure virtual returns (string memory) {
return "A";
}
}
contract B {
function foo() public pure virtual returns (string memory) {
return "B";
}
}
contract C is A, B {
function foo() public pure override(A, B) returns (string memory) {
return "C";
}
}Interfaces
Interfaces define contract structure without implementation.
interface IERC20 {
function transfer(address to, uint256 amount) external returns (bool);
function balanceOf(address account) external view returns (uint256);
}
contract MyContract {
IERC20 token;
constructor(address _token) {
token = IERC20(_token);
}
function getTokenBalance(address account) public view returns (uint256) {
return token.balanceOf(account);
}
}Libraries
Libraries are reusable contracts without state.
library Math {
function max(uint256 a, uint256 b) public pure returns (uint256) {
return a >= b ? a : b;
}
}
contract MyContract {
using Math for uint256;
function findMax(uint256 a, uint256 b) public pure returns (uint256) {
return a.max(b);
}
}Global Variables
Block and Transaction
uint256 blockNumber = block.number;
uint256 timestamp = block.timestamp;
address sender = msg.sender;
uint256 value = msg.value;
bytes4 signature = msg.sig;
bytes calldata data = msg.data;Contract Information
address contractAddress = address(this);
uint256 balance = address(this).balance;Gas Optimization
Use Calldata
// Expensive
function expensive(string memory data) public {}
// Cheaper
function cheap(string calldata data) public {}Pack Variables
// Wastes gas
struct Bad {
uint128 a;
uint128 b;
uint256 c;
}
// Optimized
struct Good {
uint256 a;
uint128 b;
uint128 c;
}Short-Circuiting
// Expensive
if (expensiveCheck() || cheapCheck()) {}
// Cheaper
if (cheapCheck() || expensiveCheck()) {}Security Best Practices
- 1Check inputs with require
- 2Protect against reentrancy
- 3Use SafeMath for critical calculations (pre-0.8.0)
- 4Keep functions simple
- 5Follow checks-effects-interactions pattern
- 6Use Events for important state changes
- 7Limit external calls
- 8Be careful with loops
Conclusion
Solidity is a powerful language for building smart contracts. Master these fundamentals, and you'll be ready to create secure, efficient contracts for the Ethereum blockchain.
Practice writing contracts, understand gas costs, and always prioritize security. Solidity is your gateway to Web3 development.