BlockLucky Documentation officielle GitHub

Réel, simulé ou à venir

Cette page fait l'inventaire honnête de l'état du projet au 12 novembre 2026. Elle distingue ce qui fonctionne réellement, ce qui n'existe qu'en mode simulé, ce qui est encore fictif dans le code, ce qui n'a pas été fait et le code mort. Elle est à relire avant toute démonstration et avant toute mise en production.

Synthèse

Catégorie Contenu
Réel Contrats Solidity, tirage Chainlink VRF, portefeuilles intégrés chiffrés, dotation, achats et cadeaux signés, versement automatique, POAP, disjoncteur, commission, authentification, vérification d'âge par la Mairie, comptes Mairie et partenaires, consentement aux traceurs, salle d'attente, RGPD, indexeur, keeper
Simulé à dessein Mode BLOCKCHAIN_MODE=simulated, coordinateur VRF local, ethers fictifs
Encore fictif Notifications push, Sentry, libellé multi-signatures, paliers de la jauge, répartition par âge par défaut, rapport PDF serveur, stockage S3 en local, solde ETH en mode simulé
Pas encore fait Déploiement Sepolia, mise en ligne sur etherbay.tech, audit externe, tests utilisateurs
Code mort supprimé : useGaslessWeb3.ts (web et mobile) et WalletConnectButton.tsx (mobile) ; reste le champ gaslessSponsored des réponses

Ce qui est réel

Ces fonctionnalités fonctionnent de bout en bout en mode onchain, contre un vrai nœud Ethereum. Elles sont couvertes par le test d'intégration backend/__tests__/chain/onchain-lottery.chain.ts, exécuté en CI sur une blockchain locale.

Fonctionnalité Ce qui la rend réelle Référence
Smart contracts déployables BlockLucky et BlockLuckyPOAP compilés, testés (54 tests, 100 % des lignes) et déployés automatiquement sur le nœud Hardhat local, avec des rôles séparés (administrateur et propriétaire VRF, trésorerie, opérateur) ; scripts Sepolia prêts Blockchain locale, Déploiement sur Sepolia
Tirage Chainlink VRF Le contrat demande le hasard au coordinateur VRF v2.5. En local, le coordinateur est le contrat simulé officiel de Chainlink (VRFCoordinatorV2_5Mock) et un oracle local (smart-contract/scripts/local-vrf-responder.ts) répond aux demandes. Sur Sepolia, ce serait le coordinateur réel de Chainlink Tirage Chainlink VRF
Portefeuilles intégrés chiffrés Une clé Ethereum aléatoire par citoyen, chiffrée en AES-256-GCM avec ENCRYPTION_KEY avant d'être stockée backend/src/blockchain/wallet.service.ts
Dotation de bienvenue Vrai transfert d'ethers fictifs du compte opérateur vers le portefeuille, une seule fois par compte, protégé par un verrou Redis grantWelcomeEthers
Achats signés par le citoyen La transaction buyTickets est signée par la clé du portefeuille du citoyen, déchiffrée à la volée par le backend. Le citoyen paie le ticket et les frais de réseau sur sa dotation backend/src/blockchain/lottery.gateway.ts
Ticket cadeau buyTicketsFor signé par l'expéditeur au profit du destinataire giftOnchain de backend/src/api/lottery/lottery.services.ts
Versement automatique de la cagnotte À l'événement WinnerPicked, l'indexeur met en file un CLAIM_PRIZE signé par le portefeuille du gagnant ; même mécanisme pour les remboursements (CLAIM_REFUND) backend/src/blockchain/chain-actions.ts
Badge POAP ERC-721 Frappé par le compte opérateur pour chaque participant, non transférable Badge POAP
Disjoncteur on-chain pause et unpause du contrat, déclenchés depuis le dashboard Mairie Disjoncteur
Reversement de la commission withdrawFees vers l'adresse treasury du contrat withdrawMunicipalityTreasury de backend/src/api/admin/admin.services.ts
Tirage et clôture automatiques Le keeper appelle requestDraw une fois écoulé le délai de révélation (DRAW_REVEAL_DELAY_SECONDS, 10 minutes par défaut) après que le contrat l'autorise, ou closeExpiredRound pour un tour expiré Keeper du tirage
Indexation Les tickets, tirages et annulations sont projetés en base depuis les événements du contrat, avec un curseur Indexeur d'événements
Connexion par code email ou par mot de passe Code à 6 chiffres haché, valable 10 minutes, envoyé par email ; mot de passe haché par bcrypt, verrouillage après échecs répétés Authentification
Vérification d'âge Recto et verso de la pièce d'identité chiffrés en AES-256-GCM, examinés par un agent de la Mairie puis effacés ; achat, cadeau et échange refusés (403 AGE_VERIFICATION_REQUIRED) tant que l'âge n'est pas vérifié backend/src/middlewares/age-verification.middleware.ts, frontend-web/src/app/mairie/identites/page.tsx, frontend-web/src/app/(citoyen)/profil/identite/page.tsx
2FA TOTP Secret chiffré, codes de secours hachés, protection contre le rejeu, code de l'application accepté seulement après le premier facteur (loginTicket), obligatoire pour les administrateurs Authentification et 2FA
RGPD Export JSON, suppression confirmée par email avec anonymisation et effacement de la clé du portefeuille Suppression RGPD
Comptes Mairie et partenaires Page /mairie/comptes : liste, création avec invitation par email, attribution, changement et retrait de rôle, journalisés dans l'audit Administration Mairie
Documents légaux et consentement Conditions générales, règlement, mentions légales, confidentialité, traceurs et charte de transparence sur le web et le mobile ; acceptation exigée à l'inscription ; mesure d'audience du web soumise à un bandeau de consentement Cookies et mentions

À savoir

« Signé par le citoyen » veut dire signé avec la clé du portefeuille du citoyen. Cette clé est gardée et utilisée par le backend : le modèle est custodial. Le citoyen ne signe rien lui-même. Ce choix est argumenté dans l'ADR 0004.

Ce qui est simulé à dessein

Le mode simulé du backend

BLOCKCHAIN_MODE accepte deux valeurs, définies dans backend/src/config/env.config.ts :

Valeur Usage Comportement
onchain (défaut) Développement normal, CI d'intégration, production Toutes les opérations passent par le contrat
simulated Tests unitaires (backend/__tests__/setup-env.ts), développement sans nœud backend/src/services/blockchain.service.ts génère des hash de transaction aléatoires, tire le gagnant avec crypto.randomInt, garde la pause dans Redis ; le webhook signé sert d'entrée d'événements

Le schéma de configuration refuse de démarrer en production avec une autre valeur que onchain : « BLOCKCHAIN_MODE doit valoir onchain en production ». Le mode simulé ne peut donc pas être déployé par erreur. Voir Mode onchain ou simulé.

Les ethers fictifs

Ce n'est pas une limite mais le principe du projet : en local, les ethers sont ceux du nœud Hardhat (chain ID 31337) ; sur Sepolia, ce seraient des ETH de test. Aucun ne peut être converti en monnaie réelle.

Ce qui est encore fictif

Ces éléments existent dans l'interface ou dans l'API mais ne reposent sur aucune donnée ni aucun traitement réel.

Élément Constat Fichier
Historique des notifications push getPushNotificationsHistory renvoie trois notifications codées en dur. Aucun envoi de notification push n'existe côté serveur ; la bannière de notification du mobile est déclenchée localement (triggerSimulatedOsPush) backend/src/api/user/user.services.ts, mobile-app/src/hooks/usePushNotifications.ts
Sentry Aucun SDK Sentry n'est installé. sentry.client.ts stocke les erreurs dans un tableau en mémoire ; EXPO_PUBLIC_SENTRY_DSN n'active qu'un message local. Côté web, « Sentry » désigne une simple modale d'erreur mobile-app/src/services/sentry.client.ts
Trésorerie « multi-signatures » Le résumé OpenAPI, l'action d'audit TREASURY_WITHDRAWAL_MULTISIG et le champ multisigWallet parlent de multi-signatures. En réalité la trésorerie est une adresse simple (treasury) du contrat. En mode onchain, si l'adresse saisie dans le dashboard diffère, le backend appelle d'abord setTreasury (rôle DEFAULT_ADMIN_ROLE, laissé à l'opérateur en local, réservé à MAIRIE_ADMIN_ADDRESS sur Sepolia, où seul un retrait vers la trésorerie enregistrée fonctionne), puis withdrawFees verse toute la commission accumulée : le montant saisi est ignoré backend/src/api/admin/admin.routes.ts, backend/src/blockchain/lottery.gateway.ts
Paliers de la jauge Quatre paliers (5 000, 7 500, 9 000 et 10 000 participants) annoncent les coulisses du tirage, une offre spéciale des commerçants, un jeu bonus et l'ouverture de la salle. Seule la fermeture des achats à 10 000 tickets découle du contrat (maxTickets) ; les trois autres avantages ne sont pas implémentés buildMilestones de backend/src/api/lottery/lottery.services.ts
Solde ETH en mode simulé walletService.balanceOf renvoie null hors mode onchain : le mobile n'affiche aucun solde et la billetterie considère le solde comme suffisant backend/src/blockchain/wallet.service.ts
Répartition par âge Sans événement portant une tranche d'âge, le dashboard affiche une répartition codée en dur (42 %, 33 %, 17 %, 8 %) ; target18_35Validated vaut toujours true backend/src/api/analytics/analytics.services.ts
Part de la Mairie au dashboard Calculée à 10 % de la somme des cagnottes en base, et non lue sur le contrat backend/src/api/admin/admin.services.ts
Rapport PDF côté serveur Le « PDF » envoyé sur S3 est un texte JSON déclaré application/pdf. Le web produit un vrai PDF par l'impression du navigateur generateAccountingReport
Stockage S3 Le backend est configuré pour un MinIO local (AWS_S3_ENDPOINT=http://localhost:9000, identifiants minioadmin), mais docker-compose.yml ne fournit aucun service MinIO. L'envoi échoue, est journalisé en avertissement, et l'URL renvoyée ne pointe vers rien backend/src/services/s3.service.ts
Quêtes sociales Validées sur simple déclaration du citoyen, sans vérification ; la liste est codée en dur et l'état est gardé dans Redis QUESTS_CATALOG de backend/src/api/user/user.services.ts
Expiration des offres La date saisie par le partenaire est écrite dans Redis mais jamais relue backend/src/api/partners/partners.services.ts
Codes promo importés Gardés uniquement dans Redis (partner:codes:<offre>) ; une purge de Redis les perd backend/src/api/marketplace/marketplace.services.ts
Événements Socket.IO déclarés draw:countdown, draw:started, queue:position et network:fallback sont typés mais jamais émis backend/src/types/socket.types.ts
Traces de Web3Auth Il n'y a pas de Web3Auth : le portefeuille est créé par le backend. EXPO_PUBLIC_WEB3AUTH_CLIENT_ID figure dans mobile-app/.env.example sans être lu. L'adresse envoyée par le web à la connexion (POST /auth/login) n'est qu'un identifiant de recherche : le vrai portefeuille est créé côté serveur. Le mobile, qui passe par /auth/signin et /auth/signup, n'envoie plus d'adresse backend/src/api/auth/auth.routes.ts, frontend-web/src/lib/wallet.ts
Tests mobiles 8 tests écrits mais sans exécuteur : ni script test ni Jest dans mobile-app/package.json mobile-app/__tests__/
Connexion Google Code complet côté serveur et mobile, mais GOOGLE_CLIENT_IDS est vide par défaut : la route répond 503 et le mobile grise son bouton tant qu'aucun identifiant client n'est déclaré. La connexion Apple a été retirée backend/src/services/oauth-verifier.service.ts

Ce qui n'est pas encore fait

Élément État Ce qu'il faut faire
Déploiement sur Sepolia Quatre scripts prêts, jamais exécutés : npm run vrf:sepolia (abonnement VRF), deploy:sepolia (contrats et rôles), automation:sepolia (upkeep) et verify:sepolia (Etherscan). Il n'existe pas de fichier deployments/sepolia.json : l'exécution demande les clés de l'équipe (déployeur, multisig de la Mairie, opérateur), de l'ETH et du LINK de test, que le dépôt ne contient pas Comptes, fonds et ordre des commandes : voir Déploiement sur Sepolia et Redéployer sur Sepolia
Vérification du code sur Etherscan Dépend du déploiement Sepolia npm run verify:sepolia, décrit dans Déploiement sur Sepolia
Mise en ligne sur etherbay.tech Application non hébergée Déploiement sur etherbay.tech
Chainlink Automation Le contrat implémente checkUpkeep et performUpkeep, et npm run automation:sepolia sait inscrire l'upkeep, mais aucun n'est enregistré faute de déploiement ; le keeper du backend joue ce rôle Chainlink Automation
Audit de sécurité externe Aucun ; seules les analyses automatiques (Slither, CodeQL, gitleaks, Trivy) tournent Audits et checklists
Tests utilisateurs Protocole rédigé par l'équipe, entretiens et tests non réalisés Profils et parcours
Garde des secrets en production ENCRYPTION_KEY et RELAYER_PRIVATE_KEY à confier à un coffre de secrets Clés et garde des portefeuilles

Code mort

La première version prévoyait des achats « gasless », c'est-à-dire relayés et payés par la Mairie. Depuis le passage au portefeuille intégré, le citoyen paie lui-même les frais sur sa dotation. Les trois fichiers de cette époque, que plus rien n'importait, ont été supprimés : les hooks useGaslessWeb3.ts du web et du mobile, remplacés par useBlockLuckyWallet et useCitizenWallet, et le composant mobile WalletConnectButton.tsx, remplacé par IntegratedWalletButton, que plus aucun écran n'affiche depuis la refonte du mobile.

La refonte du mobile du 12 novembre 2026 a aussi supprimé LoginScreen, SettingsStack et sept composants de loterie (ChatBox, CountdownTimer, LeaderboardList, POAPBadge, RouletteAnimation, TicketCard, WinnerClaimCard), remplacés par les écrans et composants décrits dans Navigation et écrans.

Il reste le champ gaslessSponsored des réponses de GET /lottery/status et POST /lottery/buy, conservé pour ne pas casser les clients : il vaut false en mode onchain. D'autres éléments installés mais inutilisés côté mobile sont recensés dans Stack et structure de l'application mobile.

Avant une démonstration

  1. Lancer la blockchain locale et réinitialiser la base si le conteneur a redémarré (Dépannage).
  2. Ne pas présenter la répartition par âge, les paliers ni les notifications comme des données réelles.
  3. Présenter la trésorerie comme une adresse simple, et le tirage local comme non certifié par Chainlink (coordinateur simulé).
  4. Montrer un ticket, sa transaction et le tirage : c'est la preuve demandée par le livrable de cadrage de l'équipe.

Le déroulé complet est dans Préparer la soutenance. La liste de la dette technique, avec les priorités de correction, est dans Dette technique et limites.