Sommario di gestione
Dusa ha contattato Sayfer per eseguire un controllo di sicurezza sui loro contratti intelligenti.
Questo rapporto documenta la ricerca condotta da Sayfer mirata alle risorse selezionate definite nell'ambito della ricerca. In particolare, questo rapporto mostra la revisione della posizione di sicurezza per i contratti intelligenti di Dusa.
Nel corso di un periodo di ricerca di 4 settimane, abbiamo scoperto 17 vulnerabilità nel contratto.
Nel complesso, Dusa è un protocollo ben costruito. Il fatto che sia derivato e tradotto da TraderJoe lo rende un protocollo molto solido con un'architettura abbastanza comune, ma considerata ottimale. Tuttavia, abbiamo alcune raccomandazioni che riteniamo potrebbero migliorare la qualità e la sicurezza del protocollo. Queste raccomandazioni sono dettagliate nella sezione "Revisione dell'architettura".
In conclusione, a seguito del rapporto dovrebbero essere implementate diverse soluzioni, ma il livello di sicurezza del sistema è adeguato.
Dopo una revisione da parte del team Sayfer, certifichiamo che tutti i problemi di sicurezza menzionati in questo rapporto sono stati affrontati o riconosciuti dal team Dusa.
Metodologia del rischio
Noi di Sayfer ci impegniamo a fornire ai nostri clienti audit di contratti intelligenti della massima qualità. Ecco perché abbiamo implementato un modello completo di valutazione del rischio per valutare la gravità dei nostri risultati e fornire ai nostri clienti le migliori raccomandazioni possibili per la mitigazione.
Il nostro modello di valutazione del rischio si basa su due fattori chiave: IMPATTO ed PROBABILITÀ. L'impatto si riferisce al danno potenziale che potrebbe derivare da un problema, come una perdita finanziaria, un danno alla reputazione o un sistema non operativo. La probabilità si riferisce alla probabilità che si verifichi un problema, tenendo conto di fattori quali la complessità del contratto e il numero di potenziali aggressori.
Combinando questi due fattori, possiamo creare una comprensione completa del rischio posto da un particolare problema e fornire ai nostri clienti una valutazione chiara e attuabile della gravità del problema. Questo approccio ci consente di dare priorità alle nostre raccomandazioni e garantire che i nostri clienti ricevano la migliore consulenza possibile su come proteggere i loro contratti intelligenti.
Il rischio è definito come segue:
Vulnerabilità per rischio
Alta – Minaccia diretta ai processi aziendali chiave.
Medio – Minaccia indiretta ai processi aziendali chiave o minaccia parziale ai processi aziendali.
Basso – Non esiste alcuna minaccia diretta. La vulnerabilità può essere sfruttata utilizzando altre vulnerabilità.
Informativo – Questo risultato non indica vulnerabilità, ma riporta un commento che segnala difetti di progettazione e implementazione impropria che potrebbero causare problemi a lungo termine.
Approccio
Panoramica del protocollo
Introduzione al protocollo
Dusa è un protocollo di finanza decentralizzata (DeFi) rivoluzionario, che introduce un'esperienza di market maker (AMM) automatizzata completamente decentralizzata e avanzata. Costruito sulla blockchain di Massa, Dusa enfatizza principi chiave come la completa decentralizzazione, l'accessibilità per tutti i profili utente, l'interoperabilità con le future applicazioni decentralizzate (DApp) e un forte impegno per la sicurezza e la resistenza alla censura. La sua tabella di marcia delinea un approccio graduale, che comprende ricerca e sviluppo, test, missioni incentivate e l'implementazione di uno scambio decentralizzato (DEX). Le caratteristiche uniche di Dusa includono liquidità concentrata, commissioni variabili, liquidità autonoma e ordini di trading complessi, tutti progettati per migliorare l'esperienza dell'utente e la massimizzazione del rendimento. Con un team dedicato, investitori strategici e consulenti, Dusa mira a aprire la strada all’adozione di contratti intelligenti autonomi ed estendere le sue soluzioni decentralizzate ad altri ecosistemi.
Revisione dell'architettura
Riconoscendo che Dusa è un derivato di una soluzione consolidata, TraderJoe, e si allinea con un modello architetturale riconosciuto, notiamo raccomandazioni aggiuntive limitate per l'architettura attuale. Tuttavia, la nostra valutazione dell’architettura del protocollo Dusa ha portato a una visione completa, che combina informazioni preziose con raccomandazioni ponderate per il miglioramento:
- Dal punto di vista delle best practice, consigliamo di considerare in futuro l’implementazione di un portafoglio multisig per la gestione dei ruoli privilegiati. Pur prendendo atto del recente rilascio del codice multisig Massa (https://github.com/massalabs/massa-standards/tree/feature/multisig-sc/smart-contracts/assembly/contracts/multisig), consigliamo prudenza e consideriamo l'uso di una chiave privata standard come un'opzione praticabile anche per il momento.
- Un’altra raccomandazione intelligente suggerisce di adottare un modello di liquidità unificato, prevedendo un Vault/PoolManager globale invece di contratti individuali per pool. Questo cambiamento strategico, ispirato dai recenti progressi nelle AMM come Uniswap V4 e Balancer, promette vantaggi significativi. Questi includono swap multi-hop convenienti e una maggiore capacità di prestito flash, attribuita all’accesso semplificato all’intero pool di liquidità.
Valutazione della sicurezza
Test specifici di Dusa
Poiché Dusa si trova nella situazione unica di rispecchiare TraderJoe in un linguaggio di programmazione diverso, abbiamo eseguito numerosi test aggiuntivi oltre a quelli abituali per garantire che non ci fossero parti mancanti. Ecco un elenco dei test eseguiti specificatamente per il protocollo Dusa:
Controlli di sicurezza
- Controlli di overflow/underflow, soprattutto a causa del cambiamento nella base di codice da un linguaggio con controlli predefiniti a uno in cui sono richiesti controlli espliciti.
- Utilizzo di librerie sicure (selezionato_*).
- Coerenza tra i contratti per quanto riguarda il flusso di fondi e le chiamate.
- Visibilità delle funzioni per garantire che le funzioni corrette vengano esportate.
- Implementazione del controllo degli accessi e protezione delle funzioni sensibili.
- Protezione dello swap per proteggere le interazioni con la liquidità dallo slippage.
Autenticazione e autorizzazione
- Controllo dell'intero processo di autenticazione, dei diritti del proprietario e dello schema di autorizzazione dei singoli punti di ingresso pubblici.
- Identificazione di potenziali vulnerabilità che portano ad autorizzazioni dannose per gli aggressori.
Matematica e gestione delle variabili
- Problemi relativi al calcolo, in particolare con più tipi di variabili, casting e gestione dei numeri a virgola mobile.
- Errori decimali legati all'aspetto unico di MASSA che ha 9 decimali.
- Gestione delle funzionalità di overflow/underflow.
Prestito Flash e Gestione della Liquidità
- Esame degli errori tipici legati ai prestiti lampo, inclusi errori di calcolo e blocco dei fondi.
- Problemi relativi alla creazione di fabbriche e coppie, come la creazione di pool pericolosi e la gestione della liquidità.
- Verifica dei calcoli di rimozione della liquidità, considerando le commissioni ottenute durante la fornitura di liquidità.
Funzionalità e implementazione
- Implementazione di swap rispetto ad altri DEX, concentrandosi sulla gestione delle commissioni e sulle differenze nei tipi di token.
- Possibilità di attacchi DoS quando si opera su tipi non sicuri come Vector o Array.
- Verifica dei problemi di centralizzazione, come la potenziale influenza sui prezzi, i trasferimenti di token utente e le modifiche alla configurazione.
Gestione dei dati contestuali
- Verificare il passaggio dei dati di contesto corretti alle funzioni per evitare vulnerabilità.
Test generici
I seguenti casi di test sono stati la linea guida durante l'audit del sistema. Questa lista di controllo è una versione modificata del SCSVS v1.2, con grammatica, chiarezza, concisione e criteri aggiuntivi migliorati. Dove c'è una lacuna nella numerazione è stato eliminato un criterio originario. I criteri contrassegnati da un asterisco sono stati aggiunti da noi.
Architettura, design e modellazione delle minacce
Architettura, design e modellazione delle minacce | Nome del test |
G1.2 | Ogni modifica di progettazione introdotta è preceduta dalla modellazione delle minacce. |
G1.3 | La documentazione definisce in modo chiaro e preciso tutti i limiti di fiducia nel contratto (relazioni di fiducia con altri contratti e flussi di dati significativi). |
G1.4 | Il SCSVS, i requisiti di sicurezza o la politica è disponibile per tutti gli sviluppatori e i tester. |
G1.5 | Gli eventi per le operazioni (cambiamento di stato/cruciali per il business) sono definiti. |
G1.6 | Il progetto include un meccanismo in grado di interrompere temporaneamente le funzionalità sensibili in caso di attacco. Questo meccanismo non dovrebbe bloccare l'accesso degli utenti alle loro risorse (ad esempio token). |
G1.7 | La quantità di criptovalute inutilizzate conservate sul contratto è controllata e al livello minimo accettabile in modo da non diventare un potenziale bersaglio di un attacco. |
G1.8 | Se la funzione di fallback può essere richiamata da chiunque, è inclusa nel modello di minaccia. |
G1.9 | La logica aziendale è coerente. Importanti cambiamenti nella logica dovrebbero essere applicati a tutti i contratti. |
G1.10 | Per rilevare le vulnerabilità vengono utilizzati strumenti di analisi automatica del codice. |
G1.11 | Viene utilizzata l'ultima major release di Solidity. |
G1.12 | Quando si utilizza un'implementazione esterna di un contratto, viene utilizzata la versione più recente. |
G1.13 | Quando le funzioni vengono sovrascritte per estendere la funzionalità, la parola chiave super viene utilizzata per mantenere la funzionalità precedente. |
G1.14 | L'ordine di eredità è accuratamente specificato. |
G1.15 | È presente un componente che monitora l'attività del contratto utilizzando gli eventi. |
G1.16 | Il modello di minaccia include transazioni di balene. |
G1.17 | La perdita di una chiave privata non compromette la sicurezza dell'intero progetto. |
Politiche e procedure
Politiche e procedure | Nome del test |
G2.2 | La sicurezza del sistema è costantemente monitorata (es. il livello previsto di fondi). |
G2.3 | Esiste una politica per tenere traccia delle nuove vulnerabilità di sicurezza e per aggiornare le librerie all'ultima versione sicura. |
G2.4 | Il dipartimento di sicurezza può essere contattato pubblicamente e che la procedura per la gestione dei bug segnalati (ad es. bug bounty completa) sia ben definita. |
G2.5 | Il processo di aggiunta di nuovi componenti al sistema è ben definito. |
G2.6 | Il processo delle principali modifiche al sistema prevede la modellazione delle minacce da parte di un'azienda esterna. |
G2.7 | Il processo di aggiunta e aggiornamento dei componenti al sistema include un controllo di sicurezza da parte di un'azienda esterna. |
G2.8 | In caso di hack, è in atto una procedura di mitigazione chiara e ben nota. |
G2.9 | La procedura in caso di hack definisce chiaramente quali persone devono eseguire le azioni richieste. |
G2.10 | La procedura include allarmare altri progetti sull'hacking attraverso canali attendibili. |
G2.11 | Viene definita una procedura di mitigazione della perdita di chiave privata. |
Aggiornabilità
Aggiornabilità | Nome del test |
G3.2 | Prima dell'aggiornamento, viene effettuata un'emulazione in un fork della rete principale e tutto funziona come previsto sulla copia locale. |
G3.3 | Il processo di aggiornamento viene eseguito da un contratto multisig in cui più di una persona deve approvare l'operazione. |
G3.4 | I timelock vengono utilizzati per operazioni importanti in modo che gli utenti abbiano il tempo di osservare i cambiamenti imminenti (si prega di notare che la rimozione di potenziali vulnerabilità in questo caso potrebbe essere più difficile). |
G3.5 | inizializzare() può essere chiamato una sola volta. |
G3.6 | inizializzare() può essere chiamato solo da un ruolo autorizzato tramite opportuni modificatori (es inizializzatore, onlyOwner). |
G3.7 | Il processo di aggiornamento viene eseguito in un'unica transazione in modo che nessuno possa eseguirlo in anticipo. |
G3.8 | I contratti aggiornabili hanno spazi riservati sugli slot per impedire la sovrascrittura. |
G3.9 | Il numero di slot riservati (come intervallo) è stato ridotto in modo appropriato se sono state aggiunte nuove variabili. |
G3.10 | Non ci sono cambiamenti nell'ordine in cui vengono dichiarate le variabili di stato del contratto, né i loro tipi. |
G3.11 | I nuovi valori restituiti dalle funzioni sono gli stessi delle versioni precedenti del contratto (es proprietario(), saldoDi(indirizzo)). |
G3.12 | L'implementazione viene inizializzata. |
G3.13 | L'implementazione non può essere distrutta. |
Logica di business
Logica di business | Nome del test |
G4.2 | La logica del contratto e l'implementazione dei parametri del protocollo corrispondono alla documentazione. |
G4.3 | La business logic procede in un ordine sequenziale e non è possibile saltare i passaggi o eseguirli in un ordine diverso da quello progettato. |
G4.4 | Il contratto ha correttamente rispettato i limiti di business. |
G4.5 | La logica di business non si basa sui valori recuperati da contratti non attendibili (soprattutto quando sono presenti più chiamate allo stesso contratto in un singolo flusso). |
G4.6 | La logica aziendale non si basa sull'equilibrio del contratto (ad esempio, saldo == 0). |
G4.7 | Le operazioni sensibili non dipendono dai dati del blocco (ad es. blocco hash, timestamp). |
G4.8 | Il contratto utilizza meccanismi che mitigano gli attacchi di ordine delle transazioni (front-running) (ad esempio schemi di pre-commit). |
G4.9 | Il contratto non invia fondi automaticamente, ma consente agli utenti di prelevare fondi in transazioni separate. |
Access Control
Access Control | Nome del test |
G5.2 | Il principio del minimo privilegio è rispettato. Altri contratti dovrebbero poter accedere solo a funzioni e dati per i quali sono in possesso di specifica autorizzazione. |
G5.3 | I nuovi contratti con accesso al contratto verificato aderiscono automaticamente al principio dei diritti minimi. I contratti devono disporre di autorizzazioni minime o nulle fino a quando l'accesso alle nuove funzionalità non viene concesso in modo esplicito. |
G5.4 | Il creatore del contratto rispetta il principio del minimo privilegio ei suoi diritti seguono rigorosamente quelli delineati nella documentazione. |
G5.5 | Il contratto applica le regole di controllo degli accessi specificate in un contratto attendibile, in particolare se il controllo degli accessi lato client dApp è presente e potrebbe essere aggirato. |
G5.6 | Le chiamate a contratti esterni sono consentite solo se necessario. |
G5.7 | Il codice modificatore è chiaro e semplice. La logica non deve contenere chiamate esterne a contratti non attendibili. |
G5.8 | Tutti gli attributi degli utenti e dei dati utilizzati dai controlli di accesso sono conservati in contratti attendibili e non possono essere manipolati da altri contratti se non specificamente autorizzati. |
G5.9 | I controlli di accesso falliscono in modo sicuro, anche quando si verifica un ripristino. |
G5.10 | Se l'input (parametri della funzione) viene convalidato, ove possibile viene utilizzato l'approccio di convalida positiva (whitelisting). |
Comunicazione
Comunicazione | Nome del test |
G6.2 | Vengono identificate le librerie che non fanno parte dell'applicazione (ma su cui si basa il contratto intelligente per funzionare). |
G6.3 | La chiamata delegata non viene utilizzata con contratti non attendibili. |
G6.4 | I contratti di terze parti non nascondono funzioni speciali (ad es. Revert). |
G6.5 | Il contratto non controlla se l'indirizzo è un contratto che utilizza il codice operativo extcodesize. |
G6.6 | Gli attacchi di rientro vengono mitigati bloccando le chiamate ricorsive da altri contratti e seguendo il modello Check-Effects-Interactions. Non utilizzare la funzione di invio a meno che non sia indispensabile. |
G6.7 | Il risultato di chiamate di funzioni di basso livello (ad es invia, delegachiama, chiama) da altri contratti è controllato. |
G6.8 | Il contratto si basa sui dati forniti dal mittente corretto e non si basa sul valore tx.origin. |
Aritmetica
Aritmetica | Nome del test |
G7.2 | I valori e le operazioni matematiche sono resistenti agli overflow di numeri interi. Utilizza la libreria SafeMath per le operazioni aritmetiche prima della solidità 0.8.*. |
G7.3 | I frammenti di codice non controllati da Solidity ≥ 0.8.* non introducono under/overflow di numeri interi. |
G7.4 | I valori estremi (es. valori massimi e minimi del tipo di variabile) sono considerati e non modificano il flusso logico del contratto |
G7.5 | La disuguaglianza non rigorosa viene utilizzata per l'uguaglianza di equilibrio. |
G7.6 | Nei calcoli vengono utilizzati gli ordini di grandezza corretti. |
G7.7 | Nei calcoli, la moltiplicazione viene eseguita prima della divisione per la precisione. |
G7.8 | Il contratto non presuppone la precisione a virgola fissa e utilizza un moltiplicatore o memorizza sia il numeratore che il denominatore. |
Denial of Service
Denial of Service | Nome del test |
G8.2 | Il contratto non esegue iterazioni su cicli non associati. |
G8.3 | La funzionalità di autodistruzione viene utilizzata solo se necessario. Se è incluso nel contratto, dovrebbe essere chiaramente descritto nella documentazione. |
G8.4 | La business logic non è bloccata se un attore (es. contratto, account, oracolo) è assente. |
G8.5 | La logica aziendale non disincentiva gli utenti a utilizzare i contratti (ad esempio, il costo della transazione è superiore al profitto). |
G8.6 | Le espressioni di funzioni assert o require hanno una variante passante. |
G8.7 | Se la funzione di fallback non è richiamabile da nessuno, non sta bloccando le funzionalità del contratto. |
G8.8 | Non ci sono operazioni costose in un ciclo. |
G8.9 | Non ci sono chiamate a contratti non attendibili in un ciclo. |
G8.10 | Se esiste la possibilità di sospendere l'esecuzione del contratto, è anche possibile riprenderlo. |
G8.11 | Se vengono utilizzate whitelist e blacklist, non interferiscono con il normale funzionamento del sistema. |
G8.12 | Non ci sono DoS causati da overflow e underflow. |
Dati Blockchain
Dati Blockchain | Nome del test |
G9.2 | Qualsiasi dato salvato nei contratti non è considerato sicuro o privato (anche variabili private). |
G9.3 | Nessun dato riservato viene memorizzato nella blockchain (password, dati personali, token ecc.). |
G9.4 | I contratti non utilizzano valori letterali stringa come chiavi per i mapping. Le costanti globali vengono utilizzate invece per prevenire l'attacco Homoglyph. |
G9.5 | Il contratto non genera banalmente numeri pseudocasuali basati sulle informazioni dalla blockchain (ad esempio seeding con il numero del blocco). |
Utilizzo e limitazioni del gas
Utilizzo e limitazioni del gas | Nome del test |
G10.1 | L'utilizzo del gas è previsto, definito e ha limiti chiari che non possono essere superati. Sia la struttura del codice che l'input dannoso non dovrebbero causare l'esaurimento del gas. |
G10.2 | L'esecuzione e la funzionalità della funzione non dipendono dalle tariffe del gas codificate (sono destinate a variare). |
Chiarezza e leggibilità
Chiarezza e leggibilità | Nome del test |
G11.2 | La logica è chiara e modularizzata in molteplici semplici contratti e funzioni. |
G11.3 | Ogni contratto ha un breve commento di 1-2 frasi che ne spiega lo scopo e la funzionalità. |
G11.4 | Vengono utilizzate implementazioni standard, questo è chiarito nel commento. Se queste implementazioni sono state modificate, le modifiche sono annotate in tutto il contratto. |
G11.5 | L'ordine di ereditarietà viene preso in considerazione nei contratti che utilizzano più funzioni di ereditarietà e shadow. |
G11.6 | Ove possibile, i contratti utilizzano codice testato esistente (ad esempio contratti token o meccanismi come possedibile) invece di implementarne uno proprio. |
G11.7 | Modelli di denominazione coerenti vengono seguiti durante tutto il progetto. |
G11.8 | Le variabili hanno nomi distintivi. |
G11.9 | Tutte le variabili di archiviazione vengono inizializzate. |
G11.10 | Le funzioni con il tipo restituito specificato restituiscono un valore di quel tipo. |
G11.11 | Vengono utilizzate tutte le funzioni e le variabili. |
G11.12 | richiedere è usato al posto di ritornare in if dichiarazioni. |
G11.13 | I affermare La funzione viene utilizzata per verificare la presenza di errori interni e il file richiedere La funzione viene utilizzata per garantire una condizione valida in ingresso da utenti e contratti esterni. |
G11.14 | Il codice assembly viene utilizzato solo se necessario. |
Copertura del test
Copertura del test | Nome del test |
G12.2 | Le narrazioni sugli abusi dettagliate nel modello di minaccia sono coperte da unit test |
G12.3 | Le funzioni sensibili nei contratti verificati sono coperte con test in fase di sviluppo. |
G12.4 | L'implementazione dei contratti verificati è stata verificata per le vulnerabilità della sicurezza utilizzando sia l'analisi statica che quella dinamica. |
G12.5 | Le specifiche del contratto sono state formalmente verificate |
G12.6 | La specifica ei risultati della verifica formale sono inclusi nella documentazione. |
Finanza decentralizzata
Finanza decentralizzata | Nome del test |
G13.1 | Il contratto del prestatore non presuppone che il suo saldo (utilizzato per confermare il rimborso del prestito) venga modificato solo con le proprie funzioni. |
G13.2 | Le funzioni che modificano il saldo dei prestatori e/o prestano criptovaluta non sono rientranti se lo smart contract consente di prendere in prestito la criptovaluta della piattaforma principale (ad es. Ethereum). Blocca gli attacchi che aggiornano il saldo del mutuatario durante l'esecuzione del prestito flash. |
G13.3 | Le funzioni di prestito flash possono chiamare solo funzioni predefinite sul contratto ricevente. Se possibile, definire un sottoinsieme attendibile di contratti da richiamare. Di solito, il contratto di invio (prestito) è quello da richiamare. |
G13.4 | Se include operazioni potenzialmente pericolose (ad es. restituzione di più ETH/token di quelli presi in prestito), la funzione del destinatario che gestisce ETH o token presi in prestito può essere richiamata solo dal pool e all'interno di un processo avviato dal proprietario del contratto ricevente o da un'altra fonte attendibile (ad es. multiseg). |
G13.5 | I calcoli della quota del pool di liquidità vengono eseguiti con la massima precisione possibile (ad esempio, se il contributo è calcolato per ETH dovrebbe essere fatto con una precisione di 18 cifre – per Wei, non Ether). Il dividendo va moltiplicato per 10 alla potenza del numero di cifre decimali (es. dividendo * 10^18 / divisore). |
G13.6 | I premi non possono essere calcolati e distribuiti all'interno della stessa chiamata di funzione che deposita i token (dovrebbe essere definita anche come non rientrante). Questo protegge dalle fluttuazioni momentanee delle azioni. |
G13.7 | I contratti di governance sono protetti dagli attacchi di prestiti flash. Una possibile tecnica di mitigazione è richiedere il processo di deposito dei token di governance e proporre una modifica da eseguire in transazioni diverse incluse in blocchi diversi. |
G13.8 | Quando si utilizzano oracoli su catena, i contratti sono in grado di sospendere le operazioni in base al risultato degli oracoli (in caso di oracolo compromesso). |
G13.9 | I contratti esterni (anche fidati) che sono autorizzati a modificare gli attributi di un contratto di progetto (es. prezzo del token) hanno le seguenti limitazioni implementate: soglie per la modifica (es. non più/meno del 5%) e un limite di aggiornamenti (es. un aggiornamento al giorno). |
G13.10 | Gli attributi del contratto che possono essere aggiornati dai contratti esterni (anche fidati) vengono monitorati (ad es. utilizzando gli eventi) e viene implementata una procedura di risposta agli incidenti (ad es. durante un attacco in corso). |
G13.11 | Operazioni matematiche complesse che consistono in operazioni sia di moltiplicazione che di divisione eseguono prima le moltiplicazioni e poi le divisioni. |
G13.12 | Quando si calcolano i prezzi di scambio (ad es. da ETH a token o viceversa), il numeratore e il denominatore vengono moltiplicati per le riserve (vedere il getInputPrice funzione nel ScambioUniswap contrarre). |
Controllo dell'ordine da Sayfer
Risultati dell'audit
[H] Rischio di corsa iniziale durante la creazione di una nuova coppia
ID | DIRE-01 |
Stato dei servizi | Fissa |
Rischio | Alta |
Affari Impact | In alcuni casi, gli utenti potrebbero perdere i fondi depositati per finanziare la creazione di coppie. Un pioniere malintenzionato può creare una coppia a spese di un altro utente, oppure più utenti potrebbero tentare di creare una coppia contemporaneamente, riuscendoci o fallendo in modo casuale. |
Dove |
|
Descrizione
Per creare una nuova coppia, l'utente deve chiamare Router::createLBPair(StaticArray ). Sotto il cofano, questa funzione chiama Factory::createLBPair(StaticArray ), che a sua volta chiama Massa transferCoins(Indirizzo, numero).
- Fabbrica.ts:264; createLBPair(StaticArray )
transferCoins(_pair._origin, 200 * ONE_COIN);
Secondo il documentazione, questa funzione trasferisce semplicemente le monete dall'indirizzo corrente a un determinato indirizzo. Ciò significa che questi fondi devono essere precedentemente inviati Fabbrica. D'altra parte, gli utenti hanno la possibilità di chiamare createPair dal Router, che è il punto di ingresso predefinito per altre operazioni. Tuttavia, se vengono depositati in una transazione separata, esiste la possibilità che qualcuno, intenzionalmente o meno, chiami createLBPair(StaticArray ).
Quando un blocco con transazioni viene finalizzato, queste verranno disposte in un ordine sconosciuto. Quindi, per la prima transazione inclusa, questi fondi verranno trasferiti da Factory a una nuova coppia, mentre per il secondo utente (quest'ultima transazione) la transazione verrà annullata a causa della mancanza di fondi disponibili.
Mitigazione
Il finanziamento della creazione della coppia deve effettivamente avvenire nella stessa transazione per evitare problemi di riordino delle transazioni.
Ad esempio, uno dei modi per ottenerlo potrebbe essere un meccanismo che controlla il saldo di fabbrica e il saldo del pool alla fine di una funzione e restituisce il surplus, simile a questo https://github.com/massalabs/coin-vester/blob/master/smart-contract/assembly/contracts/main.ts#L34
Il meccanismo sarà in vigore nel creareLBPair funzione di Fabbrica), nel costruttore, menta, bruciare, raccogliereCommissioni, aumentareOracleLength, safeTransferFrom & safeBatchTransferFrom (di coppia). Queste sono le funzioni che possono spendere Massa per lo storage in quanto possono creare nuove voci nello storage (modificare lo storage non costa nulla). Anche le funzioni che interagiscono con queste funzioni devono avere questo meccanismo.
Possono essere distinti in due diverse tipologie:
- Funzioni che interagiscono con questi e che non interagiscono con WMAS (creareLBPair dal router e dalla fabbrica + aggiungiLiquidità, rimuovereLiquidità) trasferirà tutti i file monete trasferite, salva il saldo della SC in modo costante e, dopo la chiamata della SC, ritrasferisci al chiamante la differenza tra il nuovo saldo e il saldo salvato.
- Funzioni che interagiscono con questi e che interagiscono con WMAS (aggiungiLiquiditàMAS, rimuoviLiquiditàMAS) avrà bisogno di un ulteriore parametro per conoscere il numero di monete che devono essere incartate, invierai la differenza monete trasferite e questo numero nella chiamata. Il resto del meccanismo di queste funzioni sarà lo stesso di quello precedente.
[H] I fondi depositati per la creazione della coppia potrebbero andare persi
ID | DIRE-02 |
Stato dei servizi | Fissa |
Rischio | Alta |
Impatto sul business | Gli utenti che depositano fondi sul router per creare una coppia potrebbero perdere i propri soldi senza vedere alcun risultato. Abbiamo valutato questo problema come elevato (piuttosto che critico) perché esiste un flusso di lavoro alternativo: le chiamate factory::createLBPair(StaticArray ) direttamente con i fondi. |
Dove |
|
Descrizione
Questo problema è il risultato di come è progettata la creazione della coppia. Un utente può chiamare Router::createLBPair(StaticArray ) e poiché la creazione di una coppia richiede un deposito, può allegare fondi alla chiamata di funzione, il che sembra una soluzione intuitiva.
Tuttavia, se l'utente lo fa, i fondi non raggiungeranno mai la coppia e andranno persi nel contratto (anche se potranno essere recuperati dal proprietario). Questo perché rimarranno bloccati nel contratto Router, mentre la creazione della coppia avviene nel contratto Factory, dove viene invece utilizzato transferCoins(), che detrae fondi dall'indirizzo corrente (della fabbrica).
- Fabbrica.ts:264; createLBPair(StaticArray )
transferCoins(_pair._origin, 200 * ONE_COIN);
Poiché è implementato in fabbrica, i fondi inviati al router non raggiungeranno la fabbrica, che inoltre non avrà i fondi per creare una coppia e ripristinarla.
Mitigazione
Un'opzione è implementare la soluzione suggerita in DIRE-01.
Al contrario, Fabbrica.ts può essere chiamato direttamente per creare coppie e richiedere agli utenti di inviare l'importo esatto richiesto insieme alla chiamata alla funzione, OPPURE almeno l'importo richiesto con il rimborso di eventuali fondi in eccesso inviati.
[M] Più coppie dello stesso tipo possono diluire la liquidità
ID | DIRE-03 |
Stato dei servizi | Fissa |
Rischio | Medio |
Impatto sul business | Se un’applicazione AMM consente la creazione di più pool con gli stessi parametri, la liquidità potrebbe essere diluita tra questi pool, invece di essere concentrata in uno solo. Ciò potrebbe avere un impatto negativo sugli utenti e potrebbe comportare scambi meno favorevoli disponibili per gli utenti del protocollo.
Inoltre, la coppia precedente non verrà più utilizzata per il routing. Ciò può comportare liquidità o manipolazione dei prezzi. |
Dove |
|
Descrizione
Quando si crea una nuova coppia, non è possibile verificare se esiste già esattamente la stessa coppia. Ciò potrebbe disperdere la liquidità degli utenti su più pool equivalenti. I pool a bassa liquidità sono più inclini alla manipolazione dei prezzi e offrono operazioni meno favorevoli e dovrebbero pertanto essere evitati.
Mitigazione
Controlla se una coppia di determinati parametri esiste già al momento della creazione della coppia, in tal caso ripristina.
[L] Il controllo esatto del saldo dopo un prestito flash può essere oggetto di abuso
ID | DIRE-04 |
Stato dei servizi | Fissa |
Rischio | Basso |
Impatto sul business | Un destinatario del prestito flash potrebbe eseguire chiamate esterne nella richiamata. Poiché il sistema richiede che il saldo dopo il prestito flash sia esattamente il saldo prima più le commissioni, questi contratti esterni potrebbero trasferire un importo molto piccolo alla coppia durante l'esecuzione per garantire che il prestito flash fallisca (il che potrebbe essere redditizio per loro). in determinate situazioni). |
Dove |
|
Descrizione
Dopo un prestito lampo, il sistema richiede che il saldo sia esattamente il saldo prima del prestito lampo più le commissioni.
- flashLoan(StaticArray )
assert(
_balanceAfter == SafeMath256.add(_balanceBefore, _fees.total),
LBPair__FlashLoanInvalidBalance(),
);
In genere, è necessario che il saldo sia maggiore, poiché richiedere una corrispondenza esatta è restrittivo.
Mitigazione
Controlla solo se il saldo è superiore al saldo prima del prestito flash più commissioni. Questa è anche la logica che TraderJoe utilizza nella sua funzione di prestito flash.
[L] Ignorare una coppia LB non emette un evento
ID | DIRE-05 |
Stato dei servizi | Fissa |
Rischio | Basso |
Affari Impact | Gli indicizzatori fuori catena potrebbero ricevere informazioni errate, portando a calcoli errati. |
Dove |
|
Descrizione
La funzione setLBPairIgnored(StaticArray ) può essere utilizzato per controllare se una coppia deve essere ignorata o meno per il routing. Qualsiasi modifica nel flag tramite questa funzione emette l'evento SET_LBPAIR_IGNORED. Tuttavia è anche possibile modificare il flag tramite setLBPairInformation(StaticArray ), che non emette un evento.
Mitigazione
[L] Riscossione delle commissioni incoerente
ID | DIRE-06 |
Stato dei servizi | Riconosciuto |
Rischio | Basso |
Affari Impact | Qualsiasi utente può attivare la riscossione delle tariffe per altri utenti, mentre la riscossione delle tariffe del protocollo può essere avviata solo dal proprietario. |
Dove |
|
Descrizione
Chiunque può chiamare CollectFees(StaticArray ) per qualsiasi indirizzo per distribuire le commissioni a questo indirizzo. Ciò potrebbe essere indesiderato per alcuni utenti che desiderano controllare quando vengono loro distribuite le commissioni.
Anche questo non è coerente con collectorProtocolFees(StaticArray ), che può essere chiamato solo dal destinatario.
- collectorProtocolFees(StaticArray )
assert(
Context.caller().equals(_feeRecipient),
LBPair__OnlyFeeRecipient(_feeRecipient, Context.caller()),
);
Mitigazione
[L] Utilizzo incoerente di SafeMath
ID | DIRE-07 |
Stato dei servizi | Fissa |
Rischio | Basso |
Affari Impact | Overflow o underflow nel codice possono portare a ripristini e/o calcoli errati imprevisti. |
Dove | - |
Descrizione
Sebbene SafeMath viene utilizzato in molti luoghi, alcune funzioni che utilizzano operazioni aritmetiche vengono lasciate non protette, il che può di conseguenza portare a overflow o underflow.
Mitigazione
[L] Enumerazione delle risorse inefficiente
ID | DIRE-08 |
Stato dei servizi | Fissa |
Rischio | Basso |
Affari Impact | Alcune funzioni di fabbrica consumeranno più gas del necessario. Nel peggiore dei casi, se il proprietario registra una quantità molto elevata di beni, ciò potrebbe portare a un DoS. Ma questo è improbabile. |
Dove |
|
Descrizione
ecco due problemi con questa funzione:
- Il ciclo non si interrompe dopo aver trovato una risorsa e ripete inutilmente il resto dell'array. Quindi, ogni caso is il caso peggiore.
- Una mappatura sarebbe più efficiente in questo caso, poiché ha una ricerca temporale costante.
- isQuoteAsset(Indirizzo)
for (let i = 0; i < quoteAssets.length; i++) {
if (quoteAssets[i].equals(_token)) {
isQuoteAsset = true;
}
}
Mitigazione
Ci sono due soluzioni qui:
- Mantieni l'attuale algoritmo di ricerca lineare e aggiungi semplicemente un'interruzione nel file if blocco dopo l'impostazione isQuoteAsset vero.
- Una soluzione migliore sarebbe passare a una mappatura. Quando un nuovo token viene aggiunto al sistema, quoteAssets[token] dovrebbe essere impostato su vero. D'ora in poi sarà possibile accedervi quando necessario, senza bisogno di cercarlo. Forse l'intera funzione può essere semplicemente rifattorizzata.
[L] La tariffa massima per il prestito Flash non viene impostata dopo la distribuzione
ID | DIRE-09 |
Stato dei servizi | Fissa |
Rischio | Basso |
Affari Impact | È possibile avere una commissione di prestito flash superiore a MAX_TARIFFA. |
Dove |
|
Descrizione
Ogni volta che la tariffa del prestito flash viene impostata utilizzando setFlashLoanFee(), viene convalidato che è inferiore al valore MAX_TARIFFA. Tuttavia, questo controllo non viene eseguito al momento della inizializzazione del contratto ed è possibile impostare una commissione di prestito flash arbitrariamente elevata.
Mitigazione
[L] Protezione dallo slittamento incompleto – Controllo della scadenza mancata
ID | DIRE-10 |
Stato dei servizi | Fissa |
Rischio | Basso |
Affari Impact | Esiste una maggiore probabilità che gli utenti ottengano operazioni meno favorevoli quando utilizzano l'AMM. |
Dove | Router.ts
|
Descrizione
Lo slippage è il risultato di una volatilità nelle soluzioni di tipo AMM e significa semplicemente che il prezzo quando si richiede una modifica di operazione/swap/liquidità è diverso rispetto a quando la transazione viene eseguita. Per proteggere gli utenti da enormi slittamenti, che possono essere naturali o il risultato di attacchi intenzionali come il Sandwiching, esistono due tipi generici di protezioni: importo minimo, che è presente, e controllo delle scadenze, che manca.
Tali controlli dovrebbero essere implementati su qualsiasi swap, aggiunta e rimozione di liquidità, poiché tutti sono soggetti a slittamento.
Un controllo della scadenza semplicemente non consente di includere la transazione dopo un certo punto temporale, che ad esempio può essere intenzionalmente incluso in seguito da un block builder dannoso per tentare comunque di eseguire la transazione a condizioni di prezzo meno favorevoli. Tuttavia, poiché viene controllato un importo minimo di token, l'impatto di questo problema è limitato.
Mitigazione
Realizzare _assicurare(scadenza) allo stesso modo è già presente in tutte le altre funzioni che si occupano di liquidità.
[L] Trasferimento della proprietà in un unico passaggio
ID | DIRE-11 |
Stato dei servizi | Fissa |
Rischio | Basso |
Affari Impact | Fornire un indirizzo errato potrebbe comportare la perdita irrevocabile della titolarità del contratto. |
Dove |
|
Descrizione
In Fabbrica.ts, il passaggio di proprietà avviene tramite il vecchio proprietario chiamando il trasferimentoProprietà([u8]) con il nuovo proprietario. La funzione quindi modifica il file PROPRIETARIO variabile di stato.
- trasferimentoProprietà([u8])
export function transferOwnership(bs: StaticArray<u8>): void {
_onlyOwner();
const _newOwner = new Address(new Args(bs).nextString().unwrap());
_setFeeRecipient(_newOwner);
Storage.set(OWNER, _newOwner.toString());
}
Mitigazione
Ti consigliamo di implementare un processo in due passaggi per il trasferimento della proprietà. Ciò è in linea con le buone pratiche ampiamente riconosciute e potrebbe prevenire trasferimenti accidentali. Per illustrare, utilizzeremo l'implementazione di OpenZepplin.
Un trasferimento di proprietà in due passaggi richiede innanzitutto che l'attuale proprietario avvii il trasferimento.
function transferOwnership(address newOwner) public virtual override onlyOwner {
_pendingOwner = newOwner;
emit OwnershipTransferStarted(owner(), newOwner);
}
Ma per completare il processo, il nuovo proprietario deve approvare il trasferimento.
function acceptOwnership() public virtual {
address sender = _msgSender();
if (pendingOwner() != sender) {
revert OwnableUnauthorizedAccount(sender);
}
_transferOwnership(sender);
}
Ciò garantisce che il nuovo proprietario sia l'obiettivo previsto.
[I] Emissione di eventi insufficiente
ID | DIRE-12 |
Stato dei servizi | Fissa |
Rischio | Informativo |
Affari Impact | Diminuzione della tracciabilità e della visibilità dei cambiamenti storici dello stato chiave che influiscono sul protocollo. |
Dove | Fabbrica.ts
|
Descrizione
Alcuni dei cambiamenti di stato chiave del protocollo non emettono un evento associato.
Nota: transferOwnership(bs: StaticArray ) emette un evento, ma solo per _setFeeRecipient.
Mitigazione
[I] Getter e setter sono confusi con funzioni esterne
ID | DIRE-13 |
Stato dei servizi | Fissa |
Rischio | Informativo |
Affari Impact | Mixing, getter, setter, entrypoint e funzioni interne rendono il codice meno leggibile. |
Dove |
|
Descrizione
Alcuni contratti hanno sezioni separate contenenti funzioni che cambiano lo stato (setter), lo leggono (getter) o fanno parte dell'albero delle chiamate di funzione (interno).
In alcuni contratti, tuttavia, quest’ordine è disturbato: le funzioni sono confuse. A volte, anche le funzioni esportate (punti di ingresso) vengono confuse con quelle interne.
Mitigazione
[I] Mancanza di test unitari
ID | DIRE-14 |
Stato dei servizi | Riconosciuto |
Rischio | Informativo |
Affari Impact | Scenari di test e test unitari aiutano gli sviluppatori a rilevare bug e vulnerabilità che altrimenti sfuggirebbero in una semplice analisi statica. I test dinamici hanno un valore inestimabile per garantire la qualità del prodotto finito. |
Dove | - |
Descrizione
A parte una dozzina circa di test per una serie di funzionalità della libreria, il codice non prevedeva test unitari, funzionali o di integrazione. Non tutti i difetti possono essere rilevati con la semplice analisi statica, lasciando il prodotto in una posizione precaria.
Mitigazione
È considerata una buona pratica garantire che la copertura del codice per i test sia la più elevata possibile e che gli scenari di test includano percorsi sia felici che infelici.
[I] Codice non necessario
ID | DIRE-15 |
Stato dei servizi | Fissa |
Rischio | Informativo |
Affari Impact | Questo problema non ha alcun impatto diretto sul codice o sul protocollo ed è stato pertanto classificato come informativo. |
Dove | Router.ts:770-772; _getAmountsIn(u64[], Indirizzo[], IERC20[], u256) |
Descrizione
La funzione _getAmountsIn() prende i passi bin come argomento ed esegue il seguente controllo:
- _getAmountsIn(u64[], Indirizzo[], IERC20[], u256)
if (_binStep == 0) {
// means TraderJoe V1 swap
} else {
...
}
Tuttavia, ciò non è necessario. Il sistema non supporta gli scambi di tipo V1 e il resto della funzione non considera i passaggi del contenitore, poiché riceve le coppie (corrispondenti ai passaggi del contenitore) come argomento aggiuntivo.
Mitigazione
[I] Importazioni non utilizzate
ID | DIRE-16 |
Stato dei servizi | Fissa |
Rischio | Informativo |
Affari Impact | Questo problema non ha alcun impatto diretto sul codice o sul protocollo ed è stato pertanto classificato come informativo. |
Dove |
|
Descrizione
Sembra che le importazioni specificate non vengano mai utilizzate nei rispettivi contratti. Potrebbero essere rimossi.
Mitigazione
[I] Utilizzo dei "numeri magici"
ID | DIRE-17 |
Stato dei servizi | Riconosciuto |
Rischio | Informativo |
Affari Impact | Ciò potrebbe aumentare la difficoltà di analisi del codice per lettori non familiari. |
Dove |
|
Descrizione
In più punti il contratto utilizza valori numerici non ben documentati, descritti o commentati.
Mitigazione
Aggiungere commenti che spieghino la scelta delle costanti.