Prefácio

Com o rápido desenvolvimento das finanças descentralizadas (DeFi), a Uniswap, como uma bolsa descentralizada líder, tem estado na vanguarda da inovação. Este artigo fornecerá uma análise aprofundada do mecanismo central do protocolo Uniswap v3 e uma explicação detalhada de seu design funcional, incluindo funções-chave como liquidez centralizada, taxas múltiplas, troca de tokens e empréstimos instantâneos, ao mesmo tempo que fornece pontos de auditoria relevantes para auditores. (Observação: as imagens neste artigo podem ser visualizadas em alta resolução em https://www.figma.com/board/QyIpAUR93MxZ4XZZf2QjDk/uniswap-v3.)

Breve análise de arquitetura

O protocolo Uniswap v3 consiste principalmente em quatro módulos:

  • PositionManager: A principal interface para os usuários realizarem operações de liquidez, por meio da qual os usuários podem criar pools de tokens, fornecer/remover liquidez e usar o ERC721 como credenciais para provedores de liquidez (LP).

  • SwapRouter: A entrada para usuários trocarem tokens. Os usuários podem realizar operações de troca de tokens por meio deste módulo.

  • Pool: Responsável pela implementação de transações de token, gerenciamento de liquidez, cobrança de taxas de transação e funções de gerenciamento de dados Oracle. Entre eles, o mecanismo Tick divide a faixa de preço em múltiplas escalas finas.

  • Fábrica: usada para criar e gerenciar contratos Pool.

Classificação de processos

Crie um par de tokens

Os usuários podem fazer isso por meio da função createAndInitializePoolIfNecessary. O usuário precisa passar o token0, token1, taxa de manuseio (fee) e preço inicial() do par de tokens. Primeiro, o sistema verificará se o par de tokens já existe por meio da função getPool. Caso não tenha sido criado, createPool será chamado e a instrução CREATE2 será usada para implantar o par de negociação. Finalmente, a função de inicialização é usada para completar a inicialização de preço, taxa de manuseio, tick, oracle e outros parâmetros relacionados.

Fornecer liquidez

Os usuários podem criar novas posições de liquidez e gerar NFTs correspondentes através da função de hortelã ou adicionar liquidez às posições de liquidez da NFT existentes por meio da função de crescente. Primeiro, o sistema verificará se a transação é executada dentro do intervalo de tempo especificado e, em seguida, chamará a função de adição para concluir a operação específica. Nesta função, primeiro calcule o endereço e a liquidez do pool e, em seguida, chame a UpdatePosition para atualizar a posidade do usuário para modificar a quantidade total de taxas mais baixas, superiores e cumulativas. Posteriormente, o sistema acrescentou liquidez através do ModifyPosition para garantir que o ticke atenda às condições de limite superior e inferior, retorna o número calculado de token0 e token1 (Int256) e o envia para o pool. Por fim, o sistema atualiza as informações de Posição correspondentes com base no tokenId do usuário.

Remover liquidez

Os usuários podem remover a liquidez por meio da função de diminuição de liquidez. Primeiro, o sistema verifica a autoridade do certificado LP e o prazo de validade da transação. Sob a premissa de garantir que o pool tenha liquidez suficiente, chame a função burn para remover a liquidez. O sistema verificará então se o número real de tokens removidos atende aos requisitos mínimos definidos pelo usuário e atualizará as informações de posição do usuário de acordo.

trocar

Os usuários podem especificar o número de tokens a serem pagos e o número mínimo de tokens que devem ser obtidos por meio da função exactInput, ou especificar o número máximo de tokens a serem pagos e definir o número de tokens que devem ser obtidos por meio da função exactOutput. O sistema primeiro analisa o caminho (caminho) e, em seguida, chama a função exactInputInternal ou exactOutputInternal em sequência para concluir cada etapa da operação de troca.

Na função swap, o sistema primeiro bloqueia o estado desbloqueado para evitar que outras transações interfiram na atualização das variáveis ​​de estado. Após entrar no loop, o sistema encontra o próximo preço da transação por meio do tick e chama a função computeSwapStep para calcular a troca em cada etapa até que tokenIn ou tokenOut atinja a expectativa do usuário. Ao mesmo tempo, o sistema atualizará os valores relacionados de taxas, liquidez, ticks e preços. Se o tick mudar, os dados do Oracle também precisarão ser atualizados. Depois de concluir essas operações, o sistema paga tokenOut ao usuário, e o usuário paga tokenIn por meio da função de retorno de chamada uniswapV3SwapCallback. Esse mecanismo pode ser considerado uma troca flash. Posteriormente, o sistema verificará se o saldo do contrato corresponde e desbloqueará o estado desbloqueado após a confirmação.

Uma transação termina com sucesso quando todas as operações de swap no caminho forem concluídas e a transação atender às expectativas do usuário.

clarão

Os usuários podem realizar operações de empréstimo flash por meio de funções flash. Primeiro, o sistema calculará a taxa de empréstimo e, em seguida, enviará o token exigido pelo usuário para o endereço de empréstimo designado. Em seguida, o sistema chama de volta a função uniswapV3FlashCallback implementada pelo usuário e o usuário conclui a operação de reembolso nesta função. O sistema verificará a alteração no saldo do contrato após o retorno de chamada para garantir que seja consistente com o valor do empréstimo do usuário e atualizará a taxa de manuseio correspondente. Além das funções flash, os usuários também podem implementar funções semelhantes de empréstimo flash por meio de operações de swap, ou seja, emprestar e reembolsar tokens durante o processo de transação.

Pontos de auditoria

1. Verifique se o refundETH é chamado após a operação de swap

Na função exactInput, o usuário precisa especificar o número de tokens a pagar e o número mínimo de tokens que se espera obter. Antes de chamar uniswapV3SwapCallback, o sistema recalculará amount0 e amount1 para garantir que o usuário possa enviar o token com precisão. Porém, ao trocar com ETH, os usuários precisam enviar ETH junto com a transação. Mesmo que não seja utilizado todo o ETH durante a transação, a função não retornará automaticamente o excesso. A função exactInput retorna apenas amountOut, portanto, os traders não podem saber diretamente quanto ETH foi realmente consumido por esta exchange.

Além disso, qualquer pessoa pode chamar a função refundETH para retirar ETH não utilizado do contrato. Portanto, é recomendado verificar se o refundETH é chamado após a operação de troca para evitar que os usuários deixem ETH não utilizado no protocolo, ou usar a função MultiCall para completar múltiplas chamadas de função em uma operação.

2. Verifique se o TWAP está implementado para obter o preço oráculo

Ao usar o Uniswap como fonte de preço, pode haver risco de manipulação de preço se o protocolo externo acessar diretamente o Slot0 para obter sqrtPriceX96. Os invasores podem manipular o status do pool de liquidez por meio de swaps e outros métodos para obter preços favoráveis ​​ao executar transações.

A fim de reduzir este risco, recomenda-se que os promotores implementem ainda mais o Preço Médio Ponderado no Tempo (TWAP) para obter preços, porque o TWAP pode efetivamente reduzir o impacto das flutuações violentas de preços no curto prazo, tornando mais difícil a manipulação de preços.

3. Recomenda-se permitir que os usuários definam os parâmetros de deslizamento por conta própria

Quando outros protocolos usam o Uniswap v3 para operações de swap, recomenda-se que os desenvolvedores definam a proteção contra deslizamento com base em cenários de negócios e permitam que os próprios usuários ajustem os parâmetros para evitar ataques sanduíche. Nesta função de troca, o quarto parâmetro sqrtPriceLimitX96 é usado para especificar o preço mínimo ou máximo pelo qual o usuário deseja realizar a troca. Este parâmetro pode prevenir eficazmente flutuações extremas de preços durante o processo de transação, reduzindo assim as perdas dos usuários devido a derrapagens excessivas.

4. Recomenda-se a introdução de um mecanismo de lista branca de pool de liquidez

No Uniswap v3, o mesmo par de tokens ERC20 pode existir em vários pools de liquidez (Pools) ao mesmo tempo com base em taxas diferentes. Normalmente, alguns pools de liquidez detêm a maior parte da liquidez, enquanto outros pools podem ter muito pouco volume total bloqueado (TVL) ou nem mesmo terem sido criados ainda. Esses pools de TVL mais baixos têm maior probabilidade de serem alvos de manipulação de preços.

Portanto, quando as partes do projeto optam por utilizar dados do pool de liquidez, devem evitar simplesmente utilizar LP como fonte de dados. Para garantir a fiabilidade dos dados, recomenda-se a introdução de um mecanismo de lista branca para excluir carteiras com liquidez suficiente e dificuldade de manipulação. Este mecanismo reduz significativamente o risco, garantindo a segurança e precisão dos dados de referência de preços, ao mesmo tempo que evita potenciais perdas decorrentes da manipulação de pools com TVL muito baixo.

5. Verifique se desmarcado é usado em TickMath.sol, FullMath.sol e Position.sol

TickMath、FullMath 和 Position 等模块在 Uniswap v3 中用于执行复杂的数学计算,这些计算依赖于 Solidity 中的溢出处理机制。在早期的 Solidity 版本(<0.8.0)中,整数溢出和下溢行为默认不抛出异常,因此代码可以基于这种假设进行正常运行。 No entanto, a partir do Solidity versão 0.8.0, overflow e underflow lançam exceções automaticamente, o que afeta a execução do código existente.为确保这些模块在 Solidity 0.8.0 及更高版本中正常运行,开发者需要在特定函数中使用 unchecked 代码块,手动禁用溢出检查。 Isto restaura o comportamento das versões anteriores e garante a execução eficiente de operações sensíveis a overflow.

Suporte oficial e ajustes foram feitos para Solidity 0.8.0 e superior. Para obter detalhes, consulte esta atualização (https://github.com/Uniswap/v3-core/commit/6562c52e8f75f0c10f9deaf44861847585fc8129). Essa alteração garante que TickMath, FullMath e outros módulos relacionados continuarão a funcionar corretamente nas novas versões do compilador.

6. Verifique se os métodos de codificação e decodificação do caminho são iguais

Nas funções exactInput e exactOutput do Uniswap v3, os usuários precisam inserir o parâmetro path, que deve ser codificado e decodificado de acordo com um formato fixo, ou seja, tokenA-fee-tokenB, para operações passo a passo de troca de tokens. Esta estrutura de caminho especifica explicitamente os dois tokens envolvidos em cada salto da transação e o nível de taxa entre eles. Se um protocolo externo escolher um método de decodificação de caminho diferente ao usar a função de troca de token do Uniswap v3, isso poderá resultar em um formato de caminho inconsistente com o formato de caminho esperado do Uniswap. Neste caso, o protocolo pode não ser capaz de resolver corretamente o caminho e, portanto, não conseguir executar com êxito a operação de troca de tokens pretendida.

Portanto, é recomendado que os desenvolvedores garantam que os protocolos externos sigam estritamente as regras de codificação de caminho do Uniswap ao integrar a função de troca de tokens do Uniswap v3. Para evitar erros de decodificação de caminho, os protocolos externos devem verificar cuidadosamente o formato do parâmetro de caminho ao chamar exactInput e exactOutput para evitar falhas de transação ou resultados inesperados.

7. Verifique se a ordem do token afeta a lógica do projeto

No Uniswap, token0 é o token de ordem inferior e é usado como token base, enquanto token1 é o token de ordem superior e é usado como token de cotação. O Uniswap classifica os endereços dos dois tokens lexicograficamente para garantir que a ordem dos pares de tokens seja sempre consistente no pool.

No entanto, como os endereços de contrato do mesmo token em diferentes redes blockchain podem ser diferentes, especialmente para contratos implantados em cadeias, a ordem de classificação dos tokens pode mudar. Essa mudança fará com que as funções do token0 e do token1 sejam revertidas, afetando assim o desempenho dos preços. Por exemplo, em algumas cadeias, um token específico pode ser token0, mas em outras cadeias, pode ser ordenado como token1, resultando em uma relação diferente entre o token base e o token cotado, afetando em última análise o preço exibido. Portanto, é recomendado que os desenvolvedores verifiquem se a ordem dos tokens afetará a lógica do projeto. Especialmente em um ambiente de cadeia cruzada, certifique-se de considerar os problemas de preço que podem ser causados ​​pela ordem dos tokens para evitar efeitos adversos no desempenho dos preços e. lógica de transação.

Resumir

Os itens de verificação básicos acima são baseados na versão atual do Uniswap v3 e são usados ​​por auditores para verificar projetos que interagem com o Uniswap v3. A implementação de diferentes projetos tem características próprias, por isso os auditores precisam ter um conhecimento profundo do acordo e realizar inspeções rigorosas com base na situação real. Para projetos em desenvolvimento, a equipe de segurança do SlowMist recomenda que os desenvolvedores considerem cuidadosamente essas verificações durante o processo de desenvolvimento para garantir a segurança e a confiabilidade do protocolo.

Autor |

Editora | Liz