Sistemas em produção para finanças, criptografia e operações críticas.
Todos os posts

Padrões de produção para Arbitrum Stylus (Parte 2): aleatoriedade verificável onchain

Uma blockchain não consegue surpreender a si mesma. A Parte 2 da nossa série Stylus é sobre o gap entre pedir aleatoriedade e recebê-la: solvência, recovery permissionless e um callback que não pode prender fundos.

Marco de produção com Arbitrum Stylus · Parte 2

Uma blockchain é uma máquina que não consegue surpreender a si mesma. Cada nó precisa chegar ao mesmo estado a partir dos mesmos inputs. Essa propriedade é exatamente o que torna a aleatoriedade onchain difícil: nada dentro de um sistema determinístico consegue produzir um output que, em princípio, não seja previsível.

Este é o segundo marco da nossa série de produção com Stylus. A Parte 1 perguntava como coordenar múltiplas fontes de liquidez sob condições reais de mercado. Desta vez a pergunta é outra: como construir uma aplicação onchain em que o resultado precisa ser comprovadamente aleatório, verificável por qualquer pessoa e totalmente determinado por infraestrutura em que o usuário não precisa confiar?

4Contratos Stylus, um único ponto de custódia
23.7KBWASM do core, abaixo de um teto rígido de 24KB
0Controles de admin no buffer de reembolso

Veja o padrão, não o produto

A superfície é um jogo de azar. O problema de engenharia é um oráculo assíncrono que não pode quebrar a solvência, deixar fundos presos nem deixar ninguém influenciar o resultado. Este walkthrough é o fluxo ao vivo: pedir, esperar, resolver ou recuperar.

Referência open source

Contratos Stylus, integração VRF, paths de Permit2 e o runbook de deploy. Estude o padrão, faça fork, estenda.

  • Rust
  • Arbitrum Stylus
  • Chainlink VRF v2.5
  • Open source
Ver código-fonte

Walkthrough ao vivo

End-to-end contra o deploy real na Sepolia: placement, callback de VRF e um reembolso realmente induzido. Não é um mock.

  • Sepolia
  • VRF v2.5
  • E2E
Abrir no YouTube

Por que esse problema, depois de DEX aggregation

Um DEX aggregator falha em voz alta. Quotes erram, rotas revertem, o usuário tenta de novo. Uma aplicação que depende de uma resposta aleatória externa não pode tolerar um resultado ambíguo, atrasado para sempre ou influenciável por uma única parte, inclusive nós.

Publicamos isso como arquitetura de referência, e depois um time separado lançou um produto ao vivo em cima. Esse deploy não é nosso, então não estamos nomeando nem linkando. O que importa aqui é mais forte do que um demo de testnet: o padrão processou requests reais e callbacks reais de VRF em condições live.

Implementação de referência, não um produto. Este post descreve uma arquitetura open source que a WakeUp Labs publicou para o ecossistema Stylus. A WakeUp Labs não opera nenhuma aplicação de consumidor construída sobre ela.

Arquitetura: quatro contratos, um único lugar onde o dinheiro pode ficar

Se você lembrar de um diagrama, que seja este: tudo que pode hold tokens vive em um único contrato. Todo o resto é substituível.

User / Mini App
      |
      v
 [ periphery ]  -- router sem estado, Permit2, views de leitura
      |
      v
 [    core    ]  -- UNICA custodia. Lifecycle. VRF. Payouts. Reembolsos.
      |     \
      |      \--> Chainlink VRF v2.5  (via trait RandomnessSource)
      v
 [ play token ] + [ faucet ]  -- sem resgate para nada de valor real

coreO monolito. Custodia cada token que entra no sistema e é dono de todo o lifecycle: placement, request de VRF, resolução do callback, payout, reembolso, accounting de fees. Chainlink VRF v2.5 fica atrás de um trait RandomnessSource, então trocar o provider depois toca um único bloco impl, nunca a lógica que consome a aleatoriedade.

peripherySem estado de propósito. Sem counters, sem estado trackeado pelo owner, nunca hold um token. Puxa fundos via Permit2, encaminha para o core como router confiável e recomputa views somente leitura (pool disponível, stake máximo, elegibilidade de reembolso) para o frontend ter um único ABI. A solvência é enforced no core em todo path de entrada. O periphery não é um security boundary. Pode ser rotacionado ou morto sem tocar a custódia.

play tokenUm ERC-20 mínimo feito à mão. Todos os stakes são denominados nele. Não tem path de resgate para nada de valor real.

faucetO onramp: um mint fixo por wallet por janela de cooldown. Permanece habilitado em todas as redes, mainnet incluída, porque o asset é play money por design.

Regra de design: se um contrato pode hold funds, ele é core. Se não pode, pode ser conveniente, burro e descartável.

A parte difícil é o gap

Um número aleatório síncrono é uma contradição onchain. O core precisa aceitar um stake, pedir entropia a um processo externo e só resolver quando esse processo faz callback. Isso pode levar vários blocos. No pior caso, nunca acontece.

O software interessante não é a chamada ao oráculo. É tudo que precisa continuar verdadeiro no silêncio entre o request e a resposta.

t0  place stake
    lock do payout worst-case no pool   [a solvencia e decidida aqui]
    request VRF
    status = Pending

        ... blocos passam. talvez muitos. talvez nunca. ...

t1a callback chega --> resolve, libera liability, payout ou nao
t1b deadline + buffer --> qualquer um reembolsa, stake completo, sem fee
t1c late / unknown    --> no-op + event, nunca revert

Trave o pesadelo antes de a resposta existir

Quando um stake é colocado, o core imediatamente faz locked_liability += win_payout(amount). Essa única reserva é o que mantém o sistema solvente sob qualquer intercalação de requests in-flight: o pool nunca pode prometer mais do que consegue pagar, não importa quantos requests estejam pending nem quanto o VRF demore.

Se você espera para travar até o callback, está apostando que o futuro será gentil. Sistemas de produção não podem assumir isso.

Escolha uma falha. Veja o que o contrato faz.

Clique nos casos para os quais realmente desenhamos. Esta é a parte interativa da arquitetura: a state machine precisa ser entediantemente correta em cada um deles.

1. O oráculo nunca responde

Cada request ganha um fulfill_deadline no placement. Se o VRF nunca faz callback, o request vira reembolsável quando now >= fulfill_deadline + DEAD_BUFFER. O buffer é de cinco minutos fixos e não é tunable pelo owner. Isso é deliberado: um controle que um admin pudesse encurtar ou estender deixaria alguém correr a janela de reembolso contra um callback atrasado.

Qualquer pessoa, não só o usuário original, pode disparar o reembolso. O stake completo volta sem fee, porque o jogo nunca se resolveu.

2. O callback chega depois que o request já morreu

Um callback para um request expirado, já resolvido ou desconhecido é um no-op mais um event. Não faz revert.

Um revert aqui queimaria o request da Chainlink sem liquidar o usuário, deixando a posição presa entre "ainda não é reembolsável" e "nunca vai resolver." Tratar input adversarial ou atrasado como no-op é como você mantém a state machine impossível de emperrar.

3. A transfer do payout falha

A resolução segue checks-effects-interactions estrito: cada mudança de storage (status, release de liability, accounting de fees) acontece antes de qualquer chamada externa de token.

Um payout vencedor é enviado via transfer direto. Se essa transfer falha, por exemplo um token pausado ou mal comportado do lado que recebe, o payout cai para um crédito claimable de pull-payment em vez de reverter a resolução inteira. O request fica settled. O usuário pode clamar. O oráculo não fica griefed em um loop de retry.

4. Muitos requests estão in flight ao mesmo tempo

É por isso que o liability é travado no placement, não no callback. Dez requests pending são dez payouts worst-case reservados. O pool disponível encolhe em tempo real. Novos stakes que superprometessem são rejeitados. Timing não consegue criar insolvência, porque insolvência foi tornada irrepresentável.

5. Alguém tenta tratar o callback como um amigo confiável

rawFulfillRandomWords é chamado por um contrato externo. O wrapper é trusted; o input mesmo assim é tratado como adversarial. Ordering, no-ops e o fallback de pull-payment existem porque "o oráculo é honesto" não é um threat model completo. Oráculos honestos ainda chegam tarde, duplicam, ou batem em um token que se recusa a receber.

Como os fundos entram: dois paths de Permit2

O periphery suporta duas formas de mover tokens para um request, mais um fallback clássico.

PathComo parecePor que existe
Mini App SignatureTransferUm permit EIP-712 de uso único por request. Sem approval prévia.As assinaturas passam pelo Permit2 sem modificação, então smart accounts ERC-1271 funcionam tanto quanto EOAs.
Standalone AllowanceTransferUm approve(Permit2, MAX) vitalício, depois um permit semanal.Cada request dentro dessa janela precisa de zero assinaturas extras.
Classic approve + callO path chato.Fallback quando nenhum dos fluxos de permit está disponível.

Cabendo no WASM gate

A Arbitrum aplica um limite de 24KB de WASM comprimido por fragmento de contrato. Não é uma constraint suave. O core precisou de um build nightly de Rust pinado com -Zbuild-std e -Cpanic=immediate-abort para compilar em 23.7KB. Perto o suficiente do teto para que o toolchain stable de estoque não fosse uma opção. O periphery, em 18.9KB, cabe confortavelmente na imagem stable.

24.0 KB  teto, rigido
23.7 KB  core em nightly pinado + immediate-abort
18.9 KB  periphery em stable
         |||||||||||||||||||||||||  core
         ||||||||||||||||          periphery

Isso não tem equivalente direto no desenvolvimento em Solidity, e molda decisões reais: quais panics você pode pagar, quanta lógica vive em um contrato versus dividida em vários, e qual receita de build você se compromete a enviar e re-verificar.

O que dá para observar de verdade

Os quatro contratos estão deployed e verificados no Arbiscan na Arbitrum Sepolia pelo build reproduzível com Docker do cargo-stylus, não um atalho --no-verify. O build nightly custom do core é verificado contra uma imagem publicada pinada por digest, então qualquer pessoa pode confirmar que o bytecode deployed bate com o source.

Uma suíte end-to-end ao vivo exercita o deploy real: o path standalone, o path Permit2 da Mini App, o path AllowanceTransfer e um reembolso realmente induzido. Tudo é open source.

As três propriedades que generalizam

1. Travar o outcome worst-case antes de a resposta externa existir, para que a solvência nunca dependa de timing.
2. Enviar um path de recovery determinístico e permissionless para quando o processo externo nunca responde.
3. Tratar o callback para que não possa ser griefed, double-spent ou deixado preso, independentemente do estado em que chega.

A superfície específica aqui é um jogo de azar. O padrão não. Um oráculo de aleatoriedade, uma mensagem de bridge, um price feed offchain, um callback de settlement cross-chain: qualquer um desses tem o mesmo formato de problema. Construir isso no Stylus nos deixou expressar a state machine, o accounting de locked-liability e a abstração de oráculo baseada em traits em Rust, com o limite de tamanho WASM forçando tradeoffs reais em vez de teóricos.

O que vem a seguir

Este é o segundo marco de uma série open source que valida o Stylus com implementações de referência de produção. A Parte 1 cobriu DEX aggregation. Cada marco responde a uma pergunta de engenharia diferente, e todos enviam fully open source para que outros times construindo no Stylus possam estudar, fazer fork e estender o padrão.

Source completo, notas de arquitetura e o runbook de deploy: github.com/wakeuplabs/ArbitrumMiniApp-2-verifiable-randomness.

Se você está construindo algo no Stylus que depende de um processo externo resolver corretamente, gostaríamos de comparar notas.

Sobre a WakeUp Labs

Na WakeUp Labs, construímos infraestrutura blockchain e produtos digitais de grau produtivo para ecossistemas líderes, startups e empresas.

Nossa filosofia de engenharia é simples: infraestrutura nova só prova valor quando roda em produção. Em vez de avaliar tecnologias emergentes com protótipos isolados, validamos construindo sistemas reais que interagem com usuários reais e restrições operacionais reais.

Se você está construindo na Arbitrum ou explorando Stylus para sistemas de produção, adoraríamos conectar.

Próximo passo

Transforme isso em uma direção clara

Fale conosco sobre o sistema que você precisa. Definimos o escopo, a operação depois do go-live e o próximo passo.