Segurança
O que tem valor
- O ETH e os tokens dos pools, em posições que ninguém pode retirar. O ETH de um lado só sai do pool quando esse lado é vendido nele: por um trader, ou pelo realinhamento do hook depois que uma negociação afastou os três preços.
- Os potes, as taxas do criador e as taxas do protocolo, guardados pelo hook como créditos de ETH no PoolManager da Uniswap V4, contabilizados separadamente para cada duelo.
O que o protege
- Liquidez travada. Só a fábrica pode abrir pools registrados ou adicionar a liquidez de lançamento deles; saques e doações revertem.
- Destinatários limitados. Um pote só compra o vencedor do próprio duelo; as taxas do criador só vão para o criador do duelo: a carteira que o lançou ou um endereço para o qual ela passou o papel e que o aceitou. As taxas do protocolo vão para a tesouraria atual, que o dono da fábrica pode mudar depois de 48 horas, inclusive para as taxas do protocolo já acumuladas.
- Poderes limitados da fábrica. Os tokens não têm dono nem função de mint, e o hook não pode ser atualizado nem pausado. O dono da fábrica pode pausar novos lançamentos, propor termos limitados para duelos futuros ou mudar a tesouraria depois do prazo. Ele também pode reduzir na hora a parte do protocolo na taxa de swap de todos os duelos, até zero, ou aumentá-la de novo, nunca acima dos termos do duelo: o que o protocolo não retém vai para os potes. O dono pode renunciar ao papel de vez (
renounceOwnership): os termos, a tesouraria, a alíquota do protocolo e uma pausa ficam então como estão, exceto uma mudança já proposta, que qualquer pessoa ainda pode aplicar depois do prazo. - Liquidação antes do swap. O primeiro swap depois de um fechamento liquida a média encerrada antes de negociar. O teto de uma recompra fica cerca de 10% acima de uma referência guardada com precisão de frações de tick: uma média exponencial de 30 minutos dos ticks do pool de ETH, limitada ao tick atual quando o ultrapassa, para que acompanhe o preço para baixo na hora.
- Rodadas só em ETH. A divisão e a média da rodada usam os dois pools de ETH. Uma troca A/B que não move nenhum preço em ETH não muda nem o sinal nem a atividade de uma rodada que, de outra forma, estaria parada.
- Taxas só com execução completa. Swaps cobrados antecipadamente revertem se não conseguirem completar o valor pedido. O gás da rede é à parte.
- Realinhamento isolado. Só são mantidos os loops lucrativos, limitados pelo ETH do pool vendedor. Uma tentativa que falha é revertida e pulada; por falta de gás, quem reverte é o swap (
ArbitrageStarved), para que quem chama não possa pulá-la segurando gás. Isso não garante que os preços coincidam nem protege contra todas as perdas de negociação. - As mesmas regras em todas as rotas. O hook cobra taxas, impostos e queimas seja o router o do Duel ou outro router V4.
A fábrica começa com os lançamentos pausados. Enquanto paused() for true, só owner() pode lançar um duelo; as chamadas dos demais revertem com CreationPaused. O dono abre os lançamentos com setPaused(false). Os duelos existentes continuam sendo negociados.
O que o site mostra
- Imagens só do site. As páginas mostram as imagens de um duelo só a partir do próprio endereço do Duel, e só imagens que o site processou: um JPEG, PNG ou WebP lido primeiro pelo cabeçalho (1536 × 1536 pixels e 5 MB no máximo), decodificado em WebAssembly, ajustado a 1024 pixels e codificado de novo como WebP, sem os metadados. A política de segurança das páginas não deixa que elas carreguem imagens de nenhum outro lugar.
- Codecs isolados. As imagens são decodificadas em um Worker próprio, que só o site acessa e que não guarda nada: nem chave, nem armazenamento. Uma falha em um codec atingiria só a imagem que ele lê. O site verifica o que esse Worker responde (um WebP estático do tamanho esperado) antes de guardá-lo, e os decodificadores em C recomeçam do zero a cada imagem.
- Duelos lançados direto pelo contrato. Os dados deles podem indicar qualquer endereço. O site não mostra imagens https; ele busca uma imagem IPFS uma única vez, em um gateway público (5 MB e 10 segundos no máximo), processa-a como um upload e mostra a própria cópia, ou as iniciais do lado.
- Uploads com limites. O site só aceita imagens das próprias páginas: dez por minuto de um mesmo endereço e um número limitado por dia. Uma imagem que nenhum duelo usa é apagada depois de um dia.
Como foi verificado
- Testes automatizados cobrem o comportamento dos contratos, o app web e o bot de alertas. A cobertura é uma métrica de testes, não a prova de que todo comportamento possível é seguro.
- Testes em fork exercitam a integração com os contratos da Uniswap V4 implantados na Robinhood Chain, incluindo o Universal Router, o Permit2 e o V4 Quoter.
- Testes de regressão guardam os casos encontrados durante a revisão interna, como a contabilidade das taxas, casos-limite do lançamento, a liquidação e pools esgotados.
- Testes de invariantes verificam a contabilidade ao longo de sequências aleatórias de negociações, liquidações e resgates: os créditos de ETH devem bater com os valores devidos, e nenhum token intermediário deve ficar no hook, na fábrica ou no router. Passar nas execuções configuradas não garante a segurança para toda sequência possível.
A revisão interna encontrou e corrigiu uma rota de saída exata que escapava do imposto antissnipe, uma divisão que não acompanhava um lado comprado sozinho, pools esgotados lidos errado pela referência de preço da recompra e links inseguros nos metadados de um duelo.
Reportar uma vulnerabilidade
Se você acha que encontrou uma vulnerabilidade, obrigado por dedicar seu tempo para reportá-la. Escreva para nós em privado no Telegram, em @chatwithdivine, contando o que encontrou e, se puder, como reproduzir, e nos dê tempo para investigar antes de divulgar qualquer coisa publicamente. Responderemos rápido e manteremos você informado.
Nunca escrevemos primeiro: confie apenas em @chatwithdivine.