Uniswap es una aplicación DeFi que permite a los traders intercambiar un token por otro de manera descentralizada (trustless). Fue uno de los primeros creadores de mercado automatizados (Automated Market Makers) para el trading (aunque no el primero).
Los creadores de mercado automatizados son una alternativa al order book (libro de órdenes), con el cual se asume que el lector ya está familiarizado.
Cómo funcionan los AMM
Un creador de mercado automatizado mantiene dos tokens (token X y token Y) en el pool (un smart contract). Permite a cualquiera retirar token X del pool, pero deben depositar una cantidad de token Y de tal manera que el “total” de activos en el pool no disminuya, donde consideramos que el “total” es el producto de las cantidades de los dos activos.
Aquí e son los balances de tokens del pool después del intercambio y e son los balances de tokens del pool antes del intercambio.
Esto garantiza que las tenencias de activos del pool solo puedan mantenerse iguales o aumentar.
La mayoría de los pools aplican algún tipo de tarifa (fee). No solo debería aumentar el producto de los balances, sino que debería aumentar al menos en una cierta cantidad para contabilizar dicha tarifa.
Los activos son proporcionados al pool por los proveedores de liquidez, quienes reciben los denominados tokens LP para representar su participación en el pool. Los balances de los proveedores de liquidez se rastrean de una manera similar a cómo funciona ERC 4626. La diferencia entre un AMM y ERC 4626 es que ERC 4626 solo soporta un activo, pero un AMM tiene dos tokens. Al igual que en una bóveda (vault), la participación de los proveedores de liquidez en el pool se mantiene igual, pero el producto se hace más grande, por lo que su porción es mayor.
Ventajas de los AMM
Los AMM no tienen un spread bid-ask
En un AMM, el descubrimiento de precios es automático. Se determina por la proporción de activos en el pool. Específicamente, si tenemos el token y el token , el precio se determina de la siguiente manera:
Y viceversa para . Específicamente, cuanto más activo se introduce en el pool, más “abundante” se vuelve, y el precio de baja.
No hay necesidad de esperar a que aparezca una orden “bid” (compra) o “ask” (venta) adecuada. Siempre existe.
Si hay un desajuste entre el precio en un AMM y otro exchange, entonces un trader arbitrará la diferencia, devolviendo los precios al equilibrio.
Debemos enfatizar que este es el precio “spot” o “marginal”. Si compras cualquier cantidad de , el precio real que pagarás será peor que el resultado de este cálculo.
Los AMM también funcionan como oráculos
Dado que el precio de los activos se determina automáticamente, otros smart contracts pueden usar un AMM como un oráculo de precios. Sin embargo, los precios de los AMM pueden ser manipulados con préstamos flash (flash loans), por lo que se deben establecer medidas de seguridad cuando se usan los AMM de esta manera. No obstante, es valioso que los datos de precios se proporcionen de forma gratuita.
Los AMM son altamente eficientes en gas en comparación con los order books
Los order books requieren una cantidad significativa de contabilidad (bookkeeping). Un AMM solo necesita mantener dos tokens y transferirlos de acuerdo con reglas simples. Esto los hace más eficientes de implementar.
Desventajas de los AMM
Existen dos grandes inconvenientes en los creadores de mercado automatizados: 1) el precio siempre se mueve y 2) la pérdida impermanente (impermanent loss) para los proveedores de liquidez.
Incluso las órdenes pequeñas mueven el precio en los AMM
Si colocas una orden para comprar 100 acciones de Apple, tu orden no hará que el precio se mueva porque hay miles de acciones disponibles para la venta al precio que especificas. Este no es el caso con un creador de mercado automatizado. Cada intercambio, sin importar cuán pequeño sea, mueve el precio.
Esto tiene dos implicaciones. Una orden de compra o venta generalmente encontrará más deslizamiento (slippage) que en un modelo de order book, y el mecanismo de intercambio (swapping) invita a los ataques sándwich (sandwich attacks).
Los ataques sándwich son en gran medida inevitables en los AMM
Dado que cada orden va a mover el precio, los traders de MEV (Maximal Extractable Value) esperarán a que entre una orden de compra lo suficientemente grande, y luego colocarán una orden de compra justo antes de la orden de la víctima y una orden de venta justo después de ella. La orden de compra principal hará subir el precio para el trader original, lo que le otorga una peor ejecución. Se llama ataque sándwich, ya que el intercambio de la víctima queda “intercalado” (sandwiched) entre los del atacante.
- Primera compra del atacante (front run): hace subir el precio para la víctima
- Compra de la víctima: hace subir el precio aún más
- Venta del atacante: vende la primera compra con ganancias
Los proveedores de liquidez no tienen control sobre el precio al que se venden sus activos
Por razones que discutiremos más adelante, los proveedores de liquidez solo pueden proporcionar activos de forma proporcional a la proporción actual de tokens en el pool. Por ejemplo, si hay 100 token y 200 token , el nuevo proveedor de liquidez debe proporcionar el doble de token que de token .
En un order book tradicional, un creador de mercado puede colocar órdenes limitadas en niveles que cree que reflejan un bid o ask deseable (por ejemplo, colocar una orden de compra por debajo del precio de mercado actual o colocar una orden de venta por encima del precio de mercado actual), pero esto no es posible con un creador de mercado automatizado. Recuerda que los Automated Market Makers usan una fórmula para establecer precios basados en las proporciones de activos en el pool, y como resultado los creadores de mercado no pueden establecer precios específicos a los que desean vender sus activos.
Los proveedores de liquidez para AMM pueden sufrir pérdidas impermanentes (Impermanent Loss)
Supongamos que en un escenario hipotético, Ether comienza en $10 y más tarde pasa a valer $1,000.
Si alguien tuviera un portafolio de 1 Ether y 10 USD, entonces su portafolio comenzaría en $20 y terminaría en $1010 (1 ETH + $10). Su ganancia total es de $990.
Si hubieran mantenido el dinero en un AMM, entonces se habrían perdido la mayor parte de las ganancias. El AMM tendría 0.1 ETH y 100 USD después del cambio de precio. Esto valora correctamente a ETH en $1,000, pero el valor neto del pool es menor a $990. Así es como se verían las tenencias del pool antes y después del cambio de precio:
Aunque la cantidad de stablecoins aumentó 10 veces, la cantidad de Ether disminuyó. El resultado neto es que el valor de nuestros activos, al estar en el pool, aumentó menos que si hubiéramos mantenido los activos por separado.
A continuación se muestra una tabla que ilustra el rendimiento relativo de mantener ETH y USD en un pool en comparación con solo holdearlos.
| Balance de Ether en el Pool | Balance de Stablecoin en el Pool | ETH × Stablecoin | Valor en \$ de 1 ETH | Valor de los activos si se provee liquidez | Valor si solo se holdea | ||
|---|---|---|---|---|---|---|---|
| Antes | 1 | 10 | 10 | 10 | $\$20 \; (\$10 \text{ ETH} + 10 \text{ USD})$ | $\$20 \; (\$10 \times 1 \text{ ETH} + 10 \text{ USD})$ | |
| Después | 0.1 | 100 | 10 | 1000 | $\$200 \; (\$100 \text{ ETH} + 100 \text{ USD})$ | $\$1010 \;(\$1000 \times 1 \text{ ETH} + 10 \text{ USD})$ | |
| Ganancia | $\$180$ | $\$990$ | |||||
Las ganancias perdidas se denominan “pérdida impermanente” (impermanent loss). En la tabla anterior, la pérdida impermanente es de .
Arquitectura de Uniswap V2
La arquitectura de Uniswap V2 es sorprendentemente simple. En su núcleo se encuentra el contrato UniswapV2Pair que mantiene dos tokens ERC 20 contra los cuales los traders pueden intercambiar, o para los cuales los proveedores de liquidez pueden suministrar liquidez. Cada posible UniswapV2Pair diferente tiene un contrato UniswapV2Pair distinto para gestionarlo. Si el contrato UniswapV2Pair deseado no existe, se puede crear uno nuevo sin necesidad de permisos desde el contrato UniswapV2Factory. Los contratos UniswapV2Pair también son tokens ERC 20 (heredan de ERC 20), y ese token se utiliza para rastrear los depósitos de manera similar a cómo funciona ERC 4626.
Aunque los traders avanzados o los smart contracts pueden interactuar directamente con un contrato pair, la mayoría de los usuarios interactuarán con un pair a través de un contrato router, el cual tiene varias funciones de conveniencia, como intercambiar entre pairs en una sola transacción para crear un par “sintético” si este no existe.
¡Eso es todo! Realmente solo hay tres smart contracts en juego en el sistema de Uniswap V2.
Factory: github.com/Uniswap/v2-core/blob/master/contracts/UniswapV2Factory.sol
Pair: (que hereda de ERC20): github.com/Uniswap/v2-core/blob/master/contracts/UniswapV2Pair.sol
Router: github.com/Uniswap/v2-periphery/tree/master/contracts
El patrón core - periphery
Observa que el contrato router anterior está en un repositorio llamado “v2 periphery” y el pair está en el repositorio “v2 core”. Uniswap V2 sigue el patrón de diseño “core / periphery” donde la lógica más esencial se mantiene en el core, mientras que la lógica “opcional” se mantiene en la periphery (periferia).
La intención detrás de esto es hacer que el core contenga la menor cantidad de código posible, lo que reduce la posibilidad de bugs en la lógica de negocio central.
Cómo localizar un pool, dadas las direcciones de dos tokens
En lugar de acceder a un mapping de pares de tokens a direcciones de pools, los smart contracts calculan la dirección del pool prediciendo la dirección create2 en función de las direcciones de los tokens y la dirección del factory. Dado que no hay acceso al storage, esto es muy eficiente en gas. A continuación, se muestra la función auxiliar proporcionada por UniswapV2Library para calcular la dirección del contrato Pair.
// calculates the CREATE2 address for a pair without making any external calls
function pairFor(address factory, address tokenA, address tokenB) internal pure returns (address pair) {
(address token0, address token1) = sortTokens(tokenA, tokenB);
pair = address(uint(keccak256(abi.encodePacked(
hex'ff',
factory,
keccak256(abi.encodePacked(token0, token1)),
hex'96e8ac4277198f8fbbf785487aa39f430f63b76db002cb326e37da348845f' // init code hash
))));
}
¿Por qué no usar clones?
El patrón EIP 1167 clone se utiliza para crear una colección de contratos similares, así que ¿por qué no usarlo aquí? Aunque el despliegue sería más barato, introduciría un costo adicional de 2,600 de gas por transacción debido al uso de delegatecall. Dado que los pools están pensados para usarse con frecuencia, el ahorro de costos en el despliegue eventualmente se perdería después de unos cientos de transacciones, por lo que vale la pena desplegar un pool como un nuevo contrato.
Problemas de práctica
Es fácil calcular incorrectamente la cantidad de tokens requeridos para un swap, lo cual podría llevar a que el pool sea vaciado. Practica esto con el siguiente desafío de seguridad: Ethernaut 22 Dex
Aprende más con RareSkills
Este artículo es parte de una serie. Por favor, consulta el Uniswap V2 Book para ver el resto. También puedes explorar nuestros Blockchain Bootcamps para conocer nuestros otros cursos.
Publicado originalmente el 15 de noviembre de 2023