Split View: 블록체인 & Web3 개발자 가이드 2025
블록체인 & Web3 개발자 가이드 2025
- 도입
- 1. 블록체인 기초 원리
- 2. Ethereum과 EVM 깊이 이해하기
- 3. Solidity 스마트 컨트랙트 개발
- 4. 개발 도구: Hardhat vs Foundry
- 5. 스마트 컨트랙트 보안
- 6. Layer 2 솔루션 비교
- 7. DeFi 프로토콜 이해
- 8. Gas Optimization 기법
- 9. Web3 프론트엔드 통합
- 10. Web3 커리어 가이드
- 11. 실전 퀴즈
- 12. 참고 자료
도입
2025년 블록체인 생태계는 투기적 광풍의 시대를 지나 실질적인 기술 인프라로 자리 잡았습니다. Ethereum의 Dencun 업그레이드, Layer 2 롤업의 폭발적 성장, 그리고 RWA(Real World Assets) 토큰화가 주류 금융과 만나면서, Web3 개발자에 대한 수요는 사상 최고치를 기록하고 있습니다.
이 가이드에서는 블록체인의 핵심 원리부터 Solidity 스마트 컨트랙트 개발, 보안, DeFi/NFT 생태계, 그리고 Web3 개발자 커리어까지 체계적으로 다룹니다.
핵심 표현 정리
표현 의미 Smart Contract 블록체인 위에서 자동 실행되는 프로그램. 코드가 곧 계약 Layer 2 메인넷(L1) 위에 구축된 확장 솔루션. 처리량 증가, 비용 감소 DeFi 탈중앙화 금융. 중개자 없이 스마트 컨트랙트로 금융 서비스 제공 Gas Optimization 스마트 컨트랙트 실행 비용(Gas)을 최소화하는 최적화 기법
1. 블록체인 기초 원리
1.1 블록체인이란?
블록체인은 분산 네트워크의 모든 참여자가 동일한 거래 기록(원장)을 공유하는 기술입니다. 각 블록은 이전 블록의 해시를 포함하여 체인을 형성합니다.
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Block 0 │───▶│ Block 1 │───▶│ Block 2 │
│ (Genesis)│ │ │ │ │
│ Hash: 0a │ │ Prev: 0a │ │ Prev: 7f │
│ Nonce: 42│ │ Hash: 7f │ │ Hash: b3 │
│ Txs: [] │ │ Txs: [..] │ │ Txs: [..]│
└──────────┘ └──────────┘ └──────────┘
1.2 합의 메커니즘 비교
| 항목 | PoW (Proof of Work) | PoS (Proof of Stake) | DPoS |
|---|---|---|---|
| 에너지 소비 | 매우 높음 | 낮음 | 낮음 |
| 분산성 | 높음 | 중간 | 낮음 |
| TPS | 7-15 | 30-100K+ | 1000+ |
| 대표 체인 | Bitcoin | Ethereum 2.0 | EOS, Tron |
| 참여 조건 | 해시파워 (GPU/ASIC) | 스테이킹 자산 | 투표 위임 |
| 파이널리티 | 확률적 (6블록) | 에포크 기반 확정 | 빠른 확정 |
1.3 해시 함수와 머클 트리
블록체인의 무결성은 암호학적 해시 함수에 의존합니다.
import hashlib
def calculate_hash(block_data):
"""블록 데이터의 SHA-256 해시 계산"""
data_string = str(block_data).encode()
return hashlib.sha256(data_string).hexdigest()
# 머클 트리 구현
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 트랜잭션 생명주기
사용자 → 트랜잭션 생성 → 서명(개인키) → 브로드캐스트
→ Mempool 진입 → 검증자/채굴자 선택 → 블록 포함
→ 합의 → 블록 확정 → 상태 업데이트
2. Ethereum과 EVM 깊이 이해하기
2.1 Ethereum 아키텍처
Ethereum은 월드 컴퓨터라는 비전으로 시작되었습니다. 비트코인이 거래 기록만 저장하는 반면, Ethereum은 Turing-complete 가상 머신(EVM)을 통해 임의의 로직을 실행할 수 있습니다.
┌─────────────────────────────────────────┐
│ Ethereum Network │
├─────────────────────────────────────────┤
│ ┌─────────┐ ┌──────────┐ ┌────────┐ │
│ │ EOA │ │ Contract │ │Contract│ │
│ │ Account │──│ Account │──│Account │ │
│ │ (사용자)│ │ (코드) │ │(코드) │ │
│ └─────────┘ └──────────┘ └────────┘ │
├─────────────────────────────────────────┤
│ EVM (실행 엔진) │
├─────────────────────────────────────────┤
│ State Trie (상태 저장) │
└─────────────────────────────────────────┘
2.2 EVM 동작 원리
EVM은 스택 기반 가상 머신으로, 256비트 워드 크기를 사용합니다.
EVM 메모리 모델:
┌─────────────┐
│ Stack │ ← 최대 1024 깊이, LIFO
│ (256-bit) │
├─────────────┤
│ Memory │ ← 바이트 배열, 휘발성
│ (확장 가능)│
├─────────────┤
│ Storage │ ← key-value, 영구 저장
│ (256→256) │
└─────────────┘
주요 Opcode 예시:
PUSH1 0x60 // 스택에 0x60 push
PUSH1 0x40 // 스택에 0x40 push
MSTORE // Memory[0x40] = 0x60
CALLVALUE // msg.value를 스택에
DUP1 // 스택 top 복제
ISZERO // 0이면 1, 아니면 0
2.3 Gas 시스템
모든 EVM 연산에는 Gas 비용이 부과됩니다.
| 연산 | Gas 비용 | 설명 |
|---|---|---|
| ADD / SUB | 3 | 산술 연산 |
| MUL / DIV | 5 | 곱셈 / 나눗셈 |
| SLOAD | 2100 (cold) / 100 (warm) | Storage 읽기 |
| SSTORE | 20000 (new) / 5000 (update) | Storage 쓰기 |
| CALL | 2600 (cold) / 100 (warm) | 외부 호출 |
| CREATE | 32000 | 컨트랙트 생성 |
Gas 비용 계산 공식:
총 비용 = Gas Used * Gas Price (Gwei)
= Gas Used * (Base Fee + Priority Fee)
예시: 21000 Gas * 30 Gwei = 630,000 Gwei = 0.00063 ETH
3. Solidity 스마트 컨트랙트 개발
3.1 기본 문법
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @title SimpleStorage
* @dev 기본적인 상태 저장 컨트랙트
*/
contract SimpleStorage {
// 상태 변수
uint256 private storedValue;
address public owner;
mapping(address => uint256) public balances;
// 이벤트
event ValueChanged(address indexed setter, uint256 newValue);
// 수정자
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 토큰 구현
// 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 구현
// 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 고급 패턴: 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 (업그레이드 후)
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. 개발 도구: Hardhat vs Foundry
4.1 Hardhat 설정 및 사용
# 프로젝트 초기화
mkdir my-project && cd my-project
npm init -y
npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox
# Hardhat 프로젝트 생성
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 테스트 예시:
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 설정 및 사용
# Foundry 설치
curl -L https://foundry.paradigm.xyz | bash
foundryup
# 프로젝트 생성
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 테스트 (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); // 이 호출은 revert 됨
}
// Fuzz 테스트
function testFuzzSetValue(uint256 value) public {
store.setValue(value);
assertEq(store.getValue(), value);
}
}
4.3 Hardhat vs Foundry 비교
| 기능 | Hardhat | Foundry |
|---|---|---|
| 언어 | JavaScript/TypeScript | Solidity |
| 테스트 속도 | 보통 | 매우 빠름 (10x+) |
| Fuzz 테스트 | 플러그인 필요 | 내장 |
| 디버깅 | console.log | forge debug (step) |
| 포크 테스트 | 지원 | 지원 (더 빠름) |
| 플러그인 생태계 | 풍부 | 성장 중 |
| Gas 리포트 | hardhat-gas-reporter | forge test --gas-report |
| 학습 곡선 | JS 개발자 친화적 | Solidity 집중 |
5. 스마트 컨트랙트 보안
5.1 Reentrancy 공격
가장 유명한 스마트 컨트랙트 취약점으로, 2016년 The DAO 해킹(6000만 달러 피해)의 원인입니다.
// 취약한 코드
contract VulnerableVault {
mapping(address => uint256) public balances;
function deposit() external payable {
balances[msg.sender] += msg.value;
}
// 위험: 외부 호출 후 상태 업데이트
function withdraw() external {
uint256 amount = balances[msg.sender];
require(amount > 0, "No balance");
// 외부 호출 (공격 벡터)
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
// 상태 업데이트가 외부 호출 뒤에!
balances[msg.sender] = 0;
}
}
// 공격자 컨트랙트
contract Attacker {
VulnerableVault public vault;
constructor(address _vault) {
vault = VulnerableVault(_vault);
}
function attack() external payable {
vault.deposit{value: msg.value}();
vault.withdraw();
}
// withdraw()의 call이 여기로 돌아옴 -> 다시 withdraw() 호출
receive() external payable {
if (address(vault).balance > 0) {
vault.withdraw();
}
}
}
해결 방법: Checks-Effects-Interactions 패턴
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 (상태 업데이트 먼저)
balances[msg.sender] = 0;
// Interactions (외부 호출 나중에)
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
}
}
5.2 Integer Overflow/Underflow
Solidity 0.8.0 이전에는 자동 오버플로우 체크가 없었습니다.
// Solidity 0.7.x 이전: 위험!
// uint8 max = 255; max + 1 = 0 (overflow)
// uint8 min = 0; min - 1 = 255 (underflow)
// Solidity 0.8.0+: 자동으로 revert
// 성능이 필요한 경우 unchecked 블록 사용
function unsafeIncrement(uint256 x) pure returns (uint256) {
unchecked {
return x + 1; // overflow 체크 안 함 (Gas 절약)
}
}
5.3 보안 체크리스트
보안 감사 체크리스트:
[1] Reentrancy
- CEI 패턴 적용 확인
- ReentrancyGuard 사용 여부
[2] 접근 제어
- onlyOwner / Role 기반 접근 제어
- 초기화 함수 보호
[3] 산술 연산
- Solidity 0.8+ 사용 (자동 체크)
- unchecked 블록 검토
[4] 외부 호출
- call 반환값 체크
- 가스 제한 설정
[5] 프론트러닝
- commit-reveal 패턴
- Flashbots 사용 고려
[6] 오라클 조작
- TWAP (시간 가중 평균 가격) 사용
- 다중 오라클 소스
[7] 플래시론 공격
- 단일 트랜잭션 조작 방어
- 가격 검증 로직
[8] 스토리지 충돌 (Proxy)
- EIP-1967 스토리지 슬롯
- 업그레이드 테스트
5.4 주요 해킹 사례
| 사건 | 연도 | 피해액 | 취약점 |
|---|---|---|---|
| The DAO | 2016 | 6000만 달러 | Reentrancy |
| Ronin Bridge | 2022 | 6.25억 달러 | 개인키 탈취 |
| Wormhole | 2022 | 3.2억 달러 | 서명 검증 우회 |
| Euler Finance | 2023 | 1.97억 달러 | Donate 함수 취약점 |
| Mango Markets | 2022 | 1.14억 달러 | 오라클 조작 |
6. Layer 2 솔루션 비교
6.1 Layer 2란?
Layer 2(L2)는 Ethereum 메인넷(L1)의 보안을 상속하면서 처리량을 확장하는 솔루션입니다.
┌─────────────────────────────────────────┐
│ 사용자 (Dapp) │
├────────────┬────────────┬───────────────┤
│ Optimistic │ ZK Rollup │ Validium │
│ Rollup │ │ │
│ (Arbitrum, │ (zkSync, │ (StarkEx, │
│ Optimism, │ Scroll, │ zkPorter) │
│ Base) │ Linea) │ │
├────────────┴────────────┴───────────────┤
│ Ethereum L1 (보안 레이어) │
└─────────────────────────────────────────┘
6.2 Optimistic vs ZK Rollup
| 특성 | Optimistic Rollup | ZK Rollup |
|---|---|---|
| 검증 방식 | 사기 증명 (Fraud Proof) | 유효성 증명 (Validity Proof) |
| 인출 시간 | 7일 (챌린지 기간) | 수분~수시간 |
| EVM 호환 | 높음 | 성장 중 (zkEVM) |
| 가스 비용 | L1 대비 10-50x 저렴 | L1 대비 50-100x 저렴 |
| 대표 체인 | Arbitrum, Optimism, Base | zkSync Era, Scroll, Linea |
| 성숙도 | 높음 | 빠르게 성장 |
6.3 주요 L2 체인 상세 비교
TVL (Total Value Locked) 순위 (2025년 기준):
1. Arbitrum One - TVL: 약 180억 달러
- Nitro 기술 스택
- EVM 완전 호환
- Stylus (Rust/C++ 지원)
2. Base - TVL: 약 120억 달러
- Coinbase 지원
- OP Stack 기반
- 소비자 앱 강세
3. Optimism - TVL: 약 80억 달러
- OP Stack (Superchain 비전)
- Bedrock 업그레이드
- RetroPGF (공공재 펀딩)
4. zkSync Era - TVL: 약 40억 달러
- zkEVM (LLVM 기반)
- 네이티브 Account Abstraction
- Hyperchain 확장
5. Scroll - TVL: 약 20억 달러
- Type-2 zkEVM
- 이더리움 바이트코드 호환
6.4 L2에서의 개발
// Arbitrum에 배포하기 (Hardhat)
// 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,
},
},
};
// 배포 스크립트
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. DeFi 프로토콜 이해
7.1 DeFi 핵심 프리미티브
DeFi 스택:
┌─────────────────────────────────────┐
│ 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의 핵심 공식: x * y = k
// 간단한 AMM 구현
contract SimpleAMM {
uint256 public reserveA;
uint256 public reserveB;
uint256 public totalLiquidity;
mapping(address => uint256) public 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;
}
// 스왑: A를 넣고 B를 받기
function swapAForB(uint256 amountAIn) external returns (uint256) {
// x * y = k 기반 계산
uint256 amountBOut = (amountAIn * reserveB) / (reserveA + amountAIn);
// 0.3% 수수료 적용
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 개념
Lending Protocol (예: Aave):
1. 예치 (Supply)
사용자 → 자산 예치 → aToken 수령 (이자 자동 누적)
2. 대출 (Borrow)
담보 예치 → 건전성 비율(HF) 확인 → 자산 대출
HF = 담보가치 * LTV / 대출가치
3. 청산 (Liquidation)
HF가 1 미만 → 청산자가 부채 상환 → 담보 할인 매입
담보비율 예시:
- ETH: LTV 80%, 청산 임계값 82.5%
- USDC: LTV 77%, 청산 임계값 80%
8. Gas Optimization 기법
8.1 Storage 최적화
// Bad: 각 변수가 별도 슬롯 사용
contract BadPacking {
uint256 a; // slot 0 (32 bytes)
uint8 b; // slot 1 (1 byte, 나머지 31 낭비)
uint256 c; // slot 2
uint8 d; // slot 3
}
// 총 4 슬롯 사용
// Good: 변수 패킹으로 슬롯 절약
contract GoodPacking {
uint256 a; // slot 0
uint256 c; // slot 1
uint8 b; // slot 2 (b와 d가 같은 슬롯)
uint8 d; // slot 2
}
// 총 3 슬롯 사용 (SSTORE 20000 gas 절약)
8.2 루프 최적화
// 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: 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 기타 최적화 기법
// 1. calldata vs memory (읽기 전용 매개변수)
function process(uint256[] calldata data) external pure { /* ... */ }
// calldata: 수정 불가, Gas 저렴
// memory: 수정 가능, 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);
}
// require(string)보다 Gas 효율적
// 3. immutable과 constant
uint256 public constant MAX_SUPPLY = 10000; // 컴파일 타임 상수
address public immutable deployer; // 배포 시 설정, 이후 불변
// 둘 다 SLOAD 대신 코드에 직접 임베드 = Gas 절약
// 4. 비트 연산 활용
function isEven(uint256 n) pure returns (bool) {
return n & 1 == 0; // n % 2 == 0 보다 효율적
}
function multiplyByPowerOf2(uint256 n, uint256 pow) pure returns (uint256) {
return n << pow; // n * (2**pow)
}
9. Web3 프론트엔드 통합
9.1 ethers.js / viem
// ethers.js v6 기본 사용
import { ethers, BrowserProvider, Contract } from "ethers";
// 지갑 연결
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;
}
// 컨트랙트 인터랙션
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);
// 읽기
const value = await contract.getValue();
console.log("Current value:", value.toString());
// 쓰기
const tx = await contract.setValue(42);
await tx.wait();
console.log("Transaction confirmed:", tx.hash);
}
9.2 wagmi + viem (React)
// wagmi v2 설정
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 컴포넌트
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 커리어 가이드
10.1 포지션별 연봉 (2025년 기준)
| 포지션 | 경력 | 연봉 범위 (USD) |
|---|---|---|
| Junior Solidity Dev | 0-2년 | 80K-120K |
| Mid Solidity Dev | 2-4년 | 120K-180K |
| Senior Smart Contract Dev | 4년+ | 180K-250K |
| Smart Contract Auditor | 3년+ | 150K-300K+ |
| Protocol Engineer | 5년+ | 200K-350K+ |
| DeFi Researcher | 3년+ | 150K-250K |
| Web3 Frontend (React + ethers) | 2년+ | 100K-180K |
| Blockchain Infra Engineer | 3년+ | 140K-220K |
10.2 필수 기술 스택
Web3 개발자 로드맵:
기초 (3-6개월):
├── Solidity 문법 및 패턴
├── EVM 이해
├── Hardhat 또는 Foundry
├── ethers.js / viem
└── OpenZeppelin 라이브러리
중급 (6-12개월):
├── DeFi 프로토콜 이해 (AMM, Lending)
├── Security (일반 취약점 + 감사)
├── 가스 최적화
├── Layer 2 개발
├── The Graph (인덱싱)
└── IPFS / Arweave (분산 스토리지)
고급 (12개월+):
├── MEV (Maximal Extractable Value)
├── ZK Proof 기초
├── Cross-chain 브릿지
├── Account Abstraction (ERC-4337)
├── 프로토콜 설계
└── 정형 검증 (Formal Verification)
10.3 포트폴리오 전략
Web3 채용 시장에서 가장 중요한 것은 온체인 증거입니다.
- GitHub에 프로젝트 공개: 테스트, 문서화 포함
- 테스트넷 배포: Sepolia, Base Goerli 등에 실제 배포
- 감사 경험: Code4rena, Sherlock 등의 감사 경진대회 참여
- 기여 활동: OpenZeppelin, Uniswap 등 OSS 기여
- 블로그/스레드: 기술 분석 글 공유
11. 실전 퀴즈
각 문제를 풀고 답을 확인해 보세요.
Q1. Reentrancy 공격을 방지하기 위한 패턴 이름은?
Checks-Effects-Interactions (CEI) 패턴
상태 확인(Checks) 후 상태 변경(Effects)을 먼저 수행하고, 마지막에 외부 호출(Interactions)을 합니다. 추가로 OpenZeppelin의 ReentrancyGuard(nonReentrant modifier)를 사용할 수 있습니다.
Q2. Optimistic Rollup과 ZK Rollup의 가장 큰 차이점은?
검증 방식의 차이입니다.
- Optimistic Rollup: 트랜잭션이 유효하다고 낙관적으로 가정하고, 7일간의 챌린지 기간 동안 사기 증명(Fraud Proof)으로 이의를 제기할 수 있습니다.
- ZK Rollup: 모든 트랜잭션 배치에 대해 수학적 유효성 증명(Validity Proof)을 즉시 생성하여 L1에 제출합니다. 인출이 더 빠릅니다.
Q3. EVM에서 SSTORE (새 값 저장)의 가스 비용은 얼마인가?
20,000 Gas (새로운 스토리지 슬롯에 값을 기록하는 경우)
기존 값을 업데이트하는 경우는 5,000 Gas입니다. 이것이 Storage 최적화가 중요한 이유이며, 변수 패킹, constant/immutable 사용 등으로 SSTORE 호출을 줄여야 합니다.
Q4. Uniswap V2의 AMM 핵심 공식은?
x * y = k (Constant Product Formula)
x는 토큰 A의 수량, y는 토큰 B의 수량, k는 상수입니다. 한 토큰을 넣으면 k를 유지하도록 다른 토큰이 빠져나옵니다. 이 공식은 슬리피지와 가격 영향을 자연스럽게 만듭니다.
Q5. Solidity에서 Gas를 절약하기 위해 require(string) 대신 사용하는 것은?
Custom Errors (Solidity 0.8.4+)
error Unauthorized();로 선언하고 revert Unauthorized();로 사용합니다. require에 문자열 메시지를 넣는 것보다 Gas 효율적이며, 매개변수도 포함할 수 있어 디버깅에도 유용합니다.
12. 참고 자료
- Ethereum 공식 문서 - Ethereum 개발 기초
- Solidity 공식 문서 - Solidity 언어 레퍼런스
- OpenZeppelin Contracts - 검증된 스마트 컨트랙트 라이브러리
- Hardhat 공식 문서 - Hardhat 개발 환경
- Foundry Book - Foundry 개발 도구
- Ethereum EVM Illustrated - EVM 시각 자료
- SWC Registry - 스마트 컨트랙트 취약점 분류
- DeFiLlama - DeFi TVL 및 프로토콜 데이터
- L2Beat - Layer 2 비교 및 TVL 트래킹
- Chainlink 공식 문서 - 오라클 솔루션
- EIP-4337: Account Abstraction - Account Abstraction 표준
- Secureum - 스마트 컨트랙트 보안 학습
- Code4rena - 스마트 컨트랙트 감사 경진대회
Blockchain & Web3 Developer Guide 2025
- 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