Master the programming language for Ethereum smart contracts
Solidity is a programming language specifically designed for writing smart contracts on Ethereum. It looks similar to JavaScript but is designed for blockchain development.
Regular programming languages like JavaScript or Python weren't designed for blockchain. Solidity was created to handle the unique requirements of smart contracts:
Statically Typed
Variable types must be declared (like TypeScript)
Object-Oriented
Supports contracts, inheritance, and libraries
Compiled Language
Compiles to EVM bytecode before deployment
EVM-Specific
Designed specifically for Ethereum Virtual Machine
Solidity has various data types to store different kinds of information. Let's explore the most common ones.
Positive whole numbers only (no negatives)
uint8 smallNumber = 255; // 0 to 255
uint256 bigNumber = 1000000; // 0 to 2^256-1 (default)
uint age = 25; // uint = uint256Can be positive or negative
int8 temperature = -10; // -128 to 127
int256 balance = -5000; // Can be negative
int profit = 1000; // int = int256True or false values
bool isActive = true;
bool hasAccess = false;
bool isOwner = msg.sender == owner; // ComparisonEthereum addresses (20 bytes)
address owner = 0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb;
address payable recipient; // Can receive ETH
recipient.transfer(1 ether); // Send ETHFixed-size byte arrays
bytes1 singleByte = 0xff;
bytes32 hash; // Common for hashes
bytes data = "Hello"; // Dynamic bytesLists of elements
// Fixed size array
uint[5] fixedArray = [1, 2, 3, 4, 5];
// Dynamic array
uint[] dynamicArray;
dynamicArray.push(10); // Add element
uint length = dynamicArray.length;
// Array of addresses
address[] public users;Text data (expensive to store!)
string public name = "Alice";
string greeting = "Hello, World!";
// Note: Strings are expensive in gas
// Use bytes32 for short strings when possibleCustom data structures
// Define a struct
struct Person {
string name;
uint age;
address wallet;
}
// Create an instance
Person public alice = Person("Alice", 25, 0x123...);
// Access properties
string memory aliceName = alice.name;Key-value pairs (like dictionaries/objects)
// Mapping from address to balance
mapping(address => uint) public balances;
// Set a value
balances[msg.sender] = 100;
// Get a value
uint myBalance = balances[msg.sender];
// Nested mapping
mapping(address => mapping(address => uint)) public allowances;Use the smallest data type that fits your needs. uint8 uses less gas than uint256 when packed together. But for single variables, uint256 is often more gas-efficient due to EVM's 256-bit architecture.
Functions are the executable units of code in smart contracts. They define what actions your contract can perform.
function functionName(parameters) visibility modifier returns (returnType) {
// Function body
}Read data but don't modify state (FREE - no gas cost when called externally)
function getBalance() public view returns (uint) {
return balance; // Just reading, not changing
}Don't read or modify state (FREE - no gas cost)
function add(uint a, uint b) public pure returns (uint) {
return a + b; // Pure calculation, no state access
}Can receive ETH
function deposit() public payable {
balance += msg.value; // msg.value = ETH sent
}Modifiers are reusable code that can be added to functions to change their behavior. Think of them as "guards" that check conditions before executing a function.
// Define a modifier
modifier onlyOwner() {
require(msg.sender == owner, "Not the owner");
_; // Continue with function execution
}
// Use the modifier
function changeOwner(address newOwner) public onlyOwner {
owner = newOwner; // Only owner can call this
}onlyOwner - Restrict to contract ownernonReentrant - Prevent reentrancy attackswhenNotPaused - Only when contract is activeVisibility determines who can call a function or access a variable. This is crucial for security!
Anyone can call (external users, other contracts, internally)
function publicFunc() public {
// Anyone can call
}Only external calls (not from within contract)
function externalFunc() external {
// Only external calls
}Only this contract and derived contracts
function internalFunc() internal {
// Only within contract
}Only this contract (not even derived contracts)
function privateFunc() private {
// Most restrictive
}"Private" doesn't mean secret! All data on the blockchain is visible. Private just means other contracts can't call the function. Never store passwords or secrets in smart contracts!
Understanding where data is stored is crucial for gas optimization and avoiding bugs.
Permanent data stored on the blockchain. Like your computer's hard drive. EXPENSIVE!
uint public balance; // Stored permanently
mapping(address => uint) balances; // StorageTemporary data during function execution. Like RAM. Cheaper than storage.
function process(string memory name) public {
// name exists only during function call
}Read-only temporary data for function parameters. Cheapest option!
function process(string calldata name) external {
// name is read-only, saves gas
}calldata for external function parametersmemory for temporary variables in functionsstorage - it's expensive!Let's put it all together with a simple but complete smart contract - a basic token.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title SimpleToken
* @dev A basic ERC-20-like token contract
*/
contract SimpleToken {
// State variables (stored permanently)
string public name = "SimpleToken";
string public symbol = "SIM";
uint8 public decimals = 18;
uint256 public totalSupply;
address public owner;
// Mapping to track balances
mapping(address => uint256) public balanceOf;
// Mapping for allowances (for transfers on behalf)
mapping(address => mapping(address => uint256)) public allowance;
// Events
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
// Modifier to restrict functions to owner only
modifier onlyOwner() {
require(msg.sender == owner, "Not the owner");
_;
}
// Constructor - runs once when contract is deployed
constructor(uint256 _initialSupply) {
owner = msg.sender;
totalSupply = _initialSupply * 10 ** uint256(decimals);
balanceOf[msg.sender] = totalSupply;
}
/**
* @dev Transfer tokens to another address
* @param _to Recipient address
* @param _value Amount to transfer
*/
function transfer(address _to, uint256 _value) public returns (bool success) {
require(_to != address(0), "Invalid address");
require(balanceOf[msg.sender] >= _value, "Insufficient balance");
balanceOf[msg.sender] -= _value;
balanceOf[_to] += _value;
emit Transfer(msg.sender, _to, _value);
return true;
}
/**
* @dev Approve another address to spend tokens on your behalf
* @param _spender Address authorized to spend
* @param _value Amount they can spend
*/
function approve(address _spender, uint256 _value) public returns (bool success) {
allowance[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}
/**
* @dev Transfer tokens from one address to another (requires approval)
* @param _from Address to transfer from
* @param _to Address to transfer to
* @param _value Amount to transfer
*/
function transferFrom(address _from, address _to, uint256 _value)
public
returns (bool success)
{
require(_value <= balanceOf[_from], "Insufficient balance");
require(_value <= allowance[_from][msg.sender], "Allowance exceeded");
require(_to != address(0), "Invalid address");
balanceOf[_from] -= _value;
balanceOf[_to] += _value;
allowance[_from][msg.sender] -= _value;
emit Transfer(_from, _to, _value);
return true;
}
/**
* @dev Mint new tokens (only owner can do this)
* @param _amount Amount of tokens to create
*/
function mint(uint256 _amount) public onlyOwner {
totalSupply += _amount;
balanceOf[owner] += _amount;
emit Transfer(address(0), owner, _amount);
}
/**
* @dev Get balance of an address
* @param _owner Address to check
* @return balance The balance
*/
function getBalance(address _owner) public view returns (uint256 balance) {
return balanceOf[_owner];
}
}✓ State Variables
name, symbol, balanceOf, etc.
✓ Mappings
Track balances and allowances
✓ Events
Transfer and Approval logging
✓ Modifiers
onlyOwner access control
✓ Functions
transfer, approve, mint, etc.
✓ Visibility
public, view functions
You now know Solidity basics! In the next module, we'll learn about smart contract security - how to protect your contracts from common vulnerabilities and attacks.