Álbum de figuritas del FIFA World Cup 2026™ construido sobre Nostr + Lightning. Sin base de datos — todo el estado vive en los relays. Desplegado en figusmundial-rho.vercel.app.
- Sobres — comprá paquetes de 7 figuritas al azar por 21 sats vía Lightning (NIP-57). El primer sobre es gratis.
- Colección — visualizá tu álbum con animaciones foil/shiny, filtrá por equipo y rareza.
- Mercado P2P — listá y comprá figuritas repetidas directamente entre usuarios, liquidado por zap.
- Fixture — calendario completo del Mundial 2026: fase de grupos (MD1–MD3) y eliminatorias (Ronda de 32 → Gran Final) con horarios en UTC y hora Argentina.
- Pronósticos — predecí resultados de cada partido; los pronós se publican como eventos Nostr (kind 30302) y se actualizan automáticamente.
- Penales 3D — minijuego de penales con cancha WebGL. Si convertís, ganás un sobre gratis.
- Penales P2P — desafiá a otros usuarios a tandas de penales. El ganador le roba una figurita aleatoria al perdedor — el resultado queda grabado en Nostr.
- Apuestas P2P — apostá sats en el resultado de cualquier partido (gana local / empate / gana visitante). El issuer detecta automáticamente el resultado vía football-data.org y paga al ganador vía Lightning. Fee del 2%.
- Premios en sats — completá la página de un equipo y recibís 210 sats; completá el álbum entero y recibís 5.000 sats. Pagados automáticamente por el issuer vía NWC.
- Compartir en Nostr — publicá tus figuritas brillantes como notas kind:1 con la imagen del card adjunta (subida a nostr.build).
- Multi-idioma — español e inglés.
- Multi-firmante — NIP-07 (extensión), NIP-46 (Nostr Connect / bunker), clave local (nsec) o generación de clave nueva.
| Logro | Premio |
|---|---|
| Completar la página de un equipo (todas sus figuritas) | ⚡ 210 sats |
| Completar el álbum entero (672 figuritas únicas) | ⚡ 5.000 sats |
| Ganar una tanda de penales P2P | Robar una figurita aleatoria del rival |
| Ganar una apuesta P2P en resultado de partido | El bote de la apuesta menos 2% de fee |
- Node.js 18+
- Una Lightning Address para el issuer (Alby, Wallet of Satoshi, etc.)
- Opcionalmente: una wallet NWC para pagos automáticos de recompensas
- Opcionalmente: API key de football-data.org para detección automática de resultados (apuestas)
npm install
cp .env.example .env.localnpm run seedLa primera vez (sin ISSUER_NSEC en .env.local) imprime un par de claves nuevo. Copialo:
NEXT_PUBLIC_ISSUER_PUBKEY=<hex>
ISSUER_NSEC=nsec1...
Con las claves ya cargadas, corré el seed de nuevo — esta vez publica el álbum, las figuritas y las definiciones de sobres en los relays:
npm run seedEscucha zap receipts (NIP-57), emite grants de ownership, settlements de trades, pagos de recompensas vía NWC, y liquida apuestas automáticamente cuando terminan los partidos:
npm run issuernpm run dev
# http://localhost:3000| Variable | Descripción | Requerida |
|---|---|---|
NEXT_PUBLIC_ALBUM_ID |
Slug único del álbum en Nostr (d tag) | Sí |
NEXT_PUBLIC_ISSUER_PUBKEY |
Pubkey hex del issuer | Sí |
ISSUER_NSEC |
Clave privada del issuer (solo servidor) | Sí |
NEXT_PUBLIC_ISSUER_LN_ADDRESS |
Lightning address del issuer (destino de zaps) | Sí |
REWARD_NWC |
Nostr Wallet Connect del issuer para pagar recompensas y apuestas | No |
REWARD_PAGE_SATS |
Sats por completar una página de equipo (default: 210) | No |
REWARD_ALBUM_SATS |
Sats por completar el álbum entero (default: 5000) | No |
FOOTBALL_API_KEY |
API key de football-data.org para detección automática de resultados | No |
NEXT_PUBLIC_RELAYS |
Lista de relays separada por comas | No |
NEXT_PUBLIC_SITE_URL |
URL pública del deploy (para links en notas compartidas) | No |
src/
app/
page.tsx # orquesta identidad, estado de juego y navegación por tabs
layout.tsx # layout raíz, fuentes
globals.css # tokens CSS, animaciones (foil, flip, shine)
components/
Album.tsx # grilla del álbum con zoom de sticker
Packs.tsx # apertura de sobres + reveal animado
Market.tsx # mercado P2P (listings, compra, mis ventas)
Fixture.tsx # fixture completo: fase de grupos + eliminatorias + pronósticos + apuestas
BetPanel.tsx # panel de apuestas P2P por partido
PenaltyGame.tsx # minijuego de penales (client-side)
PenaltyMatch.tsx # desafíos P2P de penales entre usuarios
PenaltyScene3D.tsx # escena 3D con Three.js / React Three Fiber
StickerCard.tsx # card de figurita (foil, shiny, gradient)
Connect.tsx # login (NIP-07, NIP-46 QR, bunker, clave local)
ShareButton.tsx # captura el card + sube a nostr.build + publica nota
Leaderboard.tsx # tabla de posiciones
Traders.tsx # historial de trades
MyStickers.tsx # mis figuritas con opción de venta
InvoiceModal.tsx # modal de pago Lightning
SettingsModal.tsx # configuración de usuario
NostrAvatar.tsx # avatar desde perfil Nostr
Flag.tsx # bandera de país
hooks/
useIdentity.ts # gestión de sesión (NIP-07 / NIP-46 / local)
useGameState.ts # estado completo del juego desde relays
usePenaltyMatch.ts # lobby + partidas de penales P2P
usePronosticos.ts # pronósticos de partidos (kind 30302)
useBets.ts # apuestas P2P por partido (kind 30400 + 1592)
useLeaderboard.ts # ranking de jugadores
useProfile.ts # perfil Nostr de un pubkey
useTraders.ts # historial de intercambios
lib/
constants.ts # kinds de eventos, relays, ALBUM_ID, ISSUER_LN_ADDRESS
catalog.ts # catálogo de figuritas, rarezas, sorteo
identity.ts # firmado (NIP-07 / NIP-46 / local) + sesión
share.ts # captura DOM con html2canvas, upload a nostr.build
zap.ts # flujo NIP-57 (zap request → invoice → receipt)
nwc.ts # Nostr Wallet Connect (pagos automáticos, cliente)
nwc-server.ts # Nostr Wallet Connect (pagos automáticos, servidor/issuer)
penalty.ts # lógica de partidas de penales P2P
pool.ts # capa de relays (nostr-tools SimplePool)
parsers.ts # eventos Nostr → tipos del dominio
i18n.ts # strings en español e inglés
types.ts # tipos del dominio
issuer/
index.ts # listener de zap receipts + resultados de fútbol → grants, settlements, apuestas
bets.ts # lógica de apuestas P2P: lock, match, settle
football.ts # cliente de football-data.org + polling de resultados
seed.ts # genera claves + publica catálogo en relays
lib.ts # helpers del issuer (pool, publish, sign)
docs/
figus-modelo-datos-nostr.md # esquemas de todos los eventos Nostr
| Kind | Nombre | Descripción |
|---|---|---|
| 1 | Note | Posts compartidos (figuritas, ganancias en penales) |
| 1573 | Grant | Figus entregadas al abrir un sobre (emitido por issuer) |
| 1574 | Settlement | Liquidación de un trade o apuesta (emitido por issuer) |
| 1575 | Claim | El cliente solicita un sobre o recompensa al issuer |
| 1576 | Penalty Commit | Compromiso de zona del pateador (hash) |
| 1577 | Penalty Block | Elección de columna del arquero |
| 1578 | Penalty Reveal | Revelación de zona + nonce del pateador |
| 1580 | Steal Claim | Reclamo de figurita ganada en penales |
| 1591 | Bet Accept | Apostador B acepta una oferta de apuesta |
| 1592 | Bet Settle | Estado de apuesta publicado por el issuer (locked / matched / settled) |
| 9734 | Zap Request | Solicitud de pago para compra de sobre o apuesta |
| 9735 | Zap Receipt | Confirmación de pago — dispara grants o bet-lock |
| 30100 | Ownership | Figurita en posesión de un usuario (emitido por issuer) |
| 30200 | Listing | Figurita listada en el mercado P2P |
| 30301 | Penalty Match | Partida de penales P2P |
| 30302 | Pronóstico | Predicción de resultado de un partido (por usuario) |
| 30400 | Bet Offer | Oferta de apuesta P2P por resultado de partido |
- Apostador A crea una oferta en el fixture: elige partido, resultado (local/empate/visitante) y monto en sats.
- Apostador A paga vía zap al issuer con
figus-action: "bet-lock"— el issuer publica una confirmación en Nostr. - Apostador B ve la oferta abierta, la acepta pagando el mismo monto vía zap — la apuesta queda "matched".
- Cuando termina el partido, el issuer consulta football-data.org (polling cada 5 min), determina el resultado y paga automáticamente al ganador vía Lightning. El fee es del 2% del bote total.
Cuatro métodos soportados:
- NIP-07 — extensión de navegador (Alby, nos2x, etc.). Solo aparece si la extensión está instalada.
- Nostr Connect QR — escaneá con Amber, nsec.app u otro firmante NIP-46.
- Nostr Connect bunker — pegá una URL
bunker://o NIP-05 de firmante remoto. - Clave local — generá o importá un nsec. Se guarda en localStorage. Útil para pruebas rápidas.
Usuarios de LibreWolf / Tor Browser:
privacy.resistFingerprintingbloquea el canvas API, lo que impide generar el QR. En ese caso usá bunker URL o clave local.
- Sin base de datos — todo el estado del juego se deriva de eventos Nostr en relays públicos.
- Issuer trustless — solo actúa ante un zap receipt
9735válido firmado por la wallet del usuario. Nunca custodia fondos más del tiempo de matching de apuestas. - Resultados automáticos — el issuer hace polling a football-data.org cada 5 minutos durante el Mundial y liquida apuestas automáticamente sin intervención manual.
- Imágenes de figuritas — subidas a nostr.build en el momento de compartir (gratis, sin auth). Se adjuntan al note con tag NIP-92
imeta. - Pronósticos descentralizados — cada usuario publica sus predicciones como eventos kind 30302 reemplazables. Sin servidor.
- Persistencia de tab — la URL usa el hash (
#fixture,#album, etc.) para recordar la sección activa al refrescar.
