fad4006f60
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
239 lines
8.0 KiB
Markdown
239 lines
8.0 KiB
Markdown
---
|
|
id: "0072f"
|
|
title: "gamedev — crypto on-chain (NFT assets, payments, leaderboards firmadas)"
|
|
status: pendiente
|
|
type: feature
|
|
domain:
|
|
- gamedev
|
|
scope: multi-app
|
|
priority: media
|
|
depends:
|
|
- "0072e"
|
|
blocks: []
|
|
related: []
|
|
created: 2026-05-10
|
|
updated: 2026-05-17
|
|
tags:
|
|
- gamedev
|
|
- crypto
|
|
- web3
|
|
- nft
|
|
---
|
|
|
|
## Objetivo
|
|
|
|
Construir las primitivas para juegos crypto-aware: cargar assets desde NFTs del wallet del jugador, gating de contenido por tenencia de tokens, pagos in-game on-chain, y leaderboards verificables (firmados, anti-cheat basico).
|
|
|
|
Toda la logica viaja por el bridge JS de 0072e + funciones backend nuevas.
|
|
|
|
## Casos de uso
|
|
|
|
| Caso | Descripcion |
|
|
|---|---|
|
|
| **NFT skin** | Player conecta wallet, juego carga PFP/skin desde NFT que posee. |
|
|
| **Token gating** | Modos/niveles desbloqueados solo si wallet tiene cierto token o NFT. |
|
|
| **In-game shop** | Pagos en stablecoin (USDC) por items. |
|
|
| **Earn-to-play** | Pagos del juego al jugador (tx outgoing del game treasury). |
|
|
| **Leaderboard firmada** | Score tagged con firma del jugador, servidor verifica autenticidad. |
|
|
| **Cross-game inventory** | Inventario portable entre varios juegos del mismo studio (NFT estandar). |
|
|
|
|
## Funciones a crear
|
|
|
|
### Lectura on-chain (en JS bridge, expuesto a WASM)
|
|
|
|
```js
|
|
window.fnGameBridge.crypto = {
|
|
async getNFTs(address, contractAddr) // ERC721/1155 listing
|
|
async getTokenBalance(address, token) // ERC20 balance
|
|
async getENS(address) // ENS reverse lookup
|
|
async ownsNFT(address, contract, tokenId) // bool, gating
|
|
};
|
|
```
|
|
|
|
Implementacion: usar `viem` (~50KB gz) o llamadas RPC directas (`fetch` a Alchemy/Infura/Public RPC).
|
|
|
|
### En C++ (registry)
|
|
|
|
`cpp/functions/crypto/nft_loader.{cpp,h,md}` (impure, WASM):
|
|
|
|
```cpp
|
|
struct NFTAsset {
|
|
std::string contract;
|
|
std::string token_id;
|
|
std::string name;
|
|
std::string image_url; // ipfs:// o https://
|
|
std::string metadata_json; // raw
|
|
};
|
|
|
|
void crypto_list_nfts(const std::string& address,
|
|
void (*cb)(std::vector<NFTAsset>, void*), void* user);
|
|
void crypto_load_nft_image(const NFTAsset& nft,
|
|
void (*cb)(std::vector<uint8_t>, void*), void* user);
|
|
```
|
|
|
|
`cpp/functions/crypto/token_gating.{cpp,h,md}` (impure, WASM):
|
|
|
|
```cpp
|
|
struct GateRule {
|
|
std::string contract;
|
|
std::string token_id; // "" = any token of contract
|
|
uint64_t min_balance;
|
|
};
|
|
|
|
void crypto_check_gate(const std::string& address, const GateRule& rule,
|
|
void (*cb)(bool ok), void* user);
|
|
```
|
|
|
|
`cpp/functions/crypto/sign_score.{cpp,h,md}` (impure, WASM):
|
|
|
|
```cpp
|
|
// Player firma su score → servidor verifica
|
|
struct ScoreEntry {
|
|
std::string game_id;
|
|
std::string level;
|
|
uint64_t score;
|
|
uint64_t timestamp;
|
|
std::string nonce;
|
|
};
|
|
void crypto_sign_score(const ScoreEntry& s,
|
|
void (*cb)(std::string sig, void*), void* user);
|
|
```
|
|
|
|
### Backend (verificacion de firmas)
|
|
|
|
Funciones Go nuevas en `functions/crypto/`:
|
|
|
|
- `eth_verify_signature_go_crypto` (pure) — recover signer address de firma + mensaje. Vendoring: `go-ethereum/crypto`.
|
|
- `eip712_hash_go_crypto` (pure) — hash de typed data segun EIP-712.
|
|
- `solana_verify_signature_go_crypto` (pure) — ed25519 verify.
|
|
|
|
Service nuevo en `apps/`: `crypto_leaderboard_api` (tag `service`):
|
|
- POST `/score` con `ScoreEntry` + firma → verifica → guarda en BD si valido.
|
|
- GET `/leaderboard?game=X&level=Y` → top scores firmados.
|
|
- BD: SQLite con tabla `scores` (game_id, level, address, score, signature, timestamp).
|
|
|
|
### Frontend dashboard
|
|
|
|
`apps/crypto_leaderboard_api/web/` — pagina React/Mantine que consume la API y muestra leaderboards. Usa `@fn_library`.
|
|
|
|
## NFT image loading
|
|
|
|
Las imagenes de NFTs vienen via:
|
|
- `ipfs://Qm...` — resolver con gateway publico (`https://ipfs.io/ipfs/`, `https://nftstorage.link`, `https://cloudflare-ipfs.com`).
|
|
- `https://...` — directo.
|
|
- `data:image/png;base64,...` — embebido.
|
|
|
|
Funcion: `crypto_resolve_ipfs_url_cpp_crypto` (pure):
|
|
|
|
```cpp
|
|
std::string resolve_ipfs(const std::string& uri,
|
|
const std::vector<std::string>& gateways = {});
|
|
```
|
|
|
|
Failover entre gateways si uno falla.
|
|
|
|
## Cache local
|
|
|
|
NFTs raramente cambian. Cache en localStorage / IndexedDB:
|
|
|
|
```js
|
|
window.fnGameBridge.cache = {
|
|
async getNFT(contract, tokenId) { /* IndexedDB lookup */ },
|
|
async putNFT(nft) { /* IndexedDB store */ },
|
|
};
|
|
```
|
|
|
|
C++ no toca cache directamente; el JS bridge gestiona cache con TTL 24h.
|
|
|
|
## Pagos in-game
|
|
|
|
Patron canonico:
|
|
|
|
```cpp
|
|
// Comprar power-up por 1 USDC
|
|
struct Payment {
|
|
std::string token_contract; // USDC mainnet
|
|
std::string to_address; // Game treasury
|
|
std::string amount_wei; // 1000000 = 1 USDC (6 decimales)
|
|
std::string memo; // "buy:powerup_001:player_123"
|
|
};
|
|
|
|
void crypto_pay(const Payment& p, void (*cb)(std::string tx_hash, void*), void* user);
|
|
```
|
|
|
|
Backend verifica via webhook / polling:
|
|
1. Watch transactions on `to_address`.
|
|
2. Match `memo` → entregar item al usuario.
|
|
3. Servicio nuevo: `crypto_payment_watcher` (tag `service`).
|
|
|
|
## EIP-712 typed data
|
|
|
|
Para firmas estructuradas (mejor UX que `personal_sign` plano):
|
|
|
|
```js
|
|
// JS bridge expone:
|
|
async signTypedData(domain, types, message) {
|
|
return await signer.signTypedData(domain, types, message);
|
|
}
|
|
```
|
|
|
|
Beneficio: MetaMask muestra el contenido legible al usuario, no un blob hex. Reduce phishing.
|
|
|
|
## Anti-cheat layers
|
|
|
|
Score firmado NO es anti-cheat completo (un cheater puede firmar scores falsos). Layers complementarios:
|
|
|
|
1. **Sanity bounds** — backend rechaza scores imposibles (ej. > max teorico).
|
|
2. **Replay attestation** — guardar inputs del juego junto al score, replay deterministic en backend para validar.
|
|
3. **Server-authoritative** para modos competitivos — el juego envia inputs, el server simula. No es para todos los juegos.
|
|
4. **Rate limit** por wallet — max N scores/hora.
|
|
|
|
Documentar en `cpp/GAMEDEV.md` que la firma es **autenticacion**, no **integridad del score**.
|
|
|
|
## Integracion con apps existentes
|
|
|
|
Si una app C++ quiere features crypto:
|
|
1. Declara `uses_functions: [bridge_web3_cpp_crypto, nft_loader_cpp_crypto, ...]`.
|
|
2. Compila con `-sASYNCIFY` y los `ASYNCIFY_IMPORTS` del bridge.
|
|
3. Embebe `web/bridge.js` + `web/crypto.js` en su `shell.html`.
|
|
|
|
## Tamaño
|
|
|
|
- C++ extra: ~10 KB total (bridge + nft loader + token gating + sign score).
|
|
- JS host extra: ~50 KB gz (viem) + ~20 KB gz (helpers). **Lazy loaded**, no afecta TTI inicial salvo que el juego conecte wallet en startup (no recomendado).
|
|
- Backend Go: parte de un service separado, no afecta runtime del juego.
|
|
|
|
## Standards a soportar
|
|
|
|
| Standard | Para qué |
|
|
|---|---|
|
|
| ERC-721 | NFT unicos (skins, characters) |
|
|
| ERC-1155 | NFT semi-fungibles (items, currency) |
|
|
| ERC-20 | Tokens fungibles (in-game currency) |
|
|
| EIP-712 | Typed data signing |
|
|
| EIP-1193 | Wallet provider interface |
|
|
| Metaplex (Solana) | NFTs Solana |
|
|
|
|
## Criterio de exito
|
|
|
|
- [x] `engine_demo` carga PFP NFT del wallet conectado.
|
|
- [x] Token gating: solo wallets con NFT X pueden entrar al modo Y.
|
|
- [x] Pago de 1 USDC testnet entrega un item in-game tras confirmacion.
|
|
- [x] Leaderboard API valida firmas correctamente y rechaza falsificaciones.
|
|
- [x] Tests: mock provider para validar flujos sin red real.
|
|
- [x] Documentacion completa en `cpp/GAMEDEV.md` seccion crypto on-chain.
|
|
|
|
## Riesgos
|
|
|
|
1. **Gas fees** — pagos pequeños on-chain en mainnet ETH son inviables (gas > tx). Estrategia: L2 (Base, Arbitrum, Polygon) o Solana de default.
|
|
2. **IPFS gateway flaky** — failover entre gateways, cache local.
|
|
3. **Wallet UX en mobile** — WalletConnect deep links requieren testing real en devices.
|
|
4. **Regulacion** — payments crypto pueden caer bajo MiCA (EU) o equivalentes. Documentar disclaimers, no consejo legal aqui.
|
|
5. **Phishing** — siempre EIP-712 con domain explicito. Documentar para devs que usen el stack.
|
|
|
|
## No-objetivos
|
|
|
|
- Wallet integrado en el juego (custodial). Demasiado complejo + responsabilidad legal.
|
|
- Smart contracts propios — issue separado por juego, no aplica al stack genérico.
|
|
- Tokenomics / gobernanza — no es problema del runtime.
|