Dicas práticas para o desenvolvimento de contratos: lições aprendidas com o código do Uniswap
Recentemente, ao desenvolver um tutorial sobre uma troca descentralizada, consultei a implementação do código do Uniswap V3 e aprendi muitos pontos interessantes. Como um desenvolvedor que está tentando pela primeira vez desenvolver contratos Defi, essas técnicas serão muito úteis para iniciantes que desejam aprender sobre desenvolvimento de contratos.
Vamos agora dar uma olhada nestas dicas úteis, algumas das quais podem até ser consideradas truques engenhosos.
Endereço de implantação do contrato de previsão
Normalmente, o endereço obtido ao implantar um contrato parece aleatório, pois está relacionado ao nonce e é difícil de prever. No entanto, em certos cenários, precisamos inferir o endereço do contrato com base nas transações e nas informações relacionadas. Isso é útil em situações como determinar permissões de transação ou obter o endereço de um pool.
A Uniswap utiliza o método CREATE2 para criar contratos, adicionando o parâmetro salt para tornar o endereço do contrato gerado previsível. A lógica de geração do novo endereço é: hash("0xFF", endereço do criador, salt, initcode). Este método torna o endereço do contrato previsível, facilitando operações subsequentes.
Uso inteligente de funções de callback
Os contratos podem chamar-se uns aos outros em Solidity. Um cenário comum é o método A chamar o B, e o B fazer uma chamada de retorno ao A no método chamado. Isso é muito útil em algumas situações.
Tomando o Uniswap como exemplo, quando o método swap do contrato UniswapV3Pool é chamado para realizar uma transação, ele irá chamar o swapCallback, passando o Token que foi calculado como necessário para a transação atual. O chamador deve transferir o Token necessário para a transação de volta ao UniswapV3Pool no callback. Este design garante a execução completa e a segurança do método swap, sem a necessidade de registros complicados de variáveis.
Utilizar a transmissão de informações de exceção, usar try catch para realizar a previsão de transações
No contrato Quoter do Uniswap, utiliza-se o try catch para envolver a execução do método swap do UniswapV3Pool. Isso é feito para simular o método swap e estimar os Tokens necessários para a transação. Como não há troca real de Tokens durante a estimativa, ocorrerá um erro. O Uniswap lança um erro especial na função de callback da transação, que é então capturado e a partir do qual as informações necessárias são extraídas.
Este método pode parecer uma solução fácil, mas é muito prático. Ele evita a necessidade de reformular o método swap para estimar a demanda de negociação, tornando a lógica mais simples.
Resolver problemas de precisão com grandes números
No código do Uniswap, há uma grande quantidade de lógica de cálculo, como calcular os tokens trocados com base no preço atual e na liquidez. Para evitar a perda de precisão devido a operações de divisão, o Uniswap frequentemente utiliza a operação "<< FixedPoint96.RESOLUTION", ou seja, desloca para a esquerda 96 bits, o que equivale a multiplicar por 2^96. Deslocar para a esquerda antes de realizar operações de divisão pode garantir a precisão, normalmente usando uint256 para calcular (, desde que não haja estouro em transações normais.
Embora teoricamente ainda haja uma pequena perda de precisão, esse nível de erro é geralmente aceitável.
Calcular rendimento com o método Share
A Uniswap precisa registrar os ganhos de taxa dos provedores de liquidez LP). Para evitar o consumo excessivo de Gas ao registrar as taxas para cada LP em cada transação, a Uniswap adotou um método engenhoso.
Na estrutura Position, foram definidos feeGrowthInside0LastX128 e feeGrowthInside1LastX128, que registram as taxas de serviço que cada liquidez deveria receber na última retirada de taxas de serviço de cada posição. Assim, é necessário apenas registrar a taxa de serviço total e a taxa de serviço que cada liquidez deve receber; quando o LP retira, a taxa de serviço que pode ser retirada pode ser calculada com base na liquidez mantida. Isso é semelhante ao mecanismo de dividendos de ações, onde, na retirada, é necessário apenas conhecer os lucros por ação históricos da empresa e os lucros da última retirada.
Escolher razoavelmente os meios de obtenção de informações
O armazenamento em blockchain é relativamente caro, por isso nem todas as informações precisam ser armazenadas na blockchain ou obtidas a partir dela. Por exemplo, muitas das interfaces chamadas pelo site front-end da Uniswap são interfaces tradicionais da Web2.
A lista de pools de negociação, informações sobre os pools de negociação, etc., podem ser armazenadas em um banco de dados comum. Alguns dados podem precisar ser sincronizados periodicamente da cadeia, mas não é necessário chamar em tempo real a interface RPC da cadeia ou dos serviços de nó para obter dados relevantes.
Alguns fornecedores de RPC de blockchain oferecem interfaces avançadas, que permitem obter certos dados de forma mais rápida e econômica. Essas interfaces geralmente utilizam cache para melhorar o desempenho e a eficiência.
Claro, as transações-chave ainda precisam ser realizadas na cadeia.
Aprender sobre a divisão de contratos e a utilização de contratos padrão existentes
Um projeto pode conter vários contratos realmente implantados. Mesmo que haja apenas um contrato realmente implantado, podemos dividir o contrato em várias partes para manutenção através da herança.
Por exemplo, o contrato NonfungiblePositionManager da Uniswap herda vários contratos. Ao implementar o contrato ERC721Permit, foi utilizado diretamente o contrato @openzeppelin/contracts/token/ERC721/ERC721.sol. Isso não só facilita a gestão de posições através de NFTs, como também aproveita contratos padrão existentes para aumentar a eficiência do desenvolvimento.
Resumo
A prática de desenvolver uma versão simplificada de uma exchange descentralizada permitirá que você compreenda mais profundamente a implementação do código do Uniswap, além de aprender mais pontos de conhecimento práticos em projetos reais. Acreditamos que essas dicas serão muito úteis para novatos que desejam aprender desenvolvimento de contratos.
Esta página pode conter conteúdos de terceiros, que são fornecidos apenas para fins informativos (sem representações/garantias) e não devem ser considerados como uma aprovação dos seus pontos de vista pela Gate, nem como aconselhamento financeiro ou profissional. Consulte a Declaração de exoneração de responsabilidade para obter mais informações.
9 gostos
Recompensa
9
7
Partilhar
Comentar
0/400
DataPickledFish
· 13h atrás
Ainda estou a estudar o código, a minha cabeça dói!
Ver originalResponder0
PermabullPete
· 13h atrás
Começou, não entendo isso, só sei perder dinheiro.
Ver originalResponder0
MEVSandwichVictim
· 13h atrás
Esse CREATE2 é bem avançado, não é à toa que é da uni.
Ver originalResponder0
MrRightClick
· 13h atrás
Hey, create2 é mesmo incrível!
Ver originalResponder0
PanicSeller69
· 13h atrás
O cão de código até chorou.
Ver originalResponder0
BearMarketBard
· 13h atrás
老干爹 é mesmo um bull
Ver originalResponder0
Deconstructionist
· 13h atrás
Estou a pesquisar códigos de produção novamente, já não consigo aprender.
Análise do código Uniswap: 7 dicas práticas para desenvolvimento de contratos
Dicas práticas para o desenvolvimento de contratos: lições aprendidas com o código do Uniswap
Recentemente, ao desenvolver um tutorial sobre uma troca descentralizada, consultei a implementação do código do Uniswap V3 e aprendi muitos pontos interessantes. Como um desenvolvedor que está tentando pela primeira vez desenvolver contratos Defi, essas técnicas serão muito úteis para iniciantes que desejam aprender sobre desenvolvimento de contratos.
Vamos agora dar uma olhada nestas dicas úteis, algumas das quais podem até ser consideradas truques engenhosos.
Endereço de implantação do contrato de previsão
Normalmente, o endereço obtido ao implantar um contrato parece aleatório, pois está relacionado ao nonce e é difícil de prever. No entanto, em certos cenários, precisamos inferir o endereço do contrato com base nas transações e nas informações relacionadas. Isso é útil em situações como determinar permissões de transação ou obter o endereço de um pool.
A Uniswap utiliza o método CREATE2 para criar contratos, adicionando o parâmetro salt para tornar o endereço do contrato gerado previsível. A lógica de geração do novo endereço é: hash("0xFF", endereço do criador, salt, initcode). Este método torna o endereço do contrato previsível, facilitando operações subsequentes.
Uso inteligente de funções de callback
Os contratos podem chamar-se uns aos outros em Solidity. Um cenário comum é o método A chamar o B, e o B fazer uma chamada de retorno ao A no método chamado. Isso é muito útil em algumas situações.
Tomando o Uniswap como exemplo, quando o método swap do contrato UniswapV3Pool é chamado para realizar uma transação, ele irá chamar o swapCallback, passando o Token que foi calculado como necessário para a transação atual. O chamador deve transferir o Token necessário para a transação de volta ao UniswapV3Pool no callback. Este design garante a execução completa e a segurança do método swap, sem a necessidade de registros complicados de variáveis.
Utilizar a transmissão de informações de exceção, usar try catch para realizar a previsão de transações
No contrato Quoter do Uniswap, utiliza-se o try catch para envolver a execução do método swap do UniswapV3Pool. Isso é feito para simular o método swap e estimar os Tokens necessários para a transação. Como não há troca real de Tokens durante a estimativa, ocorrerá um erro. O Uniswap lança um erro especial na função de callback da transação, que é então capturado e a partir do qual as informações necessárias são extraídas.
Este método pode parecer uma solução fácil, mas é muito prático. Ele evita a necessidade de reformular o método swap para estimar a demanda de negociação, tornando a lógica mais simples.
Resolver problemas de precisão com grandes números
No código do Uniswap, há uma grande quantidade de lógica de cálculo, como calcular os tokens trocados com base no preço atual e na liquidez. Para evitar a perda de precisão devido a operações de divisão, o Uniswap frequentemente utiliza a operação "<< FixedPoint96.RESOLUTION", ou seja, desloca para a esquerda 96 bits, o que equivale a multiplicar por 2^96. Deslocar para a esquerda antes de realizar operações de divisão pode garantir a precisão, normalmente usando uint256 para calcular (, desde que não haja estouro em transações normais.
Embora teoricamente ainda haja uma pequena perda de precisão, esse nível de erro é geralmente aceitável.
Calcular rendimento com o método Share
A Uniswap precisa registrar os ganhos de taxa dos provedores de liquidez LP). Para evitar o consumo excessivo de Gas ao registrar as taxas para cada LP em cada transação, a Uniswap adotou um método engenhoso.
Na estrutura Position, foram definidos feeGrowthInside0LastX128 e feeGrowthInside1LastX128, que registram as taxas de serviço que cada liquidez deveria receber na última retirada de taxas de serviço de cada posição. Assim, é necessário apenas registrar a taxa de serviço total e a taxa de serviço que cada liquidez deve receber; quando o LP retira, a taxa de serviço que pode ser retirada pode ser calculada com base na liquidez mantida. Isso é semelhante ao mecanismo de dividendos de ações, onde, na retirada, é necessário apenas conhecer os lucros por ação históricos da empresa e os lucros da última retirada.
Escolher razoavelmente os meios de obtenção de informações
O armazenamento em blockchain é relativamente caro, por isso nem todas as informações precisam ser armazenadas na blockchain ou obtidas a partir dela. Por exemplo, muitas das interfaces chamadas pelo site front-end da Uniswap são interfaces tradicionais da Web2.
A lista de pools de negociação, informações sobre os pools de negociação, etc., podem ser armazenadas em um banco de dados comum. Alguns dados podem precisar ser sincronizados periodicamente da cadeia, mas não é necessário chamar em tempo real a interface RPC da cadeia ou dos serviços de nó para obter dados relevantes.
Alguns fornecedores de RPC de blockchain oferecem interfaces avançadas, que permitem obter certos dados de forma mais rápida e econômica. Essas interfaces geralmente utilizam cache para melhorar o desempenho e a eficiência.
Claro, as transações-chave ainda precisam ser realizadas na cadeia.
Aprender sobre a divisão de contratos e a utilização de contratos padrão existentes
Um projeto pode conter vários contratos realmente implantados. Mesmo que haja apenas um contrato realmente implantado, podemos dividir o contrato em várias partes para manutenção através da herança.
Por exemplo, o contrato NonfungiblePositionManager da Uniswap herda vários contratos. Ao implementar o contrato ERC721Permit, foi utilizado diretamente o contrato @openzeppelin/contracts/token/ERC721/ERC721.sol. Isso não só facilita a gestão de posições através de NFTs, como também aproveita contratos padrão existentes para aumentar a eficiência do desenvolvimento.
Resumo
A prática de desenvolver uma versão simplificada de uma exchange descentralizada permitirá que você compreenda mais profundamente a implementação do código do Uniswap, além de aprender mais pontos de conhecimento práticos em projetos reais. Acreditamos que essas dicas serão muito úteis para novatos que desejam aprender desenvolvimento de contratos.