- Authors

- Name
- Youngju Kim
- @fjvbn20031
- Introduction
- 1. Blockchain Fundamentals
- 2. Ethereum and the EVM Deep Dive
- 3. Solidity Smart Contract Development
- 4. Development Tools: Hardhat vs Foundry
- 5. Smart Contract Security
- 6. Layer 2 Solutions Comparison
- 7. Understanding DeFi Protocols
- 8. Gas Optimization Techniques
- 9. Web3 Frontend Integration
- 10. Web3 Career Guide
- 11. Practice Quiz
- 12. References
Introduction
In 2025, the blockchain ecosystem has matured beyond speculative hype into genuine technical infrastructure. Ethereum's Dencun upgrade, the explosive growth of Layer 2 rollups, and RWA (Real World Assets) tokenization meeting mainstream finance have driven demand for Web3 developers to all-time highs.
This guide systematically covers blockchain fundamentals, Solidity smart contract development, security, the DeFi/NFT ecosystem, and Web3 developer careers.
Key Expressions
Expression Meaning Smart Contract Self-executing program on the blockchain. Code is the contract Layer 2 Scaling solutions built on top of mainnet (L1). Increases throughput, reduces cost DeFi Decentralized Finance. Financial services via smart contracts without intermediaries Gas Optimization Techniques to minimize smart contract execution cost (Gas)
1. Blockchain Fundamentals
1.1 What Is a Blockchain?
A blockchain is a technology where all participants in a distributed network share the same transaction record (ledger). Each block contains the hash of the previous block, forming a chain.
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Block 0 │───▶│ Block 1 │───▶│ Block 2 │
│ (Genesis)│ │ │ │ │
│ Hash: 0a │ │ Prev: 0a │ │ Prev: 7f │
│ Nonce: 42│ │ Hash: 7f │ │ Hash: b3 │
│ Txs: [] │ │ Txs: [..] │ │ Txs: [..]│
└──────────┘ └──────────┘ └──────────┘
1.2 Consensus Mechanism Comparison
| Feature | PoW (Proof of Work) | PoS (Proof of Stake) | DPoS |
|---|---|---|---|
| Energy Consumption | Very High | Low | Low |
| Decentralization | High | Medium | Low |
| TPS | 7-15 | 30-100K+ | 1000+ |
| Representative Chains | Bitcoin | Ethereum 2.0 | EOS, Tron |
| Participation Requirement | Hash power (GPU/ASIC) | Staked assets | Delegated votes |
| Finality | Probabilistic (6 blocks) | Epoch-based deterministic | Fast deterministic |
1.3 Hash Functions and Merkle Trees
Blockchain integrity relies on cryptographic hash functions.
import hashlib
def calculate_hash(block_data):
"""Calculate SHA-256 hash of block data"""
data_string = str(block_data).encode()
return hashlib.sha256(data_string).hexdigest()
# Merkle Tree implementation
def merkle_root(transactions):
if len(transactions) == 0:
return hashlib.sha256(b'').hexdigest()
if len(transactions) == 1:
return transactions[0]
new_level = []
for i in range(0, len(transactions), 2):
left = transactions[i]
right = transactions[i + 1] if i + 1 < len(transactions) else left
combined = hashlib.sha256((left + right).encode()).hexdigest()
new_level.append(combined)
return merkle_root(new_level)
1.4 Transaction Lifecycle
User -> Create Transaction -> Sign (Private Key) -> Broadcast
-> Enter Mempool -> Validator/Miner Selection -> Block Inclusion
-> Consensus -> Block Finalization -> State Update
2. Ethereum and the EVM Deep Dive
2.1 Ethereum Architecture
Ethereum started with the vision of a World Computer. While Bitcoin only stores transaction records, Ethereum can execute arbitrary logic through its Turing-complete virtual machine (EVM).
┌─────────────────────────────────────────┐
│ Ethereum Network │
├─────────────────────────────────────────┤
│ ┌─────────┐ ┌──────────┐ ┌────────┐ │
│ │ EOA │ │ Contract │ │Contract│ │
│ │ Account │──│ Account │──│Account │ │
│ │ (User) │ │ (Code) │ │(Code) │ │
│ └─────────┘ └──────────┘ └────────┘ │
├─────────────────────────────────────────┤
│ EVM (Execution Engine) │
├─────────────────────────────────────────┤
│ State Trie (State Storage) │
└─────────────────────────────────────────┘
2.2 How the EVM Works
The EVM is a stack-based virtual machine using 256-bit word sizes.
EVM Memory Model:
┌─────────────┐
│ Stack │ Max 1024 depth, LIFO
│ (256-bit) │
├─────────────┤
│ Memory │ Byte array, volatile
│ (expandable)│
├─────────────┤
│ Storage │ Key-value, persistent
│ (256->256) │
└─────────────┘
Key Opcode examples:
PUSH1 0x60 // Push 0x60 to stack
PUSH1 0x40 // Push 0x40 to stack
MSTORE // Memory[0x40] = 0x60
CALLVALUE // Push msg.value to stack
DUP1 // Duplicate stack top
ISZERO // 1 if zero, else 0
2.3 Gas System
Every EVM operation incurs a Gas cost.
| Operation | Gas Cost | Description |
|---|---|---|
| ADD / SUB | 3 | Arithmetic |
| MUL / DIV | 5 | Multiplication / Division |
| SLOAD | 2100 (cold) / 100 (warm) | Storage read |
| SSTORE | 20000 (new) / 5000 (update) | Storage write |
| CALL | 2600 (cold) / 100 (warm) | External call |
| CREATE | 32000 | Contract creation |
Gas cost formula:
Total Cost = Gas Used * Gas Price (Gwei)
= Gas Used * (Base Fee + Priority Fee)
Example: 21000 Gas * 30 Gwei = 630,000 Gwei = 0.00063 ETH
3. Solidity Smart Contract Development
3.1 Basic Syntax
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @title SimpleStorage
* @dev Basic state storage contract
*/
contract SimpleStorage {
// State variables
uint256 private storedValue;
address public owner;
mapping(address => uint256) public balances;
// Events
event ValueChanged(address indexed setter, uint256 newValue);
// Modifiers
modifier onlyOwner() {
require(msg.sender == owner, "Not the owner");
_;
}
constructor(uint256 _initialValue) {
owner = msg.sender;
storedValue = _initialValue;
}
function setValue(uint256 _value) external onlyOwner {
storedValue = _value;
emit ValueChanged(msg.sender, _value);
}
function getValue() external view returns (uint256) {
return storedValue;
}
}
3.2 ERC-20 Token Implementation
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract MyToken is ERC20, Ownable {
uint256 public constant MAX_SUPPLY = 1_000_000 * 10**18;
constructor() ERC20("MyToken", "MTK") Ownable(msg.sender) {
_mint(msg.sender, MAX_SUPPLY);
}
function burn(uint256 amount) external {
_burn(msg.sender, amount);
}
}
3.3 ERC-721 NFT Implementation
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract MyNFT is ERC721, ERC721URIStorage, Ownable {
uint256 private _nextTokenId;
uint256 public mintPrice = 0.05 ether;
uint256 public maxSupply = 10000;
constructor() ERC721("MyNFT", "MNFT") Ownable(msg.sender) {}
function mint(string memory uri) external payable {
require(msg.value >= mintPrice, "Insufficient payment");
require(_nextTokenId < maxSupply, "Max supply reached");
uint256 tokenId = _nextTokenId++;
_safeMint(msg.sender, tokenId);
_setTokenURI(tokenId, uri);
}
function tokenURI(uint256 tokenId)
public view override(ERC721, ERC721URIStorage)
returns (string memory)
{
return super.tokenURI(tokenId);
}
function supportsInterface(bytes4 interfaceId)
public view override(ERC721, ERC721URIStorage)
returns (bool)
{
return super.supportsInterface(interfaceId);
}
function withdraw() external onlyOwner {
payable(owner()).transfer(address(this).balance);
}
}
3.4 Advanced Pattern: Upgradeable Proxy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
// Implementation V1
contract BoxV1 {
uint256 private value;
function store(uint256 newValue) public {
value = newValue;
}
function retrieve() public view returns (uint256) {
return value;
}
}
// Implementation V2 (after upgrade)
contract BoxV2 {
uint256 private value;
function store(uint256 newValue) public {
value = newValue;
}
function retrieve() public view returns (uint256) {
return value;
}
function increment() public {
value += 1;
}
}
4. Development Tools: Hardhat vs Foundry
4.1 Hardhat Setup and Usage
# Project initialization
mkdir my-project && cd my-project
npm init -y
npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox
# Create Hardhat project
npx hardhat init
// hardhat.config.js
require("@nomicfoundation/hardhat-toolbox");
module.exports = {
solidity: {
version: "0.8.20",
settings: {
optimizer: {
enabled: true,
runs: 200,
},
},
},
networks: {
sepolia: {
url: process.env.SEPOLIA_RPC_URL || "",
accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
},
hardhat: {
forking: {
url: process.env.MAINNET_RPC_URL || "",
blockNumber: 18_000_000,
},
},
},
};
Hardhat test example:
const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("SimpleStorage", function () {
let storage;
let owner;
let addr1;
beforeEach(async function () {
[owner, addr1] = await ethers.getSigners();
const SimpleStorage = await ethers.getContractFactory("SimpleStorage");
storage = await SimpleStorage.deploy(42);
});
it("Should store the initial value", async function () {
expect(await storage.getValue()).to.equal(42);
});
it("Should allow owner to set value", async function () {
await storage.setValue(100);
expect(await storage.getValue()).to.equal(100);
});
it("Should reject non-owner setValue", async function () {
await expect(
storage.connect(addr1).setValue(100)
).to.be.revertedWith("Not the owner");
});
});
4.2 Foundry Setup and Usage
# Install Foundry
curl -L https://foundry.paradigm.xyz | bash
foundryup
# Create project
forge init my-foundry-project
cd my-foundry-project
# foundry.toml
[profile.default]
src = "src"
out = "out"
libs = ["lib"]
optimizer = true
optimizer_runs = 200
solc_version = "0.8.20"
[profile.default.fuzz]
runs = 1000
max_test_rejects = 65536
[rpc_endpoints]
mainnet = "https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY"
sepolia = "https://eth-sepolia.g.alchemy.com/v2/YOUR_KEY"
Foundry test (written in Solidity):
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "forge-std/Test.sol";
import "../src/SimpleStorage.sol";
contract SimpleStorageTest is Test {
SimpleStorage public store;
address public owner = address(this);
address public user = address(0x1);
function setUp() public {
store = new SimpleStorage(42);
}
function testInitialValue() public view {
assertEq(store.getValue(), 42);
}
function testSetValue() public {
store.setValue(100);
assertEq(store.getValue(), 100);
}
function testFailNonOwnerSetValue() public {
vm.prank(user);
store.setValue(100); // This call will revert
}
// Fuzz test
function testFuzzSetValue(uint256 value) public {
store.setValue(value);
assertEq(store.getValue(), value);
}
}
4.3 Hardhat vs Foundry Comparison
| Feature | Hardhat | Foundry |
|---|---|---|
| Language | JavaScript/TypeScript | Solidity |
| Test Speed | Moderate | Very fast (10x+) |
| Fuzz Testing | Plugin required | Built-in |
| Debugging | console.log | forge debug (step) |
| Fork Testing | Supported | Supported (faster) |
| Plugin Ecosystem | Rich | Growing |
| Gas Reports | hardhat-gas-reporter | forge test --gas-report |
| Learning Curve | JS developer friendly | Solidity-focused |
5. Smart Contract Security
5.1 Reentrancy Attack
The most famous smart contract vulnerability, responsible for the 2016 DAO hack (60 million dollar loss).
// Vulnerable code
contract VulnerableVault {
mapping(address => uint256) public balances;
function deposit() external payable {
balances[msg.sender] += msg.value;
}
// DANGEROUS: state update after external call
function withdraw() external {
uint256 amount = balances[msg.sender];
require(amount > 0, "No balance");
// External call (attack vector)
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
// State update AFTER external call!
balances[msg.sender] = 0;
}
}
// Attacker contract
contract Attacker {
VulnerableVault public vault;
constructor(address _vault) {
vault = VulnerableVault(_vault);
}
function attack() external payable {
vault.deposit{value: msg.value}();
vault.withdraw();
}
// withdraw()'s call returns here -> calls withdraw() again
receive() external payable {
if (address(vault).balance > 0) {
vault.withdraw();
}
}
}
Solution: Checks-Effects-Interactions Pattern
contract SecureVault {
mapping(address => uint256) public balances;
bool private locked;
modifier noReentrant() {
require(!locked, "Reentrant call");
locked = true;
_;
locked = false;
}
function withdraw() external noReentrant {
uint256 amount = balances[msg.sender];
require(amount > 0, "No balance");
// Effects (update state first)
balances[msg.sender] = 0;
// Interactions (external call last)
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
}
}
5.2 Integer Overflow/Underflow
Before Solidity 0.8.0, there was no automatic overflow check.
// Solidity before 0.7.x: DANGEROUS!
// uint8 max = 255; max + 1 = 0 (overflow)
// uint8 min = 0; min - 1 = 255 (underflow)
// Solidity 0.8.0+: Automatically reverts
// Use unchecked blocks when performance is needed
function unsafeIncrement(uint256 x) pure returns (uint256) {
unchecked {
return x + 1; // No overflow check (saves Gas)
}
}
5.3 Security Checklist
Security Audit Checklist:
[1] Reentrancy
- CEI pattern applied
- ReentrancyGuard usage
[2] Access Control
- onlyOwner / Role-based access
- Initialization function protection
[3] Arithmetic
- Solidity 0.8+ (automatic checks)
- Review unchecked blocks
[4] External Calls
- Check call return values
- Set gas limits
[5] Frontrunning
- Commit-reveal pattern
- Consider Flashbots
[6] Oracle Manipulation
- Use TWAP (Time-Weighted Average Price)
- Multiple oracle sources
[7] Flash Loan Attacks
- Defense against single-tx manipulation
- Price validation logic
[8] Storage Collision (Proxy)
- EIP-1967 storage slots
- Upgrade testing
5.4 Major Hack Incidents
| Incident | Year | Loss | Vulnerability |
|---|---|---|---|
| The DAO | 2016 | 60M USD | Reentrancy |
| Ronin Bridge | 2022 | 625M USD | Private key compromise |
| Wormhole | 2022 | 320M USD | Signature verification bypass |
| Euler Finance | 2023 | 197M USD | Donate function vulnerability |
| Mango Markets | 2022 | 114M USD | Oracle manipulation |
6. Layer 2 Solutions Comparison
6.1 What Is Layer 2?
Layer 2 (L2) extends throughput while inheriting Ethereum mainnet (L1) security.
┌─────────────────────────────────────────┐
│ Users (Dapps) │
├────────────┬────────────┬───────────────┤
│ Optimistic │ ZK Rollup │ Validium │
│ Rollup │ │ │
│ (Arbitrum, │ (zkSync, │ (StarkEx, │
│ Optimism, │ Scroll, │ zkPorter) │
│ Base) │ Linea) │ │
├────────────┴────────────┴───────────────┤
│ Ethereum L1 (Security Layer) │
└─────────────────────────────────────────┘
6.2 Optimistic vs ZK Rollup
| Feature | Optimistic Rollup | ZK Rollup |
|---|---|---|
| Verification | Fraud Proof | Validity Proof |
| Withdrawal Time | 7 days (challenge period) | Minutes to hours |
| EVM Compatibility | High | Growing (zkEVM) |
| Gas Cost | 10-50x cheaper than L1 | 50-100x cheaper than L1 |
| Representative Chains | Arbitrum, Optimism, Base | zkSync Era, Scroll, Linea |
| Maturity | High | Rapidly growing |
6.3 Major L2 Chains Detailed Comparison
TVL (Total Value Locked) Rankings (2025):
1. Arbitrum One - TVL: ~18B USD
- Nitro tech stack
- Full EVM compatibility
- Stylus (Rust/C++ support)
2. Base - TVL: ~12B USD
- Coinbase backed
- OP Stack based
- Strong consumer app presence
3. Optimism - TVL: ~8B USD
- OP Stack (Superchain vision)
- Bedrock upgrade
- RetroPGF (public goods funding)
4. zkSync Era - TVL: ~4B USD
- zkEVM (LLVM-based)
- Native Account Abstraction
- Hyperchain scaling
5. Scroll - TVL: ~2B USD
- Type-2 zkEVM
- Ethereum bytecode compatible
6.4 Developing on L2
// Deploy to Arbitrum (Hardhat)
// Add networks to hardhat.config.js
module.exports = {
networks: {
arbitrum: {
url: "https://arb1.arbitrum.io/rpc",
accounts: [process.env.PRIVATE_KEY],
chainId: 42161,
},
base: {
url: "https://mainnet.base.org",
accounts: [process.env.PRIVATE_KEY],
chainId: 8453,
},
optimism: {
url: "https://mainnet.optimism.io",
accounts: [process.env.PRIVATE_KEY],
chainId: 10,
},
},
};
// Deploy script
async function main() {
const Contract = await ethers.getContractFactory("MyContract");
const contract = await Contract.deploy();
await contract.waitForDeployment();
console.log("Deployed to:", await contract.getAddress());
}
7. Understanding DeFi Protocols
7.1 DeFi Core Primitives
DeFi Stack:
┌─────────────────────────────────────┐
│ Aggregator / Frontend │
│ (1inch, DefiLlama, Zapper) │
├─────────────────────────────────────┤
│ Application Layer │
│ (Aave, Uniswap, MakerDAO) │
├─────────────────────────────────────┤
│ Protocol Layer │
│ (AMM, Lending, Derivatives) │
├─────────────────────────────────────┤
│ Asset Layer │
│ (ERC-20, ERC-721, Wrapped) │
├─────────────────────────────────────┤
│ Settlement Layer │
│ (Ethereum, L2 Rollups) │
└─────────────────────────────────────┘
7.2 AMM (Automated Market Maker)
Uniswap V2's core formula: x * y = k
// Simple AMM implementation
contract SimpleAMM {
uint256 public reserveA;
uint256 public reserveB;
uint256 public totalLiquidity;
mapping(address => uint256) public liquidity;
// Add liquidity
function addLiquidity(uint256 amountA, uint256 amountB) external {
reserveA += amountA;
reserveB += amountB;
uint256 liq;
if (totalLiquidity == 0) {
liq = sqrt(amountA * amountB);
} else {
liq = min(
(amountA * totalLiquidity) / reserveA,
(amountB * totalLiquidity) / reserveB
);
}
liquidity[msg.sender] += liq;
totalLiquidity += liq;
}
// Swap: put A, get B
function swapAForB(uint256 amountAIn) external returns (uint256) {
// Calculate based on x * y = k
uint256 amountBOut = (amountAIn * reserveB) / (reserveA + amountAIn);
// Apply 0.3% fee
amountBOut = (amountBOut * 997) / 1000;
reserveA += amountAIn;
reserveB -= amountBOut;
return amountBOut;
}
function sqrt(uint256 x) internal pure returns (uint256) {
uint256 z = (x + 1) / 2;
uint256 y = x;
while (z < y) {
y = z;
z = (x / z + z) / 2;
}
return y;
}
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
}
7.3 Lending Protocol Concepts
Lending Protocol (e.g., Aave):
1. Supply (Deposit)
User -> Deposit assets -> Receive aTokens (auto-accruing interest)
2. Borrow
Deposit collateral -> Check Health Factor (HF) -> Borrow assets
HF = Collateral Value * LTV / Debt Value
3. Liquidation
HF below 1 -> Liquidator repays debt -> Buys collateral at discount
Collateral ratio examples:
- ETH: LTV 80%, Liquidation threshold 82.5%
- USDC: LTV 77%, Liquidation threshold 80%
8. Gas Optimization Techniques
8.1 Storage Optimization
// Bad: each variable uses a separate slot
contract BadPacking {
uint256 a; // slot 0 (32 bytes)
uint8 b; // slot 1 (1 byte, 31 wasted)
uint256 c; // slot 2
uint8 d; // slot 3
}
// Total: 4 slots
// Good: variable packing saves slots
contract GoodPacking {
uint256 a; // slot 0
uint256 c; // slot 1
uint8 b; // slot 2 (b and d share a slot)
uint8 d; // slot 2
}
// Total: 3 slots (saves 20000 gas from SSTORE)
8.2 Loop Optimization
// Bad
function sumBad(uint256[] memory arr) public pure returns (uint256) {
uint256 total = 0;
for (uint256 i = 0; i < arr.length; i++) {
total += arr[i];
}
return total;
}
// Good: cache length + unchecked increment
function sumGood(uint256[] memory arr) public pure returns (uint256) {
uint256 total = 0;
uint256 len = arr.length;
for (uint256 i = 0; i < len; ) {
total += arr[i];
unchecked { ++i; }
}
return total;
}
8.3 Additional Optimization Techniques
// 1. calldata vs memory (read-only parameters)
function process(uint256[] calldata data) external pure { /* ... */ }
// calldata: immutable, cheaper Gas
// memory: mutable, more expensive Gas
// 2. Custom Errors (Solidity 0.8.4+)
error Unauthorized(address caller);
error InsufficientBalance(uint256 available, uint256 required);
function withdraw(uint256 amount) external {
if (msg.sender != owner) revert Unauthorized(msg.sender);
if (balances[msg.sender] < amount)
revert InsufficientBalance(balances[msg.sender], amount);
}
// More Gas-efficient than require(string)
// 3. immutable and constant
uint256 public constant MAX_SUPPLY = 10000; // Compile-time constant
address public immutable deployer; // Set at deploy, then immutable
// Both embed directly in code instead of SLOAD = Gas savings
// 4. Bitwise operations
function isEven(uint256 n) pure returns (bool) {
return n & 1 == 0; // More efficient than n % 2 == 0
}
function multiplyByPowerOf2(uint256 n, uint256 pow) pure returns (uint256) {
return n << pow; // n * (2**pow)
}
9. Web3 Frontend Integration
9.1 ethers.js / viem
// ethers.js v6 basic usage
import { ethers, BrowserProvider, Contract } from "ethers";
// Wallet connection
async function connectWallet() {
if (!window.ethereum) {
throw new Error("MetaMask not installed");
}
const provider = new BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const address = await signer.getAddress();
const balance = await provider.getBalance(address);
console.log("Address:", address);
console.log("Balance:", ethers.formatEther(balance), "ETH");
return signer;
}
// Contract interaction
async function interactWithContract(signer) {
const contractAddress = "0x...";
const abi = [
"function getValue() view returns (uint256)",
"function setValue(uint256) external",
"event ValueChanged(address indexed, uint256)"
];
const contract = new Contract(contractAddress, abi, signer);
// Read
const value = await contract.getValue();
console.log("Current value:", value.toString());
// Write
const tx = await contract.setValue(42);
await tx.wait();
console.log("Transaction confirmed:", tx.hash);
}
9.2 wagmi + viem (React)
// wagmi v2 setup
import { createConfig, http } from "wagmi";
import { mainnet, arbitrum, base } from "wagmi/chains";
import { injected, walletConnect } from "wagmi/connectors";
const config = createConfig({
chains: [mainnet, arbitrum, base],
connectors: [
injected(),
walletConnect({ projectId: "YOUR_PROJECT_ID" }),
],
transports: {
[mainnet.id]: http(),
[arbitrum.id]: http(),
[base.id]: http(),
},
});
// React component
function WalletConnect() {
const { address, isConnected } = useAccount();
const { connect } = useConnect();
const { disconnect } = useDisconnect();
const { data: balance } = useBalance({ address });
if (isConnected) {
return (
<div>
<p>Connected: {address}</p>
<p>Balance: {balance?.formatted} {balance?.symbol}</p>
<button onClick={() => disconnect()}>Disconnect</button>
</div>
);
}
return (
<button onClick={() => connect({ connector: injected() })}>
Connect Wallet
</button>
);
}
10. Web3 Career Guide
10.1 Salary by Position (2025)
| Position | Experience | Salary Range (USD) |
|---|---|---|
| Junior Solidity Dev | 0-2 years | 80K-120K |
| Mid Solidity Dev | 2-4 years | 120K-180K |
| Senior Smart Contract Dev | 4+ years | 180K-250K |
| Smart Contract Auditor | 3+ years | 150K-300K+ |
| Protocol Engineer | 5+ years | 200K-350K+ |
| DeFi Researcher | 3+ years | 150K-250K |
| Web3 Frontend (React + ethers) | 2+ years | 100K-180K |
| Blockchain Infra Engineer | 3+ years | 140K-220K |
10.2 Essential Tech Stack
Web3 Developer Roadmap:
Foundation (3-6 months):
├── Solidity syntax and patterns
├── EVM understanding
├── Hardhat or Foundry
├── ethers.js / viem
└── OpenZeppelin library
Intermediate (6-12 months):
├── DeFi protocol understanding (AMM, Lending)
├── Security (common vulnerabilities + auditing)
├── Gas optimization
├── Layer 2 development
├── The Graph (indexing)
└── IPFS / Arweave (distributed storage)
Advanced (12+ months):
├── MEV (Maximal Extractable Value)
├── ZK Proof fundamentals
├── Cross-chain bridges
├── Account Abstraction (ERC-4337)
├── Protocol design
└── Formal Verification
10.3 Portfolio Strategy
The most important thing in the Web3 job market is on-chain evidence.
- Open-source GitHub projects: Include tests and documentation
- Testnet deployments: Deploy on Sepolia, Base Goerli, etc.
- Audit experience: Participate in Code4rena, Sherlock competitions
- Contributions: OSS contributions to OpenZeppelin, Uniswap, etc.
- Blog/Threads: Share technical analysis articles
11. Practice Quiz
Work through each problem and check your answers.
Q1. What pattern prevents reentrancy attacks?
Checks-Effects-Interactions (CEI) Pattern
Perform state checks (Checks) first, then state changes (Effects), and finally external calls (Interactions). Additionally, you can use OpenZeppelin's ReentrancyGuard (nonReentrant modifier).
Q2. What is the biggest difference between Optimistic and ZK Rollups?
The verification method.
- Optimistic Rollup: Optimistically assumes transactions are valid, with a 7-day challenge period where fraud proofs can be submitted.
- ZK Rollup: Generates mathematical validity proofs for every transaction batch and submits them to L1 immediately. Withdrawals are faster.
Q3. How much Gas does SSTORE (new value) cost on the EVM?
20,000 Gas (for writing a value to a new storage slot)
Updating an existing value costs 5,000 Gas. This is why storage optimization matters - use variable packing, constant/immutable declarations, etc. to reduce SSTORE calls.
Q4. What is Uniswap V2's core AMM formula?
x * y = k (Constant Product Formula)
x is the quantity of token A, y is the quantity of token B, and k is a constant. When you input one token, the other is released to maintain k. This formula naturally creates slippage and price impact.
Q5. What should you use instead of require(string) to save Gas in Solidity?
Custom Errors (Solidity 0.8.4+)
Declare with error Unauthorized(); and use with revert Unauthorized();. More Gas-efficient than require with string messages, and can include parameters for better debugging.
12. References
- Ethereum Official Docs - Ethereum development fundamentals
- Solidity Official Docs - Solidity language reference
- OpenZeppelin Contracts - Audited smart contract library
- Hardhat Official Docs - Hardhat development environment
- Foundry Book - Foundry development tools
- Ethereum EVM Illustrated - EVM visual guide
- SWC Registry - Smart contract weakness classification
- DeFiLlama - DeFi TVL and protocol data
- L2Beat - Layer 2 comparison and TVL tracking
- Chainlink Official Docs - Oracle solution
- EIP-4337: Account Abstraction - Account Abstraction standard
- Secureum - Smart contract security learning
- Code4rena - Smart contract audit competitions