Blockchain and Smart Contracts: Fundamentals and Ethereum Development
Blockchain and Smart Contracts: Fundamentals and Ethereum Development
Blockchain technology powers decentralized applications. Understanding core concepts and smart contracts is essential for Web3 development.
Blockchain Fundamentals
What is Blockchain?
A blockchain is a distributed, immutable ledger of transactions maintained by a network of computers (nodes).
Key Characteristics:
- Distributed: No single authority
- Immutable: Transactions cannot be altered (cryptographically secured)
- Transparent: All transactions visible to network participants
- Decentralized: Consensus-driven validation
How Blockchain Works
1. User initiates transaction
↓
2. Transaction broadcast to network
↓
3. Nodes validate transaction
↓
4. Transaction included in block
↓
5. Block linked to previous blocks via cryptographic hash
↓
6. Consensus mechanism confirms block
↓
7. Block immutable on chain
Consensus Mechanisms
Proof of Work (PoW):
- Miners solve complex math puzzles
- High computational cost
- Bitcoin uses PoW
- Energy intensive but highly secure
Miner 1: Solve puzzle... ✓ Found!
Miner 2: Solve puzzle... ✗ Outpaced
Result: Miner 1 adds block, earns reward
Proof of Stake (PoS):
- Validators chosen based on stake (coins locked)
- Energy efficient
- Ethereum 2.0 uses PoS
- Lower barriers to entry
Validator 1: Staked 32 ETH
Validator 2: Staked 32 ETH
Result: Random selection, add block, earn reward
Ethereum Basics
Ethereum Architecture
┌─────────────────────────────────────┐
│ Ethereum Network │
├─────────────────────────────────────┤
│ • Distributed Ledger │
│ • Smart Contracts (EVM) │
│ • Decentralized Apps (dApps) │
└─────────────────────────────────────┘
Accounts
// Two types of accounts:
// 1. Externally Owned Accounts (EOA)
// - Controlled by private keys
// - Can initiate transactions
// - Have balance (ETH)
Address: 0x742d35Cc6634C0532925a3b844Bc9e7595f
// 2. Contract Accounts
// - Controlled by smart contract code
// - Can receive and send transactions
// - Have associated code and storage
Address: 0x1234567890123456789012345678901234567890
Balance: 5 ETH
Code: [bytecode]
Storage: [state variables]
Gas and Transactions
// Every operation costs gas (computational units)
// Gas price measured in Gwei (10^-9 ETH)
Transaction {
from: 0x742d...,
to: 0x1234...,
value: 1 ETH,
gas: 21000,
gasPrice: 50 Gwei,
data: 0x [encoded function call]
}
// Cost = gas * gasPrice = 21000 * 50 Gwei = 0.00105 ETH
Smart Contracts with Solidity
Smart Contract Basics
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Simple contract
contract Counter {
// State variable (stored on blockchain)
uint256 public count = 0;
// Function to increment counter
function increment() public {
count += 1;
}
// Read-only function (doesn't modify state)
function getCount() public view returns (uint256) {
return count;
}
// Pure function (no state access)
function add(uint256 a, uint256 b) public pure returns (uint256) {
return a + b;
}
}
Data Types
pragma solidity ^0.8.0;
contract DataTypes {
// Integers
uint256 public largeNumber = 1000;
int256 public signedNumber = -50;
// Boolean
bool public isActive = true;
// Address
address public owner = 0x742d35Cc6634C0532925a3b844Bc9e7595f;
// String
string public name = "John";
// Arrays
uint256[] public numbers;
address[] public participants;
// Mapping (key-value)
mapping(address => uint256) public balances;
mapping(address => bool) public whitelisted;
// Struct
struct User {
string name;
uint256 age;
address wallet;
}
User[] public users;
}
Functions and Modifiers
pragma solidity ^0.8.0;
contract Functions {
address public owner;
uint256 public balance;
constructor() {
owner = msg.sender; // sender of transaction
}
// Modifier - reusable access control
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_; // Execute function body
}
// Public function - accessible externally
function deposit() public payable {
balance += msg.value;
}
// Only owner can withdraw
function withdraw(uint256 amount) public onlyOwner {
require(amount <= balance, "Insufficient balance");
balance -= amount;
payable(msg.sender).transfer(amount);
}
// Internal function - only callable from within contract
function _log(string memory message) internal pure {
// Implementation
}
// Private function - only callable in this contract
function _secret() private pure returns (string memory) {
return "secret";
}
// View functions - read state (free)
function getBalance() public view returns (uint256) {
return balance;
}
}
Events and Logging
pragma solidity ^0.8.0;
contract EventExample {
// Event declaration
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
mapping(address => uint256) public balances;
function transfer(address to, uint256 amount) public {
require(balances[msg.sender] >= amount, "Insufficient balance");
balances[msg.sender] -= amount;
balances[to] += amount;
// Emit event
emit Transfer(msg.sender, to, amount);
}
function approve(address spender, uint256 amount) public {
// Emit approval event
emit Approval(msg.sender, spender, amount);
}
}
Token Standards
ERC-20 (Fungible Tokens)
pragma solidity ^0.8.0;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address from, address to, 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);
}
// ERC-20 Token Implementation
contract MyToken is IERC20 {
mapping(address => uint256) public balances;
mapping(address => mapping(address => uint256)) public allowances;
uint256 public totalSupply = 1000000 * 10 ** 18;
string public name = "My Token";
string public symbol = "MTK";
constructor() {
balances[msg.sender] = totalSupply;
}
function balanceOf(address account) public view override returns (uint256) {
return balances[account];
}
function transfer(address to, uint256 amount) public override returns (bool) {
require(balances[msg.sender] >= amount, "Insufficient balance");
balances[msg.sender] -= amount;
balances[to] += amount;
emit Transfer(msg.sender, to, amount);
return true;
}
}
ERC-721 (NFTs)
pragma solidity ^0.8.0;
interface IERC721 {
function balanceOf(address owner) external view returns (uint256);
function ownerOf(uint256 tokenId) external view returns (address);
function transferFrom(address from, address to, uint256 tokenId) external;
function approve(address to, uint256 tokenId) external;
function getApproved(uint256 tokenId) external view returns (address);
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
}
// NFT Implementation
contract MyNFT is IERC721 {
mapping(uint256 => address) public owners;
mapping(address => uint256) public balances;
uint256 public nextTokenId = 1;
function mint(address to) public {
owners[nextTokenId] = to;
balances[to] += 1;
emit Transfer(address(0), to, nextTokenId);
nextTokenId += 1;
}
function transferFrom(address from, address to, uint256 tokenId) public override {
require(owners[tokenId] == from, "Not owner");
owners[tokenId] = to;
balances[from] -= 1;
balances[to] += 1;
emit Transfer(from, to, tokenId);
}
}
Smart Contract Security
Common Vulnerabilities
// VULNERABILITY: Reentrancy Attack
// BAD
function withdraw(uint256 amount) public {
require(balances[msg.sender] >= amount);
// Danger: Attacker can call withdraw again
(bool success, ) = payable(msg.sender).call{value: amount}("");
require(success);
balances[msg.sender] -= amount; // After transfer!
}
// GOOD: Checks-Effects-Interactions pattern
function withdraw(uint256 amount) public {
require(balances[msg.sender] >= amount);
balances[msg.sender] -= amount; // Modify state first
(bool success, ) = payable(msg.sender).call{value: amount}("");
require(success);
}
// VULNERABILITY: Integer Overflow
// BAD (pre-0.8.0)
uint256 a = type(uint256).max;
uint256 b = a + 1; // Wraps to 0
// GOOD (Solidity 0.8.0+)
// Overflow automatically reverts
// VULNERABILITY: Unchecked External Calls
// BAD
address(externalContract).call(abi.encodeWithSignature("transfer(...)"));
// GOOD
(bool success, ) = address(externalContract).call(...);
require(success, "External call failed");
Security Best Practices
pragma solidity ^0.8.0;
// GOOD: Secure contract pattern
contract SecureContract {
mapping(address => uint256) public balances;
bool private locked;
modifier nonReentrant() {
require(!locked, "No reentrancy");
locked = true;
_;
locked = false;
}
// Use safeTransfer for ERC-20
function depositToken(address token, uint256 amount) public {
require(amount > 0, "Amount must be > 0");
// Use OpenZeppelin's SafeERC20
IERC20(token).transferFrom(msg.sender, address(this), amount);
balances[msg.sender] += amount;
}
// Reentrancy-safe withdrawal
function withdraw(uint256 amount) public nonReentrant {
require(balances[msg.sender] >= amount, "Insufficient balance");
balances[msg.sender] -= amount;
(bool success, ) = payable(msg.sender).call{value: amount}("");
require(success, "Transfer failed");
}
}
Development Tools
Hardhat - Smart Contract Development
// hardhat.config.js
module.exports = {
solidity: {
version: "0.8.0",
},
networks: {
goerli: {
url: `https://goerli.infura.io/v3/${INFURA_KEY}`,
accounts: [PRIVATE_KEY],
},
},
};
// test/MyContract.js
const { expect } = require("chai");
describe("MyContract", function () {
it("Should deploy and work", async function () {
const MyContract = await ethers.getContractFactory("MyContract");
const contract = await MyContract.deploy();
expect(await contract.getCount()).to.equal(0);
await contract.increment();
expect(await contract.getCount()).to.equal(1);
});
});
Conclusion
Blockchain and smart contract essentials:
- Understand consensus mechanisms
- Learn Solidity syntax and patterns
- Follow security best practices
- Use established token standards (ERC-20, ERC-721)
- Test contracts thoroughly
- Use established libraries (OpenZeppelin)
- Avoid reentrancy and overflow vulnerabilities
- Consider gas optimization