Ethereum: de historia a labs
Artículo 41 de 48
85%
completado al llegar aquí
Sistemas distribuidosNota de investigación13 min de lectura
AMMs: x*y=k sin vender humo matemático
Un AMM de producto constante con números reales: invariante x·y=k, comisión, LP shares, por qué el precio spot no es un oráculo y qué pierde el LP.
Ruta Ethereum: 0/48
Prerequisitos recomendados
Si alguno de estos conceptos todavía está medio nebuloso, empieza por aquí. Te ahorra tropiezos y un par de cejas fruncidas.
Un contrato guarda 100 ETH y 200.000 USDC. Llega alguien con 10.000 USDC y quiere ETH. No hay vendedores con órdenes publicadas ni un motor de emparejamiento: el contrato calcula cuánto ETH entregar con una fórmula, recibe los USDC y actualiza sus saldos. El comprador se lleva unos 4,75 ETH. ¿Por qué 4,75 y no 5, si el precio "era" 2.000 USDC por ETH?
Ese contrato es un AMM (automated market maker, creador de mercado automático). La documentación de Uniswap lo describe como un conjunto de contratos que reemplaza el libro de órdenes por un pool de dos tokens ERC-20 cuyo precio sale del estado del pool; quien intercambia opera contra las reservas, no contra otra persona. [1] Este capítulo reconstruye ese cálculo desde el código de Uniswap v2, el diseño de producto constante más estudiado.
Brújula de lectura
Actor, evidencia y estado
Actor
Trader, LP y arbitrajistaEl trader cambia reservas, el LP las aporta y el arbitrajista alinea el precio con otros mercados.
Evidencia
Reservas y evento SwapgetReserves muestra el estado; el evento registra entradas y salidas de cada intercambio.
Estado
reserve0, reserve1 y totalSupplyDos reservas y la oferta de LP tokens definen precio y propiedad del pool.
Preguntas guía
- ¿Qué cantidad debe mantenerse (o crecer) después de cada intercambio?
- ¿Por qué el precio recibido empeora a medida que crece la operación?
- ¿Qué representa un LP token y cómo se calcula cuántos se emiten?
- ¿Por qué otro contrato no debe leer el precio del pool en el mismo bloque como si fuera un oráculo?
La invariante: x · y = k
Llama x a las reservas de ETH e y a las de USDC. Un pool de producto constante exige que, después de cada intercambio, el producto x · y no disminuya. Con 100 ETH y 200.000 USDC, k = 20.000.000. El precio spot (el precio marginal para una operación infinitamente pequeña) es el cociente de reservas: y / x = 2.000 USDC por ETH.
El whitepaper de Uniswap v2 resume así el diseño heredado de v1: cada par guarda reservas de dos activos y mantiene la invariante de que el producto de las reservas no puede disminuir; los traders pagan una comisión de 30 puntos básicos (0,3 %) que va a los proveedores de liquidez. [2]
En el contrato del par, la comprobación es literal. Tras transferir los tokens de salida, swap mide los saldos nuevos, descuenta 3/1000 de lo que entró y exige que el producto ajustado no sea menor que el producto de las reservas anteriores:
// Extracto de UniswapV2Pair.swap (Solidity 0.5.16, versión original)
uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3));
uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3));
require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000**2), 'UniswapV2: K');El contrato no calcula cuánto entregar: confía en que quien llama pida una salida que respete la invariante, y revierte si no. Por eso existe una biblioteca periférica que hace el cálculo previo. [3]
Traza de un swap con números
La función getAmountOut de la biblioteca de v2 despeja la salida máxima que respeta la invariante con comisión. Aquí está la misma aritmética, ilustrativa, portada a Solidity 0.8:
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.24;
/// Ilustrativo: misma aritmética que UniswapV2Library.getAmountOut, portada a 0.8.
library AmmMath {
function getAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut)
internal
pure
returns (uint256 amountOut)
{
require(amountIn > 0, "INSUFFICIENT_INPUT_AMOUNT");
require(reserveIn > 0 && reserveOut > 0, "INSUFFICIENT_LIQUIDITY");
uint256 amountInWithFee = amountIn * 997;
uint256 numerator = amountInWithFee * reserveOut;
uint256 denominator = reserveIn * 1000 + amountInWithFee;
amountOut = numerator / denominator;
}
}Apliquemos la fórmula al ejemplo. [4]
| Paso | Cálculo | Resultado |
|---|---|---|
| Entrada con comisión | 10.000 × 997 / 1000 | 9.970 USDC efectivos |
| Salida | 9.970 × 100 / (200.000 + 9.970) | 4,7483 ETH |
| Precio pagado | 10.000 / 4,7483 | ≈ 2.106 USDC/ETH |
| Reservas nuevas | 95,2517 ETH y 210.000 USDC | spot ≈ 2.205 USDC/ETH |
| Producto nuevo | 95,2517 × 210.000 | ≈ 20.002.858, mayor que k |
Sin comisión, la salida sería 100 × 10.000 / 210.000 ≈ 4,7619 ETH. La diferencia entre 2.000 y 2.106 tiene dos partes: la comisión y el impacto de precio, que es el movimiento de la curva causado por la propia operación. Cuanto mayor es la operación respecto de las reservas, mayor el impacto. Y el producto final queda por encima de 20.000.000: esa diferencia es la comisión que se queda en el pool.
Un swap recorre la curva x·y = k
Fuentes:
[2]Cómo leer esta figura
Gráfico con las reservas de ETH en el eje horizontal, entre 90 y 105, y las de USDC en el eje vertical, entre 190 mil y 220 mil. Una curva azul representa x por y igual a 20 millones. Un punto marca el estado inicial en 100 ETH y 200.000 USDC, con precio spot y sobre x igual a 2000. Una flecha roja horizontal hacia la izquierda indica que salen 4,75 ETH y una flecha verde vertical hacia arriba indica que entran 10.000 USDC, llegando al punto final en 95,25 ETH y 210.000 USDC, con precio spot cercano a 2205. Un recuadro explica que el precio pagado es 10.000 dividido 4,75, cerca de 2106, mayor que el spot inicial por el impacto de precio y la comisión de 0,3 por ciento.
LP shares: quién es dueño de qué
Un LP (liquidity provider, proveedor de liquidez) deposita ambos tokens y recibe LP tokens, un ERC-20 que representa su fracción del pool. En mint, el primer depósito emite sqrt(amount0 · amount1) menos MINIMUM_LIQUIDITY (1.000 unidades mínimas), que se envían a la dirección cero y quedan bloqueadas para siempre. Los depósitos posteriores reciben el mínimo entre amount0 · totalSupply / reserve0 y amount1 · totalSupply / reserve1, así que depositar en una proporción distinta de la del pool regala la diferencia. [3]
Las comisiones no se reparten en cada swap. Se quedan en las reservas, hacen crecer k y, por tanto, el valor de cada LP token. Al retirar con burn, el LP recibe su fracción de reservas ya engordadas. Si la comisión de protocolo está activada (feeTo distinto de cero), el contrato emite para el protocolo LP tokens equivalentes a 1/6 del crecimiento de sqrt(k) desde el último cálculo.
La misma documentación de Uniswap aclara que en v2 los LP tokens son fungibles, mientras que en v3 y v4 cada LP elige un rango de precios y su posición deja de ser intercambiable con la de otro. [1] Ese cambio es el tema del capítulo siguiente.
Riesgo operativo: el precio spot no es un oráculo
Supón que un contrato de préstamos valora colateral leyendo reserve1 / reserve0 del pool. El whitepaper de v2 describe el ataque: alguien compra ETH en el par para inflar el precio, dispara la operación que lee ese precio (por ejemplo, una liquidación o el cierre de un derivado) y vende el ETH de vuelta. Todo puede ocurrir en una sola transacción atómica. El AMM funcionó según su curva; lo que falló fue el contrato que confundió un precio transitorio con evidencia. [2]
La respuesta de v2 fue registrar el precio antes del primer intercambio de cada bloque, que es más difícil de manipular, y acumularlo ponderado por tiempo. En _update, los contadores price0CumulativeLast y price1CumulativeLast suman el precio por los segundos transcurridos, en formato de punto fijo UQ112.112. Quien necesite un precio promedio ponderado por tiempo (TWAP) guarda el acumulador en dos momentos y divide la diferencia por el tiempo. Una ventana más larga encarece la manipulación a costa de un precio menos actual, y los timestamps de bloque no son exactos.
Para un contrato en producción, la regla práctica es leer un TWAP o un feed dedicado, nunca el spot del mismo bloque. El capítulo de oráculos desarrolla las alternativas.
Riesgo del LP: pérdida frente a mantener y frente al rebalanceo
El LP no solo cobra comisiones: también vende el activo que sube y compra el que baja, porque los arbitrajistas llevan el pool al precio externo. Se puede calcular cuánto cuesta eso con la invariante. Si el precio externo se multiplica por r, un pool de producto constante sin comisiones queda con un valor igual a 2·√r / (1 + r) veces el que tendrían los mismos tokens guardados fuera. Con r = 2 (el precio se duplica) la diferencia es de unos −5,7 %; con r = 4, de −20 %. Es la llamada pérdida impermanente: desaparece si el precio vuelve, y se realiza si el LP retira antes.
La investigación académica pone números más precisos. Angeris y coautores muestran que, bajo condiciones comunes, los mercados de producto constante siguen de cerca el precio de referencia, justamente porque los arbitrajistas lo imponen. [5] Milionis, Moallemi, Roughgarden y Zhang descomponen el retorno del LP en una exposición al mercado y un componente de microestructura: comisiones cobradas menos pérdidas frente a arbitrajistas, que llaman loss-versus-rebalancing (LVR). [6]
Para quien decide poner tesorería en un pool, la conclusión es operativa: comparar "APR de comisiones" con cero es un error. La comparación correcta es contra mantener los activos o contra una estrategia que rebalancea a precios de mercado.
La capa periférica: mínimo a recibir y plazo
Ningún usuario debería llamar a swap directamente. El router de v2 ofrece funciones como swapExactTokensForTokens(amountIn, amountOutMin, path, to, deadline): calcula las salidas por la ruta, revierte si la última es menor que amountOutMin y revierte si el bloque llega después de deadline. [7] Esos dos parámetros son la defensa del usuario frente a cambios de precio y frente a quien reordena transacciones, como se vio en el capítulo de MEV.
Antes de integrar un AMM en un sistema propio
0 de 5 revisados
Lab propuesto: reproducir la tabla en una red local
Desplegar en Anvil dos ERC-20 de prueba y un par de producto constante, aportar 100 y 200.000 unidades y ejecutar el swap de 10.000. La validación observable es que getReserves devuelva las reservas de la tabla (salvo redondeo entero), que el evento Swap registre las cantidades y que un segundo swap con amountOutMin mayor que la salida calculada revierta. Como teardown basta detener Anvil.
Conexiones dentro de la serie
- Antes: MEV y PBS usa esta misma fórmula para medir un sandwich.
- Base necesaria: el estándar ERC-20 y aprobaciones y permit, porque todo swap empieza con un
approve. - Relacionado: oráculos y price feeds para no usar el spot como precio.
- Después: Uniswap v3 y liquidez concentrada.
Bibliografía académica
- [1]
Uniswap Labs, "How Uniswap Works," Uniswap Docs (repositorio Uniswap/docs).
Pools de dos ERC-20, fórmula de producto constante e impacto de precio.
- [2]
H. Adams, N. Zinsmeister y D. Robinson, "Uniswap v2 Core," whitepaper, marzo de 2020.
Producto constante, comisión de 0,3 %, oráculo TWAP y riesgos de manipulación del precio spot.
- [3]
Uniswap, "UniswapV2Pair.sol," Uniswap/v2-core, GitHub.
swap con comprobación de K, mint con MINIMUM_LIQUIDITY, comisión de protocolo y acumuladores de precio.
- [4]
Uniswap, "UniswapV2Library.sol," Uniswap/v2-periphery, GitHub.
getAmountOut con comisión de 0,3 % (factor 997/1000).
- [5]
G. Angeris, H.-T. Kao, R. Chiang, C. Noyes y T. Chitra, "An analysis of Uniswap markets," arXiv:1911.03380, 2019.
Los mercados de producto constante siguen el precio de referencia bajo condiciones comunes.
- [6]
J. Milionis, C. C. Moallemi, T. Roughgarden y A. L. Zhang, "Automated Market Making and Loss-Versus-Rebalancing," arXiv:2208.06046, 2022.
Retorno del LP: exposición al mercado más comisiones menos pérdidas frente a arbitrajistas (LVR).
- [7]
Uniswap, "UniswapV2Router02.sol," Uniswap/v2-periphery, GitHub.
swapExactTokensForTokens con amountOutMin y deadline.
Rutas vecinas
Si este tema te abrió hambre, sigue por aquí.
Series y labs que comparten área o dependen de esta ruta. Pocas opciones, para no convertir la curiosidad en menú infinito.
Series relacionadas
HotStuff: de cero a estado del arte
Ruta progresiva para entender consenso BFT moderno: quórums, sincronía parcial, locks, taxonomía one/two/three-chain, QC/highQC, multipipeline, view-change, responsiveness, AggQC, Fast-HotStuff, sync y frontera DAG-BFT.
Substrate: del runtime al consenso propio
Serie de 20 capítulos, de neófito a experto, sobre Substrate (Polkadot SDK): runtime en Wasm, FRAME, extrinsics, estado, pesos, actualizaciones sin fork, Aura, BABE, GRANDPA, sincronización, parachains, XCM, Sassafras, BEEFY y cómo conectar un consenso BFT propio a un nodo.
Ruta práctica: activos digitales en la operación
Nueve módulos, de menor a mayor dependencia, para decidir cómo usar custodia, wallets, llaves, firmas multifirma y MPC, AML/KYT y stablecoins con Fireblocks, BitGo, Kaleido, Circle y Tether.
Labs para convertir lectura en práctica
Prueba de trabajo y reajuste de dificultad
Laboratorio determinista para medir el costo de la prueba de trabajo y ver cómo el reajuste por altura de bloque estabiliza el tiempo entre bloques.
Consensus benchmark
Banco reproducible para comparar consenso BFT y documentar los trade-offs de HotStuff-2.
Ethereum local: Anvil, wallet y primera transacción
Laboratorio base para levantar una red local, crear cuentas, firmar transacciones y observar receipts sin gastar ETH real.
RPC y Cast: leer Ethereum con lupa
Ejercicios con JSON-RPC y Cast para consultar bloques, nonces, gas, receipts, logs y errores reproducibles.
Sigue leyendo
Ideas conectadas con este artículo
Una ruta corta para continuar sin abrir veinte pestañas y perder el hilo.
ERC-4626: vaults, shares y contabilidad peligrosa
Tokenized vaults, assets, shares, exchange rate, rounding y ataques de inflación en vaults.
Oráculos: datos externos con dientes
Price feeds, stale data, TWAP, manipulación, dependencia de oráculos y diseño defensivo.
Uniswap v3: liquidez concentrada y ticks
Rangos, ticks, capital efficiency, posiciones LP y nuevos riesgos de liquidez concentrada.
Lending y liquidaciones: cuando el colateral respira mal
Collateral, borrow, health factor, liquidations, oracle dependency y diseño básico de mercados lending.