Arquitectura

El protocolo vive en dos cadenas. Ethereum aloja el launchpad, los pools y los vaults; Robinhood Chain aloja las acciones tokenizadas.

graph TD subgraph ETH[Ethereum] F[StockFunFactory] -->|despliega| TK[StockFunToken] F -->|despliega| V[TreasuryVault] F --> LL[LiquidityLock] TK -->|cada transferencia| HR[HoldingRecorder] LL -->|dos posiciones bloqueadas| PM[Uniswap v4 PoolManager] PM --> H[StockFunHook] H -->|2 %| V H -->|2 %| CR[Creador] H -->|0.5 %| TW[Wallet del equipo] H -->|0.5 %| BB[BuybackBurner] BB -->|compra + burn| SF[$STOCKFUN] V -->|ETH → USDC → USDG| BH[BridgeHub] V -->|sendToAirdrop, rail local| AD[AirdropDistributor] AD -.lee.-> HR AD -->|reclamaciones, a prorrata| HO[Holders del token] L[StockFunLens] -.lee.-> F O[TreasuryOracle] -.limita.-> V end subgraph RH[Robinhood Chain] RH2[RemoteHub] --> MV[Vault espejo por mercado] MV -->|pools secundarios| ST[Stock Tokens] end BH -->|LayerZero, OFT de USDG| RH2 ST -->|sendToAirdrop, OFT de acciones| AD K[Keeper offchain] -.dispara.-> V K -.dispara.-> BH

Los contratos

Esta tabla describe el protocolo tal como se ha decidido. La parte del lanzamiento está en el código desde el 2026-09-28, y la distribución del airdrop desde el 2026-10-04, no desplegada; los adaptadores LayerZero de las acciones no están en el repositorio. Se omiten los routers y los adaptadores distintos del router de swap oficial: UniswapV4StockRouter en Ethereum, RobinhoodStockRouter en Robinhood Chain y el UsdgOftAdapter. Consulta Estado de avance. Desde el 2026-10-02, cada contrato de la tabla es upgradeable, salvo los tokens, el lock de liquidez y los deployers: ver más abajo. Los porcentajes del diagrama son los ajustes por defecto del impuesto.

Contrato Rol Cardinalidad
StockFunFactory Crea los mercados, cobra la comisión de creación, mantiene el registro y los ajustes del owner para los nuevos mercados y para los vaults; la autoridad de upgrade de cada módulo de Ethereum Uno
MarketDeployer · VaultDeployer Sortean el límite de EIP-170 llevando el código de creación del token y el del proxy del vault; no tienen estado, se sustituyen a través de la factory en lugar de recibir un upgrade Uno cada uno
StockFunToken ERC-20 simple, no upgradeable, sin mint y sin burn; sin lógica propia más allá de un único setter, setRecorder, y sus rescues, para el owner del protocolo; notifica cada cambio de saldo al registrador de tenencias Uno por mercado
HoldingRecorder Registra, para cada token, las tenencias de cada dirección a lo largo del tiempo y el supply fuera del PoolManager de Uniswap; una transferencia falla si su registro falla, la única excepción deliberada a la regla de aislamiento que se describe más abajo Uno
TreasuryVault Contiene ETH, USDC y luego acciones; conversión, cada tramo de compra por separado, entrega de sus acciones al airdrop en el rail local (sendToAirdrop), cada acción por separado, y recuperación de emergencia inmediata Un proxy por mercado
LiquidityLock Crea el pool y deposita las dos posiciones, bloqueadas, y guarda la comisión de LP y el tick spacing de cada pool; guarda para su destinatario la parte rechazada de un cobro de comisiones; no upgradeable; su única salida es el modo de cierre, 30 días después de anunciarse Uno
StockFunHook Cobra la comisión en cada swap, aplica el anti-snipe, guarda los ajustes del impuesto y la whitelist de cada pool, rechaza la liquidez de cualquiera que no sea el lock, mantiene los saldos de los creadores, del equipo y del buyback hasta que se reclaman, y lo que se le debe a un vault que rechazó su parte Uno
StockFunSwapRouter El router oficial para los trades, el único a través del cual una dirección de la whitelist queda exenta del anti-snipe Uno, reemplazable por el owner
StockFunLens Solo lectura; agrega el estado de un mercado para la dapp, leyendo cada vault por separado; desde el 2026-10-06, su página lleva también el rail del vault, su estado de pausa y el USDC que ha enviado por el bridge, el estado de bloqueo del pool, el registrador de tenencias del token, y lo que el hook y el lock deben al vault Uno
TreasuryOracle Feeds de Chainlink, escritos una sola vez; sus heartbeats son ajustes y, desde el 2026-10-06, también sus dos salvaguardas en Robinhood Chain: la comprobación del secuenciador, desactivada hasta que Chainlink publique un feed de disponibilidad para esa cadena, y la pausa del oráculo de cada acción, que retiene el precio de esa acción durante una operación corporativa Uno por cadena
BuybackBurner Compra $STOCKFUN y lo envía al burn. Mientras el pool de $STOCKFUN esté bloqueado, no existe otro camino Uno
BridgeHub · RemoteHub · RemoteTreasuryVault El rail cross-chain; el hub remoto es la autoridad de upgrade en Robinhood Chain y designa la ruta del airdrop y el adaptador de cada acción; el vault espejo envía sus acciones al airdrop (sendToAirdrop) Uno, uno, un proxy por mercado
StockFunProtocolToken $STOCKFUN, no upgradeable, como un token de mercado; todo su supply va a su posición bloqueada Uno
AirdropDistributor Distribuye, en Ethereum, las acciones que compró cada tesorería a los holders de su token, por ciclo diario; solo abona lo que llega de los vaults del propio mercado; cada holder reclama, y una reclamación paga todas las acciones que puede; ligado a la factory, que lo designa Uno

Proxies y upgrades

Desde el 2026-10-02, cada módulo es un proxy ERC-1967 delante de una implementación, con upgrades mediante UUPS. El proxy guarda la dirección y el estado; la implementación guarda el código, y un upgrade la sustituye. Una implementación no se puede inicializar nunca: cada proxy se inicializa dentro de su propio constructor.

Cada módulo pregunta a un contrato quién puede hacerle un upgrade, su autoridad de upgrade, fijada en su implementación: la factory en Ethereum, que responde con su owner, y el hub remoto en Robinhood Chain, que responde con su admin de emergencia, el owner de Ethereum tal como lo llevó el último lote del bridge. Por tanto, una transferencia de la propiedad de la factory traslada de golpe el poder de upgrade de todos los módulos de Ethereum, y el de los módulos de Robinhood Chain con el lote siguiente. Un upgrade se rechaza si la nueva implementación designa otra autoridad; las del hook y del registrador de tenencias también deben conservar el mismo PoolManager.

No son upgradeables: los tokens, el lock de liquidez y, en Robinhood Chain, el deployer de los vaults espejo, de cuya dirección se deriva la de cada vault espejo. MarketDeployer y VaultDeployer no tienen estado: la factory los sustituye (setDeployers) en lugar de hacerles un upgrade.

El almacenamiento se amplía por el final, nunca se reordena: la disposición de cada módulo está registrada en contracts/storage-layouts/, y contracts/script/check-storage-layouts.sh la compara antes de cada upgrade. Desde el 2026-10-05 compara cada nivel de cada struct, tamaños incluidos, y rechaza cualquier cambio en un struct que sea el elemento de un array de almacenamiento: solo un struct que sea el valor de un mapping, o la última variable de estado, puede crecer, por su final.

Un contrato de implementación nunca se usa directamente, pero lo que se envía por error a su propia dirección también tiene una palanca. La mayoría de las implementaciones leen su autoridad de un immutable, así que el owner del protocolo lo saca; las de la factory y del hub remoto guardan su admin en el almacenamiento del proxy, así que en sus implementaciones la palanca es la dirección que las desplegó.

Quién puede hacer upgrades, y con qué rapidez: consulta Modelo de confianza.

El contrato del airdrop

Desde el 2026-10-04, AirdropDistributor distribuye las acciones de cada tesorería en Ethereum. Es un módulo upgradeable como los demás, un proxy cuya autoridad de upgrade es la factory. La factory lo designa (setAirdropDistributor) y los vaults lo leen en directo; un distribuidor sustituido conserva cada ciclo reclamable allí donde está. Lee las tenencias del registrador de tenencias, y sus reglas están en El airdrop.

Dos caminos llegan a él, y nada más abona un ciclo:

  • El rail del bridge. El sendToAirdrop del vault espejo envía cada acción de la lista a través del adaptador LayerZero de esa acción, que designa el hub remoto (setStockAdapter), hacia el distribuidor que designa el hub remoto (setAirdrop), con el identificador del mercado como carga útil. En Ethereum, el OFT de la acción acuña la acción wrapped al distribuidor, y el endpoint de LayerZero lo llama. Solo abona la entrega si viene de un OFT de acción que registró el owner, desde Robinhood Chain, enviada por el vault espejo que el hub del bridge deriva para ese mercado.
  • El rail local. El sendToAirdrop del vault de Ethereum aprueba los montos exactos, el distribuidor los retira, solo del vault del propio mercado, y abona lo que ha recibido; las aprobaciones se vuelven a cerrar. Un vault conectado al hub del bridge rechaza esta llamada.

El keeper dispara los dos, y solo decide cuándo; en el rail del bridge, desde el 2026-10-06, también el gas que recibe cada entrega en Ethereum, que el hub remoto mantiene dentro de los límites que fija allí el owner de StockFun.

Propiedades estructurales

El hook es un singleton. Un solo contrato sirve a todos los pools, lo que evita minar una dirección por mercado: la dirección de un hook v4 codifica sus permisos en sus bits bajos, y encontrar una cuesta cómputo. Desde el 2026-10-02 es un proxy cuya dirección lleva los 14 permisos v4: un upgrade conserva esa dirección, y una implementación posterior puede usar cualquier callback.

La liquidez no es un NFT. Se mantiene directamente en el PoolManager, indexada por la dirección del lock. No hay posición que transferir, ni approve que revocar, ni tokenId que perder. Las únicas operaciones de liquidez que puede hacer el contrato son un modifyLiquidity con un delta de exactamente cero, para cobrar las comisiones, y, a través del modo de cierre, la retirada de todas las posiciones de un pool, 30 días después de anunciarse el cierre.

Solo el lock añade liquidez. Desde el 2026-10-01, el hook rechaza cualquier otra posición en un pool de StockFun, así que todo trade es un swap contra las posiciones bloqueadas, y paga el impuesto.

Los vaults no tienen retiro. Ninguna función permite a nadie enviar los activos de un vault a una dirección de su elección. Las dos salidas son el airdrop, cuyo único destino es el contrato del airdrop que designa el protocolo, que paga a los holders del token a prorrata según una regla que nadie elige, y el modo de emergencia, por el que el owner de StockFun mueve un activo a cualquier dirección, de inmediato. Esas son las reglas de la implementación actual: el owner de StockFun puede hacer un upgrade de un vault, con efecto inmediato.

Los límites son ajustes, los feeds no. Los límites de precio de los vaults, 50 y 200 puntos básicos por defecto, son ajustes del owner de StockFun, que cada vault lee en directo; el keeper solo puede estrecharlos. El registro de feeds escribe cada feed una sola vez; los heartbeats de los feeds son ajustes, y también lo son, desde el 2026-10-06, sus dos salvaguardas, que solo pueden retener un precio, nunca cambiarlo: la comprobación del secuenciador (desactivada en Robinhood Chain hasta que Chainlink publique un feed de disponibilidad para ella) y la pausa del oráculo de cada acción (activada para todas las acciones de Robinhood Chain; ver El rail de Robinhood). El modo de emergencia no toca ni lo uno ni lo otro: mueve activos, no cambia las reglas de ejecución. El registro, como los vaults, puede recibir un upgrade del owner de StockFun.

Aislamiento y palancas

El 2026-10-05, el fundador fijó una regla de diseño: cuando algo hace fallar una función, las demás funciones no deben pagar por ello; todo debe poder seguir funcionando, y todo debe tener una palanca para recuperar los fondos perdidos y recibir la corrección. El cuarto bucle de auditoría de ese día la llevó al código, y desde entonces los bucles de auditoría cuentan como un defecto cualquier infracción de sus dos partes.

Aislamiento. Un fallo en una función, un mercado, una acción, un ciclo, un registro o un destinatario nunca bloquea a los demás: el elemento se omite, se guarda como adeudado o se difiere, con un evento, y el resto sigue.

  • Un lote del bridge deja fuera un mercado cuyo vault no puede liberar su efectivo (MarketSkipped), y los demás cruzan. Una entrega que el token de efectivo rechaza a un vault espejo se queda en el hub remoto, adeudada a ese mercado (DeliveryRefused), y los demás mercados cobran
  • Una compra ejecuta el tramo de cada acción por separado (LegFailed), y un envío al airdrop, cada acción por separado (AirdropSendFailed)
  • Una reclamación paga todas las acciones que puede y difiere las demás (ClaimDeferred)
  • Un vault que rechaza ETH ya no detiene el trading de su mercado: el hook guarda lo que no pudo pagar como adeudado a ese vault (treasuryOwed), y el lock guarda para su destinatario la parte rechazada de un cobro de comisiones (vaultOwed, creatorOwed). Cualquiera las paga en cuanto el destinatario vuelve a aceptar ETH (payTreasury, payOwed), y el keeper lo hace en cada ciclo
  • La Lens lee cada vault en su propia llamada, así que un vault que no puede responder deja legibles a los demás (vaultReadable). Desde el 2026-10-06, esa llamada lleva también el rail del vault, su estado de pausa y el USDC que ha enviado por el bridge, y la página lleva lo que el hook y el lock deben al vault: el servicio de datos de la app no lee nada más por mercado, así que un vault que consume todo su gas solo hace fallar sus propias cifras. Desde el séptimo bucle de auditoría, ese servicio también lee cada módulo upgradeable (el hook, el oráculo, el contrato del airdrop, los dos hubs del bridge) y el token del protocolo en un grupo propio, así que un módulo que consume todo su gas, tras un upgrade defectuoso, solo hace fallar sus propias cifras

Palancas. Cada contrato que puede guardar ETH o tokens, aunque sea de paso o por error, tiene una palanca para sacar lo que se queda atascado, y cada módulo puede recibir una corrección: un upgrade, o un setter que sustituye el módulo.

  • Los módulos que no guardan nada de nadie entre transacciones —la factory, la Lens, los oráculos, el registrador de tenencias, los routers de acciones, el router de swap oficial y los adaptadores del bridge— tienen el rescue(asset, amount, to) del owner del protocolo
  • Los módulos que llevan sus propias cuentas —los vaults, los dos hubs y el contrato del airdrop— tienen el modo de emergencia
  • El rescue del hook solo toma lo que se le envió por error, nunca lo que debe. El del lock nunca toma las partes que guarda, ni las posiciones, a las que solo llega el modo de cierre. El del BuybackBurner solo toma su ETH cuando ningún burn pueda ya gastarlo. Los tokens, que no son upgradeables, pueden sacar lo que se envió a su propia dirección
  • Sin palanca, por diseño: los dos deployers de Ethereum y el deployer de los vaults espejo, que no guardan estado, no aceptan ETH y no tienen owner

Detalle en Modo de emergencia.

La única excepción deliberada. La notificación de un token a su registrador de tenencias sigue siendo bloqueante: si la notificación falla, la transferencia falla. Una notificación a la que se permitiera fallar dejaría que un holder la privara de gas en su propia transferencia, de modo que el registro omitiera el movimiento y su parte del airdrop creciera. La palanca es inmediata, de una transacción cada una: setRecorder(0) en el token, que detiene su registro, o un upgrade del registrador en el propio contrato. Consulta El airdrop.

Paquetes offchain

Paquete Rol
shared/ ABIs generadas, constantes del protocolo, formateo. Una única fuente compartida por todo lo demás
backend/ Servicio de precios. Aísla la única dependencia externa de la dapp
keeper/ Conversión, lotes de bridge, enrutamiento remoto, vigilancia de las entregas y, desde el 2026-10-05, el paso del airdrop, el pago de lo que el hook y el lock guardan para un destinatario, y el cobro de las comisiones de LP
cairn-app/ La app, un front end en Next.js, junto a projet/ desde el 2026-10-02, cuando sustituyó a la dapp React + Vite: consulta La dapp
cairn-worker/ La capa de datos de la app, en el edge: lee el protocolo una sola vez para todas las páginas abiertas y les envía los cambios