Flussi di token, NFT e smart contract · Lezione 4 di 6
Swap DEX e flussi di liquidità
9 min di lettura
Gli exchange decentralizzati sono il luogo in cui i token rubati vengono convertiti in ETH o stablecoin, in cui i rug pull del giorno del lancio prosciugano i fondi degli acquirenti e in cui i bot estraggono valore dai trader comuni. Ogni swap lascia prove precise: l'evento del pool stesso, i trasferimenti di token che lo circondano e le riserve del pool prima e dopo. Questa lezione mostra come leggere queste prove, ricostruire ciò che un trader ha effettivamente fatto tramite un aggregatore, misurare l'impatto sul prezzo e riconoscere gli schemi MEV.
Pool, token0 e token1
Una coppia in stile Uniswap v2 è un contratto che detiene due token. Il token con l'indirizzo più basso è token0 e l'altro è token1; quest'ordine viene fissato al momento della creazione e non ha nulla a che fare con quale token rappresenti «il prezzo». Ogni evento riporta gli importi in quest'ordine, quindi un decoder deve individuare token0, token1 e i decimali di entrambi prima di convertire qualsiasi valore.
L'evento Swap di v2 è Swap(address indexed sender, uint256 amount0In, uint256 amount1In, uint256 amount0Out, uint256 amount1Out, address indexed to), topic0 0xd78ad95f…d822. Dopo ogni variazione delle riserve, la coppia emette anche Sync(uint112 reserve0, uint112 reserve1) con i nuovi saldi. Uno swap decodificato in una coppia in cui token0 è USDC (6 decimali) e token1 è WETH (18 decimali):
{
"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"}
}
Interpretazione: sono entrati 5 WETH e sono usciti 14.880,819116 USDC; il prezzo di esecuzione è 14.880,82 ÷ 5 = 2.976,16 USDC per WETH. sender è di solito un contratto router, non il trader; to indica dove è finito l'importo in uscita. Il trader è chi ha inviato l'input e ricevuto l'output, cosa che si conferma dai trasferimenti di token nella stessa transazione.
Pool a liquidità concentrata e altri design
I pool Uniswap v3 emettono Swap(address indexed sender, address indexed recipient, int256 amount0, int256 amount1, uint160 sqrtPriceX96, uint128 liquidity, int24 tick), topic0 0xc42079f9…ca67. Gli importi sono con segno dal punto di vista del pool: un valore positivo indica che il token è entrato nel pool, uno negativo che ne è uscito. Uno swap di 2 WETH in cambio di USDC in un pool USDC/WETH mostra amount0 = -5994120000 (sono usciti 5.994,12 USDC) e amount1 = 2000000000000000000 (sono entrati 2 WETH).
L'evento fornisce anche il prezzo del pool dopo lo swap, codificato come radice quadrata in formato a virgola fissa:
prezzo_raw = (sqrtPriceX96 / 2^96)^2 -- unità base di token1 per unità base di token0
prezzo = prezzo_raw * 10^(decimali0 - decimali1)
esempio : sqrtPriceX96 = 1446501726624926496477173928747177
prezzo_raw = 333.333.333,33 -> prezzo = 0,000333333 WETH per USDC
inverso = 3.000 USDC per WETH (tick ≈ 196.256, dato che prezzo_raw = 1,0001^tick)
Il valore elevato al quadrato è espresso in unità base, quindi la correzione per i decimali (6 − 18 = −12) è essenziale; se la si omette, il prezzo è errato di un fattore pari a 10^12.
Gli altri design richiedono decoder specifici. Uniswap v4 (2025) mantiene tutti i pool all'interno di un unico contratto singleton PoolManager, quindi l'address del log non identifica più il pool; a identificarlo è l'id indicizzato del pool. Il suo evento Swap, Swap(bytes32 indexed id, address indexed sender, int128 amount0, int128 amount1, uint160 sqrtPriceX96, uint128 liquidity, int24 tick, uint24 fee) (topic0 0x40e9cecb…112f), riporta gli importi con segno dal punto di vista dell'utente, al contrario di v3, come avverte la guida ai dati della Uniswap Foundation. I pool Curve emettono TokenExchange con indici delle monete invece che indirizzi dei token. Usa un decoder per ogni protocollo e versione e testali tutti su una transazione di cui conosci il risultato.
Instradamento tramite aggregatori
Un aggregatore suddivide un singolo ordine tra pool e catene di pool all'interno di una sola transazione. Gli eventi dei pool mostrano ogni passaggio; i trasferimenti di token mostrano il risultato netto per il trader. Un percorso decodificato, con il router che detiene i fondi tra un passaggio e l'altro:
| logIndex | Token | Da | A | Importo |
|---|---|---|---|---|
| 10 | WETH | Trader | Router | 10 |
| 11 | WETH | Router | Pool A (WETH/USDC) | 6 |
| 12 | USDC | Pool A | Router | 17.880 |
| 13 | WETH | Router | Pool B (WETH/DAI) | 4 |
| 14 | DAI | Pool B | Pool C (DAI/USDC) | 11.930 |
| 15 | USDC | Pool C | Router | 11.925 |
| 16 | USDC | Router | Trader | 29.805 |
Calcolando il saldo netto di entrate e uscite di ogni indirizzo, il tutto si riduce a un'unica operazione: il trader ha venduto 10 WETH in cambio di 29.805 USDC, a un prezzo medio di 2.980,50 USDC per WETH. Il saldo netto del router e dei pool intermedi è zero, oppure riflette le variazioni dei rispettivi pool.
-- Variazione netta dei token per indirizzo all'interno di una transazione
SELECT address, token_contract, SUM(delta) AS net_raw
FROM (
SELECT to_address AS address, token_contract, value_raw AS delta
FROM token_transfers WHERE tx_hash = '0x9e1d…b7a4'
UNION ALL
SELECT from_address, token_contract, -value_raw
FROM token_transfers WHERE tx_hash = '0x9e1d…b7a4'
) t
GROUP BY address, token_contract
HAVING SUM(delta) <> 0;
La query somma ogni trasferimento in entrata e sottrae ogni trasferimento in uscita per ciascun indirizzo e token, mantenendo solo i risultati diversi da zero. I passaggi in ETH nativo non compaiono nei trasferimenti di token e vanno aggiunti a partire dalle trace. Nei sistemi basati su intent, la transazione viene inviata da un solver, non dall'utente, quindi tx.from identifica il solver; l'utente si individua tramite gli eventi di trading del contratto di settlement e i trasferimenti da e verso l'indirizzo dell'utente.
Aggiunte e rimozioni di liquidità
Gli eventi di liquidità rivelano chi finanzia un mercato e chi ne ritira i fondi.
| Protocollo | Aggiunta | Rimozione | Record di proprietà |
|---|---|---|---|
| Uniswap v2 | Mint(sender, amount0, amount1) più un trasferimento di token LP dall'indirizzo zero | Token LP inviati alla coppia, Burn(sender, amount0, amount1, to), trasferimento LP all'indirizzo zero | Token LP fungibile (il contratto della coppia) |
| Uniswap v3 | Mint del pool con intervallo di tick; IncreaseLiquidity sul position manager | Burn del pool (accredita i token), poi Collect (li paga); DecreaseLiquidity | NFT di posizione (ERC-721) |
| Uniswap v4 | ModifyLiquidity nel PoolManager | Stesso evento, variazione negativa della liquidità | Record di posizione, spesso un NFT |
Due insidie. In v3, il Mint del pool mostra il contratto position manager come proprietario, quindi il vero fornitore di liquidità è il titolare dell'NFT di posizione: segui l'NFT. Inoltre, un Burn in v3 non sposta token; il denaro esce con Collect. Un classico schema di rug pull si verifica quando un deployer crea un token, aggiunge liquidità, aspetta gli acquirenti e poi rimuove quasi tutta la liquidità in un'unica transazione; gli importi di Burn o DecreaseLiquidity misurano quanto è stato prelevato.
Calcolo dell'impatto sul prezzo
L'impatto sul prezzo confronta il prezzo di esecuzione con il prezzo spot del pool immediatamente prima dell'operazione. Per una coppia v2, le riserve prima dello swap sono pari alle riserve indicate nell'evento Sync meno gli input più gli output: in questo caso, 2.985.119,18 + 14.880,82 = 3.000.000 USDC e 1.005 − 5 = 1.000 WETH, con un prezzo spot di 3.000.
| Misura | Valore |
|---|---|
| Prezzo spot prima | 3.000,00 USDC per WETH |
| Prezzo di esecuzione | 2.976,16 (−0,79%) |
| Output senza la commissione dello 0,30% | 14.925,37, prezzo 2.985,07 (−0,50%) |
| Prezzo spot dopo (da Sync) | 2.970,27 (−0,99%) |
Quindi, dello 0,79% di scarto, circa lo 0,30% è dovuto alla commissione e circa lo 0,50% è puro impatto sul prezzo. Il prezzo spot dopo lo scambio si è mosso il doppio rispetto al prezzo medio pagato, cosa normale nei pool a prodotto costante. Un forte impatto in un pool poco profondo è un segnale di vendita forzata, per esempio quando un attaccante scarica sul mercato un token rubato.
Individuare MEV e schemi sandwich
L'attività di valore massimo estraibile (MEV) presenta forme riconoscibili nei dati dei blocchi:
- Sandwich: nello stesso blocco e nello stesso pool, l'attore X acquista il token T, poi una vittima acquista T e subito dopo X vende T. Di solito le tre transazioni sono adiacenti per indice, la quantità venduta da X è approssimativamente pari a quella acquistata e X spesso paga direttamente il block builder (un trasferimento di ETH al destinatario delle commissioni del blocco, visibile nelle trace).
- Arbitraggio atomico: una transazione esegue swap attraverso un ciclo di pool (WETH → USDC → DAI → WETH) e termina con una quantità maggiore del token iniziale.
- Liquidità just-in-time (JIT): un Mint v3 immediatamente prima di un grande swap, seguito subito dopo da un Burn e un Collect, effettuati dallo stesso indirizzo.
- Liquidazioni: trattate nella lezione Prestiti, bridge e contabilità dei protocolli.
-- Candidati sandwich: stesso blocco e pool, apertura e chiusura da parte dello stesso attore
SELECT f.block_number, f.pool, f.actor, v.tx_hash AS victim_tx
FROM dex_swaps f
JOIN dex_swaps v
ON v.block_number = f.block_number AND v.pool = f.pool
AND v.tx_index > f.tx_index AND v.token_bought = f.token_bought
JOIN dex_swaps b
ON b.block_number = f.block_number AND b.pool = f.pool
AND b.tx_index > v.tx_index AND b.actor = f.actor
AND b.token_sold = f.token_bought
WHERE v.actor <> f.actor;
La query cerca un front-run (f), una vittima (v) che acquista lo stesso token più avanti nel blocco e un back-run (b) in cui lo stesso attore lo vende. Definisci actor come il contratto bot che ha ricevuto l'output dello swap, anziché come il mittente della transazione, perché i bot sandwich spesso alternano gli indirizzi mittenti. Considera i risultati come candidati: verifica le quantità, il profitto e l'assenza di spiegazioni innocenti, come due arbitraggisti non correlati.
Focus sull'esame: ordine di token0/token1 e decimali; decodifica degli eventi Swap (amountIn/amountOut) e Sync di Uniswap v2; importi con segno in v3 (positivi quando entrano nel pool) e conversione di sqrtPriceX96 in un prezzo con la correzione per i decimali; singleton PoolManager di v4, pool id e convenzione dei segni inversa; calcolo netto dei trasferimenti per individuare il risultato di un trader tramite un aggregatore e motivi per cui tx.from può essere un router o un solver; eventi di liquidità v2 e v3, NFT di posizione e schema Burn-then-Collect; calcolo dell'impatto sul prezzo a partire dalle riserve; segnali distintivi dei sandwich, dell'arbitraggio atomico e della liquidità JIT e relativi falsi positivi.
Domande di esercitazione
Verifica la tua comprensione. Queste domande non contano per l’esame.
Accedi per salvare i tuoi progressi e riprendere da dove hai lasciato. È gratuito. Accedi