Implantação
O protocolo é implantado em duas chains, numa ordem que não é negociável.
As carteiras
Três papéis distintos, nunca a mesma chave.
| Carteira | O que pode fazer |
|---|---|
| Owner do protocolo | Fazer o upgrade de todos os módulos, exceto os tokens, o lock de liquidez e o deployer dos vaults espelho; conectar a factory; registrar baskets; definir os endereços write-once; mudar os parâmetros do protocolo; definir as listas de exclusão do airdrop e registrar o OFT de cada ação; retirar ativos numa emergência, de imediato; retirar o que um módulo detém por engano (rescue); iniciar ou cancelar o modo de encerramento, e recuperar a liquidez depois de transcorridos os seus 30 dias |
| Keeper | Acionar conversões, lotes da bridge e o airdrop: abrir ciclos, enviar ações, alocar as ações guardadas à parte. Desde 2026-10-05, também pagar o que o hook e o lock guardam para um destinatário e coletar as taxas de LP, chamadas abertas a qualquer pessoa |
| Deployer | Implantar os contratos. É o owner da factory até que o owner do protocolo aceite a propriedade, e administra o hub remoto até que o primeiro lote da bridge designe ali o owner do protocolo |
Os scripts obtêm o seu signatário da linha de comando do forge ou de uma
DEPLOYER_PRIVATE_KEY bruta no ambiente. DeployEthereumRail, DeployProtocol,
DeployRemote e DeployBridge aceitam qualquer um dos dois; LaunchProtocol,
RegisterBaskets e CreateMarket só leem DEPLOYER_PRIVATE_KEY.
Toda transmissão (broadcast) é feita com --slow --skip-simulation, desde o décimo loop de
auditoria. Sem eles, o forge dá a cada transação o gas que a sua própria simulação contou, aos
preços anteriores à atualização Glamsterdam do Ethereum, e uma criação de contrato precisa de
quatro a sete vezes mais depois dela: cada criação ficaria sem gas. Com eles, o forge usa a
estimativa do nó para cada transação, depois de minerada a anterior. Os números de gas de um
dry run também não servem de orçamento: com a Glamsterdam, o DeployProtocol precisa de cerca
de 240 milhões de gas, e o lançamento de cada mercado de 15 a 24 milhões.
A ordem
Três restrições a determinam. Todo módulo do Ethereum recebe o endereço da factory na construção, então a factory vem primeiro, e o seu owner conecta o resto a ela depois. O router e o oráculo do trilho no Ethereum, como o hub da bridge, estão vinculados à factory, então vêm depois do protocolo. Os dois hubs fixam um ao outro por endereço previsto.
DeployProtocol: a factory primeiro, depois o hook, minerado para todas as 14 permissões v4, o lock, o registrador de posições, os deployers, a implementação dos vaults, a lens, o swap router e o contrato do airdrop,AirdropDistributor, todos conectados à factory. A propriedade passa então para o owner do protocolo, que precisa aceitá-laDeployEthereumRail, com o endereço da factory: o oráculo e o router ETH → USDC no Ethereum, que o owner define na factoryDeployBridge --sig "predict()": imprime os endereços que o hub da bridge e o seu adaptador vão receberDeployRemotena Robinhood Chain: primeiro o hub remoto, fixado nesses endereços previstos, depois o router de ações, o oráculo e a implementação dos vaults espelho, que o admin do hub conecta a ele, com a rota do airdrop e os adaptadores de ações quando eles forem informados (abaixo). Desde 2026-10-06, ele define em toda execução as duas políticas de gas do hub remoto para as entregas do airdrop no Ethereum, antes da rota do airdrop (abaixo), e as duas salvaguardas do oráculo, e se recusa a iniciar sem uma decisão sobre a verificação do sequenciador:SEQUENCER_UPTIME_FEED, o feed de disponibilidade do sequenciador L2 da Chainlink na Robinhood Chain, ouSEQUENCER_CHECK_OFF=true, a verificação desligada por escolha, nunca os dois, comSEQUENCER_GRACE_PERIOD(3.600 segundos por padrão) só ao lado de um feed. A Chainlink não publica nenhum feed assim para a Robinhood Chain, então uma execução na mainnet hoje defineSEQUENCER_CHECK_OFF=true, e o owner do StockFun define o feed depois (setSequencerUptimeFeed) se algum for publicado. O script então liga a pausa do oráculo de cada ação (setOraclePauseCheck), depois do hub, do router e do oráculo, para que nenhum endereço previsto mudeDeployBridge: o hub da bridge e o seu adaptador, nos endereços previstos; o owner designa o adaptador no hub, uma única vez (setAdapter). Desde 2026-10-05, o script para antes de implantar qualquer coisa quando a factory já designa um hub (desde 2026-10-06, é a sua primeira verificação, antes dos endereços previstos) e, quando o seu signatário é o owner da factory, mapeia as ações antes de designar o hubsetBridgeHub— antes do primeiro mercado. Desde 2026-10-05, ele recusa um hub que não consegue transportar um basket já registrado (UnmappedBridgeStock)addStockMappingno hub da bridge, para cada ação de um basket- Registrar os baskets:
PlanBridgeBasketsimprime as chamadas do owner. Desde 2026-10-06, um basket contém no máximo cinco ações: veja Baskets LaunchProtocol:$STOCKFUN, cujo supply inteiro vai para a sua posição travada, o seu vault, oBuybackBurneresetBuybackWallet. Ele precisa do contrato do airdrop designado antes, o que oDeployProtocolfaz: desde 2026-10-05, ele coloca o operador do lançamento na lista de exclusão do$STOCKFUNantes da cunhagem, no endereço que o token vai ocupar. Desde 2026-10-06, uma execução que parou antes de o mercado do protocolo ser designado é retomada com o token e o vault que deixou (--sig "resume(address,address)"), verificados primeiro, em vez de cunhar um segundo$STOCKFUN; depois que o mercado do protocolo é designado, o script não roda mais, e as etapas restantes são feitas à mãoregisterStockOftno contrato do airdrop, pelo owner, para o OFT de cada ação no Ethereum
Cada módulo upgradável é implantado como dois contratos, a sua implementação e depois o
seu proxy. predict() os conta: o proxy do hub da bridge vem no nonce + 1 do deployer, o
do seu adaptador no nonce + 3.
O StockFun não pareia nenhum peer da LayerZero: os peers do OFT do USDG pertencem ao seu emissor, e o preflight apenas os verifica.
Desde 2026-10-06, o script local e o de testnet, LocalRun e DeployTestnetBridge, só
gravam o seu arquivo de implantação quando de fato enviam as suas transações (broadcast),
e o DeployTestnetRail também desde o nono loop de auditoria: os endereços de um dry run
não têm código. Na testnet, o DeployTestnetRail recebe as mesmas três entradas do
sequenciador, todas opcionais (sem feed, a verificação fica desligada, já que a Chainlink
também não lista nenhum para a testnet), e liga a pausa do oráculo de cada ação; os
scripts do Ethereum deixam as duas salvaguardas desligadas. Um keeper de testnet roda com
KEEPER_REQUIRE_MARKET_OPEN, KEEPER_AIRDROP_AFTER_SESSION e, desde 2026-10-06,
KEEPER_CONVERT_ONCE_PER_WINDOW em false, para que converta a cada passagem: veja
O keeper. Desde o sétimo loop de auditoria, em 2026-10-06, o keeper verifica
a chain de cada RPC contra a sua configuração antes de iniciar: um keeper de testnet
define KEEPER_CHAIN_ID=11155111 e, com a bridge, KEEPER_REMOTE_CHAIN_ID=46630, e um
keeper de mainnet KEEPER_CHAIN_ID=1 com 4663. Desde o oitavo loop de auditoria, os dois
são obrigatórios: o keeper se recusa a iniciar sem KEEPER_CHAIN_ID, ou sem
KEEPER_REMOTE_CHAIN_ID ao lado da bridge. O Worker de dados do app verifica a chain dos
seus endpoints da mesma forma, e os seus RPCs públicos seguem os seus dois ids de chain,
então um Worker de testnet só precisa deles.
A execução em testnet com a LayerZero de 2026-10-06 implantou o protocolo na Sepolia e na
testnet da Robinhood Chain (chain 46630) com os scripts de produção, ou wrappers de
testnet que mantêm o seu corpo, e os seus próprios tokens, locais de negociação e
adaptadores de teste, numa pasta dos contratos só para testnet. Desde o nono loop de
auditoria, o preflight também verifica essa execução, a partir dos seus dois arquivos,
com SEPOLIA_RPC_URL e ROBINHOOD_TESTNET_RPC_URL. O que a execução provou, e o que não
provou, está em Testes e verificação.
O airdrop
Desde 2026-10-04, o contrato do airdrop é implantado com o protocolo. As suas configurações vêm do ambiente:
| Script | Variável | Padrão | Papel |
|---|---|---|---|
DeployProtocol |
AIRDROP_LZ_ENDPOINT |
Nenhum: só o trilho local | Endpoint da LayerZero no Ethereum, para as ações compradas na Robinhood Chain |
DeployProtocol |
AIRDROP_REMOTE_EID |
30416 com um endpoint | O id de endpoint LayerZero da Robinhood Chain, a única origem de uma entrega |
DeployProtocol |
AIRDROP_CYCLE_LENGTH |
86.400 (24 horas) | Duração de uma janela, em segundos, um número inteiro de horas |
DeployProtocol |
AIRDROP_CYCLE_OFFSET |
46.800 (13:00 UTC) | Onde as janelas terminam, em segundos depois de 00:00 UTC, um número inteiro de horas: antes da abertura americana o ano inteiro |
DeployRemote |
AIRDROP_DISTRIBUTOR |
Nenhum | O contrato do airdrop no Ethereum para o qual os vaults espelho enviam |
DeployRemote |
AIRDROP_RECEIVE_GAS, _MIN, _MAX |
650.000, 200.000, 1.500.000 | Desde 2026-10-06: o gas do lzReceive de cada entrega no Ethereum, além do que o OFT da ação impõe, quando o keeper pede o padrão, e o piso e o teto do que ele pode pedir |
DeployRemote |
AIRDROP_COMPOSE_GAS, _MIN, _MAX |
1.250.000, 600.000, 4.000.000 | O gas da chamada de cada entrega no contrato do airdrop, lzCompose, da mesma forma. Até 2026-10-06, um único número, 600.000, definia toda entrega |
DeployRemote |
STOCK_ADAPTERS |
Nenhum | Lista separada por vírgulas, um adaptador LayerZero por entrada de STOCKS, zero para uma ação sem adaptador |
Estes são valores iniciais: o owner pode mudar o cronograma (setCycleSchedule) e o
endpoint LayerZero (setLayerZero) depois. No DeployRemote, AIRDROP_DISTRIBUTOR e
STOCK_ADAPTERS são opcionais: o admin do hub remoto pode defini-las depois
(setAirdrop, setStockAdapter). As duas políticas de gas são definidas em toda
execução, a partir do ambiente ou dos padrões do hub, antes do distribuidor, cujo gas de
compose precisa ficar dentro delas; o admin pode mudá-las depois
(setAirdropReceiveGas, setAirdropComposeGas). Um valor acima de uint128 interrompe
a execução. Em seguida vêm as etapas do owner:
setAirdropDistributorna factory, feito peloDeployProtocol. O owner pode designar outro depois; os vaults o leem em tempo real, e um distribuidor substituído mantém cada ciclo reivindicável onde estáregisterStockOftno contrato do airdrop, para o OFT de cada ação no Ethereum, que precisa usar o próprio endpoint LayerZero do contratosetExclusions, só para um token que precise de endereços excluídos além do endereço de burn: nenhum token de mercado precisa por padrão; a lista do$STOCKFUN, com o seu operador do lançamento, é definida peloLaunchProtocol
Os próprios adaptadores de ações, um por ação — o adaptador de travamento na Robinhood
Chain e o seu OFT no Ethereum —, não estão no repositório para a mainnet: eles precisam do
pacote oft-evm da LayerZero. O DeployRemote recebe os seus endereços. A execução em
testnet com a LayerZero de 2026-10-06 usou adaptadores de teste, o OFTAdapter da
LayerZero sobre ações de teste e o OFT da LayerZero para as ações wrapped, na sua pasta
só para testnet.
Cada entrega vinda da Robinhood Chain roda no Ethereum em duas chamadas: o lzReceive do
OFT da ação, que cunha a ação wrapped para o contrato do airdrop, e depois o lzCompose
do contrato do airdrop, que a credita. Desde 2026-10-06, o keeper indica o gas das duas em
cada envio, escolhido a partir de simulações no Ethereum (veja O keeper), e
o hub remoto limita cada valor à sua política, e zero assume o padrão. Os padrões cobrem
com 35 % e 30 % de folga os casos mais pesados medidos na Sepolia depois da atualização
Glamsterdam do Ethereum: lá, um lzReceive precisa de 184.702 de gas para um saldo que o
contrato do airdrop já detém, e 481.548 para a primeira entrega de uma ação; um compose,
105.075 quando o ciclo já lista a ação, 433.645 quando o ciclo que o keeper abriu ainda
não a lista, cerca de 531.600 quando a entrega é também o primeiro crédito do ciclo, e
962.154 quando ela mesma abre o ciclo. A entrega mais pesada construída nos testes, uma
abertura contra dezesseis holders excluídos com históricos longos que também leva junto
quatro ações guardadas à parte, precisa de cerca de 2,8 milhões aos preços da
Glamsterdam, abaixo do teto do compose. Antes da Glamsterdam, medido a frio nos testes,
uma entrega num ciclo aberto levava cerca de 85.000, uma que abre um ciclo contra um
endereço excluído cerca de 275.000, e a mais pesada cerca de 1.016.000. Uma entrega sem
gas suficiente falha sem perder nada: ela fica armazenada no endpoint da LayerZero, com
as ações wrapped já no contrato do airdrop quando só o compose falhou, e qualquer pessoa
pode executá-la de novo com mais gas. Desde 2026-10-06, o próprio keeper faz isso, dentro
do seu próprio limite, e alerta uma segunda falha.
A conexão da factory
A factory é inicializada apenas com o seu owner e as suas três carteiras; os seus parâmetros começam nos seus padrões. O owner designa todo o resto depois:
setLaunchModules: o hook e o lock, de uma vez por todas; os dois precisam designar esta factory, e o lock, este hooksetDeployers: os dois deployers, que podem ser substituídossetVaultImplementation: a implementação por trás dos vaults dos mercados criados depoissetHoldingRecorder: o registrador que os novos tokens de mercado notificamsetAirdropDistributor: o contrato do airdrop ao qual os vaults entregam as suas ações; ele precisa designar esta factorysetTreasuryRouter,setTreasuryOracle,setSwapRouter,setBridgeHubesetProtocolMarket
Nenhum mercado pode ser criado enquanto o lock, uma implementação de vault e um registrador de posições não forem designados.
O hub da bridge e o swap router
setBridgeHub só pode ser definido uma vez. Uma omissão aqui não pode ser desfeita. Desde
2026-10-05, ele recusa um hub que não consegue traduzir todas as ações dos baskets já
registrados: mapeie-as antes no hub.
setBridgeHub precisa ser chamado antes da criação do primeiro mercado. Cada
TreasuryVault fixa o endereço do hub da bridge na construção. Um vault criado enquanto o
endereço é zero fica no trilho local para sempre e nunca envia nada para a Robinhood
Chain.
setSwapRouter não é write-once: o owner pode mudá-lo a qualquer momento. Ele não afeta
mais nenhum vault: os vaults deixaram de lê-lo quando o buyback do criador foi removido do
código, em 2026-09-28. Ele registra o endereço do swap router oficial, que o preflight
verifica.
O hook e o seu endereço minerado
O endereço de um hook codifica as suas permissões v4 nos bits menos significativos: ele é encontrado por força bruta sobre o salt do CREATE2. Desde 2026-10-02, o endereço minerado é o do proxy do hook, com todos os 14 bits de permissão ativados. Ele depende do bytecode do proxy e dos argumentos do seu construtor, que carregam o endereço da implementação. Uma nova implantação precisa ser minerada de novo; um upgrade mantém o endereço.
foundry.toml precisa conter bytecode_hash = "none" e evm_version = "cancun", senão o
endereço minerado não vai corresponder ao contrato implantado.
Upgrades
Um upgrade é uma chamada do owner do protocolo ao proxy do módulo, que designa a nova
implementação; ele tem efeito imediato. Antes de cada upgrade,
contracts/script/check-storage-layouts.sh compara o novo layout de storage com o
registrado em contracts/storage-layouts/, e falha com qualquer mudança que não seja um
acréscimo. Desde 2026-10-05, ele compara cada nível de cada struct, tamanhos incluídos, e
recusa qualquer mudança numa struct que seja o elemento de um array de storage: só uma
struct que seja o valor de um mapping, ou a última variável de estado, pode crescer, no
seu final. --write atualiza os registros depois de uma mudança deliberada.
As políticas de gas do hub remoto vieram com o nono loop de auditoria, em 2026-10-06. Um
hub implantado antes delas e que recebeu upgrade lê as duas políticas como zero, e um
vault espelho que recebeu upgrade para o código correspondente recusa todo envio ao
airdrop e toda cotação (AirdropGasNotSet) até que as duas sejam definidas. A ordem é,
portanto: fazer o upgrade do hub remoto, definir as duas políticas
(setAirdropReceiveGas, setAirdropComposeGas), depois designar a nova implementação
dos vaults espelho e fazer o upgrade de cada vault espelho, e só então iniciar um keeper
do nono loop, que pede as políticas ao hub e envia com três argumentos. Um keeper
anterior continua enviando com os padrões do hub enquanto isso. Um hub implantado com o
novo código define os padrões na inicialização.
Os parâmetros de lote do adaptador do USDG vieram com o décimo loop de auditoria, em
2026-10-06: o gas de compose que cada mercado de um lote da bridge acrescenta, e o máximo
de mercados que um lote leva. Um adaptador implantado antes deles e que recebeu upgrade lê
os dois como zero e recusa todo lote e toda cotação (BatchGasNotSet) até que eles sejam
definidos. O seu upgrade, portanto, os traz na mesma transação (upgradeToAndCall com
setBatchGas(400000, 17)), depois o owner baixa o seu antigo gas de compose, 1.200.000,
para a base de que todo lote agora precisa (setSettings, 200.000), e só então inicia um
keeper do décimo loop, que lê o teto a cada lote. Um keeper anterior continua funcionando
com o adaptador que recebeu upgrade enquanto não houver mais de 17 mercados prontos ao
mesmo tempo. O adaptador da testnet recebeu upgrade dessa forma em 2026-10-06, e o seu
lote seguinte passou com o seu novo gas de compose.
Um upgrade que muda o que o serviço de dados do app lê entra em produção antes desse serviço. Desde 2026-10-06, a Lens traz o estado de cada vault e o que o hook e o lock lhe devem, e o Worker e o app que leem esses campos não conseguem ler uma Lens anterior: faça primeiro o upgrade da Lens. O sétimo loop de auditoria não muda nem a Lens nem o formato do que o Worker publica (esquema 7): o seu Worker e o seu app são implantados em qualquer ordem. O oitavo muda o formato (esquema 8: cada preço de ação diz por que falta, quando o oráculo da Robinhood Chain o retém), não a Lens, e o seu Worker e o seu app continuam sendo implantados em qualquer ordem: um app mais antigo ignora o motivo, e este app lê um Worker mais antigo sem ele. O nono mantém o esquema 8.
O oráculo do trilho Robinhood, como qualquer TreasuryOracle, começa com as suas duas
salvaguardas desligadas. Um oráculo substituto designado no hub remoto (setOracle)
começa, portanto, com elas desligadas também, e o admin do hub as liga de novo para ele,
como faz o script de implantação: a pausa do oráculo de cada ação, e o feed do
sequenciador se algum tinha sido definido. Os vaults espelho já inicializados mantêm o
oráculo com que foram inicializados.
Parâmetros
Um parâmetro é uma chamada do owner do protocolo ao módulo que o guarda, ou, na Robinhood
Chain, do admin do hub remoto; ele tem efeito imediato e emite um evento. Todo módulo
começa com os padrões listados em Modelo de confiança. Os adaptadores da
bridge começam com o gas da implantação: no trilho do USDG, COMPOSE_GAS, a
parte do último passo de um lote de que todo lote precisa, 200.000 por padrão no
DeployBridge desde o décimo loop de auditoria (1.200.000 para o passo inteiro
até então), mais 400.000 para cada mercado do lote e no máximo 17 mercados por
lote (setBatchGas; o gas do maior lote, no máximo 24.000.000), ao lado de
um limite de 30 pontos-base na Curve; no trilho canônico, o gas dos dois tickets
e, desde 2026-10-05, os bytes sobre os quais o custo do
ticket de depósito é calculado (DEPOSIT_CALLDATA_LENGTH no DeployTestnetBridge; zero
assume o padrão, 1.024). O hub remoto começa com as duas políticas de gas das entregas do
airdrop (acima).
Antes da mainnet
- Um ensaio completo do modo de emergência: transferência, pausa, fim da pausa
- Um primeiro airdrop pequeno num vault de verificação, antes de qualquer abertura pública. A execução em testnet com a LayerZero de 2026-10-06 fez o código do StockFun funcionar de ponta a ponta sobre os endpoints, o DVN e o executor de testnet da LayerZero; ela não prova nem o par USDG da Paxos, nem as ações da Robinhood e os seus adaptadores, nem feeds e liquidez reais, nem o gas, as taxas e a finalidade da mainnet
- Um levantamento atualizado dos feeds de preço na chain remota
- Verificação dos peers da LayerZero do OFT do USDG e do estado de pausa do USDG, via preflight; desde o oitavo loop de auditoria, o preflight também verifica as salvaguardas do oráculo contra o seu manifesto, que precisa declarar a verificação do sequenciador, hoje desligada
- O limite de tamanho da rota que o USDG da Paxos usa até a Robinhood Chain, lido na
biblioteca de envio da LayerZero (
getExecutorConfig), e o teto de mercados de um lote da bridge ajustado de acordo se ele não for de 10.000 bytes: no máximo (tamanho − 392) ÷ 544 mercados (desde o décimo loop de auditoria) - Revisão externa — as provas formais existentes não cobrem o trilho cross-chain