+ )
+ }
+
+ return null
+}
diff --git a/week-04/dev/my-dapp/src/counter.ts b/week-04/dev/my-dapp/src/counter.ts
new file mode 100644
index 0000000..8c56d1f
--- /dev/null
+++ b/week-04/dev/my-dapp/src/counter.ts
@@ -0,0 +1,39 @@
+export const counterAbi = [
+ {
+ type: 'function',
+ name: 'count',
+ inputs: [],
+ outputs: [{ name: '', type: 'uint256' }],
+ stateMutability: 'view',
+ },
+ {
+ type: 'function',
+ name: 'getCount',
+ inputs: [],
+ outputs: [{ name: '', type: 'uint256' }],
+ stateMutability: 'view',
+ },
+ {
+ type: 'function',
+ name: 'increment',
+ inputs: [],
+ outputs: [],
+ stateMutability: 'nonpayable',
+ },
+ {
+ type: 'function',
+ name: 'decrement',
+ inputs: [],
+ outputs: [],
+ stateMutability: 'nonpayable',
+ },
+ {
+ type: 'function',
+ name: 'reset',
+ inputs: [],
+ outputs: [],
+ stateMutability: 'nonpayable',
+ },
+] as const
+
+export const counterAddress = import.meta.env.VITE_COUNTER_ADDRESS as `0x${string}`
diff --git a/week-04/dev/my-dapp/src/main.tsx b/week-04/dev/my-dapp/src/main.tsx
new file mode 100644
index 0000000..43528b2
--- /dev/null
+++ b/week-04/dev/my-dapp/src/main.tsx
@@ -0,0 +1,10 @@
+import { StrictMode } from 'react'
+import { createRoot } from 'react-dom/client'
+import App from './App'
+import './App.css'
+
+createRoot(document.getElementById('root')!).render(
+
+
+ ,
+)
diff --git a/week-04/dev/my-dapp/src/vite-env.d.ts b/week-04/dev/my-dapp/src/vite-env.d.ts
new file mode 100644
index 0000000..c6c1093
--- /dev/null
+++ b/week-04/dev/my-dapp/src/vite-env.d.ts
@@ -0,0 +1,10 @@
+///
+
+interface ImportMetaEnv {
+ readonly VITE_COUNTER_ADDRESS: string
+ readonly VITE_WALLETCONNECT_PROJECT_ID: string
+}
+
+interface ImportMeta {
+ readonly env: ImportMetaEnv
+}
diff --git a/week-04/dev/my-dapp/src/wagmi.ts b/week-04/dev/my-dapp/src/wagmi.ts
new file mode 100644
index 0000000..cd783b1
--- /dev/null
+++ b/week-04/dev/my-dapp/src/wagmi.ts
@@ -0,0 +1,13 @@
+import { getDefaultConfig } from '@rainbow-me/rainbowkit'
+import { http } from 'wagmi'
+import { foundry, sepolia } from 'wagmi/chains'
+
+export const config = getDefaultConfig({
+ appName: 'Counter DApp',
+ projectId: import.meta.env.VITE_WALLETCONNECT_PROJECT_ID,
+ chains: [foundry, sepolia],
+ transports: {
+ [foundry.id]: http(),
+ [sepolia.id]: http(),
+ },
+})
diff --git a/week-04/dev/my-dapp/tsconfig.json b/week-04/dev/my-dapp/tsconfig.json
new file mode 100644
index 0000000..02bc281
--- /dev/null
+++ b/week-04/dev/my-dapp/tsconfig.json
@@ -0,0 +1,23 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
+ "target": "ES2020",
+ "useDefineForClassFields": true,
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "isolatedModules": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+ "jsx": "react-jsx",
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedSideEffectImports": true
+ },
+ "include": ["src"],
+ "references": [{ "path": "./tsconfig.node.json" }]
+}
diff --git a/week-04/dev/my-dapp/tsconfig.node.json b/week-04/dev/my-dapp/tsconfig.node.json
new file mode 100644
index 0000000..be1e141
--- /dev/null
+++ b/week-04/dev/my-dapp/tsconfig.node.json
@@ -0,0 +1,23 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
+ "target": "ES2022",
+ "lib": ["ES2023"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "composite": true,
+ "isolatedModules": true,
+ "moduleDetection": "force",
+ "noEmit": false,
+ "declaration": true,
+ "declarationMap": true,
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedSideEffectImports": true
+ },
+ "include": ["vite.config.ts"]
+}
diff --git a/week-04/dev/my-dapp/vite.config.ts b/week-04/dev/my-dapp/vite.config.ts
new file mode 100644
index 0000000..9ffcc67
--- /dev/null
+++ b/week-04/dev/my-dapp/vite.config.ts
@@ -0,0 +1,6 @@
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
+
+export default defineConfig({
+ plugins: [react()],
+})
diff --git a/week-04/dev/script/Counter.s.sol b/week-04/dev/script/Counter.s.sol
new file mode 100644
index 0000000..8bb8fd4
--- /dev/null
+++ b/week-04/dev/script/Counter.s.sol
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.26;
+
+import "forge-std/Script.sol";
+import "../src/Counter.sol";
+
+contract CounterScript is Script {
+ function run() external {
+ vm.broadcast();
+ Counter counter = new Counter();
+ console.log("Counter deployed at:", address(counter));
+ }
+}
diff --git a/week-04/dev/src/Counter.sol b/week-04/dev/src/Counter.sol
new file mode 100644
index 0000000..f7cde30
--- /dev/null
+++ b/week-04/dev/src/Counter.sol
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.26;
+
+contract Counter {
+ uint256 public count;
+
+ function getCount() public view returns (uint256) {
+ return count;
+ }
+
+ function increment() public {
+ count += 1;
+ }
+
+ function decrement() public {
+ require(count > 0, "Count cannot go below zero");
+ count -= 1;
+ }
+
+ function reset() public {
+ count = 0;
+ }
+}
diff --git a/week-04/quiz/quiz-04-solution.md b/week-04/quiz/quiz-04-solution.md
new file mode 100644
index 0000000..55363f7
--- /dev/null
+++ b/week-04/quiz/quiz-04-solution.md
@@ -0,0 +1,392 @@
+# Week 4 Quiz: Network/Block + wagmi
+
+> **제출 방법:** 이 파일을 복사하여 답변을 작성한 후, PR로 제출하세요.
+> **평가 기준:** 개념 이해도 중심 - 문법 오류보다 논리적 설명을 중시합니다.
+
+---
+
+## 문제 1: 블록 헤더 필드 (객관식)
+
+다음 상황을 고려하세요:
+
+```
+블록 100의 해시: 0xabc123...
+블록 101의 해시: 0xdef456...
+```
+
+블록 101의 `parentHash` 필드에는 어떤 값이 저장되어 있나요? 그리고 **왜** 이런 방식으로 연결하나요?
+
+**보기:**
+A) 0xdef456... - 자기 자신의 해시를 저장하여 무결성을 보장한다
+B) 0xabc123... - 이전 블록의 해시를 저장하여 체인 연결과 불변성을 보장한다
+C) 블록 번호 100 - 숫자로 순서를 추적한다
+D) 빈 값 - 헤더에는 해시가 저장되지 않는다
+
+**답변:**
+
+B, 다음 블록은 부모 블록의 해시를 가지고 있어야한다. 말그대로 체인이기때문이다. A 값이 바뀌면 B 값도 바뀌어야한다.
+
+A, 역설이다. 해시는 블록이 생성된 이후에 계산된다. 생성되기도 전에 해시를 알 수 없다.
+C, 해시는 완전 무결한 값이기에 블록 번호로 하면 안된다. 뭐가 바뀌었는지 모른다.
+D, 헤더에 아무것도 안하면 블록체인이 아니다.
+
+---
+
+## 문제 2: MPT 목적 (객관식)
+
+이더리움에서 Merkle Patricia Trie(MPT)를 사용하는 **가장 중요한 이유**는 무엇인가요?
+
+**보기:**
+A) 데이터를 암호화하여 외부에서 읽을 수 없게 한다
+B) 트랜잭션 처리 속도를 10배 이상 높인다
+C) 전체 데이터 없이도 특정 데이터의 존재와 정확성을 효율적으로 증명한다
+D) 블록 크기를 줄여서 저장 공간을 절약한다
+
+**답변:**
+
+C, 머클 증명을 통해 데이터 처리에 한계가 있는 디바이스가 쉽게 데이터 확인을 할 수 있다.
+
+---
+
+## 문제 3: 체인 연결과 보안 (객관식)
+
+공격자가 블록 50의 트랜잭션을 수정하려고 합니다. 현재 체인의 최신 블록은 100입니다. 이 공격이 **왜** 어려운가요?
+
+**보기:**
+A) 블록 50은 너무 오래되어서 시스템에서 접근할 수 없다
+B) 블록 50을 수정하면 해시가 바뀌고, 블록 51부터 100까지 모든 블록의 parentHash가 불일치하게 된다
+C) 블록 50은 이미 암호화되어 있어서 복호화 키가 필요하다
+D) 네트워크 관리자만 과거 블록을 수정할 수 있다
+
+**답변:**
+
+블록 100 부터 50까지 전부 바꿔야하므로 매우 어려운 작업이다. 근데 101, 102 도 계속 생성되니 바꾸기가 비현실적인것.
+
+---
+
+## 문제 4: MPT 진화 과정 (단답형)
+
+MPT(Merkle Patricia Trie)는 세 가지 자료구조의 장점을 결합한 것입니다:
+1. **Trie** -> 2. **Patricia Trie** -> 3. **Merkle Patricia Trie**
+
+**왜** 각 단계의 발전이 필요했나요? 각 단계가 해결하는 문제를 간단히 설명하세요.
+
+**답변:**
+
+1. Trie가 해결하는 문제:
+- 사전구조와 비슷하므로 데이터 길이만 길어질뿐 찾는 속도는 빠르다. 하지만 데이터가 길어지면 메모리 낭비가 심하다.
+2. Patricia Trie가 해결하는 문제 (Trie의 한계):
+- 자식 노드가 하나뿐인 노드를 합쳐버렸다. 저장공간을 줄였다. 하지만 수정이 되었는지 증명하는 방법이 부족했다.
+3. Merkle Patricia Trie가 해결하는 문제 (Patricia Trie의 한계):
+- 머클 증명을 통해 트리 전체를 대표하는 머클 해시를 만들어 굳이 전체 탐색을 안해도 증명할 수 있게 되었다.
+
+
+
+---
+
+## 문제 5: Eclipse Attack 방어 (단답형)
+
+Eclipse Attack은 공격자가 피해자 노드의 **모든 피어 연결**을 자신이 통제하는 노드로 바꾸는 공격입니다.
+
+1) 이 공격이 성공하면 피해자에게 **어떤 피해**가 발생할 수 있나요?
+2) 개인 노드 운영자가 이 공격을 **방어**하기 위해 할 수 있는 행동은 무엇인가요?
+
+**답변:**
+1) 가능한 피해 (2가지 이상):
+- 이중지불
+- 채굴 낭비
+- 담합
+
+2) 방어 방법 (2가지 이상):
+- 신뢰할만한 노드 추가
+- 피어 교체
+- ip 다양성 확보
+
+
+---
+
+## 문제 6: 노드 종류 선택 (단답형)
+
+친구가 이더리움 개발을 시작하려고 합니다. 다음 세 가지 상황에서 각각 어떤 노드 타입(Full, Light, Archive)을 추천하시겠습니까? **왜** 그 노드를 추천하는지도 설명하세요.
+
+1) 모바일 지갑 앱 개발
+2) 블록체인 데이터 분석 서비스 개발
+3) 일반적인 dApp 백엔드 개발
+
+**답변:**
+1) 모바일 지갑 앱:
+ 추천 노드: Light Node
+ 이유: 모바일 환경은 제한된 저장공간을 가지고있다. Light Node는 모든 블록 데이터를 받지 않고 블록 헤더 구조만 다운로드하여 용량을 획기적으로 줄이면서도, 머클 증명을 통해 트랜잭션의 유효성을 검증할 수 있기 때문에 모바일 환경에 적합하다.
+
+2) 블록체인 데이터 분석:
+ 추천 노드: Archive Node
+ 이유: Archive Node는 블록체인의 모든 기록과 과거의 상태(State)를 빠짐없이 가지고 있어 데이터 조회 요구사항을 충족할 수 있다.
+
+3) dApp 백엔드:
+ 추천 노드: Full Node
+ 이유: 일반적인 dApp 운영에서는 새 트랜잭션을 검증 및 전파하고, 최신 블록체인 상태 데이터를 읽어오는 것이 가장 중요하다. Full Node는 이러한 독자적인 데이터 검증 및 상태 관리 등을 자체적으로 처리할 수 있으면서도, Archive Node에 비해 스토리지 용량 유지 및 운영 비용 면에서 훨씬 합리적이다.
+
+---
+
+## 문제 7: useAccount Hook (빈칸 채우기)
+
+다음 코드의 빈칸을 채워서 지갑 연결 상태를 표시하는 컴포넌트를 완성하세요:
+
+```typescript
+import { _________________ } from 'wagmi';
+
+function WalletStatus() {
+ // TODO: useAccount hook에서 필요한 값들을 가져오세요
+ const { _________________, _________________ } = useAccount();
+
+ if (!isConnected) {
+ return