Architecture
Le protocole vit sur deux chaînes. Ethereum porte le launchpad, les pools et les vaults ; Robinhood Chain porte les actions tokenisées.
Les contrats
Ce tableau décrit le protocole tel qu'il a été décidé. La partie lancement est dans le code
depuis le 2026-09-28, la distribution de l'airdrop depuis le 2026-10-04, et l'étape du
keeper et l'écran de réclamation depuis le 2026-10-05, rien n'étant déployé ; les
adaptateurs LayerZero des actions ne sont pas dans le dépôt. Les routeurs et
adaptateurs autres que le routeur de swap officiel n'y figurent pas : UniswapV4StockRouter
sur Ethereum, RobinhoodStockRouter sur Robinhood Chain et l'UsdgOftAdapter. Voir
État d'avancement. Depuis le 2026-10-02, chaque contrat du tableau
est upgradable, sauf les tokens, le lock de liquidité et les déployeurs : voir plus bas.
Les pourcentages du diagramme sont les réglages par défaut de la taxe.
| Contrat | Rôle | Cardinalité |
|---|---|---|
StockFunFactory |
Crée les marchés, perçoit les frais de création, tient le registre et les réglages de l'owner pour les nouveaux marchés et pour les vaults ; l'autorité d'upgrade de chaque module Ethereum | Un |
MarketDeployer · VaultDeployer |
Contournent la limite EIP-170 en portant le creation code du token et celui du proxy du vault ; sans état, remplacés via la factory plutôt qu'upgradés | Un chacun |
StockFunToken |
ERC-20 simple, non upgradable, sans mint et sans burn ; aucune logique propre au-delà d'un setter, setRecorder, et, depuis le 2026-10-05, de ses rescues, tous à l'owner du protocole ; déclare chaque changement de solde à l'enregistreur de détention |
Un par marché |
HoldingRecorder |
Enregistre, pour chaque token, la détention de chaque adresse dans le temps et la supply hors du PoolManager d'Uniswap ; un transfert échoue si son enregistrement échoue, la seule exception voulue à la règle d'isolation |
Un |
TreasuryVault |
Détient ETH, USDC puis actions ; conversion, chaque jambe à part, remise de ses actions à l'airdrop sur le rail local (sendToAirdrop), chaque action à part, et récupération d'urgence immédiate |
Un proxy par marché |
LiquidityLock |
Crée le pool et y dépose les deux positions, verrouillées, et garde la commission de LP et le tick spacing de chaque pool ; garde pour son destinataire une part de frais de LP refusée (payOwed) ; non upgradable ; sa seule sortie est le mode fin, 30 jours après son annonce |
Un |
StockFunHook |
Prélève la taxe sur chaque swap, applique l'anti-snipe, garde les réglages de la taxe et la whitelist de chaque pool, refuse la liquidité de quiconque sauf le lock, tient les soldes créateur, équipe et buyback jusqu'à leur réclamation, et ce qu'il doit à un vault qui a refusé sa part (payTreasury) |
Un |
StockFunSwapRouter |
Le routeur officiel des trades, le seul par lequel une adresse de la whitelist est exemptée de l'anti-snipe | Un, remplaçable par l'owner |
StockFunLens |
Lecture seule, agrège l'état d'un marché pour la dapp ; depuis le 2026-10-05, lit chaque vault à part, par un appel plafonné en gas (readVault), si bien qu'un vault qui ne répond pas ne fait plus échouer la page : il est marqué illisible (vaultReadable), garde son ETH, son basket et ses soldes lus sur les tokens, et les totaux disent combien de vaults ils laissent de côté (unreadableVaults). Depuis le 2026-10-06, sa page porte aussi le rail, la pause et l'USDC passé par le pont du vault, l'état du lock du pool, le module d'enregistrement du token, et ce que le hook et le lock doivent au vault : le service de données de l'app ne lit plus rien d'autre par marché, si bien qu'un vault qui brûle son gas ne fait échouer que ses propres chiffres. Depuis la septième boucle d'audit, ce service lit aussi chaque module upgradable (le hook, l'oracle, le contrat de l'airdrop, les deux hubs du pont) et le token du protocole chacun dans son groupe : un module qui brûle son gas, après un upgrade raté, ne fait échouer que ses propres chiffres |
Un |
TreasuryOracle |
Feeds Chainlink, inscrits une fois ; leurs heartbeats sont des réglages, et depuis le 2026-10-06 ses deux gardes sur Robinhood Chain : le contrôle du séquenceur, éteint tant que Chainlink ne publie pas de feed de disponibilité pour cette chaîne, et la pause d'oracle de chaque action, qui retient le prix de cette action pendant une opération sur titres | Un par chaîne |
BuybackBurner |
Rachète $STOCKFUN et l'envoie au burn. Aucun autre chemin pour son ETH tant que le pool $STOCKFUN est verrouillé |
Un |
BridgeHub · RemoteHub · RemoteTreasuryVault |
Le rail cross-chain, chaque marché d'un batch à part ; le hub distant est l'autorité d'upgrade sur Robinhood Chain et désigne la route de l'airdrop et l'adaptateur de chaque action ; le vault miroir envoie ses actions à l'airdrop (sendToAirdrop) |
Un, un, un proxy par marché |
StockFunProtocolToken |
$STOCKFUN, non upgradable comme un token de marché ; toute sa supply part dans sa position verrouillée |
Un |
AirdropDistributor |
Distribue, sur Ethereum, les actions achetées par chaque trésorerie aux holders de son token, par cycle quotidien ; ne crédite que les vaults du marché ; chaque holder réclame ; lié à la factory, qui le désigne | Un |
Proxies et upgrades
Depuis le 2026-10-02, chaque module est un proxy ERC-1967 placé devant une implémentation, upgradé en UUPS. Le proxy détient l'adresse et l'état ; l'implémentation détient le code, et un upgrade la remplace. Une implémentation ne peut jamais être initialisée : chaque proxy est initialisé dans son propre constructeur.
Chaque module demande à un contrat qui peut l'upgrader, son autorité d'upgrade, fixée dans
son implémentation : la factory sur Ethereum, qui répond par son owner, et le hub distant
sur Robinhood Chain, qui répond par son admin d'urgence, l'owner Ethereum tel que l'a porté
le dernier batch du pont. Un transfert de la propriété de la factory déplace donc d'un coup
le pouvoir d'upgrade de chaque module Ethereum, et celui des modules de Robinhood Chain avec
le batch suivant. Un upgrade est refusé si la nouvelle implémentation désigne une autre
autorité ; celles du hook et de l'enregistreur de détention doivent aussi garder le même
PoolManager.
Une implémentation n'est jamais utilisée directement. Depuis la cinquième boucle d'audit du 2026-10-05, celles de la factory et du hub distant, qui lisent leur admin dans le stockage du proxy, où personne ne le détient sur l'implémentation (l'owner de celle de la factory est l'adresse morte que pose son constructeur, l'admin de celle du hub vaut zéro), ont pour levier leur déployeur, pour un actif envoyé par erreur à leur propre adresse.
Non upgradables : les tokens, le lock de liquidité et, sur Robinhood Chain, le déployeur
des vaults miroirs, de l'adresse duquel est dérivée celle de chaque vault miroir.
MarketDeployer et VaultDeployer ne détiennent aucun état : la factory les remplace
(setDeployers) plutôt que de les upgrader.
Le stockage s'allonge, il n'est jamais réordonné : la disposition de chaque module est
enregistrée dans contracts/storage-layouts/, et contracts/script/check-storage-layouts.sh
la compare avant chaque upgrade. Depuis le 2026-10-05, il compare chaque niveau de chaque
structure, taille comprise, et tient pour immuable la structure d'un élément de tableau :
seule une structure valeur d'un mapping, ou la dernière variable, peut grandir à sa fin.
Qui peut upgrader, et à quelle vitesse : voir Modèle de confiance.
Le contrat de l'airdrop
Depuis le 2026-10-04, AirdropDistributor distribue les actions de chaque trésorerie sur
Ethereum. C'est un module upgradable comme les autres, un proxy dont l'autorité d'upgrade
est la factory. La factory le désigne (setAirdropDistributor) et les vaults le lisent en
direct ; un contrat remplacé garde chacun de ses cycles réclamable chez lui. Il lit la
détention dans l'enregistreur de détention, et ses règles sont dans
L'airdrop.
Deux chemins y mènent, et rien d'autre ne crédite un cycle :
- Le rail du pont.
sendToAirdropsur le vault miroir envoie chaque action listée par l'adaptateur LayerZero de cette action, que le hub distant désigne (setStockAdapter), vers le contrat que le hub distant désigne (setAirdrop), avec l'identifiant du marché en charge utile. Sur Ethereum, l'OFT de l'action frappe l'action wrappée au contrat, et l'endpoint de LayerZero l'appelle. Il ne crédite la livraison que si elle vient d'un OFT d'action enregistré par l'owner, de Robinhood Chain, envoyée par le vault miroir que le hub de pont dérive pour ce marché. - Le rail local.
sendToAirdropsur le vault Ethereum approuve les montants exacts, le contrat les tire, depuis le seul vault du marché, et crédite ce qu'il a reçu ; les approbations sont refermées. Un vault câblé au hub de pont refuse cet appel.
Le keeper déclenche les deux, et ne décide que du moment ; sur le rail du pont, depuis le 2026-10-06, aussi du gas que chaque livraison reçoit sur Ethereum, que le hub distant tient dans les bornes que l'owner de StockFun y fixe.
Les propriétés structurelles
Une panne ne bloque jamais le reste. C'est la règle de conception du 2026-10-05. Une
fonction, un marché, une action, un cycle, un enregistrement ou un destinataire qui échoue
est sauté, gardé comme dû ou différé, avec un événement, et le reste passe : le batch de pont
emporte les autres marchés, l'achat les autres jambes, l'envoi à l'airdrop les autres
actions, la réclamation les autres actifs ; le hook doit à un vault qui refuse sa part, et le
lock garde pour lui la part qu'il refuse. Chaque contrat qui peut détenir de l'ETH ou des
tokens a un levier pour en sortir ce qui est coincé — le mode urgence sur les vaults, les
hubs et le contrat de l'airdrop, rescue sur les autres —, et chaque module peut recevoir
une correction. Une seule exception, voulue : la déclaration d'un transfert de token à
l'enregistreur de détention reste bloquante, sans quoi un holder pourrait la faire sauter en
privant l'appel de gas, sur son propre transfert, pour grossir sa part de l'airdrop ; son
levier, setRecorder(0) ou un upgrade en place de l'enregistreur, est immédiat. Voir
Modèle de confiance et Le mode urgence.
Le hook est unique. Un seul contrat sert tous les pools, ce qui évite de miner une adresse par marché — l'adresse d'un hook v4 encode ses permissions dans ses bits de poids faible, et la trouver coûte du calcul. Depuis le 2026-10-02, c'est un proxy dont l'adresse porte les 14 permissions v4 : un upgrade garde cette adresse, et une implémentation future peut utiliser n'importe quel callback.
La liquidité n'est pas un NFT. Elle est détenue directement dans le PoolManager,
indexée par l'adresse du lock. Il n'y a donc pas de position à transférer, pas d'approve
à révoquer, pas de tokenId à perdre. Les seules opérations de liquidité que le contrat
sache faire sont un modifyLiquidity de delta exactement nul, pour encaisser les frais,
et, par le mode fin, le retrait de toutes les positions d'un pool, 30 jours après
l'annonce de la fin.
Seul le lock ajoute de la liquidité. Depuis le 2026-10-01, le hook refuse toute autre position dans un pool StockFun : chaque trade est un swap contre les positions verrouillées, et paie la taxe.
Les vaults n'ont pas de retrait. Aucune fonction ne permet à quiconque d'envoyer les actifs d'un vault vers une adresse de son choix. Les deux sorties sont l'airdrop, dont la seule destination est le contrat de l'airdrop que désigne le protocole, qui paie les holders du token au prorata selon une règle que personne ne choisit, et le mode urgence, par lequel l'owner de StockFun déplace un actif vers n'importe quelle adresse, aussitôt. Ce sont les règles de l'implémentation actuelle : l'owner de StockFun peut upgrader un vault, avec effet immédiat.
Les bornes sont des réglages, les feeds non. Les bornes de prix des vaults, 50 et 200 points de base par défaut, sont des réglages de l'owner de StockFun, lus en direct par chaque vault ; le keeper ne peut que les resserrer. Le registre de feeds inscrit chaque feed une fois ; les heartbeats des feeds sont des réglages, et depuis le 2026-10-06 ses deux gardes aussi, qui ne peuvent que retenir un prix, jamais en changer un : le contrôle du séquenceur (éteint sur Robinhood Chain tant que Chainlink n'y publie pas de feed de disponibilité) et la pause d'oracle de chaque action (allumée pour chaque action de Robinhood Chain ; voir Le rail Robinhood). Le mode urgence ne touche ni les uns ni les autres : il déplace des actifs, il ne change pas les règles d'exécution. Le registre, comme les vaults, peut être upgradé par l'owner de StockFun.
Les packages hors chaîne
| Package | Rôle |
|---|---|
shared/ |
ABIs générées, constantes du protocole, formatage. Source unique partagée par tout le reste |
backend/ |
Service de prix. Isole la seule dépendance externe de la dapp |
keeper/ |
Conversion, batches de pont, routage distant, surveillance des livraisons, l'étape de l'airdrop, les dettes du hook et du lock, les frais de LP |
L'app vit à côté de projet/, à la racine du dépôt, depuis le 2026-10-02 : cairn-app/,
le front Next.js, et cairn-worker/, le Worker qui lit les contrats une fois pour tous et
pousse les changements aux pages ouvertes. Elle remplace la dapp React + Vite provisoire de
projet/dapp/, retirée ce jour-là. Voir La dapp.