Securitate
Ce deține valoare
- ETH-ul și tokenurile poolurilor, în poziții pe care nimeni nu le poate retrage. ETH-ul unei tabere iese din poolul ei doar când acea tabără e vândută în el: de un trader sau de realinierea hookului, după ce o tranzacție a îndepărtat cele trei prețuri unul de altul.
- Poturile, comisioanele de creator și comisioanele de protocol, deținute de hook sub formă de creanțe în ETH în PoolManagerul Uniswap V4, contabilizate separat pentru fiecare duel.
Ce o protejează
- Lichiditate blocată. Numai factory-ul poate deschide poolurile înregistrate sau adăuga lichiditatea lor de lansare; retragerile și donațiile se anulează.
- Beneficiari limitați. Potul răscumpără numai câștigătorul propriului duel; comisioanele de creator merg numai la creatorul duelului, portofelul care l-a lansat sau o adresă căreia i-a cedat rolul și care l-a acceptat. Comisioanele de protocol merg la trezoreria actuală, pe care proprietarul factory-ului o poate schimba după 48 de ore, inclusiv pentru comisioanele de protocol deja acumulate.
- Puteri limitate ale factory-ului. Tokenurile nu au proprietar sau funcție de mint, iar hookul nu poate fi actualizat sau suspendat. Proprietarul poate suspenda lansările noi, propune condiții limitate pentru duelurile viitoare sau schimba trezoreria după termen. Poate și să scadă imediat partea protocolului din comisionul de swap al tuturor duelurilor, până la zero, sau s-o ridice la loc, niciodată peste condițiile duelului: ce nu ia protocolul merge în poturi. Proprietarul poate renunța definitiv la rol (
renounceOwnership): condițiile, trezoreria, rata protocolului și o suspendare rămân atunci cum sunt, cu excepția unei schimbări deja propuse, pe care oricine o mai poate aplica după termen. - Decontare înainte de swap. Primul swap după închidere decontează media încheiată înainte de tranzacționare. Plafonul răscumpărării este circa 10% peste o referință păstrată cu precizie de fracțiuni de tick: o medie exponențială la 30 de minute a tickurilor poolului ETH, limitată la cel mult tickul curent, astfel încât urmează prețul în jos imediat.
- Runde bazate numai pe ETH. Repartiția și media folosesc cele două pooluri ETH. O schimbare A/B fără mișcarea unui preț în ETH nu schimbă semnalul sau activitatea unei runde altfel liniștite.
- Comisioane pe execuții complete. Swapurile taxate în avans se anulează dacă nu pot executa întreaga sumă cerută. Taxa de rețea este separată.
- Realiniere izolată. Se păstrează numai bucle profitabile, limitate de ETH-ul poolului care cumpără tokenurile. O încercare eșuată se anulează și se sare; din lipsă de gaz se anulează swapul (
ArbitrageStarved), ca un apelant să nu o poată sări reținând gaz. Nu se garantează alinierea prețurilor sau protecția împotriva tuturor pierderilor de tranzacționare. - Aceleași reguli pe toate rutele. Hookul ia comisioanele, taxele și arderile, indiferent dacă routerul este al Duel sau alt router V4.
Fabrica pornește cu lansările suspendate. Cât timp paused() este true, doar owner() poate lansa un duel; apelurile celorlalți sunt anulate cu CreationPaused. Proprietarul deschide lansările prin setPaused(false). Duelurile existente se tranzacționează în continuare.
Ce afișează site-ul
- Imagini doar de la site. Paginile afișează imaginile unui duel doar de la adresa Duel și doar imagini procesate de site: un JPEG, PNG sau WebP citit mai întâi din antet (cel mult 1536 × 1536 de pixeli și 5 MB), decodat în WebAssembly, încadrat în 1024 de pixeli și recodificat în WebP, fără metadatele lui. Politica de securitate a paginilor nu le lasă să încarce imagini de altundeva.
- Codecuri ținute deoparte. Imaginile sunt decodate într-un Worker separat, pe care doar site-ul îl poate apela și care nu deține nimic: nicio cheie, niciun spațiu de stocare. O vulnerabilitate într-un codec ar atinge doar imaginea pe care o citește. Site-ul verifică ce îi răspunde acest Worker (un WebP static, de mărimea așteptată) înainte să-l păstreze, iar decodoarele scrise în C pornesc de la zero pentru fiecare imagine.
- Duelurile lansate direct din contract. Detaliile lor pot numi orice adresă. Site-ul nu afișează nicio imagine https; o imagine IPFS o descarcă o singură dată, de la un gateway public (cel mult 5 MB și 10 secunde), o procesează ca pe o încărcare și își afișează propria copie sau inițialele taberei.
- Încărcări limitate. Site-ul primește imagini doar din propriile pagini: zece pe minut de la aceeași adresă și un număr limitat pe zi. O imagine pe care n-o folosește niciun duel este ștearsă după o zi.
Cum a fost verificat
- Teste automate acoperă comportamentul contractelor, aplicația web și botul de alerte. Acoperirea este o măsură a testelor, nu dovada că toate comportamentele posibile sunt sigure.
- Teste pe fork verifică integrarea cu contractele Uniswap V4 de pe Robinhood Chain, inclusiv Universal Router, Permit2 și V4 Quoter.
- Teste de regresie păstrează cazurile găsite în revizuirea internă, inclusiv contabilitatea comisioanelor, cazurile-limită de lansare, decontarea și poolurile epuizate.
- Teste de invariante verifică contabilitatea pe secvențe aleatorii de tranzacții, decontări și revendicări: creanțele în ETH trebuie să corespundă sumelor datorate, fără tokenuri intermediare rămase în hook, factory sau router. Trecerea execuțiilor configurate nu dovedește siguranța tuturor secvențelor posibile.
Revizuirea internă a găsit și a reparat o rută cu ieșire exactă care ocolea taxa anti-snipe, o repartiție care nu urma o tabără cumpărată singură, pooluri golite de ETH citite greșit de prețul de referință al răscumpărării și linkuri nesigure în metadatele unui duel.
Raportarea unei vulnerabilități
Dacă crezi că ai găsit o vulnerabilitate, îți mulțumim că îți faci timp s-o raportezi. Scrie-ne în privat pe Telegram, la @chatwithdivine, ce ai găsit și, dacă poți, cum se reproduce, și lasă-ne timp s-o analizăm înainte să spui ceva public. Îți răspundem repede și te ținem la curent.
Nu scriem niciodată primii: ai încredere doar în @chatwithdivine.