Consejos prácticos para el desarrollo de contratos: lecciones aprendidas del código de Uniswap
Recientemente, al desarrollar un tutorial sobre un intercambio descentralizado, consulté la implementación del código de Uniswap V3 y aprendí muchos puntos interesantes. Como desarrollador que intenta por primera vez crear contratos DeFi, estas técnicas serán muy útiles para los principiantes que desean aprender sobre el desarrollo de contratos.
A continuación, echemos un vistazo a estos útiles trucos, algunos de los cuales incluso podrían considerarse ingeniosos.
Dirección de despliegue de contratos de predicción
Normalmente, la dirección obtenida al desplegar un contrato parece aleatoria, ya que está relacionada con el nonce y es difícil de predecir. Sin embargo, en ciertos escenarios, necesitamos inferir la dirección del contrato a través de pares de transacciones e información relacionada. Esto es útil para determinar los permisos de transacción o para obtener la dirección de un pool, entre otros casos.
Uniswap utiliza el método CREATE2 para crear contratos, añadiendo un parámetro salt que hace que la dirección del contrato generado sea predecible. La lógica de generación de la nueva dirección es: hash("0xFF", dirección del creador, salt, initcode). Este método hace que la dirección del contrato sea predecible, facilitando las operaciones posteriores.
Uso inteligente de funciones de callback
En Solidity, los contratos pueden llamarse entre sí. Un escenario común es que el método A llame a B, y B, en el método que se le llama, haga una llamada de regreso a A. Esto es muy útil en ciertas situaciones.
Tomando como ejemplo Uniswap, cuando se llama al método swap del contrato UniswapV3Pool para realizar una transacción, se invoca swapCallback, pasando el Token que se calcula que se necesita para esta transacción. El llamador debe transferir el Token necesario para la transacción al UniswapV3Pool en la devolución de llamada. Este diseño asegura la ejecución completa y la seguridad del método swap, sin necesidad de registros de variables complicados.
Utilizar la transmisión de información por excepciones, implementar la estimación de transacciones con try catch
En el contrato Quoter de Uniswap, se utiliza try catch para envolver la ejecución del método swap de UniswapV3Pool. Esto es para simular el método swap y estimar los tokens requeridos para la transacción. Dado que no se produce un intercambio real de tokens durante la estimación, se genera un error. Uniswap lanza un error especial en la función de devolución de llamada de la transacción, que luego se captura y se analiza para extraer la información necesaria.
Este método parece un atajo, pero es muy práctico. Evita modificar el método swap para estimar la demanda de transacciones, lo que hace que la lógica sea más simple.
Resolver problemas de precisión con grandes números
El código de Uniswap involucra una gran cantidad de lógica de cálculo, como calcular los tokens intercambiados según el precio actual y la liquidez. Para evitar la pérdida de precisión causada por operaciones de división, Uniswap utiliza a menudo la operación "<< FixedPoint96.RESOLUTION", que equivale a un desplazamiento a la izquierda de 96 bits, lo que equivale a multiplicar por 2^96. Desplazar a la izquierda antes de realizar la operación de división puede garantizar la precisión al calcular ( con uint256 bajo condiciones normales de transacción sin desbordamiento.
Aunque teóricamente todavía habrá una pequeña pérdida de precisión, este nivel de error suele ser aceptable.
Calcular ingresos usando Share
Uniswap necesita registrar los ingresos por tarifas de los proveedores de liquidez LP). Para evitar el consumo masivo de Gas al registrar las tarifas para cada LP en cada transacción, Uniswap utiliza un método ingenioso.
En la estructura Position se definen feeGrowthInside0LastX128 y feeGrowthInside1LastX128, que registran las tarifas de transacción que le corresponden a cada liquidez en el último retiro de tarifas de cada posición. De este modo, solo es necesario registrar la tarifa total y la tarifa que debe asignarse a cada liquidez; al retirar LP, se puede calcular la tarifa que se puede retirar según la liquidez poseída. Esto es similar al mecanismo de dividendos de acciones, donde al retirar solo es necesario conocer el historial de ganancias por acción de la empresa y las ganancias del último retiro.
Elección razonable de los canales de obtención de información
El almacenamiento en cadena es relativamente caro, por lo que no toda la información necesita estar en la cadena o recuperarse de la cadena. Por ejemplo, muchas de las interfaces que utiliza el sitio web frontal de Uniswap son interfaces tradicionales de Web2.
La lista de pools de negociación, la información de los pools de negociación, etc., se pueden almacenar en una base de datos normal. Algunos datos pueden necesitar ser sincronizados periódicamente desde la cadena, pero no es necesario llamar en tiempo real a la interfaz RPC de la cadena o de los nodos para obtener datos relacionados.
Algunos proveedores de RPC de blockchain ofrecen interfaces avanzadas que permiten obtener ciertos datos de manera más rápida y económica. Estas interfaces suelen utilizar caché para mejorar el rendimiento y la eficiencia.
Por supuesto, las transacciones clave aún deben realizarse en la cadena.
Aprender sobre la división de contratos y el uso de contratos estándar existentes
Un proyecto puede incluir múltiples contratos desplegados en la práctica. Incluso si solo hay un contrato desplegado en la práctica, podemos dividir el contrato en múltiples partes para su mantenimiento a través de la herencia.
Por ejemplo, el contrato NonfungiblePositionManager de Uniswap hereda de varios contratos. Al implementar el contrato ERC721Permit, se utiliza directamente el contrato @openzeppelin/contracts/token/ERC721/ERC721.sol. Esto no solo facilita la gestión de posiciones a través de NFT, sino que también aprovecha los contratos estándar existentes para mejorar la eficiencia del desarrollo.
Resumen
La práctica personal de desarrollar una versión simplificada de un intercambio descentralizado te permitirá comprender más profundamente la implementación del código de Uniswap y también aprender más sobre los puntos de conocimiento en proyectos reales. Estoy seguro de que estas habilidades serán de gran ayuda para los novatos que desean aprender sobre el desarrollo de contratos.
Esta página puede contener contenido de terceros, que se proporciona únicamente con fines informativos (sin garantías ni declaraciones) y no debe considerarse como un respaldo por parte de Gate a las opiniones expresadas ni como asesoramiento financiero o profesional. Consulte el Descargo de responsabilidad para obtener más detalles.
10 me gusta
Recompensa
10
7
Compartir
Comentar
0/400
DataPickledFish
· hace19h
¡Otra vez estudiando código, me duele la cabeza!
Ver originalesResponder0
PermabullPete
· hace19h
Ya está, no entiendo esto, solo sé perder dinero.
Ver originalesResponder0
MEVSandwichVictim
· hace19h
Este CREATE2 es bastante avanzado, no es de extrañar que sea uni.
Ver originalesResponder0
MrRightClick
· hace19h
¡Hey, create2 es realmente increíble!
Ver originalesResponder0
PanicSeller69
· hace19h
El perro de código también llora.
Ver originalesResponder0
BearMarketBard
· hace19h
老干爹是真滴 alcista
Ver originalesResponder0
Deconstructionist
· hace19h
Otra vez estudiando código de producción, ya no puedo más.
Análisis del código de Uniswap: 7 consejos prácticos para el desarrollo de contratos
Consejos prácticos para el desarrollo de contratos: lecciones aprendidas del código de Uniswap
Recientemente, al desarrollar un tutorial sobre un intercambio descentralizado, consulté la implementación del código de Uniswap V3 y aprendí muchos puntos interesantes. Como desarrollador que intenta por primera vez crear contratos DeFi, estas técnicas serán muy útiles para los principiantes que desean aprender sobre el desarrollo de contratos.
A continuación, echemos un vistazo a estos útiles trucos, algunos de los cuales incluso podrían considerarse ingeniosos.
Dirección de despliegue de contratos de predicción
Normalmente, la dirección obtenida al desplegar un contrato parece aleatoria, ya que está relacionada con el nonce y es difícil de predecir. Sin embargo, en ciertos escenarios, necesitamos inferir la dirección del contrato a través de pares de transacciones e información relacionada. Esto es útil para determinar los permisos de transacción o para obtener la dirección de un pool, entre otros casos.
Uniswap utiliza el método CREATE2 para crear contratos, añadiendo un parámetro salt que hace que la dirección del contrato generado sea predecible. La lógica de generación de la nueva dirección es: hash("0xFF", dirección del creador, salt, initcode). Este método hace que la dirección del contrato sea predecible, facilitando las operaciones posteriores.
Uso inteligente de funciones de callback
En Solidity, los contratos pueden llamarse entre sí. Un escenario común es que el método A llame a B, y B, en el método que se le llama, haga una llamada de regreso a A. Esto es muy útil en ciertas situaciones.
Tomando como ejemplo Uniswap, cuando se llama al método swap del contrato UniswapV3Pool para realizar una transacción, se invoca swapCallback, pasando el Token que se calcula que se necesita para esta transacción. El llamador debe transferir el Token necesario para la transacción al UniswapV3Pool en la devolución de llamada. Este diseño asegura la ejecución completa y la seguridad del método swap, sin necesidad de registros de variables complicados.
Utilizar la transmisión de información por excepciones, implementar la estimación de transacciones con try catch
En el contrato Quoter de Uniswap, se utiliza try catch para envolver la ejecución del método swap de UniswapV3Pool. Esto es para simular el método swap y estimar los tokens requeridos para la transacción. Dado que no se produce un intercambio real de tokens durante la estimación, se genera un error. Uniswap lanza un error especial en la función de devolución de llamada de la transacción, que luego se captura y se analiza para extraer la información necesaria.
Este método parece un atajo, pero es muy práctico. Evita modificar el método swap para estimar la demanda de transacciones, lo que hace que la lógica sea más simple.
Resolver problemas de precisión con grandes números
El código de Uniswap involucra una gran cantidad de lógica de cálculo, como calcular los tokens intercambiados según el precio actual y la liquidez. Para evitar la pérdida de precisión causada por operaciones de división, Uniswap utiliza a menudo la operación "<< FixedPoint96.RESOLUTION", que equivale a un desplazamiento a la izquierda de 96 bits, lo que equivale a multiplicar por 2^96. Desplazar a la izquierda antes de realizar la operación de división puede garantizar la precisión al calcular ( con uint256 bajo condiciones normales de transacción sin desbordamiento.
Aunque teóricamente todavía habrá una pequeña pérdida de precisión, este nivel de error suele ser aceptable.
Calcular ingresos usando Share
Uniswap necesita registrar los ingresos por tarifas de los proveedores de liquidez LP). Para evitar el consumo masivo de Gas al registrar las tarifas para cada LP en cada transacción, Uniswap utiliza un método ingenioso.
En la estructura Position se definen feeGrowthInside0LastX128 y feeGrowthInside1LastX128, que registran las tarifas de transacción que le corresponden a cada liquidez en el último retiro de tarifas de cada posición. De este modo, solo es necesario registrar la tarifa total y la tarifa que debe asignarse a cada liquidez; al retirar LP, se puede calcular la tarifa que se puede retirar según la liquidez poseída. Esto es similar al mecanismo de dividendos de acciones, donde al retirar solo es necesario conocer el historial de ganancias por acción de la empresa y las ganancias del último retiro.
Elección razonable de los canales de obtención de información
El almacenamiento en cadena es relativamente caro, por lo que no toda la información necesita estar en la cadena o recuperarse de la cadena. Por ejemplo, muchas de las interfaces que utiliza el sitio web frontal de Uniswap son interfaces tradicionales de Web2.
La lista de pools de negociación, la información de los pools de negociación, etc., se pueden almacenar en una base de datos normal. Algunos datos pueden necesitar ser sincronizados periódicamente desde la cadena, pero no es necesario llamar en tiempo real a la interfaz RPC de la cadena o de los nodos para obtener datos relacionados.
Algunos proveedores de RPC de blockchain ofrecen interfaces avanzadas que permiten obtener ciertos datos de manera más rápida y económica. Estas interfaces suelen utilizar caché para mejorar el rendimiento y la eficiencia.
Por supuesto, las transacciones clave aún deben realizarse en la cadena.
Aprender sobre la división de contratos y el uso de contratos estándar existentes
Un proyecto puede incluir múltiples contratos desplegados en la práctica. Incluso si solo hay un contrato desplegado en la práctica, podemos dividir el contrato en múltiples partes para su mantenimiento a través de la herencia.
Por ejemplo, el contrato NonfungiblePositionManager de Uniswap hereda de varios contratos. Al implementar el contrato ERC721Permit, se utiliza directamente el contrato @openzeppelin/contracts/token/ERC721/ERC721.sol. Esto no solo facilita la gestión de posiciones a través de NFT, sino que también aprovecha los contratos estándar existentes para mejorar la eficiencia del desarrollo.
Resumen
La práctica personal de desarrollar una versión simplificada de un intercambio descentralizado te permitirá comprender más profundamente la implementación del código de Uniswap y también aprender más sobre los puntos de conocimiento en proyectos reales. Estoy seguro de que estas habilidades serán de gran ayuda para los novatos que desean aprender sobre el desarrollo de contratos.