Files
fn_registry/dev/issues/0072f-gamedev-crypto-onchain-assets-payments.md
T

8.0 KiB

id, title, status, type, domain, scope, priority, depends, blocks, related, created, updated, tags
id title status type domain scope priority depends blocks related created updated tags
0072f gamedev — crypto on-chain (NFT assets, payments, leaderboards firmadas) pendiente feature
gamedev
multi-app media
0072e
2026-05-10 2026-05-17
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)

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):

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):

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):

// 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):

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:

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:

// 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 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

  • engine_demo carga PFP NFT del wallet conectado.
  • Token gating: solo wallets con NFT X pueden entrar al modo Y.
  • Pago de 1 USDC testnet entrega un item in-game tras confirmacion.
  • Leaderboard API valida firmas correctamente y rechaza falsificaciones.
  • Tests: mock provider para validar flujos sin red real.
  • 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.