Operaciones entre cadenas, DeFi y contratos inteligentes · Lección 2 de 6
Swaps en DEX, exchanges instantáneos y flujos de liquidez
14 min de lectura
Casi todo rastro de lavado de activos cambia de activo en algún punto, y la forma en que lo hace determina la solidez del siguiente vínculo. Un swap en un exchange descentralizado (DEX) queda registrado en una sola transacción, con el evento del pool, las transferencias de tokens y las reservas anteriores y posteriores. Un protocolo de swaps entre cadenas puede registrar el destino en la cadena de origen y el origen en la cadena de destino. Un servicio de exchange instantáneo o una mesa OTC solo deja un depósito en un lado y un pago en el otro. Esta lección muestra cómo interpretar cada tipo de swap como evidencia, qué revelan los eventos de liquidez, qué registros existen fuera de la cadena y qué se puede afirmar sobre cada vínculo. La decodificación general se trata en la lección 5.1; la correlación de saltos entre cadenas, en la lección 5.3; y los puentes, en la lección 5.4.
Interpretación de un swap en un DEX
Pools, token0 y token1
Un par de tipo Uniswap v2 es un contrato que mantiene dos tokens. El token con la dirección de menor valor es token0 y el otro es token1; el orden se fija al crearlo y no tiene nada que ver con cuál de los tokens es «el precio». Todos los eventos informan los importes en este orden, así que consulte token0, token1 y los decimales de ambos antes de convertir cualquier valor.
El evento de v2 es Swap(address indexed sender, uint256 amount0In, uint256 amount1In, uint256 amount0Out, uint256 amount1Out, address indexed to), con topic0 0xd78ad95f…d822. Después de cada cambio en las reservas, el par también emite Sync(uint112 reserve0, uint112 reserve1). Un swap decodificado en un par donde token0 es USDC (6 decimales) y token1 es WETH (18 decimales):
{
"event": "Swap",
"pair": "0x3f8a…e2d1",
"sender": "0x7a2c…48d0",
"to": "0xa1b2c3d4…9f0e",
"amount0In": "0",
"amount1In": "5000000000000000000",
"amount0Out": "14880819116",
"amount1Out": "0",
"next_log": {"event": "Sync", "reserve0": "2985119180884", "reserve1": "1005000000000000000000"}
}
Entraron cinco WETH y salieron 14.880,819116 USDC, con un precio de ejecución de 2.976,16 USDC por WETH. El sender suele ser un enrutador, no el operador, y to indica adónde fue la salida; confirme quién es el operador a partir de las transferencias de tokens de la misma transacción.
Otros diseños de pools
Los pools de Uniswap v3 emiten Swap(address indexed sender, address indexed recipient, int256 amount0, int256 amount1, uint160 sqrtPriceX96, uint128 liquidity, int24 tick), con topic0 0xc42079f9…ca67, e importes con signo desde la perspectiva del pool: positivos para las entradas al pool y negativos para las salidas. Un swap de 2 WETH por USDC muestra amount0 = -5994120000 (salida de 5.994,12 USDC) y amount1 = 2000000000000000000 (entrada de 2 WETH). El precio posterior al swap es price_raw = (sqrtPriceX96 ÷ 2^96)², en unidades base de token1 por unidad base de token0, multiplicado por 10^(decimals0 − decimals1). Para sqrtPriceX96 = 1446501726624926496477173928747177, price_raw ≈ 333.333.333,33; multiplicado por 10^−12 da 0,000333333 WETH por USDC, o 3.000 USDC por WETH (tick ≈ 196.256, ya que price_raw = 1,0001^tick). Omitir el ajuste de decimales da un precio erróneo por un factor de 10^12.
Uniswap v4 (2025) mantiene todos los pools en un único contrato PoolManager, por lo que el campo address del registro ya no identifica el pool; lo identifica el id indexado del pool. Su evento Swap (topic0 0x40e9cecb…112f) asigna el signo a los importes desde la perspectiva del usuario, al contrario que v3. Los pools de Curve emiten TokenExchange con índices de monedas. Mantenga un decodificador por protocolo y versión, probado con una transacción cuyo resultado conozca.
Cuatro reglas para un rastreo
- El vínculo es determinista; el activo cambia. La entrada y la salida aparecen en la misma transacción. Registre ambos importes, el tipo de cambio efectivo y, para un valor en moneda fiduciaria, la fuente del precio y la hora. Sea cual sea el método de rastreo que aplique (lección 3.3), la salida del swap es el producto de su entrada.
- El pool no es una contraparte. Un pool de liquidez es un contrato que mantiene fondos de muchos proveedores. Los fondos no «van al» propietario del pool; siga la salida, no el pool.
- El destinatario puede ser distinto del remitente. Los enrutadores y agregadores aceptan un parámetro de destinatario, por lo que la salida puede pagarse a una dirección nueva en la misma transacción. Lea la llamada decodificada o las transferencias; nunca suponga que quien hizo la llamada recibió la salida.
- Calcule el neto de la ruta. Un agregador divide una orden entre varios pools en una sola transacción. Lo que salió del sujeto y lo que llegó al destinatario son los hechos; la ruta es un detalle.
Los contratos de los DEX no tienen un operador que conserve registros, pero la interfaz que utilizó un sujeto sí puede tenerlo: las interfaces web y las API de los agregadores pueden registrar direcciones IP, agentes de usuario y solicitudes de cotización, y las aplicaciones de billetera pueden enrutar los swaps a través de sus propias API. La conservación depende del operador.
Cálculo del neto de una ruta de agregador
| logIndex | Token | Desde | Hacia | Importe |
|---|---|---|---|---|
| 10 | WETH | Operador | Enrutador | 10 |
| 11 | WETH | Enrutador | Pool A (WETH/USDC) | 6 |
| 12 | USDC | Pool A | Enrutador | 17.880 |
| 13 | WETH | Enrutador | Pool B (WETH/DAI) | 4 |
| 14 | DAI | Pool B | Pool C (DAI/USDC) | 11.930 |
| 15 | USDC | Pool C | Enrutador | 11.925 |
| 16 | USDC | Enrutador | Operador | 29.805 |
Calcular el neto de las entradas y salidas de cada dirección reduce todo esto a una sola afirmación: el operador vendió 10 WETH por 29.805 USDC, a un promedio de 2.980,50 USDC por WETH. El neto del enrutador es cero y el de cada pool equivale a su propio cambio en las reservas.
-- Cambio neto de tokens por dirección dentro de una transacción
SELECT address, token_address, SUM(delta) AS net_raw
FROM (
SELECT to_address AS address, token_address, amount_raw AS delta
FROM token_transfers WHERE transaction_hash = '0x9e1d…b7a4'
UNION ALL
SELECT from_address, token_address, -amount_raw
FROM token_transfers WHERE transaction_hash = '0x9e1d…b7a4'
) t
GROUP BY address, token_address
HAVING SUM(delta) <> 0;
Los tramos en ETH nativo deben añadirse a partir de las trazas. En los sistemas basados en intenciones, un solucionador, no el usuario, envía la transacción, por lo que tx.from es el solucionador; identifique al usuario a partir de los eventos de operaciones y las transferencias del contrato de liquidación.
Flujos de liquidez, impacto en el precio y MEV
Aportes y retiros
Aportar fondos robados a un pool y retirarlos más tarde no es un swap: el depositante recibe un token LP o una posición, y el retiro devuelve la participación. Siga la posición, no el saldo del pool.
| Protocolo | Aporte | Retiro | Registro de propiedad |
|---|---|---|---|
| Uniswap v2 | Mint(sender, amount0, amount1) más Transfer de tokens LP desde la dirección cero | Tokens LP enviados al par, Burn(sender, amount0, amount1, to), Transfer de LP a la dirección cero | Token LP fungible (el contrato del par) |
| Uniswap v3 | Mint del pool con rango de ticks; IncreaseLiquidity en el gestor de posiciones | Burn del pool (acredita tokens), luego Collect (los paga); DecreaseLiquidity | NFT de posición (ERC-721) |
| Uniswap v4 | ModifyLiquidity en el PoolManager | El mismo evento, con delta de liquidez negativo | Registros de posiciones, a menudo un NFT |
Dos trampas: en v3, el Mint del pool muestra al gestor de posiciones como propietario, así que siga el NFT de posición hasta el proveedor real; y un Burn de v3 no mueve tokens: el dinero sale en Collect. Un rug pull clásico ocurre cuando alguien despliega un token, aporta liquidez, espera a que lleguen compradores y luego retira casi toda la liquidez en una sola transacción; los importes de Burn o DecreaseLiquidity permiten medir lo que se llevó.
Impacto en el precio
El impacto en el precio compara el precio de ejecución con el precio al contado del pool justo antes de la operación. Para un par de v2, las reservas anteriores al swap equivalen a las reservas de su evento Sync menos las entradas más las salidas. En el swap anterior: 2.985.119,18 + 14.880,82 = 3.000.000 USDC y 1.005 − 5 = 1.000 WETH, con un precio al contado de 3.000.
| Medida | Valor |
|---|---|
| Precio al contado anterior | 3.000,00 USDC por WETH |
| Precio de ejecución | 2.976,16 (−0,79%) |
| Salida sin la comisión del 0,30% | 14.925,37, precio de 2.985,07 (−0,50%) |
| Precio al contado posterior (a partir de Sync) | 2.970,27 (−0,99%) |
De la diferencia del 0,79%, alrededor del 0,30% corresponde a la comisión y el 0,50% al impacto puro en el precio; el precio al contado se movió aproximadamente el doble que el precio promedio pagado, algo normal en los pools de producto constante. Un impacto grande en un pool con poca liquidez indica ventas forzadas, como un atacante que vende masivamente un token robado.
Patrones de MEV
El valor máximo extraíble (MEV) presenta patrones reconocibles. En un sándwich, dentro de un mismo bloque y pool, el actor X compra el token T, una víctima compra T y X vende T inmediatamente después, a menudo pagando directamente al constructor del bloque (una transferencia de ETH al destinatario de las comisiones, visible en las trazas); defina al actor como el contrato del bot que recibió la salida, porque los bots rotan las direcciones de envío. El arbitraje atómico realiza swaps a través de un ciclo de pools (WETH → USDC → DAI → WETH) en una sola transacción y termina con una cantidad mayor del token inicial. La liquidez justo a tiempo (JIT) consiste en un Mint de v3 justo antes de un swap grande y un Burn y Collect justo después, realizados por una misma dirección. Las coincidencias son casos candidatos: compruebe las cantidades y el beneficio, y descarte explicaciones inocentes, como dos arbitrajistas no relacionados.
Protocolos de swaps entre cadenas: memos como evidencia
Los protocolos de swaps entre cadenas intercambian activos nativos entre cadenas mediante bóvedas controladas por los nodos del protocolo. THORChain es el ejemplo de referencia, y sus registros son especialmente útiles porque ambos lados contienen texto que identifica al otro.
En el lado de entrada, el usuario envía el activo a la bóveda del protocolo: en las cadenas EVM, mediante un contrato enrutador que registra el memo en su evento de depósito; en Bitcoin, en una salida OP_RETURN, que la documentación del protocolo limita a 80 bytes. El formato del memo de swap es SWAP:ASSET:DESTADDR/REFUNDADDR:LIMIT/INTERVAL/QUANTITY:AFFILIATE:FEE (la dirección de reembolso es opcional), y suele abreviarse con = para un swap a mercado. En el lado de salida, la bóveda paga el activo de destino en una transacción cuyo memo es OUT: seguido del hash de la transacción de entrada; un swap fallido se reembolsa con un memo REFUND: que contiene el mismo hash. Un par ficticio:
Ethereum (entrada, ficticia)
tx 0x9a3f…c21e desde 0x3f9c…a1d7 al enrutador de swaps 130.0 ETH
memo del evento de depósito "=:BTC.BTC:bc1q…r7w2::xx:20"
Bitcoin (salida, ficticia)
txid 6d0b…e45a desde la bóveda bc1q…v9tn
vout 0 5.16800000 BTC bc1q…r7w2
vout 1 OP_RETURN "OUT:9A3F…C21E"
El memo de entrada identifica al destinatario en Bitcoin; el memo de salida identifica la transacción de Ethereum. El par puede verificarse en ambas cadenas desde sus propios nodos, y los registros públicos del protocolo proporcionan una tercera confirmación independiente. Hay tres detalles importantes:
- Tiempos. Los swaps por flujo se ejecutan como subswaps a lo largo de varios bloques, y los swaps con límite pueden esperar en una cola hasta 43.200 bloques del protocolo (unos tres días) de forma predeterminada. Establezca las ventanas temporales a partir de los parámetros del memo.
- Afiliados. El campo de afiliado identifica, mediante un nombre corto registrado, la interfaz o billetera que construyó la transacción: una posible fuente de registros, aunque el propio protocolo no los tiene.
- Memos de referencia. Para ahorrar espacio en el memo, se puede registrar un memo de antemano y hacer referencia a él, por ejemplo mediante una referencia codificada en el importe; los registros caducan una vez transcurrido el plazo del parámetro
MemolessTxnTTL, de forma predeterminada 3.600 bloques (unas seis horas). Preserve el registro de inscripción junto con las transacciones.
Otros protocolos utilizan campos distintos, pero el método es el mismo: averiguar qué dice cada lado sobre el otro.
Servicios de exchange instantáneo y mesas OTC
Los servicios de exchange instantáneo convierten un activo en otro sin necesidad de una cuenta: el usuario proporciona una dirección de pago, recibe una dirección de depósito nueva para la orden, envía los fondos y recibe el pago, a menudo en otra cadena. Muchos se autodenominan sin custodia porque no mantienen saldos, pero controlan los fondos entre el depósito y el pago, motivo por el que el rastro se interrumpe allí; además, intercambiar un activo virtual por otro para un cliente o en su nombre es una actividad de proveedor de servicios de activos virtuales (VASP) según la definición del GAFI utilizada en la Recomendación 15. En la cadena se parecen a cualquier servicio de custodia: direcciones de depósito por orden, barridos y pagos desde una billetera caliente. Sin registros, el vínculo entre depósito y pago es una inferencia basada en el importe y el tiempo (lección 5.3); con registros, queda documentado. Un servicio puede conservar:
| Registro | Uso probatorio |
|---|---|
| ID de la orden, hora de creación, activos y tipo de cambio cotizado | Vincula una dirección de depósito con una orden |
| Dirección de depósito y txid del depósito | Confirma qué depósito pertenece a la orden |
| Dirección de pago y txid del pago | Convierte el vínculo inferido en uno registrado |
| Dirección de reembolso | A menudo es una billetera controlada por el sujeto; vincula otras órdenes |
| Dirección IP, agente de usuario, configuración de idioma | Pistas para la atribución (lección 4.7) |
| Correo electrónico o contacto proporcionado para soporte | Identificador para otras actuaciones legales |
| ID de socio o afiliado | Identifica la billetera o el sitio web que envió la orden |
| Alertas de riesgo, retenciones y resultados de controles | Muestra si el servicio congeló o cuestionó la orden |
Solicite todas las órdenes que compartan la dirección de pago, la dirección de reembolso o la dirección IP durante el período, no solo la que encontró: el historial suele revelar otras billeteras del sujeto. Las IP pueden corresponder a VPN y los datos de contacto no están verificados. La cooperación suele ser lenta, así que envíe solicitudes de preservación en cuanto se identifique el depósito (lección 1.4).
Una mesa OTC negocia operaciones fuera de la cadena y las liquida mediante entradas y salidas de sus propias direcciones. Los tramos pueden estar en distintas cadenas, o uno puede ser una transferencia bancaria, por lo que a menudo no existe ningún vínculo en la cadena; los registros de la mesa (debida diligencia, confirmaciones de operaciones, instrucciones de liquidación, registros de chat, datos bancarios) son el único puente. Un intermediario que opera mediante una cuenta en un exchange de mayor tamaño añade una capa: el exchange ve a un solo cliente, el intermediario (lección 6.5).
Qué puede afirmar sobre cada vínculo de swap
| Evidencia disponible | Fundamento del vínculo | Redacción en un informe |
|---|---|---|
| Swap en DEX dentro de una misma transacción | Determinista (grado A) | «En la transacción T, X se intercambió por Y y se pagó a la dirección B» |
| Memo entre cadenas en ambas cadenas | Determinista en ambas cadenas (grado A), también registrado por el protocolo | «El protocolo pagó Z a la dirección B en relación con la transacción de entrada A» |
| Exchange instantáneo, solo importe y tiempo | Inferencia (grado D) | «Compatible con», con el número de candidatos y la tasa de coincidencias fortuitas |
| Exchange instantáneo con registro de la orden | Confirmado mediante registros (grado B) | «Según los registros del servicio, la orden N pagó Z a la dirección B» |
| Mesa OTC, sin registros | Ninguno | «Se desconoce el destino de estos fondos» |
Los grados son los de la lección 3.2. Un vínculo confirmado mediante registros solo es tan fiable como el registro: indique quién lo produjo, la fecha de recepción y el hash del archivo tal como se recibió.
Enfoque del examen: orden de token0/token1 y decimales; eventos Swap y Sync de v2; importes con signo de v3 (positivos para las entradas al pool) y conversión de sqrtPriceX96 con el ajuste de decimales; PoolManager de v4, id del pool y signo invertido; swaps en DEX como vínculos deterministas que cambian el activo, pools que no son contrapartes y parámetros de destinatario; cálculo del neto de una ruta de agregador y por qué tx.from puede ser un enrutador o un solucionador; NFT de posición, Burn seguido de Collect y rug pulls; impacto en el precio a partir de las reservas; patrones de sándwich, arbitraje y JIT; memos entre cadenas, OUT: y REFUND: que contienen el hash de entrada, tiempos de los swaps, afiliados y memos de referencia; exchanges instantáneos como VASP y sus registros; mesas OTC sin vínculo en la cadena; adecuación de la redacción al fundamento de cada vínculo.