Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# ── Claude Code (memoria local de la sesión) ────────────────────────────────
.claude/

# ── Foundry — artefactos de compilación ─────────────────────────────────────
**/cache/
**/out/

# ── Foundry — broadcasts de red local (Anvil, chainId 31337) ─────────────────
**/broadcast/*/31337/
**/broadcast/**/dry-run/

# ── Node.js / Next.js ────────────────────────────────────────────────────────
**/node_modules/
**/.next/
**/*.tsbuildinfo

# ── Logs ─────────────────────────────────────────────────────────────────────
*.log

# ── OS ───────────────────────────────────────────────────────────────────────
.DS_Store
Thumbs.db
6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[submodule "P01-eth-document-registry/contracts/lib/openzeppelin-contracts"]
path = P01-eth-document-registry/contracts/lib/openzeppelin-contracts
url = https://github.com/OpenZeppelin/openzeppelin-contracts
[submodule "P01-eth-document-registry/contracts/lib/forge-std"]
path = P01-eth-document-registry/contracts/lib/forge-std
url = https://github.com/foundry-rs/forge-std
118 changes: 118 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# CLAUDE.md — Contexto del Proyecto

Este archivo es leído automáticamente por Claude Code al inicio de cada sesión.

---

## Quién eres

Estás asistiendo a **MarkFrFn** con sus prácticas del curso **"Desarrollo de dApps con Ethereum"** de CODECRYPTO Academy.

---

## Estructura del repositorio

Monorepo en `d:/MAFL_Bibliotecas/Documentos/Proyectos/CodeCrypto-MarkFrFn/`

```
CodeCrypto-MarkFrFn/ ← raíz del monorepo (único .git aquí)
├── CLAUDE.md ← este archivo
├── README.md
├── docs/
│ └── LECCIONES_APRENDIDAS.md
├── P01-eth-document-registry/
│ ├── contracts/ ← Solidity + Foundry
│ ├── dapp/ ← Next.js 14 + TypeScript + ethers.js v6
│ ├── foundry.toml
│ └── start.sh
└── P02-dao/
├── sc/ ← Solidity + Foundry (MinimalForwarder + DAOVoting)
├── web/ ← Next.js 15 + TypeScript + ethers.js v6
├── foundry.toml
└── start.sh
```

**Convención de nombres:** `P##-descripcion-del-proyecto` (ej: `P01-eth-document-registry`)

---

## Decisiones técnicas tomadas

### Git / Monorepo
- Un único `.git` en la raíz — cada práctica es una carpeta, no un subrepositorio.
- Los proyectos se ejecutan de forma independiente desde su propia carpeta.
- Git se gestiona siempre desde la raíz del monorepo.

### Foundry (P01)
- El `foundry.toml` **raíz de P01** apunta rutas relativas a la carpeta `contracts/`:
```toml
src = "contracts/src"
out = "out"
libs = ["contracts/lib"]
test = "contracts/test"
script = "contracts/script"
remappings = ["openzeppelin-contracts/=contracts/lib/openzeppelin-contracts/"]
```
- OpenZeppelin instalado como submódulo en `contracts/lib/openzeppelin-contracts/`.
- El `remappings` fue ajustado al migrar al monorepo para que Foundry resuelva correctamente los imports desde la raíz de P01.

---

## P01 — ETH Document Registry

| Aspecto | Detalle |
|---------|---------|
| Objetivo | dApp para almacenar y verificar autenticidad de documentos en Ethereum mediante firmas ECDSA |
| Smart Contract | `DocumentRegistry.sol` — Solidity 0.8.20, OpenZeppelin ECDSA |
| Frontend | Next.js 14, TypeScript, ethers.js v6, Tailwind CSS |
| Red local | Anvil (Foundry) en `http://localhost:8545`, chain ID 31337 |
| Tests | 11/11 pasando — suite completa en `contracts/test/DocumentRegistry.t.sol` |

### Optimizaciones del contrato
- Existencia verificada via `signer != address(0)` — sin `bool exists` redundante.
- Modifiers `documentNotExists` / `documentExists` para guards reutilizables.
- Optimizer habilitado (`optimizer_runs = 200`). Ahorro estimado ~39% en gas.

### Arrancar P01
```bash
cd P01-eth-document-registry
bash start.sh # Anvil + deploy + Next.js en un solo comando
```

---

## Stack común del curso

- **Solidity** + **Foundry** (forge, anvil) para contratos
- **Next.js 14** + **TypeScript** + **ethers.js v6** para frontends
- **Tailwind CSS** para estilos
- **Node.js v18+** requerido

---

## Preferencias de trabajo

- Nombres de carpetas cortos: `P##-descripcion` en lugar de `practica-##-descripcion`.
- Cada práctica tiene su propio `README.md` y `start.sh`.
- Documentación adicional en subcarpeta `docs/` dentro de cada práctica.

---

## Documentación transversal

- [docs/LECCIONES_APRENDIDAS.md](./docs/LECCIONES_APRENDIDAS.md) — patrones y antipatrones acumulados de todos los proyectos. **Leer antes de iniciar un nuevo proyecto.**

---

## Progreso del curso

| # | Carpeta | Estado |
|---|---------|--------|
| P01 | `P01-eth-document-registry` | Completado |
| P02 | `P02-dao` | Completado |
| P03 | — | Pendiente |

**Al iniciar o completar cada práctica, actualizar:**
1. La tabla de progreso de este archivo (`CLAUDE.md`)
2. El `README.md` raíz — agregar fila en la tabla de prácticas
3. `docs/LECCIONES_APRENDIDAS.md` — añadir nuevos patrones aprendidos
43 changes: 43 additions & 0 deletions P01-eth-document-registry/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# ─── Foundry (raiz) ───────────────────────────────────────
cache/
out/

# Broadcasts de red local (31337 = Anvil); conservar testnet/mainnet
broadcast/*/31337/
broadcast/**/dry-run/

# ─── Contratos ────────────────────────────────────────────
contracts/cache/
contracts/out/

# ─── Frontend ─────────────────────────────────────────────
dapp/node_modules/
dapp/.next/
dapp/out/
dapp/dist/
dapp/build/

# ─── Logs y temporales ────────────────────────────────────
*.log
test_results.log

# ─── Entorno ──────────────────────────────────────────────
.env
.env.local.backup
*.env

# Mantener .env.local del dapp (solo contiene datos publicos de Anvil)
!dapp/.env.local

# ─── Sistema operativo ────────────────────────────────────
.DS_Store
Thumbs.db

# ─── Editor ───────────────────────────────────────────────
.vscode/
.idea/
*.swp
*.swo

# ─── Claude Code ──────────────────────────────────────────
.claude/
3 changes: 3 additions & 0 deletions P01-eth-document-registry/.gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "contracts/lib/openzeppelin-contracts"]
path = contracts/lib/openzeppelin-contracts
url = https://github.com/OpenZeppelin/openzeppelin-contracts
189 changes: 189 additions & 0 deletions P01-eth-document-registry/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
# ETH Document Registry

dApp para almacenar y verificar la autenticidad de documentos en blockchain Ethereum mediante firmas digitales ECDSA.

## Stack

| Capa | Tecnología |
|------|-----------|
| Smart Contract | Solidity 0.8.18, Foundry, OpenZeppelin ECDSA |
| Frontend | Next.js 14, TypeScript, ethers.js v6, Tailwind CSS |
| Red local | Anvil (Foundry) |

## Estructura del proyecto

```
eth-database-document/
├── start.sh # Script para arrancar todo el stack de desarrollo
├── contracts/ # Smart contracts (Foundry)
│ ├── src/
│ │ └── DocumentRegistry.sol
│ ├── test/
│ │ └── DocumentRegistry.t.sol
│ ├── script/
│ │ └── Deploy.s.sol
│ ├── foundry.toml
│ └── run_tests.sh
├── dapp/ # Frontend (Next.js)
│ ├── app/
│ │ ├── page.tsx # Página principal con tabs
│ │ ├── layout.tsx
│ │ └── providers.tsx
│ ├── components/
│ │ ├── FileUploader.tsx # Carga, hash y drag & drop
│ │ ├── DocumentSigner.tsx # Firma y almacenamiento
│ │ ├── DocumentVerifier.tsx
│ │ ├── DocumentHistory.tsx # Historial on-chain con búsqueda y exportar CSV
│ │ └── WalletSelector.tsx
│ ├── contexts/
│ │ └── MetaMaskContext.tsx # Wallets Anvil via JsonRpcProvider
│ ├── hooks/
│ │ ├── useContract.ts
│ │ ├── useFileHash.ts
│ │ └── useTheme.ts # Dark mode con persistencia en localStorage
│ └── utils/
│ ├── ethers.ts
│ └── hash.ts
└── docs/ # Documentación adicional
├── DEPLOYMENT_GUIDE.md
├── GUIA_DE_USO.md
├── QUICK_START.md
└── LECCIONES_APRENDIDAS.md
```

## Requisitos

- Node.js v18+
- [Foundry](https://book.getfoundry.sh/getting-started/installation)
- Git

```bash
# Verificar
node --version # v18+
forge --version # forge 0.2+
```

## Instalación

```bash
# 1. Clonar con submodulos (OpenZeppelin)
git clone --recurse-submodules <url-del-repo>
cd eth-database-document

# 2. Instalar dependencias del frontend
cd dapp && npm install && cd ..
```

## Uso

### Opción A — Script todo-en-uno (recomendado)

Un único comando arranca Anvil, despliega el contrato y lanza el frontend:

```bash
bash start.sh
```

El script:
1. Verifica que `forge`, `anvil` y `node`/`npm` estén disponibles
2. Inicia Anvil en `http://localhost:8545`
3. Despliega `DocumentRegistry` y actualiza `dapp/.env.local` con la dirección
4. Arranca el frontend en `http://localhost:3000`

Presiona `Ctrl+C` para detener todos los servicios.

### Opción B — Pasos manuales

```bash
# Terminal 1 — red local
anvil

# Terminal 2 — desplegar contrato
cd contracts
forge script script/Deploy.s.sol \
--rpc-url http://localhost:8545 \
--broadcast \
--private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80

# Copiar la dirección del contrato y actualizar dapp/.env.local:
# NEXT_PUBLIC_CONTRACT_ADDRESS=0x<dirección>

# Terminal 3 — frontend
cd dapp && npm run dev
```

Abrir [http://localhost:3000](http://localhost:3000)

## Flujo de uso

1. **Conectar wallet** — seleccionar una de las 10 wallets de Anvil
2. **Upload & Sign** — subir un archivo (click o drag & drop), firmar su hash keccak256
3. **Store** — almacenar hash + firma + timestamp en blockchain
4. **Verify** — subir el mismo archivo, ingresar la dirección del firmante; el frontend consulta `isDocumentStored()` + `getDocumentInfo()` y compara el firmante almacenado
5. **History** — consulta `getDocumentCount()` + `getDocumentHashByIndex(i)` + `getDocumentInfo(hash)` para listar todos los documentos directamente desde la blockchain (hash, firmante, timestamp, firma truncada), ordenados del más reciente al más antiguo

## Funcionalidades de UI

| Feature | Descripción |
|---------|-------------|
| Drag & Drop | Arrastrar archivos directamente al área de carga |
| Dark mode | Toggle sol/luna en el header; persiste en `localStorage`; anti-FOUC |
| Animaciones | Fade + slide al cambiar de tab y al mostrar resultados |
| Exportar CSV | Botón en History descarga todos los documentos (RFC 4180) |
| Búsqueda | Filtro en tiempo real por hash o dirección del firmante |
| Skeleton loader | Filas animadas mientras History carga desde blockchain |

## Tests del contrato

```bash
cd contracts

# Ejecutar los 11 tests con reporte detallado
bash run_tests.sh

# Comandos directos de forge
forge test -vv # tests con logs
forge coverage # cobertura de codigo
forge build # compilar
```

### Suite de tests (11/11)

| # | Test | Caso |
|---|------|------|
| 1 | `testStoreAndVerify` | Happy path completo |
| 2 | `testCannotStoreTwice` | Duplicado rechazado |
| 3 | `testVerifyWrongSigner` | Firmante incorrecto → false |
| 4 | `testDocumentCount_StartsAtZero` | Contador inicial en 0 |
| 5 | `testDocumentCount_AfterStore` | Contador incrementa |
| 6 | `testGetDocumentHashByIndex` | Iteración por índice |
| 7 | `testGetDocumentHashByIndex_OutOfBounds` | Índice inválido revierte |
| 8 | `testGetDocumentInfo_Reverts_IfNotStored` | Info doc inexistente revierte |
| 9 | `testIsDocumentStored_ReturnsFalse` | Doc inexistente → false |
| 10 | `testVerifyDocument_ReturnsFalse_IfNotStored` | Verificar inexistente → false |
| 11 | `testStoreMultipleDocuments` | Multi-wallet, conteo e iteración |

## Variables de entorno

`dapp/.env.local`:

```env
NEXT_PUBLIC_CONTRACT_ADDRESS=0x... # Actualizado automáticamente por start.sh
NEXT_PUBLIC_RPC_URL=http://localhost:8545
NEXT_PUBLIC_CHAIN_ID=31337
NEXT_PUBLIC_MNEMONIC="test test test test test test test test test test test junk"
```

> Las wallets de Anvil usan el mnemonic público estándar. No usar en mainnet.

## Optimizaciones del contrato

- Sin campo `bool exists` redundante — existencia verificada via `signer != address(0)`
- Sin mapping `hashExists` separado
- Modifiers `documentNotExists` / `documentExists` para guards reutilizables
- Optimizer habilitado (`optimizer_runs = 200`)
- Ahorro estimado: ~39% en gas de almacenamiento respecto al diseño naive

---

**Curso**: Desarrollo de dApps con Ethereum — CODECRYPTO
Loading