Vai al contenuto

Disallineamenti dei decimali negli oracoli

Un disallineamento dei decimali applica la scala di unità errata a una risposta dell'oracolo, una quantità di token, un tasso del wrapper o un valore del protocollo, causando errori di ordini di grandezza in prestiti, emissioni, rimborsi e liquidazioni.

Aggiornato

Solo a scopo educativo; non costituisce consulenza né raccomandazione d’investimento. Gli investimenti possono comportare perdite.

Risposta diretta

Un disallineamento dei decimali dell’oracolo si verifica quando un contratto interpreta un intero grezzo con l’esponente dell’unità, la direzione del prezzo o la scala interna errati. I decimali del feed, del token e del token di quotazione, le scale dei tassi di wrapper o quote e le convenzioni WAD o RAY del protocollo sono indipendenti. Un frontend apparentemente corretto non dimostra che il contratto consumer esegua la stessa aritmetica.

Per una quantità grezza del token base A_raw con d_t decimali e una risposta positiva del feed P_raw che quota un token base in token di quotazione con d_p decimali, la quantità grezza del token di quotazione con d_q decimali è V_raw = round(A_raw * P_raw * 10^d_q / 10^(d_t + d_p)). La formula vale solo per la direzione indicata e dopo aver validato feed, unità, tipi e regola di arrotondamento. Un adapter che ha già normalizzato un valore non deve scalarlo di nuovo.

In ERC-20, decimals() è un metadato di visualizzazione facoltativo, non una garanzia universale di 18 decimali. Il decimals() di un feed descrive la risposta del feed, non la scala del token o del protocollo. L’aritmetica controllata può bloccare un overflow con un revert, ma non rileva una formula dimensionalmente errata e può trasformare un risultato rappresentabile in denial of service se trabocca un prodotto intermedio.

Come funziona

  1. Fissare chain, blocco, consumer, adapter, proxy e aggregatore del feed, token o vault, versioni del compilatore e della libreria matematica e stato dell’upgrade.
  2. Inventariare ogni intero con tipo e unità: quantità grezza, decimali del token, risposta con segno e decimali del feed, orientamento base/quotazione, tasso di wrapper o quota, scala interna e decimali del token di output.
  3. Validare la fonte prima della conversione: indirizzo e direzione corretti, risposta positiva, timestamp e stato, età e intervallo accettabili, semantica del fallback e regole del periodo di grazia del sequencer L2.
  4. Derivare un’unica formula dimensionale dalle unità grezze di input a quelle di output. Distinguere esplicitamente aumento e riduzione della scala, limitare ogni esponente di dieci e normalizzare ogni componente una sola volta.
  5. Usare un multiply-divide a precisione completa verificato o una cancellazione limitata. Evitare troncamento da divisione anticipata, overflow intermedio controllato, wraparound unchecked e cast con segno o restrittivi non sicuri.
  6. Specificare arrotondamento per difetto, eccesso o al più vicino per ogni azione. Garanzia, debito, prestito, emissione, rimborso, commissioni, liquidazione e conversione di quote possono richiedere direzioni conservative diverse.
  7. Testare vettori noti e proprietà su importi estremi, combinazioni di decimali, reciproci, feed composti, risposte nulle, negative e obsolete, upgrade e dust; riconciliare i risultati onchain con un modello indipendente ad alta precisione e limitare l’esposizione.

La normalizzazione è analisi dimensionale, non formattazione. BTC/USD e USD/BTC richiedono formule reciproche; cambiare etichetta non inverte un prezzo. I feed composti richiedono scala e timestamp di ogni componente. La divisione intera di Solidity tronca verso zero, quindi riordinamenti algebricamente equivalenti possono produrre risultati onchain diversi. Un mulDiv a precisione completa risolve il problema della larghezza intermedia, ma richiede comunque numeratore, denominatore, unità, limiti e direzione di arrotondamento corretti.

Esempi svolti

  • Otto contro diciotto decimali. Un feed BTC/USD restituisce 6,000,000,000,000 con d_p = 8, quindi il prezzo è 60,000 USD/BTC. Interpretare la risposta grezza a 18 decimali produce 0.000006 USD/BTC, una sottovalutazione di 10^10, non 10^9. La scalatura a WAD dà 6,000,000,000,000 * 10^(18 - 8) = 60,000,000,000,000,000,000,000.
  • Unità di quantità, prezzo e output. A_raw = 2,500,000 rappresenta 2.5 token con d_t = 6; P_raw = 200,000,000 rappresenta 2 token di quotazione con d_p = 8. Per un valore interno a 18 decimali, 2,500,000 * 200,000,000 * 10^18 / 10^(6 + 8) = 5,000,000,000,000,000,000, ossia 5 quote tokens. Omettere il denominatore del token sopravvaluta la posizione di 10^6.
  • Reciproco e troncamento. ETH/USD in scala WAD è 2,000 * 10^18. USD/ETH alla stessa scala è floor(10^36 / (2,000 * 10^18)) = 500,000,000,000,000, ossia 0.0005 ETH/USD. Separatamente, con A_raw = 999,999, d_t = 6 e prezzo 2 * 10^18, il multiply-divide completo produce 1,999,998,000,000,000,000; dividere prima la quantità per 10^6 produce 0 e perde tutto il valore.
  • Overflow intermedio e arrotondamento. Siano x = 2^200, y = 2^100 e il denominatore 2^100. Il risultato esatto 2^200 rientra in uint256, ma x * y = 2^300 no. La moltiplicazione controllata effettua revert e quella unchecked va in wrap; mulDiv a precisione completa restituisce 2^200. La divisione intera 5 / 2 arrotonda per difetto a 2, mentre una regola per eccesso restituisce 3; l’arrotondamento fa quindi parte dell’invariante economica.

Rischi

  • Indirizzo errato di chain, feed, proxy, adapter, token, vault o consumer.
  • Orientamento base/quotazione invertito senza conversione reciproca.
  • Si presumono 18 decimali del token oppure i metadati facoltativi sono assenti o errati.
  • Si presumono 8 decimali del feed anziché leggerli e fissarli.
  • I decimali del token di quotazione sono confusi con scala WAD, RAY, di mercato o contabile.
  • Decimali di wrapper, quota, indice o tasso di cambio omessi.
  • Fattore di scala applicato due volte dopo la normalizzazione dell’adapter.
  • Fattore di scala o denominatore necessario omesso.
  • Risposta con segno convertita in unsigned prima di verificarne la positività.
  • Risultato nullo, obsoleto, incompleto, limitato o non valido accettato.
  • Esponente decimale o potenza di dieci va in underflow, overflow o supera i limiti.
  • Prodotto intermedio va in overflow sebbene il quoziente finale sia rappresentabile.
  • Aritmetica unchecked, bit shift o narrowing cast effettuano wrap o troncano silenziosamente.
  • Dividere prima di moltiplicare distrugge precisione o riduce il dust a zero.
  • Arrotondamento per difetto, eccesso o al più vicino errato per l’azione.
  • Conversioni ripetute accumulano perdita di precisione o dispersione sistematica di valore.
  • Prezzi, cap, rapporti, percentuali, punti base, WAD e RAY sono confrontati in unità diverse.
  • Upgrade di feed, token, proxy, adapter o vault invalidano ipotesi sui decimali in cache.
  • Formattazione di frontend, wallet, RPC o indexer nasconde un calcolo onchain diverso.
  • Valutazione errata amplifica prestiti, emissioni, rimborsi, liquidazioni, cap, crediti inesigibili o emissione iniqua di quote.

Idee errate comuni

  • «Ogni token ERC-20 usa 18 decimali». Il metodo dei metadati ERC-20 è facoltativo e gli asset implementati usano valori e comportamenti differenti.
  • «Ogni feed di prezzo in USD usa 8 decimali». La precisione è una proprietà dell’interfaccia dello specifico deployment e deve essere letta e versionata.
  • «Un grande intero grezzo dimostra manipolazione». La grandezza non significa nulla senza unità, direzione, decimali, timestamp e scala del consumer.
  • «Solidity 0.8 rende corretta la scalatura». L’overflow controllato può effettuare revert, ma non corregge unità, troncamento, cast o regole di arrotondamento.
  • «Moltiplicare prima di dividere o aggiungere decimali migliora sempre la precisione». Può causare overflow, doppia scalatura o preservare l’unità errata; anche l’aritmetica a precisione completa richiede la formula corretta.

Argomenti correlati

Fonti

Navigazione

Cerca nella wiki...