Cross-chain, DeFi e smart contract · Lezione 2 di 6
Swap su DEX, exchange istantanei e flussi di liquidità
14 min di lettura
Quasi ogni percorso di riciclaggio comporta, a un certo punto, un cambio di asset, e la modalità con cui avviene determina quanto possa essere solido il collegamento successivo. Uno swap su un exchange decentralizzato (DEX) è registrato in un'unica transazione, con l'evento del pool, i trasferimenti di token e le riserve prima e dopo. Un protocollo di swap cross-chain può registrare la destinazione nella blockchain di origine e l'origine nella blockchain di destinazione. Un servizio di exchange istantaneo o un desk OTC lascia soltanto un deposito da un lato e un pagamento dall'altro. Questa lezione mostra come interpretare ogni tipo di swap come elemento probatorio, che cosa rivelano gli eventi di liquidità, quali registrazioni esistono off-chain e che cosa si può affermare su ciascun collegamento. La decodifica generale è trattata nella Lezione 5.1, la correlazione dei passaggi tra blockchain nella Lezione 5.3 e i bridge nella Lezione 5.4.
Interpretare uno swap su DEX
Pool, token0 e token1
Una coppia in stile Uniswap v2 è un contratto che detiene due token. Il token con l'indirizzo di valore inferiore è token0 e l'altro è token1; l'ordine è fissato alla creazione e non ha nulla a che vedere con quale token rappresenti «il prezzo». Ogni evento riporta gli importi in questo ordine, quindi occorre verificare token0, token1 e il numero di decimali di entrambi prima di effettuare qualsiasi conversione.
L'evento 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). 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"}
}
Sono entrati cinque WETH e sono usciti 14.880,819116 USDC, con un prezzo di esecuzione di 2.976,16 USDC per WETH. Il sender è solitamente un router, non il trader, e to indica dove è stato inviato l'output; occorre confermare il trader tramite i trasferimenti di token nella stessa transazione.
Altre architetture dei pool
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, con importi con segno dal punto di vista del pool: positivi in entrata nel pool, negativi in uscita. Uno swap di 2 WETH in USDC mostra amount0 = -5994120000 (5.994,12 USDC in uscita) e amount1 = 2000000000000000000 (2 WETH in entrata). Il prezzo dopo lo swap è price_raw = (sqrtPriceX96 ÷ 2^96)², espresso in unità base di token1 per unità base di token0, moltiplicato per 10^(decimals0 − decimals1). Per sqrtPriceX96 = 1446501726624926496477173928747177, price_raw ≈ 333.333.333,33; moltiplicato per 10^−12 equivale a 0,000333333 WETH per USDC, ovvero 3.000 USDC per WETH (tick ≈ 196.256, poiché price_raw = 1,0001^tick). Omettere la correzione per i decimali produce un prezzo errato di un fattore 10^12.
Uniswap v4 (2025) mantiene tutti i pool in un unico contratto singleton PoolManager, quindi l'address del log non identifica più il pool; lo identifica invece l'id indicizzato del pool. Il suo evento Swap (topic0 0x40e9cecb…112f) assegna il segno agli importi dal punto di vista dell'utente, l'opposto di v3. I pool Curve emettono TokenExchange con gli indici dei token. Occorre mantenere un decoder per ciascun protocollo e versione, verificato su una transazione di cui si conosce il risultato.
Quattro regole per il tracciamento
- Il collegamento è deterministico, l'asset cambia. Input e output compaiono nella stessa transazione. Registrare entrambi gli importi, il tasso effettivo e, per un valore in valuta fiat, la fonte del prezzo e il momento di rilevazione. Qualunque sia il metodo di tracciamento applicato (Lezione 3.3), l'output dello swap costituisce il ricavato del relativo input.
- Il pool non è una controparte. Un pool di liquidità è un contratto che detiene i fondi di numerosi fornitori. I fondi non «vanno» al proprietario del pool; occorre seguire l'output, non il pool.
- Il destinatario può essere diverso dal mittente. Router e aggregatori accettano un parametro che specifica il destinatario, quindi l'output può essere versato a un nuovo indirizzo nella stessa transazione. Esaminare la chiamata decodificata o i trasferimenti; non presumere mai che il chiamante abbia ricevuto l'output.
- Calcolare il saldo netto del percorso. Un aggregatore suddivide un ordine tra più pool in un'unica transazione. Ciò che è uscito dal soggetto e ciò che è arrivato al destinatario sono i fatti; il percorso è un dettaglio.
I contratti DEX non hanno un operatore che conservi registrazioni, ma l'interfaccia utilizzata da un soggetto potrebbe averlo: i front-end web e le API degli aggregatori possono registrare indirizzi IP, user agent e richieste di quotazione, e le applicazioni wallet possono instradare gli swap attraverso le proprie API. La conservazione dipende dall'operatore.
Calcolo del saldo netto di un percorso tramite aggregatore
| 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 |
Il calcolo del saldo netto delle entrate e delle uscite di ogni indirizzo riduce tutto a una sola affermazione: il trader ha venduto 10 WETH per 29.805 USDC, a una media di 2.980,50 USDC per WETH. Il saldo netto del router è zero e quello di ciascun pool corrisponde alla variazione delle proprie riserve.
-- Variazione netta dei token per indirizzo in una singola transazione
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;
I passaggi in ETH nativo devono essere aggiunti ricavandoli dalle tracce di esecuzione. Nei sistemi basati su intenti è un solver, non l'utente, a inviare la transazione, quindi tx.from è il solver; occorre individuare l'utente tramite gli eventi di scambio e i trasferimenti del contratto di regolamento.
Flussi di liquidità, impatto sul prezzo e MEV
Aggiunte e rimozioni
Aggiungere fondi rubati a un pool e rimuoverli in seguito non è uno swap: il depositante riceve un token LP o una posizione, e la rimozione restituisce la quota. Occorre seguire la posizione, non il saldo del pool.
| Protocollo | Aggiunta | Rimozione | Registrazione della titolarità |
|---|---|---|---|
| Uniswap v2 | Mint(sender, amount0, amount1) più Transfer del token LP dall'indirizzo zero | Token LP inviati alla coppia, Burn(sender, amount0, amount1, to), Transfer LP verso l'indirizzo zero | Token LP fungibile (il contratto della coppia) |
| Uniswap v3 | Mint del pool con intervallo di tick; IncreaseLiquidity sul gestore delle posizioni | Burn del pool (accredita i token), poi Collect (li versa); DecreaseLiquidity | NFT della posizione (ERC-721) |
| Uniswap v4 | ModifyLiquidity nel PoolManager | Stesso evento, variazione negativa della liquidità | Registrazioni delle posizioni, spesso un NFT |
Due insidie: in v3 il Mint del pool mostra il gestore delle posizioni come proprietario, quindi occorre seguire l'NFT della posizione fino al fornitore effettivo; inoltre, un Burn v3 non sposta alcun token: i fondi escono con Collect. Un classico rug pull si verifica quando chi distribuisce un contratto crea un token, aggiunge liquidità, attende gli acquirenti e poi rimuove quasi tutta la liquidità in un'unica transazione; gli importi di Burn o DecreaseLiquidity misurano quanto è stato sottratto.
Impatto sul prezzo
L'impatto sul prezzo confronta il prezzo di esecuzione con il prezzo spot del pool immediatamente prima dello scambio. Per una coppia v2, le riserve prima dello swap equivalgono alle riserve nel relativo evento Sync meno gli input più gli output. Nello swap precedente: 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%) |
Della differenza dello 0,79%, circa lo 0,30% è la commissione e lo 0,50% è puro impatto sul prezzo; il prezzo spot si è mosso di circa il doppio rispetto al prezzo medio pagato, una dinamica normale per i pool a prodotto costante. Un impatto elevato su un pool con scarsa liquidità segnala vendite forzate, come nel caso di un attaccante che vende massicciamente un token rubato.
Schemi MEV
Il valore massimo estraibile (MEV) presenta schemi riconoscibili. In un sandwich, nello stesso blocco e pool, l'attore X acquista il token T, una vittima acquista T e X vende T subito dopo, spesso pagando direttamente il costruttore del blocco (un trasferimento di ETH al destinatario delle commissioni, visibile nelle tracce di esecuzione); occorre identificare l'attore con il contratto del bot che ha ricevuto l'output, poiché i bot cambiano gli indirizzi mittenti. L'arbitraggio atomico esegue swap lungo un ciclo di pool (WETH → USDC → DAI → WETH) in un'unica transazione e termina con una quantità maggiore del token iniziale. La liquidità just-in-time (JIT) consiste in un Mint v3 immediatamente prima di uno swap di grande entità e in un Burn e un Collect subito dopo, da parte di un unico indirizzo. Le corrispondenze rilevate sono casi candidati: occorre verificare quantità e profitto ed escludere spiegazioni innocue, come due arbitraggisti non collegati.
Protocolli di swap cross-chain: i memo come elementi probatori
I protocolli di swap cross-chain scambiano asset nativi tra blockchain tramite vault controllati dai nodi del protocollo. THORChain è l'esempio di riferimento, e le sue registrazioni sono particolarmente utili perché entrambi i lati contengono testo che identifica l'altro.
Sul lato in entrata, l'utente invia l'asset al vault del protocollo: sulle blockchain EVM tramite un contratto router che registra il memo nel proprio evento di deposito; su Bitcoin in un output OP_RETURN, che la documentazione del protocollo limita a 80 byte. Il formato del memo di swap è SWAP:ASSET:DESTADDR/REFUNDADDR:LIMIT/INTERVAL/QUANTITY:AFFILIATE:FEE (l'indirizzo di rimborso è facoltativo), solitamente abbreviato con = per uno swap a mercato. Sul lato in uscita, il vault versa l'asset di destinazione in una transazione il cui memo è OUT: seguito dall'hash della transazione in entrata; uno swap non riuscito viene rimborsato con un memo REFUND: contenente lo stesso hash. Una coppia fittizia:
Ethereum (in entrata, fittizio)
tx 0x9a3f…c21e da 0x3f9c…a1d7 a router di swap 130.0 ETH
memo dell'evento di deposito "=:BTC.BTC:bc1q…r7w2::xx:20"
Bitcoin (in uscita, fittizio)
txid 6d0b…e45a da vault bc1q…v9tn
vout 0 5.16800000 BTC bc1q…r7w2
vout 1 OP_RETURN "OUT:9A3F…C21E"
Il memo in entrata identifica il destinatario Bitcoin; il memo in uscita identifica la transazione Ethereum. La coppia può essere verificata su entrambe le blockchain tramite i propri nodi, e le registrazioni pubbliche del protocollo forniscono una terza conferma indipendente. Sono importanti tre dettagli:
- Tempistiche. Gli swap in streaming vengono eseguiti come sotto-swap su più blocchi, e gli swap con limite possono restare in coda, per impostazione predefinita, fino a 43.200 blocchi del protocollo (circa tre giorni). Occorre impostare le finestre temporali in base ai parametri del memo.
- Affiliati. Il campo dell'affiliato identifica, tramite un nome breve registrato, l'interfaccia o il wallet che ha costruito la transazione: una possibile fonte di log, sebbene il protocollo stesso non ne abbia.
- Memo di riferimento. Per risparmiare spazio nel memo, è possibile registrare un memo in anticipo e richiamarlo, ad esempio tramite un riferimento codificato nell'importo; le registrazioni scadono dopo il periodo stabilito dal parametro
MemolessTxnTTL, pari per impostazione predefinita a 3.600 blocchi (circa sei ore). Occorre conservare la registrazione insieme alle transazioni.
Altri protocolli utilizzano campi diversi, ma il metodo è lo stesso: individuare ciò che ciascun lato dichiara sull'altro.
Servizi di exchange istantaneo e desk OTC
I servizi di exchange istantaneo convertono un asset in un altro senza un conto: l'utente fornisce un indirizzo di pagamento, riceve un nuovo indirizzo di deposito per l'ordine, invia i fondi e riceve il pagamento, spesso su un'altra blockchain. Molti si definiscono non custodial perché non mantengono saldi, ma controllano i fondi tra il deposito e il pagamento, motivo per cui il percorso di tracciamento si interrompe in quel punto; inoltre, scambiare un asset virtuale con un altro per un cliente o per suo conto costituisce un'attività VASP secondo la definizione del FATF utilizzata nella Raccomandazione 15. On-chain si presentano come qualsiasi servizio custodial: indirizzi di deposito per singolo ordine, trasferimenti di consolidamento, pagamenti da un hot wallet. Senza registrazioni, il collegamento deposito-pagamento è un'inferenza basata su importo e tempo (Lezione 5.3); con le registrazioni, è documentato. Un servizio può detenere:
| Registrazione | Utilità probatoria |
|---|---|
| ID dell'ordine, momento di creazione, asset e tasso quotato | Collega un indirizzo di deposito a un ordine |
| Indirizzo di deposito e txid del deposito | Conferma quale deposito appartiene all'ordine |
| Indirizzo di pagamento e txid del pagamento | Trasforma il collegamento inferito in un collegamento documentato |
| Indirizzo di rimborso | Spesso è un wallet controllato dal soggetto; collega altri ordini |
| Indirizzo IP, user agent, impostazioni della lingua | Piste per l'attribuzione (Lezione 4.7) |
| Email o contatto fornito per l'assistenza | Identificativo per ulteriori iniziative giudiziarie |
| ID del partner o dell'affiliato | Identifica il wallet o il sito web che ha inviato l'ordine |
| Segnalazioni di rischio, sospensioni e risultati dello screening | Mostra se il servizio ha bloccato l'ordine o richiesto chiarimenti al riguardo |
Occorre richiedere tutti gli ordini che condividono l'indirizzo di pagamento, l'indirizzo di rimborso o l'indirizzo IP nel periodo, non soltanto quello individuato: la cronologia rivela spesso gli altri wallet del soggetto. Gli IP possono appartenere a VPN e i dati di contatto non sono verificati. La collaborazione è spesso lenta, quindi occorre inviare richieste di conservazione non appena viene identificato il deposito (Lezione 1.4).
Un desk OTC negozia scambi off-chain ed effettua i regolamenti da e verso i propri indirizzi. I passaggi possono trovarsi su blockchain diverse, oppure uno di essi può essere un bonifico bancario, quindi spesso non esiste alcun collegamento on-chain; le registrazioni del desk (adeguata verifica, conferme degli scambi, istruzioni di regolamento, log delle chat, coordinate bancarie) costituiscono l'unico ponte. Un intermediario che opera tramite un conto presso un exchange più grande aggiunge un livello: l'exchange vede un solo cliente, l'intermediario (Lezione 6.5).
Che cosa si può affermare su ciascun collegamento di swap
| Elementi probatori disponibili | Base del collegamento | Formulazione in una relazione |
|---|---|---|
| Swap su DEX nella stessa transazione | Deterministico (grado A) | «Nella transazione T, X è stato scambiato con Y, versato all'indirizzo B» |
| Memo cross-chain su entrambe le blockchain | Deterministico su entrambe le blockchain (grado A), anche registrato dal protocollo | «Il protocollo ha versato Z all'indirizzo B in relazione alla transazione in entrata A» |
| Exchange istantaneo, soltanto importo e tempo | Inferenza (grado D) | «Compatibile con», con il numero di candidati e il tasso di corrispondenza casuale |
| Exchange istantaneo con registrazione dell'ordine | Confermato da registrazioni (grado B) | «Secondo le registrazioni del servizio, l'ordine N ha versato Z all'indirizzo B» |
| Desk OTC, nessuna registrazione | Nessuna | «La destinazione di questi fondi non è nota» |
I gradi sono quelli della Lezione 3.2. Un collegamento confermato da registrazioni è affidabile soltanto quanto la registrazione stessa: occorre indicare chi l'ha prodotta, la data di ricezione e l'hash del file così come ricevuto.
Focus sull'esame: ordine token0/token1 e decimali; eventi Swap e Sync di v2; importi con segno di v3 (positivi in entrata nel pool) e conversione di sqrtPriceX96 con la correzione per i decimali; PoolManager di v4, id del pool e segno invertito; swap su DEX come collegamenti deterministici che cambiano l'asset, pool che non sono controparti e parametri del destinatario; calcolo del saldo netto di un percorso tramite aggregatore e motivi per cui tx.from può essere un router o un solver; NFT delle posizioni, sequenza Burn-Collect e rug pull; impatto sul prezzo ricavato dalle riserve; schemi caratteristici di sandwich, arbitraggio e JIT; memo cross-chain, OUT: e REFUND: contenenti l'hash in entrata, tempistiche degli swap, affiliati e memo di riferimento; exchange istantanei come VASP e relative registrazioni; desk OTC senza collegamento on-chain; adeguamento della formulazione alla base di ciascun collegamento.