Journal des décisions

Le registre canonique est doc/DECISIONS.md. Cette page résume les tournants.

2026-08-25 — Revue de sécurité interne

Une vulnérabilité haute : les remplissages partiels laissaient de l'ETH et de l'USDC bloqués dans les routeurs, entièrement taxés. Corrigée en refusant le remplissage partiel plutôt qu'en le gérant.

2026-08-27 — La taxe passe de 4 % à 5 %

Part créateur doublée, de 1 % à 2 %. Barème $STOCKFUN de 2 / 1,5 / 0,5 à 2 / 2,5 / 0,5. La part trésorerie reste à 2 %, et c'est le chiffre qui ne bouge jamais.

Conséquence assumée : l'aller-retour passe de 7,84 % à 9,75 %. Depuis le 2026-10-05, le taux et sa répartition sont des réglages de l'owner, ces valeurs étant celles par défaut.

2026-08-27 — Le buyback créateur

Lève l'exclusion « aucune sortie » sur un seul chemin, qui finit toujours au burn. Le Treasury Ratio peut désormais baisser au choix du créateur. Supprimé le 2026-09-27, remplacé par l'airdrop.

2026-08-29 — Le mode urgence

L'owner peut déplacer les actifs d'un treasury après 48 heures publiques. Remplace la promesse « personne ne peut toucher au treasury ». Le protocole cesse d'être décrit comme sans confiance. Immédiat depuis le 2026-10-05 : le délai a disparu.

2026-09-11 — Le rail passe à Robinhood Chain

Le blocage était l'éligibilité d'un contrat vault chez un émetteur à mint primaire, jamais documentée publiquement. Les pools secondaires de Robinhood Chain ne l'exigent pas. Prix payé : une dépendance cross-chain assumée.

2026-09-12 — La bonding curve disparaît

Remplacée par deux positions Uniswap v4 single-sided verrouillées dès la création. Plus de graduation, plus de migration, plus de fee de graduation.

La FDV de lancement ne change pas : elle reste celle qu'ouvrait la courbe, 3,409 ETH. Le premier ticket de 2 ETH prend 36,06 % de la supply contre 36,99 % avant — le lancement se comporte comme avant, à moins d'un point près.

Écarté en chemin : un lancement à $5 000 avec borne haute à $50 000, qui ne donnait que 3,25 ETH de profondeur et laissait le premier acheteur de 2 ETH prendre 57 % de la supply. Et l'idée qu'une borne haute plus basse calmerait le marché — elle passe la main plus tôt à la bande infinie, qui est fine, et double la FDV atteinte à 50 ETH dépensés.

2026-09-12 — L'anti-snipe en trois blocs

Créateur seul au bloc 1, 99 % vers le buyback au bloc 2 sauf créateur et whitelist owner, régime normal ensuite. Remplacé le 2026-09-28 par l'anti-snipe dégressif.

Pris en connaissance de l'objection : une taxe à 99 % ne produit quelque chose que si quelqu'un perd son argent, et fermer le bloc 2 aurait le même effet dissuasif sans victime. La whitelist est au niveau du protocole et non du créateur, précisément pour qu'elle ne puisse pas servir d'avantage d'initié marché par marché.

2026-09-12 — Pas de burn sur les ventes

L'idée d'envoyer 10 % des tokens entrants au burn à chaque vente est écartée. Payée par le vendeur, elle portait l'aller-retour à 18,78 %. Payée par la LP, elle rendait « liquidité verrouillée à vie » faux et donnait à quiconque un levier à environ 1:1 pour brûler le flottant et pomper son propre sac.

2026-09-27 — L'airdrop remplace le buyback créateur

La trésorerie d'un marché n'a plus qu'un usage : 100 % des actions qu'elle achète sont airdroppées aux holders de son token, au prorata, en nature, sur Robinhood Chain. Le buyback créateur est supprimé, et avec lui le trajet retour du pont. Le rachat-burn de $STOCKFUN reste.

Écartés en chemin : garder le buyback créateur, et un découpage qui gardait 60 % dans la trésorerie et en distribuait 40 %, l'ancien plan V2. Conséquences assumées : aucune trésorerie ne s'accumule plus, le Treasury Ratio perd son sens, et le slogan et la tagline sont à revoir. Le mécanisme a été codé le 2026-10-04 : voir L'airdrop.

2026-09-27 — Un cycle toutes les 24 heures

La cadence de l'airdrop est fixée le même jour. Une fois toutes les 24 heures, pour chaque marché dont la trésorerie a accumulé au moins 0,1 ETH, le keeper convertit, fait passer le pont, achète les actions, puis les airdroppe. En dessous du seuil, rien ne se passe ce jour-là ; l'ETH attend.

Les actions ne s'achètent que bourse US ouverte : il n'y a donc pas de cycle le week-end ni les jours fériés NYSE. Dans le code du keeper, jusqu'au 2026-10-06, la conversion se faisait pendant la séance, au premier passage où le vault atteignait le seuil ; depuis, elle suit cette décision, une fois par fenêtre, au premier passage du keeper après la fermeture de la fenêtre et seulement si le vault détient alors le seuil (voir le 2026-10-06 plus bas) ; depuis la sixième boucle d'audit, le même jour, l'USDC d'un vault suit lui aussi cette cadence. L'envoi à l'airdrop se fait une fois par fenêtre depuis le 2026-10-05, après la séance du jour : voir Le keeper.

2026-09-27 — Fonds bloqués et mesure de la détention

Les reliquats d'un cycle interrompu sont terminés au cycle suivant, seuil atteint ou non. Des fonds d'airdrop bloqués depuis 24 heures dans un vault miroir peuvent être déplacés par l'owner sans préavis : c'est la seule exception au délai public de 48 heures, choisie en connaissance de cause. La détention se mesure comme le solde moyen sur les 24 heures qui précèdent le cycle, le lundi compris. Le délai des fonds bloqués est passé à 48 heures le 2026-09-28, et la règle a été close le 2026-10-05, sans avoir été codée.

2026-09-28 — L'anti-snipe dégressif

80 % au bloc d'ouverture, moins 8 points par bloc, 5 % à partir du 11e bloc, à l'achat comme à la vente. Le surplus va à la trésorerie du marché, ou aux frais réclamables de l'équipe sur $STOCKFUN. Exemptés : l'achat du créateur au lancement et une whitelist fixée par le créateur dans la transaction de création, publique, immuable et plafonnée à 20 adresses ; sur $STOCKFUN, fixée par l'owner au lancement. Depuis le 2026-10-05, ces chiffres sont des réglages de l'owner, les valeurs ci-dessus étant celles par défaut.

Écarté en chemin : marquer les wallets qui achètent pendant les premiers blocs et taxer leurs ventes plus tard, même après un transfert. Un pool v4 ne voit pas le wallet, une taxe prise par le token fait échouer la vente, et bloquer les wallets marqués hors du routeur StockFun ferait signaler le token par les scanners. Conséquence assumée : la whitelist passe du protocole au créateur, ce que la décision du 2026-09-12 écartait pour éviter un avantage d'initié.

2026-09-28 — Fonds d'airdrop bloqués : 48 heures

Des fonds d'airdrop restés 48 heures dans un vault miroir, au lieu de 24, peuvent être déplacés par l'owner sans préavis. À 24 heures, la fenêtre égalait la période du cycle, et un report ordinaire d'un cycle devenait déplaçable sans préavis ; à 48 heures, ce n'est plus le cas. Un échec du vendredi reste déplaçable le dimanche, avant le cycle du lundi. Décidé le même jour : le compteur ne s'arrête jamais, ni pendant une pause ni le week-end. Close le 2026-10-05, sans avoir été codée : le transfert d'urgence est immédiat partout.

2026-09-28 — La détention enregistrée onchain

Le token de chaque marché enregistre la détention de chaque wallet dans le temps : un contrat calcule la moyenne sur les 24 heures qui précèdent un cycle, et chaque part ; le keeper ne les fournit jamais. Écarté : une répartition calculée hors chaîne par le keeper et publiée avec un délai de contestation. Coût assumé : chaque transfert du token paie un surcoût de gas.

2026-09-28 — L'airdrop en actions wrappées, sur Ethereum

Les actions achetées sur Robinhood Chain y sont verrouillées dans des adaptateurs LayerZero déployés par StockFun, un par action, et envoyées sur Ethereum sous forme d'actions wrappées. La distribution se fait sur Ethereum, où vivent le token et sa détention, et chaque holder réclame sa part à ses frais. Conséquences assumées : les actions wrappées ne valent que par les actions verrouillées et par la configuration LayerZero, elles n'ont pas de marché sur Ethereum, et un gel des Stock Tokens par leur émetteur bloquerait les actions verrouillées. Décidé le même jour : l'owner tient la configuration LayerZero des adaptateurs, sans délai et sans plafond de sortie, comme Paxos pour l'USDG. Écartés : une configuration figée après le déploiement, et des changements sous préavis de 48 heures.

2026-09-28 — Le routeur officiel reste modifiable

L'owner peut changer à tout moment le routeur de swap officiel, et le hook reconnaît la whitelist anti-snipe à travers ce routeur. Conséquence assumée : en désignant un autre routeur, l'owner peut exempter n'importe qui de l'anti-snipe.

2026-09-28 — Des frais de création de 0,001 ETH

Les frais de création passent à 0,001 ETH, gravés dans le code, au lieu d'environ 3 $ : pas de feed de prix, pas de réglage par l'owner. Leur valeur en dollars suit désormais le prix de l'ETH. Un réglage de l'owner depuis le 2026-10-05, 0,001 ETH par défaut.

2026-09-28 — Le PoolManager hors de l'historique de détention

Le token n'enregistre pas le PoolManager d'Uniswap, partie de chaque swap qui détient les tokens de toutes les pools et ne reçoit jamais d'airdrop : son historique se lit à zéro, celui du trader reste enregistré. Économie mesurée : environ 24 000 gas par swap, à l'achat comme à la vente.

2026-09-29 — Aucun frais de LP sur les pools StockFun

Les pools sont créées avec une commission de LP Uniswap nulle au lieu de 0,30 % : la taxe du hook est tout le coût d'un trade, 5 % par sens et 9,75 % pour un aller-retour hors impact de prix. Coût assumé : la trésorerie, l'équipe et le créateur perdent leur part des frais de LP, et le burn du côté token de ces frais disparaît. Depuis le 2026-10-05, la commission de LP est un réglage du lancement, nulle par défaut ; chaque pool garde celle avec laquelle il a été créé.

2026-10-01 — Les correctifs de l'audit de sécurité du 2026-09-29

Appliqués les 2026-09-30 et 2026-10-01, après la revue de chaque constat et le choix de son correctif. Chaque constat corrigé a des tests de non-régression qui échouent sur le code audité.

Seul le lock de liquidité peut ajouter de la liquidité dans un pool StockFun : une position tierce tradait contre les swaps taxés sans payer la taxe ni l'anti-snipe. La nouvelle permission du hook a changé son adresse, qui a été re-minée.

Seule la part trésorerie est envoyée pendant un trade. Les parts équipe et buyback sont créditées sur le hook et payées par des réclamations que n'importe qui peut appeler, aux wallets en vigueur dans la factory ; le séquestre a disparu. Le routeur et le lock paient l'entrée avant le swap. Jusque-là, un wallet de frais qui rappelait Uniswap pouvait bloquer le trading sur tous les pools.

Chaque étape de conversion dépense un montant que fixe le keeper : un vault plus gros que sa place d'échange convertit par tranches. Le cash est réservé par action du basket à mesure qu'il arrive, et une action que le keeper saute garde sa réservation ; le vault miroir fonctionne de la même façon.

Chaque token enregistre dans le temps sa supply hors du PoolManager d'Uniswap, le dénominateur de la part de l'airdrop au prorata. Des trois options de l'audit, celle-ci coûte une écriture de plus par swap ; enregistrer à nouveau le PoolManager aurait coûté environ 24 000 gas par swap. Conséquence assumée : les fenêtres de l'airdrop commencent et finissent sur une heure pleine.

Sur le rail Robinhood, un basket refusé ne bloque plus les autres marchés d'un batch, un token distant n'a qu'un identifiant, un remplissage partiel v3 fait échouer sa route, un hub distant en pause applique quand même les changements de rôles, et les enregistrements canoniques sont payés dans leur ordre d'arrivée.

Mesuré : un achat via le routeur coûte 113 529 gas et une vente 137 250, contre 125 862 et 150 416 avant. Restent ouverts, en attente de la décision de l'owner : la rotation de l'adaptateur de pont (M-2, et L-8 avec elle), les attestations Ondo (M-7), et les routeurs qui s'acceptent eux-mêmes comme destinataires (L-4). Le 2026-10-05, M-7 et L-4 sont corrigés et M-2 est laissé tel quel ; L-8 attend toujours.

2026-10-01 — Le pipeline de sécurité complète les correctifs

Le même jour, un second tour d'audit, par deux auditeurs indépendants, a complété plusieurs correctifs. Chacun a ses tests de non-régression ; aucun ne change une décision de design ni un paramètre immuable.

Sur Robinhood Chain, une route v3 s'exécute un pool à la fois, et chaque pool doit prendre toute son entrée : la vérification précédente ne voyait que le premier pool, et un remplissage partiel plus loin laissait le token intermédiaire là où n'importe qui pouvait le prendre. Un ticket comptable canonique paie au plus autant d'enregistrements qu'il en a mis dans la file, les plus anciens d'abord : un arriéré ne le pousse plus au-delà du gas de son exécution automatique, et le sweep paie le reste.

Une urgence exécutée qui fait passer le cash d'un vault sous ses réservations les remet toutes à zéro, dans les deux vaults : ce qui reste, et tout ce qui arrive ensuite, est réparti à nouveau aux poids. Jusque-là, la remise à zéro n'existait que dans la lecture, et les anciennes réservations revenaient avec l'entrée suivante.

Le keeper retient n−1 unités sur la dernière action d'un basket à chaque achat : le reste d'arrondi va à cette action, et un transfert de poussière pouvait faire échouer l'achat. Il divise aussi par deux une tranche de burn que le pool $STOCKFUN ne peut pas remplir en entier. Chaque token pose son premier point horaire au déploiement, ce qui ne comptait que sur une horloge qui commence à l'heure 0, comme celle d'une chaîne de test. Le routeur d'actions Ethereum appelle sync avant de payer en ETH, et la Lens refuse une page vide.

Mesuré : le premier swap de chaque heure sur un token coûte environ 28 000 à 30 000 gas de plus en transaction à part, et non environ 23 000.

En attente de la décision de l'owner, non tranchés : une urgence sur le cash enregistré du hub distant, dont les enregistrements sont ensuite payés avec le cash d'autres marchés (R2H-1) ; un token de la jambe cash qui refuse un vault miroir, ce qui arrête la file canonique et fait échouer les batches qui groupent ce marché (R2H-2) ; le côté contrat de la marge de la dernière action, qui change la règle d'allocation documentée (R2T-1) ; prendre la taxe d'achat en claims du PoolManager, pour que tout routeur puisse acheter (R2F-1, option b) ; faire déployer le vault de $STOCKFUN par la factory elle-même (R2F-2) ; et trois points informatifs (R2H-4, R2H-6, R2H-7). Une action qu'on ne peut plus jamais acheter reçoit toujours son poids de chaque entrée de cash : c'est voulu, car retirer une jambe changerait les poids figés du basket. Depuis le 2026-10-05, R2H-1 est couvert par les outils de règlement des boucles d'audit de ce jour-là et par une procédure, l'owner mettant le hub distant en pause avant le transfert ; R2H-2 est fermé par la livraison non bloquante de la quatrième boucle, et R2F-1 est laissé tel quel : voir les entrées du 2026-10-05 plus bas.

2026-10-02 — Tous les modules upgradables

Jusque-là, aucun contrat du protocole ne pouvait être upgradé. Tous les modules sauf les tokens et le lock de liquidité deviennent upgradables par l'owner de StockFun, avec effet immédiat : pas de timelock. Chacun est un proxy upgradé en UUPS, et demande à son autorité d'upgrade qui peut l'upgrader : la factory sur Ethereum, le hub distant sur Robinhood Chain. Les vaults, sur les deux chaînes, sont un proxy par marché, upgradé marché par marché. Le proxy du hook est miné avec les 14 permissions v4 : une implémentation future peut utiliser n'importe quel callback sans déplacer l'adresse.

Les tokens ne portent plus aucune logique propre : un ERC-20 simple à supply fixe, avec un seul setter, setRecorder, pour l'owner. L'enregistrement de la détention les quitte pour un nouveau module, l'enregistreur de détention, que chaque token appelle à chaque transfert ; si l'appel échoue, le transfert échoue. Depuis le 2026-10-05, les tokens ont un setter et leurs rescues, pour ce qui est envoyé par erreur à leur propre adresse.

Le lock de liquidité reste non upgradable et gagne un mode fin : l'owner l'annonce, et 30 jours plus tard peut récupérer toute la liquidité de chaque pool, celle de $STOCKFUN comprise, vers n'importe quelle adresse. L'owner peut annuler ; le trading continue pendant les 30 jours, et aucun nouveau marché ne peut être lancé tant qu'une fin est en attente. Il existe pour le cas où le projet s'arrête ; les 30 jours sont le préavis des holders. Le déployeur des vaults miroirs sur Robinhood Chain ne peut pas être upgradé non plus : l'adresse de chaque vault miroir en est dérivée.

Ce que cela change : un upgrade n'attend pas les 48 heures du mode urgence, qui reste ; les règles que ce livre dit fixes, des bornes des vaults au burn et au taux de 5 %, tiennent tant que l'owner n'upgrade pas le module qui les porte ; la factory est déployée en premier et câblée par l'owner, ce qui change l'ordre de déploiement. Un swap coûte environ 27 000 gas de plus, mesuré isolément : un achat 237 190 gas au lieu de 210 105, une vente 285 031 au lieu de 258 278 — le prix des proxies sur son chemin et de l'appel à l'enregistreur. Depuis le 2026-10-05, le mode urgence n'a plus de délai non plus, et les bornes des vaults et le taux de 5 % sont des réglages de l'owner.

2026-10-04 — Le contrat de l'airdrop

Codé et testé, pas déployé. AirdropDistributor, un module upgradable sur Ethereum lié à la factory, distribue les actions de chaque marché par cycle quotidien. La fenêtre d'un cycle se ferme à 13:00 UTC, avant l'ouverture de la séance US toute l'année ; ses achats et ses envois se font pendant la séance qui suit. Un cycle s'ouvre à son premier envoi, ou avant, et fige ses chiffres : l'enregistreur de détention, la liste d'exclusion en vigueur à la fermeture de la fenêtre et la détention éligible. Deux chemins l'alimentent, sendToAirdrop sur le vault miroir, par l'adaptateur LayerZero de chaque action, et sur le vault Ethereum pour le rail local ; rien d'autre ne crédite un cycle.

Tranché le même jour : aucun envoi par le keeper, seul le holder réclame, à ses frais (TBD 5) ; ni plafond par wallet, ni minimum, ni échéance (TBD 7) ; une liste d'exclusion par token, fixée par l'owner, 16 adresses au plus, avec toujours l'adresse de burn et, pour $STOCKFUN, le contrat de vesting (TBD 8). Un envoi pour une fenêtre sans détention éligible est mis de côté pour la première fenêtre qui en a, quels que soient l'appelant et le moment, tant que l'heure des cycles ne change pas et que l'enregistreur de détention du token n'est pas remplacé entre-temps.

Assumé et documenté : le keeper choisit le moment, donc un envoi arrivé après le 13:00 UTC suivant est mesuré sur la fenêtre du lendemain ; changer l'heure des cycles redécoupe les fenêtres pas encore ouvertes, y compris celles que des actions mises de côté doivent encore examiner ; l'enregistreur de détention d'un token s'upgrade en place, il n'est jamais remplacé sur un token vivant. Hors de ce changement : la règle des 48 heures pour les fonds bloqués, les adaptateurs d'actions, l'étape de l'airdrop dans le keeper et l'écran de réclamation de la dapp. Depuis le 2026-10-05, il n'y a plus de contrat de vesting à exclure, la règle des 48 heures est close, et l'heure des cycles fait partie d'un calendrier que fixe l'owner, la durée des fenêtres et leur heure de fermeture (setCycleSchedule) ; un enregistreur désigné sur un token vivant part de la supply du token hors du PoolManager (voir l'entrée de la boucle d'audit plus bas) ; l'étape de l'airdrop du keeper et l'écran de réclamation sont écrits.

2026-10-05 — Plus d'allocation d'équipe, un mode urgence immédiat, chaque chiffre un réglage

Trois décisions, codées et testées le même jour, pas déployées.

L'allocation d'équipe disparaît. Le contrat de vesting de l'équipe, TeamVesting, est supprimé, et aucun $STOCKFUN n'est réservé à l'équipe : toute la supply, 1 000 000 000 de tokens par défaut, part dans la position single-sided verrouillée, comme toute la supply d'un marché part dans ses deux positions. Jusque-là, 10 % allaient au contrat de vesting sur 12 mois, et l'airdrop excluait ce contrat des cycles de $STOCKFUN ; seule l'adresse de burn est exclue désormais. Depuis la troisième boucle d'audit du même jour, l'opérateur du lancement l'est aussi, lui qui détient la supply entre la frappe et le lock.

Le mode urgence devient immédiat. emergencyTransfer déplace un actif hors d'un vault, d'un hub de pont ou du contrat de l'airdrop, vers n'importe quelle adresse, aussitôt ; chaque transfert a un identifiant séquentiel et un événement public. La programmation sur 48 heures du 2026-08-29 disparaît, avec sa fenêtre d'annulation, et le délai n'est pas un paramètre : une urgence agit dès que l'owner s'en sert, jamais après une attente. Le remplacement de l'adaptateur de pont, changeAdapter, est immédiat lui aussi, sous les mêmes épinglages. La règle des fonds d'airdrop bloqués, décidée les 2026-09-27 et 2026-09-28 et jamais codée, est close : le transfert immédiat la couvre, sur n'importe quel contrat, à tout moment.

Chaque chiffre devient un réglage. Les chiffres du protocole sont désormais des réglages onchain de l'owner, sur les deux chaînes, les valeurs actuelles étant celles par défaut : la taxe, sa répartition et l'anti-snipe ; les frais de création et les limites de longueur du nom, du ticker, de l'image et de la description d'un nouveau marché ; la forme des nouveaux marchés, commission de LP et tick spacing compris, et les parts de ce qu'encaissent les positions verrouillées ; le seuil de conversion et les bornes de prix des vaults ; le calendrier et les limites de l'airdrop ; les heartbeats des oracles ; le gas et le slippage du pont. Chacun prend effet aussitôt et refuse les valeurs impossibles. Un changement de la taxe vaut dès le swap suivant sur chaque pool, y compris un pool encore dans ses blocs anti-snipe ; un changement de forme vaut pour les marchés créés ensuite, et chaque pool garde la commission de LP et le tick spacing avec lesquels il a été créé.

Reste fixe : le préavis de 30 jours du mode fin, END_DELAY, dans le lock, qui ne peut pas être upgradé ; les unités et les encodages, du point de base à l'heure de l'enregistrement de la détention ; et le câblage, des feeds de prix aux endpoints du pont, que seul un upgrade peut faire pointer ailleurs.

Conséquence assumée : les chiffres que donne ce livre pour la taxe, le lancement, les vaults et l'airdrop sont des valeurs par défaut, pas des garanties ; l'owner peut changer chacun d'eux à tout moment, sans préavis. Mesuré : lire les réglages de la taxe coûte à un swap environ 1 200 gas de plus, à chaud. Voir Modèle de confiance.

2026-10-05 — Les correctifs de la boucle d'audit

Le même jour, une boucle de revue de tout le code a conduit à des correctifs des contrats, chacun avec son test de régression, pas déployés. Quatre d'entre eux changent le comportement.

Après un transfert d'urgence, les comptes sont soldés, jamais payés par un autre marché. Le contrat de l'airdrop et le hub distant détiennent des actifs dus à plusieurs marchés. Après un transfert hors de l'un d'eux, les paiements qui dépendent de ce qui manque attendent — les réclamations de l'airdrop sur cette action, les paiements du hub distant sur le rail USDG — jusqu'au retour des actifs ou jusqu'à ce que l'owner de StockFun passe la perte sur le cycle ou le marché qui l'a subie ; sur le rail canonique, l'owner retire l'enregistrement dont le transfert a pris le cash avant l'arrivée du dépôt suivant (depuis la quatrième boucle, une urgence chargée à l'enregistrement le fait dans le même appel). Un cycle ne peut être déprécié en partie que tant que personne n'y a réclamé l'action, chaque holder perdant alors la même part ; une fois des holders payés, il ne peut l'être que de tout le reste, que perdent les holders pas encore payés. Le transfert lui-même reste immédiat et sans condition. Voir Le mode urgence.

Seul le keeper fait passer le pont. Jusque-là, n'importe qui pouvait envoyer un batch de pont : un tiers pouvait en glisser un au minimum le plus lâche de l'adaptateur entre deux trades à lui sur le pool Curve, ou faire échouer le batch du keeper en faisant passer avant lui l'un de ses vaults.

Seuls le keeper et l'owner de StockFun pré-déploient un vault miroir sur Robinhood Chain. Jusque-là, n'importe qui le pouvait, même pour un marché qui n'existait pas encore, lié au câblage de ce jour-là. Un vault pré-déployé prend désormais le routeur d'actions et l'oracle du hub distant à son premier batch.

Un enregistreur de détention désigné sur un token vivant part de la supply du token hors du PoolManager, et un enregistreur qui a déjà enregistré ce token le refuse. Jusque-là, un enregistreur neuf partait de zéro : chaque vente échouait, et les parts d'airdrop des cycles ouverts ensuite étaient cassées pour de bon. Désormais le trading continue, et les holders que le nouvel enregistreur n'a pas vus bouger se lisent comme n'ayant rien détenu jusqu'à leur prochain mouvement. Upgrader l'enregistreur en place reste la règle.

Correctifs plus petits : les vaults tiennent le minimum du keeper sur ce qui arrive réellement, pas seulement leur propre borne ; le ticket comptable du pont canonique paie pour la taille du batch ; les réglages refusent un tick de lancement où aucun pool ne peut s'ouvrir, une limite de nom nulle, et un hook et un lock sur des PoolManager différents ; un passage à des fenêtres d'airdrop plus longues ne bloque plus un marché qui a des actions mises de côté. Voir Modèle de confiance.

2026-10-05 — Le second passage de la boucle d'audit

Le même jour, une seconde boucle de revue des correctifs a conduit à d'autres correctifs des contrats, chacun avec son test, pas déployés, et à des changements du keeper.

Les comptes se lisent sur ce qui les couvre, jamais sur le solde. Le solde du contrat de l'airdrop, et du hub distant sur le rail USDG, contient aussi des tokens arrivés mais pas encore crédités : une livraison dont la dernière étape n'a pas tourné. Après une urgence, de tels tokens pouvaient rouvrir les réclamations d'un cycle vidé et les payer avec les actions d'un autre marché. Les deux contrats comptent désormais eux-mêmes ce qui couvre leurs comptes. Un transfert d'urgence prend d'abord là-dessus ; ce qu'il prend au-delà venait de tokens pas encore crédités, et les livraisons suivantes le remboursent avant de couvrir quoi que ce soit, le hub distant enregistrant ces batches au lieu de les livrer. Les actifs reviennent par restore, que n'importe qui peut appeler ; un transfert simple ne couvre rien, si bien qu'on ne sort des tokens égarés que pour les rapporter. Voir Le mode urgence.

Après une passation en perte de tout le reste d'un cycle, qui suit une réclamation, ce qui arrive ensuite à ce cycle se partage au prorata entre tous ses holders, comme si le montant passé en perte n'y avait jamais été ; jusque-là, il allait d'abord aux holders pas encore payés, dans l'ordre de leurs réclamations. Une passation en perte qui ne laisse plus rien de côté pour un marché met fin à sa recherche d'une fenêtre.

Un token refuse un enregistreur de détention lié à un autre PoolManager d'Uniswap que celui de son pool, qui compterait le pool comme un holder et paierait à chaque holder une fraction de sa part. Et un enregistreur désigné sur un token vivant tient une supply enregistrée au moins égale au total des holders, et non égale, tant que chaque holder n'a pas bougé.

Sur le pont canonique, les frais se chiffrent à un base fee que nomme le keeper, deux fois le dernier, l'excédent étant rendu : un devis lu sans prix du gas voyait un base fee nul, et un long batch échouait. Le hub distant n'apprend le keeper que d'un batch : avant le premier, l'owner de StockFun pré-déploie le vault miroir du premier marché, et un marché dont le keeper ne peut pas pré-déployer le vault est laissé hors de son batch, avec une alerte. Le keeper compte séparément les échecs d'un vault sur Ethereum et sur Robinhood Chain.

Du second tour du 2026-10-01 : R2H-1 est couvert par les outils de règlement et par une procédure, l'owner mettant le hub distant en pause avant le transfert ; R2H-2 est atténué, toujours ouvert : un vault miroir gelé n'arrête plus la file canonique pour de bon, et seul le keeper compose un batch, mais une livraison à ce vault échoue toujours en entier : le keeper doit laisser ce marché dehors. Fermé le même jour par la quatrième boucle : la livraison non bloquante, plus bas. Voir Modèle de confiance.

2026-10-05 — La troisième boucle d'audit : les séquences

Le même jour, une troisième boucle de revue a porté sur les séquences : des opérations justes une à une, qui tournent mal dans un certain ordre. Ses correctifs, chacun avec son test, pas déployés.

restore rembourse d'abord ce qu'une urgence a pris au-delà de ce qui couvrait les comptes, sur le contrat de l'airdrop comme sur le hub distant, et ne couvre les comptes qu'avec le reste : rapporter les tokens d'une livraison en attente laissait payer avec eux le marché vidé. Une action passée en perte en entier avant toute réclamation quitte la liste du cycle, si bien qu'un crédit ultérieur ne l'y inscrit qu'une fois.

Un enregistreur désigné sur un token vivant part de la bascule : le contrat de l'airdrop ne mesure aucune fenêtre commencée avant, dont les actions attendent, mises de côté, la première fenêtre qu'il couvre en entier, et les tokens lui déclarent à la bascule le solde de l'adresse de burn. Jusque-là, une fenêtre à cheval sur la bascule versait trop à qui bougeait après. Procédure : ouvrir d'abord les cycles des fenêtres déjà fermées, puis basculer juste après la fin d'une fenêtre ; upgrader l'enregistreur en place reste la règle.

Le lancement de $STOCKFUN exclut son opérateur des cycles de $STOCKFUN, lui qui détient la supply entre la frappe et le lock. Les scripts du pont s'arrêtent avant de déployer quand la factory désigne déjà un autre hub, et posent les mappings des actions avant de désigner le hub ; la factory refuse un hub qui ne sait pas porter un basket déjà enregistré.

Hors chaîne : un envoi du keeper dont le reçu ne se lit pas est suivi par son hash, jamais renvoyé ; la veille des transferts d'urgence lit par tranches de 1 000 blocs et alerte une fois quand elle échoue, une fois quand elle reprend ; un marché dont l'entrée de batch ne se construit pas est laissé dehors, avec une alerte. Dans l'app, une transaction non confirmée garde son hash (depuis la dixième boucle d'audit, elle est suivie jusqu'à ce qui est miné à son nonce, une annulation dans le wallet n'étant jamais prise pour faite : plus bas) ; le cash en transit ne compte plus la perte du swap, le cash vidé d'un vault miroir ni les dettes passées en perte ; un pool récupéré par le mode fin n'affiche ni prix ni trading ; un réglage lu à sa valeur par défaut est signalé.

2026-10-05 — Une panne ne bloque jamais le reste

Le fondateur pose une règle de conception : « Quand il y a un truc qui fait buguer une fonction, il ne faut pas que ça pénalise les autres fonctions ; tout doit pouvoir continuer à marcher, et tout doit avoir des setters pour récupérer les fonds perdus et brancher la correction. »

L'isolation : une panne dans une fonction, un marché, une action, un cycle, un enregistrement ou un destinataire ne bloque jamais les autres ; l'élément est sauté, gardé comme dû ou différé, avec un événement, et le reste passe. Les leviers : chaque contrat qui peut détenir de l'ETH ou des tokens a un levier pour en sortir ce qui est coincé, et chaque module peut recevoir une correction, un upgrade ou un réglage qui le remplace. Les boucles d'audit comptent toute entorse comme un défaut.

Une seule exception, voulue : la déclaration de chaque transfert de token à l'enregistreur de détention reste bloquante. Une déclaration non bloquante laisserait un holder priver l'appel de gas, sur son propre transfert, pour que l'enregistrement le saute et que sa part de l'airdrop grossisse. Le levier est immédiat : setRecorder(0) sur le token, ou un upgrade en place de l'enregistreur, une transaction chacun.

Conséquences assumées : une part refusée attend son destinataire au lieu d'arrêter le reste, sur le hook, le lock, le hub distant et le contrat de l'airdrop ; un transfert d'urgence non attribué fait toujours attendre tout l'actif jusqu'à son règlement ; setRecorder(0) arrête l'enregistrement d'un token, et le contrat de l'airdrop n'ouvre alors aucun cycle de ce token, ses envois étant mis de côté, tant qu'aucun enregistreur n'est désigné. Voir Modèle de confiance.

2026-10-05 — La quatrième boucle d'audit : la règle dans le code

Le même jour, une quatrième boucle de revue a mis la règle dans le code. Ses correctifs, chacun avec son test, pas déployés.

Les livraisons du pont ne bloquent plus d'autre marché, décision prise sur R2H-2 : la part d'un vault miroir que le token de la jambe cash refuse attend sur le hub distant, due, et un sweep la lui paie une fois le vault dégelé ; sur le rail canonique, son enregistrement quitte la file pour une réserve à part, et les suivants sont payés. Sur le hub distant, une urgence peut se charger à un seul marché, en passant sa perte dans le même appel. Les réclamations paient ce qu'elles peuvent : une action à court ou refusée est différée, reste due, et les autres sont payées. Chaque jambe d'achat, chaque action envoyée à l'airdrop et chaque marché d'un batch de pont passent à part.

Un vault qui refuse l'ETH n'arrête plus son marché : le hook lui doit sa part, que n'importe qui lui paie ensuite, et l'erreur TreasuryTransferFailed disparaît ; le créateur peut réclamer vers une autre adresse. Le lock garde pour son destinataire une part de frais de LP refusée. La Lens lit chaque vault à part : un vault qui ne répond pas ne fait plus échouer sa page. Chaque contrat qui peut détenir des fonds a son levier, les rescues de l'owner du protocole, bornés là où le contrat garde des fonds pour d'autres.

Corrigés avec eux, de l'audit du 2026-09-29 : le routeur Ondo ne sert que les vaults de sa factory (M-7), et chaque routeur refuse d'être lui-même le destinataire (L-4). Le ticket de dépôt du rail canonique se chiffre au base fee ; le lancement de $STOCKFUN exclut son opérateur avant la frappe, si bien qu'aucune fenêtre ne peut se fermer entre les deux ; la vérification du stockage compare chaque niveau de chaque structure.

Tranchés le même jour et laissés tels quels : la rotation de l'adaptateur de pont (M-2), un adaptateur se changeant en l'upgradant en place, et la taxe d'achat prise en claims du PoolManager (R2F-1). Voir Le mode urgence et Le rail Robinhood.

2026-10-05 — L'étape de l'airdrop du keeper et l'écran de réclamation

Écrits le même jour, à la demande du fondateur, pas déployés. Une fois par fenêtre, après la séance US par défaut, le keeper place les actions mises de côté, ouvre le cycle et envoie les actions de chaque vault, chaque action et chaque marché à part, puis suit chaque livraison venue de Robinhood Chain jusqu'à son exécution sur Ethereum ; une livraison en retard lève une alerte qui donne la commande à rejouer. L'écran de réclamation de l'app liste ce que le wallet peut réclamer, fenêtre par fenêtre, et le réclame par lots de dix cycles, chaque appel simulé d'abord. Voir Le keeper et La dapp.

2026-10-05 — La cinquième boucle d'audit, et le keeper de la quatrième

Le même jour, une cinquième boucle de revue a trouvé 7 Low dans les contrats, tous corrigés, chacun avec son test, pas déployés. L'urgence chargée à un enregistrement canonique ne sert qu'à un enregistrement dont le dépôt est arrivé : elle refuse un montant au-delà du cash que ne retiennent pas les parts refusées, et un enregistrement dont le dépôt est perdu est retiré de la file. Une action dont le solde ne se lit pas, gelée par son émetteur, reste au vault quand les autres partent à l'airdrop. Le lock et les tokens, qui ne peuvent pas être upgradés, gagnent rescueClaims et rescueNft, pour des claims v4 ou un NFT envoyés par erreur ; le lock n'a toujours aucun appel générique. Les implémentations de la factory et du hub distant ont pour levier leur déployeur. Un marché du batch de pont dont le vault n'a pas de code est laissé dehors.

Le keeper suit la quatrième boucle : il paie à chaque passage ce que le hook et le lock doivent à un destinataire qui l'avait refusé, encaisse une fois par jour les frais de LP d'un pool qui en a, lit les jambes échouées, les marchés laissés hors d'un batch et les livraisons refusées et alerte sur ce qui reste bloqué, planifie chaque jambe à part, et garde son état dans un fichier, si bien qu'un redémarrage n'oublie rien. L'app affiche « Figures unavailable » pour une trésorerie dont le vault ne répond pas. Voir Le keeper.

2026-10-06 — Le passage hors chaîne de la cinquième boucle

La revue du code hors chaîne de la cinquième boucle a trouvé 3 Medium et 16 Low, tous corrigés le même jour, chacun avec son test, pas déployés. Le keeper se tient désormais à la cadence du 2026-09-27 : l'ETH d'un vault se convertit une fois par fenêtre de l'airdrop, si le vault détient le seuil au premier passage du keeper après la fermeture de la fenêtre ; en dessous, l'ETH attend la fenêtre suivante. Jusque-là, le keeper le convertissait à n'importe quel passage de la séance dès que le vault détenait le seuil. Chaque transaction qu'il envoie s'inscrit dans son fichier d'état, avec son nonce, avant que son reçu soit attendu, si bien qu'un keeper tué pendant l'attente ne la renvoie pas ; une transaction qu'aucun nœud ne connaît plus est abandonnée passé un délai ; un seul keeper à la fois utilise un dossier d'état. Sur le rail Ondo, un achat qu'Ondo cote sous la borne du vault n'est plus envoyé pour échouer, ni payé d'une attestation. Une action dont l'adaptateur ne répond pas reste seule hors d'un envoi à l'airdrop.

Un basket compte au plus cinq actions, une borne technique que l'owner de StockFun peut changer par un upgrade : chaque coût qui grandit avec un basket est mesuré jusqu'à cette taille, et les trois baskets du lancement comptent trois, trois et deux actions. La Lens porte l'état de chaque vault et ce que le hook et le lock lui doivent, et son upgrade passe avant le Worker et l'app qui la lisent. Le script de lancement de $STOCKFUN reprend un passage arrêté avant la désignation du marché du protocole, au lieu de frapper un second token. L'app compte dans la trésorerie l'ETH dû au vault, laisse à une réclamation la place d'une action créditée avant son inclusion, retient une action que son token a refusée, et n'affiche jamais comme « rien » un chiffre qu'elle n'a pas pu lire. Voir Le keeper, Les baskets et La dapp.

2026-10-06 — La sixième boucle d'audit

La sixième boucle a trouvé un défaut de gravité élevée et cinq mineurs, tous corrigés le même jour, chacun avec son test, pas déployés. Sur le pont canonique, celui du testnet et du repli, le keeper cessait de rejouer les tickets d'un batch dès que ses marchés étaient crédités, et un marché pouvait l'être avec le cash d'un autre batch : un dépôt qui avait manqué son exécution automatique pouvait expirer. Le keeper suit désormais chaque ticket jusqu'à le savoir exécuté, quel que soit le crédit de son transfert, rejoue celui qui est encore vivant, et alerte sur un ticket qu'il ne sait pas exécuté au bout de six heures par défaut, ou qui est perdu ; il ne crédite plus un transfert canonique que par les propres événements du hub distant, jamais par un solde.

Une décision accompagne les correctifs, prise par l'audit comme son choix par défaut recommandé : la cadence du 2026-09-27 s'étend à l'USDC. L'USDC d'un vault, acheté en actions sur Ethereum ou envoyé par le pont, part une fois après chaque conversion de son ETH, et au plus une fois par fenêtre sinon ; jusque-là, quelques unités d'USDC envoyées à un vault faisaient envoyer au keeper une transaction, ou tout un batch de pont, à chaque passage. Les achats sur Robinhood Chain restent à chaque passage : le solde d'un vault miroir ne distingue pas une livraison d'un don, et les plafonds de chaque achat étalent exprès une grosse livraison sur plusieurs passages. Les runs locaux et de testnet coupent les deux cadences avec la même variable.

Corrigés aussi : une recherche d'actions mises de côté qui ne placerait rien, pour un vault qui n'a rien à envoyer, ne part plus chaque jour ; un vault sous le seuil se vérifie sur un solde lu après la fermeture de la fenêtre ; plus tôt le même jour, le fichier de déploiement local est vérifié contre la chaîne avant usage ; l'app marque le prix de $STOCKFUN « (last read) » tant que la Lens ne sait pas lire sa trésorerie, et le worker n'en enregistre aucun prix pendant ce temps. La boucle suivante, ouverte le même jour, a trouvé que le keeper cotait tous les vaults sur un seul routeur alors que chacun garde celui de sa création : chaque vault se cote désormais sur le sien. Voir Le keeper.

Depuis la septième boucle, plus bas, « rien à envoyer » veut dire rien qui vaille d'être envoyé, et le keeper lit chaque ticket sur le reçu de sa création d'abord.

2026-10-06 — La septième boucle d'audit

La septième boucle a trouvé un défaut de gravité moyenne et quatorze mineurs, tous corrigés le même jour, chacun avec son test, pas déployés ; les contrats étaient sains. Sur le pont canonique, le keeper lisait si un ticket existait encore avant de lire le reçu de sa création : un dépôt créé entre les deux lectures, ou vu par deux nœuds à un bloc d'écart, pouvait passer pour exécuté alors qu'il était encore vivant, et expirer sans rejeu ni alerte. Il les lit désormais dans l'ordre du SDK Arbitrum : le reçu d'abord, puis l'exécution automatique, puis le ticket à un bloc au moins égal à celui de sa création.

Une décision accompagne les correctifs, prise par l'audit comme son choix par défaut recommandé : le keeper n'envoie à l'airdrop que ce qui vaut ce que son envoi coûte. Une action au-dessus de la poussière que le pont LayerZero ne transporte pas, valorisée avec l'oracle de son vault au dernier prix de son feed, doit valoir ses propres frais LayerZero, ou sa part du gas de l'envoi sur Ethereum, et les actions qui passent doivent ensemble valoir tout l'envoi, plus l'ouverture du cycle de la fenêtre quand il n'est pas encore ouvert ; sinon elles attendent au vault une fenêtre suivante, avec ce qui s'accumule. Une valeur que le keeper ne peut pas lire laisse partir l'envoi, comme avant. Le multiple est un réglage du keeper, KEEPER_AIRDROP_MIN_VALUE_BPS : une fois le coût par défaut, 0 coupant la règle. Elle ne décide que du moment où une action part, jamais de combien, de quoi ni d'où. Jusque-là, un don d'actions à peine au-dessus de la poussière au vault d'un marché que personne ne trade faisait ouvrir au keeper le cycle de la fenêtre et payer un message LayerZero chaque jour ; et la même poussière comptait comme « quelque chose à envoyer », si bien que la limite des recherches d'actions mises de côté de la sixième boucle ne valait pas sur le pont.

Corrigés aussi : chaque lecture de journaux du keeper s'arrête quelques blocs sous le dernier bloc de la chaîne, si bien qu'un événement n'est jamais manqué derrière un nœud en retard d'un bloc ou deux ; le keeper vérifie que chaque RPC sert la chaîne que nomme sa configuration, et refuse de démarrer sinon ; son webhook d'alerte a un délai et sa réponse est lue, une alerte qu'il ne prend pas étant gardée et renvoyée ; un réglage vide prend sa valeur par défaut. Le Worker de l'app lit chaque contrat upgradable à part, si bien qu'un contrat qui échoue après un upgrade raté n'efface plus les chiffres du token $STOCKFUN ; il dit le volume sur 24 h inconnu tant que ses lectures des journaux de trades échouent, vérifie que chacun de ses endpoints sert sa chaîne, et le graphique de prix place chaque point à son heure. Ni la Lens ni le schéma des données du Worker ne changent. Voir Le keeper et La dapp.

Depuis la huitième boucle, plus bas, une action au devis propre nul est demandée à son adaptateur, l'ouverture de la fenêtre n'est comptée qu'une fois sur le rail local, le ticket se lit quelques blocs sous le dernier, et le keeper ne suppose plus d'identifiant de chaîne quand aucun n'est fixé.

2026-10-06 — Les protections des prix sur Robinhood Chain

Le plan de lancement listait deux protections que l'oracle n'avait pas encore, toutes deux recommandées par la documentation de Robinhood. Elles sont construites, non déployées, comme des réglages de l'owner de StockFun éteints au départ ; chacune ne peut que retenir un prix, jamais en changer un.

  • Une opération sur titres. Pendant qu'une action en traverse une, son token dit son oracle en pause, et son feed de prix garde sa dernière valeur, qui peut sembler fraîche alors que le multiplicateur du token change. L'oracle retient désormais le prix de cette action : sa jambe d'achat échoue seule, son cash gardé pour elle, et les autres actions du basket sont achetées. Le contrôle est allumé pour chaque action du rail Robinhood, action par action, si bien qu'un token dont la réponse ne peut pas servir, ou dont le drapeau reste levé, peut être éteint seul. Un token qui ne répond pas compte comme non en pause : Robinhood dit le drapeau indicatif, et le contrôle de fraîcheur du feed reste la garde principale.
  • Le séquenceur. Robinhood Chain est une chaîne Arbitrum à séquenceur unique. Avec le feed de disponibilité du séquenceur L2 de Chainlink, l'oracle retient tous les prix tant que le séquenceur est arrêté, revenu depuis au plus le délai de grâce (une heure par défaut), ou que le feed ne se lit pas. Aucun feed de ce type n'existe pour Robinhood Chain, et Chainlink n'en ajoute plus sur de nouveaux réseaux : le rail est déployé avec ce contrôle éteint, par un choix explicite qu'exige le script de déploiement, et l'owner de StockFun posera le feed s'il est un jour publié.

Sur Ethereum, les deux restent éteintes. Voir Le rail Robinhood.

2026-10-06 — La huitième boucle d'audit

La huitième boucle a trouvé un défaut moyen et huit mineurs, chacun corrigé le même jour avec un test, non déployé ; les contrats et les protections des prix étaient propres. Avant que l'app ait lu le registre des baskets de la chaîne, son formulaire de lancement proposait les baskets de la configuration sous leurs numéros de configuration : sur un déploiement qui numérote ses baskets autrement, un créateur pouvait lancer un marché, de façon irréversible, sur un autre basket que celui montré. Le formulaire ne propose plus que les baskets lus sur la chaîne, et relit sur la factory celui qui est choisi juste avant l'envoi, qu'il refuse si le nom ou les actions diffèrent.

Corrigés aussi : le keeper ne suit un batch de pont qu'une fois le bloc qui le tient enfoncé de quelques blocs, si bien qu'une réorganisation des derniers blocs d'Ethereum ne peut plus le laisser suivre des identifiants qui n'existent pas ; il distingue la poussière que le pont ne transporte pas d'une action dont les frais LayerZero ne se cotent pas, qu'il laisse désormais de côté avec une alerte au lieu de l'abandonner en silence ; il garde une seule alerte en attente par alerte distincte, avec le nombre de fois et le moment où elle a été levée, si bien que des alertes répétées à chaque passage n'évincent plus une alerte unique ; il ne met plus de côté un fichier d'état écrit pour une autre chaîne avant d'avoir vérifié quelle chaîne sert son RPC, et exige les deux identifiants de chaîne ; il lit un ticket quelques blocs sous le dernier, un bloc que tous les nœuds d'un endpoint ont ; et, sur le rail local, il ne compte qu'une fois l'ouverture de la fenêtre. Le keeper, son préflight et son inspecteur connaissent les protections des prix : les achats sur Robinhood Chain attendent tant que l'oracle retient les prix pour le séquenceur, avec une alerte, et une action en pleine opération sur titres attend seule, sans jamais compter comme un échec. Le Worker de l'app ne fait plus jamais reculer sa fenêtre des trades, si bien qu'un nœud en retard de quelques blocs ne fait plus compter deux fois des blocs au volume sur 24 h ; son proxy RPC refuse toute adresse qui n'en est pas une ; il publie pourquoi le prix d'une action manque (schéma de données 8), ce que l'app affiche ; et le panneau de trade fait payer à un wallet de la whitelist la taxe normale pendant la fenêtre anti-snipe, comme le fait le hook. La Lens ne change pas, et le Worker et l'app se déploient toujours dans n'importe quel ordre. Voir Le keeper et La dapp.

2026-10-06 — Le gas des livraisons de l'airdrop sous Glamsterdam

Sepolia a activé la mise à niveau Glamsterdam d'Ethereum le 2026-10-06, pendant le test LayerZero sur testnet (plus bas) ; Hoodi et le mainnet n'avaient pas encore de date. Elle remet à prix la croissance de l'état : un slot de stockage neuf écrit à froid coûte 110 020 gas, contre 22 100 avant. Chaque chiffre de gas fixe des contrats et des scripts a été mesuré de nouveau sur Sepolia, et tous tiennent sauf une paire, le gas qu'une livraison de l'airdrop reçoit sur Ethereum : l'envoi du vault miroir ne portait qu'un seul chiffre de compose, 600 000, et le lzReceive de l'OFT de l'action ne recevait que ce qu'impose cet OFT. Chaque livraison du premier cycle du test a manqué de gas chez l'exécuteur de LayerZero et a été lancée à la main. La dixième boucle d'audit en a trouvé deux autres ensuite : le gas propre des scripts de déploiement et celui du compose du pont pour un lot (voir plus bas).

La décision du fondateur, le même jour : le keeper simule chaque livraison et choisit son gas, tenu dans des bornes que l'owner de StockFun fixe sur le hub distant ; une livraison encore bloquée est réexécutée par le keeper, et alertée au second échec. Dans le code, non déployé sur mainnet :

  • Le hub distant porte deux bornes de gas, chacune une valeur par défaut, un plancher et un plafond : le gas lzReceive en plus de ce qu'impose l'OFT de l'action, 650 000 entre 200 000 et 1 500 000 (setAirdropReceiveGas), et le gas du compose, 1 250 000 entre 600 000 et 4 000 000 (setAirdropComposeGas). Le sendToAirdrop(stocks, receiveGas, composeGas) du vault miroir envoie ce que le hub accorde pour ce que demande le keeper, zéro prenant la valeur par défaut ; l'appel avec les seules actions prend les deux valeurs par défaut. Un hub mis à jour depuis une version sans ces bornes refuse tout envoi jusqu'à ce que les deux soient posées
  • Le keeper simule le lzReceive et le compose de chaque action sur Ethereum, depuis l'adresse de l'endpoint, et demande le besoin plus 25 % ; quand une simulation ne peut pas tourner, il prend ce qu'ont utilisé les dernières livraisons du marché, puis les valeurs par défaut du hub. Une livraison qui reste stockée sur l'endpoint, une fois que l'exécuteur de LayerZero l'a fait échouer ou au bout de dix minutes, est relancée depuis la propre clé du keeper, avec son besoin simulé plus 25 %, au plus 4 000 000 gas par défaut ; une relance qui ne peut pas partir est alertée avec la commande pour la lancer à la main, et un second échec est alerté et jamais renvoyé
  • L'app réclame cinq fenêtres par transaction au lieu de dix, laisse 400 000 gas par action qu'une fenêtre peut encore recevoir, et ajoutait 150 000 gas à l'estimation de chaque écriture, ce que la neuvième boucle d'audit a remplacé par la limite propre de chaque écriture (plus bas)

Le correctif a été appliqué par upgrade au hub distant et au vault miroir du test sur testnet, et a porté ses cycles suivants. Voir Le keeper et Le rail Robinhood.

2026-10-06 — Le test LayerZero sur testnet

Le protocole a été déployé sur Sepolia et sur le testnet de Robinhood Chain, par les scripts de production ou des enveloppes de testnet qui gardent leur corps, en 167 transactions, toutes réussies, avec des actions, des adaptateurs et un USDG de test et des feeds simulés, et mené de 13:50 à 19:07 UTC : sept fenêtres horaires, l'ETH de chacune converti, passé par LayerZero et dépensé en actions de test ; six cycles de l'airdrop renvoyés par LayerZero, les quatre premiers réclamés en entier par cinq holders, chaque paiement exactement la part calculée à partir du module d'enregistrement ; les exercices d'incident joués sur des messages réels et rétablis comme documenté, sauf deux moitiés. Il prouve le code de StockFun sur les endpoints, le DVN et l'exécuteur réels de LayerZero. Il ne prouve ni la paire USDG de Paxos, ni les actions de Robinhood et leurs adaptateurs, ni de vrais feeds ou une vraie liquidité, ni le gas, les frais et la finalité du mainnet : reste un essai sur mainnet. Ce qu'il a trouvé dans le code de production est la remise à prix de Glamsterdam (plus haut) et une partie de la neuvième boucle d'audit (plus bas). Voir Le rail Robinhood.

2026-10-06 — La neuvième boucle d'audit, et la porte de vérification

La neuvième boucle a relu les correctifs de la huitième, et repris les constats de l'opérateur du test LayerZero sur testnet. Elle a trouvé deux défauts moyens, le gas des livraisons de l'airdrop (plus haut) et le Worker de l'app qui reconstruisait son basculement de RPC à chaque éviction de son objet Cloudflare, ce qui, pendant une panne du RPC principal, lui envoyait 37 requêtes en moins de sept minutes là où il en part désormais 8 ; et des défauts mineurs dans le keeper, le Worker et un script de testnet, chacun corrigé le même jour avec un test. Pendant une minute après l'une de ses propres transactions, le keeper lit ce qu'elle a changé, et estime le gas de ce qu'il envoie ensuite, au bloc de cette transaction, si bien qu'un nœud en retard d'un bloc ne fait plus du batch de pont envoyé juste après une conversion un faux échec ; chacune de ses transactions part avec son estimation de gas plus 25 % ; les alertes qu'il n'a pas pu livrer partent dans l'ordre de leur dernière levée ; un ticket que son propre rejeu a supprimé n'est plus alerté comme vivant ; une heure de chaîne qu'aucune date ne peut tenir se lit « an unknown time » ; il décode les erreurs de l'oracle ; et son préflight tourne sur le déploiement du test. Le Worker relève le numéro de bloc propre de Robinhood Chain.

Depuis cette boucle, un second agent relit chaque correctif avant qu'il soit poussé, contre les classes de défauts que les boucles précédentes ont trouvées, la plupart dans les correctifs de la boucle d'avant : la porte de vérification. Ce qu'elle trouve est corrigé comme un constat de revue, et ce correctif repasse par la porte. Dans cette boucle, elle a trouvé des défauts mineurs dans plusieurs correctifs, dont une relance qui croyait une alerte que n'importe qui peut émettre, désormais crue seulement de l'exécuteur de LayerZero, et une autre qui tranchait une relance échouée au bloc même de cette relance, ce qu'une réorganisation de ce bloc pouvait changer en faux second échec : elle attend désormais que ce bloc soit enfoncé de quelques blocs. Tous sont corrigés.

La revue du Worker et de l'app de la même boucle a ensuite trouvé encore un défaut moyen, les 150 000 gas que l'app ajoutait à l'estimation d'une écriture, trop courts quand un trade rencontre plus de stockage neuf que la marque horaire de l'offre, corrigé le même jour : chaque écriture reçoit désormais son estimation plus 50 000 gas, et un trade aussi le gas de chaque écriture que son estimation ne voit pas et qui peut encore arriver, lue au bloc de l'estimation ; une écriture dont l'estimation échoue part avec une limite fixe, et un lancement attend alors. Ses deux constats mineurs sont corrigés aussi : un pot qui est une estimation porte un « + » partout où il s'affiche, et une approbation illisible n'est jamais prise pour une absence d'approbation. La boucle n'est pas propre, et le compte des boucles propres reste à zéro. Voir Le keeper et La dapp.

2026-10-06 — La dixième boucle d'audit

La dixième boucle a relu les correctifs de la neuvième et le travail sur la mise à niveau Glamsterdam d'Ethereum, chaque zone sous un angle à elle : les contrats sous les nouveaux prix du gas, chaque message cross-chain qui échoue à destination, le keeper sur des mois, l'app avec de vrais wallets, et les documents face au code. Elle a trouvé trois défauts moyens et six mineurs, chacun corrigé le même jour avec un test, chaque correctif relu par la porte de vérification, rien de déployé sur mainnet. Les contrats sont propres sous l'angle du gas, sous les deux barèmes.

  • La procédure de déploiement sous Glamsterdam. Le déploiement documenté sur Ethereum ne pouvait pas réussir : l'outil de déploiement donnait à chaque transaction le gas de sa propre simulation locale, aux anciens prix, quand la création d'un contrat en demande désormais de quatre à sept fois plus. Chaque diffusion demande désormais au nœud le gas de chaque transaction, une fois la précédente minée ; joué sur Sepolia
  • Le gas du compose d'un batch du pont. La dernière étape d'un batch du pont sur Robinhood Chain recevait un seul chiffre de gas, 1 200 000, quel que soit le contenu du batch : quatre nouveaux marchés à cinq actions l'épuisaient, leur USDG restait alors sur le hub distant sans aucun enregistrement, et chaque batch suivant avec les mêmes marchés échouait de même. Désormais, ce gas est une base plus une part par marché, 200 000 et 400 000 par défaut, deux réglages de l'owner de StockFun, et un batch porte au plus 17 marchés, ce qui tient son message sous la limite de taille de LayerZero et son gas sous la limite de Robinhood Chain par transaction ; le keeper n'en envoie pas davantage et laisse les autres à son passage suivant, ceux qui attendent depuis le plus longtemps d'abord. Le gas en plus coûte peu : l'exécuteur de LayerZero le facture au prix du gas de Robinhood Chain, 0,00001 ETH par million de gas sur le testnet. L'alerte du keeper pour un transfert bloqué dit désormais où en est le message du batch. L'adaptateur du testnet a été mis à jour le même jour, et son batch suivant est passé au nouveau gas
  • La veille d'urgence. Le keeper nommait tous les vaults surveillés dans une seule requête de journaux, qu'un endpoint refuse au-delà de sa limite : une fois le registre trop grand, plus aucun transfert d'urgence n'aurait été relayé. Il les nomme désormais par groupes, bornés par un nouveau réglage
  • Des correctifs plus petits. Les messages d'erreur du keeper ne gardent d'une adresse de RPC que l'hôte ; ses lectures de chaque marché passent par Multicall3, cent marchés par appel, au lieu d'une à une à chaque passage ; l'app suit une transaction que le wallet annule ou remplace, sans jamais montrer une annulation comme faite, et pendant une minute après sa propre transaction lit à un bloc au moins égal à celui de cette transaction, si bien qu'une vente juste après son approbation n'est plus refusée par un nœud en retard d'un bloc ; et deux documents ont été remis à jour du code

Avec son réglage par défaut, qui envoie après la séance US, le keeper ne relit désormais un vault sans rien à envoyer après la clôture qu'à la séance suivante. Une conséquence de cette économie, et non une règle nouvelle : une action donnée à un vault après la clôture, hors de tout achat, part avec l'envoi de la séance suivante, dans une fenêtre plus tardive ; ce qu'apportent les achats du vault lui-même part toujours dans la fenêtre fermée ce jour-là.

L'audit a aussi pesé une recommandation, qui n'est pas un défaut : des réclamations qui laissent un wei dans chacun des cumuls de frais du hook, pour que le trade suivant ne paie jamais l'écriture de ces slots depuis zéro. Elle n'est pas retenue pour l'instant. Le gas en plus tombe sur le premier trade après chaque réclamation, environ 104 000 par slot, et la marge de l'app pour lui relève la limite d'un trade, pas ce qu'il paie ; le changement toucherait un code de réclamation dont les règles formelles ne pourraient pas être prouvées de nouveau sans passage au prover ; et le hook et le burner peuvent le prendre plus tard par upgrade, sans migration. La boucle n'est pas propre, et le compte des boucles propres reste à zéro. Voir Le keeper, Le rail Robinhood et La dapp.