Selepas pelancaran utama Uniswap v4, mekanisme Hook menjadi salah satu inovasi paling diperhatikan dalam DeFi. Platform pelancaran memecoin di rantai Base, Flaunch, menggunakan Hook untuk mencapai harga pra-jual tetap dan mekanisme pelaksanaan automatik; protokol likuiditi Bunni v2 menggunakan Hook untuk membina model likuiditi boleh diprogramkan dan semula-jamin; pada tahun ini, token-token seperti SATO, uPEG (Unipeg), dan Slonks yang berpusat pada permainan Hook juga mencatatkan kenaikan puluhan kali ganda dalam tempoh singkat.
Di sebalik kemakmuran ekosistem Hook, serangan terhadap kelemahan pelaksanaan Hook juga meningkat secara ketara. Artikel ini akan bermula dengan mekanisme Hook Uniswap v4, menganalisis secara bertahap stak pemanggilan utamanya, membantu pihak projek memahami kelemahan yang mungkin wujud.
Keselamatan Uniswap v4 Hook
1. Pengenalan
Perubahan arsitektur paling ketara Uniswap v4 berbanding v3 ialah pengenalan mekanisme Hook: membenarkan pembangun memasang kontrak tersuai pada peristiwa kitaran hidup kolam likuiditi, serta menyuntik logik apa sahaja pada titik-titik seperti swap, menambah atau mengurangkan likuiditi, dan inisialisasi.
Beberapa perubahan utama dalam v4 adalah:
Pola Singleton: Semua status kolam dikelola secara terpusat oleh kontrak PoolManager tunggal, tanpa perlu menerapkan kontrak terpisah untuk setiap kolam
- Akuntansi kilat: Perubahan saldo sementara semasa proses perdagangan hanya dicatat dalam penyimpanan sementara, dan hanya dijumlahkan sekali pada akhir transaksi
Mekanisme Hook: Setiap kolam boleh dikaitkan dengan kontrak Hook, dan PoolManager akan memanggil semula kontrak tersebut pada titik penting (beforeInitialize, beforeSwap, afterAddLiquidity, dsb.)
- Hook tidak boleh ditukar: Setelah proses inisialisasi kolam selesai, alamat Hook yang terikat akan tetap tetap (alamat Hook yang terikat dengan kolam tidak boleh diubah, tetapi sama ada kontrak Hook itu sendiri boleh dinaik taraf bergantung kepada cara pelaksanaannya)

Pada era v3, pembangun hanya perlu mempercayai protokol Uniswap itu sendiri; manakala pada era v4, keselamatan setiap pasaran bergantung kepada Hook yang melekat padanya. Hook mengembangkan AMM daripada satu primitif kewangan tetap menjadi satu infrastruktur kewangan yang boleh diprogramkan, tetapi model keselamatan telah terpecah daripada “peringkat protokol” kepada “peringkat pasaran”.
2. Struktur Hook
2.1 PoolManager dan model unlock/callback
Kontrak inti v4 adalah PoolManager yang merupakan singleton. Sebarang operasi perubahan status kolam (swap, menambah atau mengurangkan likuiditi) mesti memanggil PoolManager.unlock() terlebih dahulu untuk mendapatkan kebenaran panggilan balik sekali sahaja, kemudian menyelesaikan tindakan spesifik di dalam unlockCallback(). Pada akhir keseluruhan proses, PoolManager akan mengesahkan sama ada buku besar seimbang:

NonzeroDeltaCount != 0 secara langsung revert, ini adalah batasan utama dalam flash accounting v4. Sebarang Hook boleh sementara menyebabkan ketidakseimbangan akaun semasa pelaksanaan, tetapi harus menyelesaikannya sendiri sebelum penghujung transaksi, jika tidak, keseluruhan transaksi akan rollback.
Setiap kolam dikenal secara unik oleh struktur PoolKey, yang mengandung medan hooks:

PoolId dihitung menggunakan keccak256(PoolKey), oleh itu, alamat hooks yang berbeza akan menghasilkan pasaran yang berbeza. Ini juga bermakna, PoolManager tidak akan mengesahkan sama ada alamat Hook tertentu pernah digunakan untuk pasaran lain, dan kontrak Hook yang sama boleh dikaitkan secara serentak dengan beberapa pasaran.

2.2 Kod bit kebenaran Hook dalam alamat
Satu reka bentuk yang tidak intuitif pada v4 ialah: kebenaran Hook bukan ditentukan oleh pemboleh ubah tertentu di dalam kontrak, tetapi ditentukan oleh alamat penghantaran kontrak Hook.
PoolManager memeriksa 14 bit terendah alamat Hook untuk menentukan sama ada Hook tersebut perlu dipanggil pada titik siklus hidup tertentu:

Sebagai contoh, BEFORE_SWAP_FLAG = 1 << 7. Jika bit ke-7 alamat Hook adalah 1, PoolManager akan memanggil beforeSwap() dari Hook sebelum swap; jika tidak, walaupun kontrak Hook telah mengimplementasikan beforeSwap(), ia tidak akan pernah dipanggil oleh PoolManager.
Ini bermaksud bahawa alamat Hook mesti dikira menggunakan CREATE2 + salt untuk membina alamat dengan rendah yang sepadan sepenuhnya dengan kebenaran sasaran. Uniswap menyediakan alat HookMiner untuk tujuan ini:

Ketika bit kebenaran tidak sejalan dengan pelaksanaan fungsi, ia akan menghasilkan dua jenis masalah:
(1) Telah melaksanakan fungsi hook tertentu, tetapi alamat tidak dikodkan dengan bit kebenaran yang sesuai—PoolManager tidak akan memanggil fungsi ini, logiknya tidak berfungsi
(2) Alamat mengkodkan bit kebenaran tertentu, tetapi hook tidak mengimplementasikan fungsi yang berkaitan—PoolManager mungkin mengalami revert semasa panggilan balik, menyebabkan DOS atau kegagalan pengesahan nilai pulangan, yang mengakibatkan operasi berkaitan tidak dapat dilaksanakan.
Ini juga merupakan rintangan semula jadi kepada peningkatan Hook: jika Hook boleh ditingkatkan melalui proxy, alamat penghantaran tidak berubah semasa peningkatan, oleh itu selepas peningkatan, hanya implementasi fungsi Hook yang sedia ada yang boleh dimodifikasi, bukan menambah jenis Hook baru. Untuk menyediakan keupayaan pengembangan masa depan, semua bit kebenaran yang mungkin digunakan mesti diprakira semasa penghantaran awal.
2.3 BaseHook dan satu jebakan kawalan akses yang sering diabaikan
Kontrak abstrak BaseHook yang disediakan oleh periphery Uniswap v4 lama membolehkan pembangun mewarisi ia untuk mengimplementasikan Hook tersuai. Salah satu peranan penting BaseHook ialah menyediakan modifikator onlyPoolManager untuk fungsi unlockCallback():

Namun—terdapat jebakan reka bentuk yang sangat mudah diabaikan—versi awal BaseHook hanya menambahkan onlyPoolManager kepada unlockCallback, tanpa perlindungan apa pun terhadap fungsi panggilan balik lain (beforeSwap, afterSwap, beforeAddLiquidity, dsb.). Kawalan akses fungsi-fungsi ini mesti ditambahkan secara eksplisit oleh pembangun Hook.
3. Bacaan kod hayat Hook
Sebagai contoh satu pertukaran exact-input, berikut adalah analisis seluruh panggilan tindakan dari pengguna memulakan transaksi hingga penyelesaian.
3.1 Inisialisasi kolam dan pengikatan Hook
Sesiapa sahaja boleh memanggil PoolManager.initialize() untuk mencipta kolam baru:

isValidHookAddress hanya mengesahkan kesesuaian bit keizinan alamat dengan bidang fee, tidak mengesahkan sama ada Hook tersebut sudah digunakan dalam kolam lain, atau sama ada Hook tersebut "bersedia" menerima PoolKey ini. Jika Hook direka tanpa logik senarai putih atau pengikatan kolam tunggal dalam beforeInitialize, sesiapa sahaja boleh membina kolam baru yang menggunakan Hook yang sama tetapi pasangan token sewenang-wenangnya, dan memicu semua panggilan balik seterusnya oleh Hook.
3.2 beforeSwap dan BeforeSwapDelta
Titik masuk proses swap ialah PoolManager.swap(), yang memanggil Hooks.beforeSwap() sebelum melaksanakan logik swap utama:

Nilai balasan beforeSwap ialah satu tiga pasangan (bytes4, BeforeSwapDelta, uint24):
- bytes4: mesti sama dengan IHooks.beforeSwap.selector, jika tidak, PoolManager akan revert secara terus
- BeforeSwapDelta: Hook menyesuaikan delta bagi token yang ditentukan dan token yang tidak ditentukan dalam swap ini
- uint24: Nilai cakupan kadar LP dinamik (hanya berkuasa apabila kolam menghidupkan kadar dinamik)
BeforeSwapDelta ialas int256, 128 bit pertama adalah delta token yang ditentukan (iaitu token yang ditentukan pengguna), dan 128 bit terakhir adalah delta token yang tidak ditentukan:

Perlu diperhatikan bahawa maksud BeforeSwapDelta ialah Hook yang mengenakan caj harus mengembalikan nilai positif, manakala Hook yang mengembalikan token harus mengembalikan nilai negatif. Pembangun mudah tersilap tanda; selain itu, hubungan antara specified dan unspecified bergantung kepada params.zeroForOne dan tanda positif/negatif amountSpecified, dan sekiranya penulisan sedikit sahaja salah, token akan tertukar.
PoolManager akan menambahkan specifiedDelta yang dikembalikan oleh beforeSwap secara langsung ke amountToSwap:

Baris ini mengandungi makna penting: Hook boleh menahan jumlah swap. Apabila hookDeltaSpecified sama dengan -params.amountSpecified, amountToSwap secara langsung menjadi sifar, yang bererti Hook mengambil alih sepenuhnya swap ini—ini dikenali sebagai Async Hook atau Custom Curve Hook.
Async Hook adalah salah satu corak reka bentuk paling berisiko dalam v4: ia pada dasarnya menggantikan logik swap Uniswap dengan logik Hook sendiri. Jika Hook mengandungi kelemahan atau secara intrinsik jahat, dana pengguna tidak lagi dilindungi oleh logik penentuan harga asli Uniswap, tetapi bergantung terutamanya pada kebetulan pelaksanaan Hook itu sendiri.
3.3 Settlemen Delta dan NonzeroDeltaCount
delta yang dikembalikan oleh beforeSwap dan afterSwap tidak akan memicu pemindahan dana secara langsung, tetapi dicatat ke dalam buku besar dalaman PoolManager:

Apabila delta terkumulatif bagi satu token berubah daripada sifar kepada bukan sifar, NonzeroDeltaCount bertambah; apabila kembali kepada sifar, ia berkurang. Seperti yang dinyatakan dalam 2.1, apabila unlock() selesai, jika NonzeroDeltaCount != 0, keseluruhan transaksi akan revert.
Hook menyeimbangkan deltanya melalui dua tindakan: settle() (mentransfer ke PoolManager) dan take() (mengambil dari PoolManager):

Mekanisme ini membawa makna keselamatan yang jelas: semua pihak akhirnya harus menutup akaun mereka. Namun, ia hanya menjamin “kekalan akaun”, bukan “kebenaran akaun”. Jika Hook mengembalikan delta yang dirakamkan secara jahat dalam beforeSwap, PoolManager akan setia mencatatkan delta tersebut, selagi ia akhirnya ditutup semasa settle, transaksi dianggap berjaya—walaupun ini bermakna Hook boleh memalsukan status perniagaan untuk membuat sistem percaya bahawa penyerang memiliki hak atas aset tertentu, sementara PoolManager tidak dapat mengenal pasti kesalahan perniagaan semacam ini.
Peristiwa keselamatan Cork Protocol sebelum ini berlaku kerana terdapat kelemahan pada Hook-nya, dan ia telah melalui audit oleh empat syarikat audit sebelum diserang. Semasa tinjauan selepas kejadian, kami mendapati:
Dari empat audit, tiga daripadanya tidak merangkumi kontrak CorkHook
- Satu-satunya pihak yang mengaudit CorkHook mengenal pasti sebahagian masalah kod dan mengemukakan cadangan penambahbaikan, tetapi tidak merangkumi sepenuhnya masalah kawalan akses
- Sebuah pihak audit lain dalam laporan mereka secara jelas mencadangkan: “an interesting follow-up engagement would be to prove the invariants for the CorkHook functions that are being invoked by different components verified within the scope of this engagement”. Cadangan ini mempunyai kejituan yang tinggi dari sudut tinjauan pasca-kejadian.
Ini mengungkapkan satu celah audit baru di era v4 Hook: pertumbuhan eksponensial dalam kompleksiti protokol menjadikan penentuan lingkup itu sendiri sebagai keputusan keselamatan. Rantai interaksi antara Hook dan kontrak lain protokol sangat panjang; mengaudit kontrak Hook secara berasingan tidak cukup untuk mengesan isu kombinasi antar-kontrak; sebaliknya, mengaudit kontrak sekitarnya sambil mengecualikan Hook dari lingkup akan melewatkan permukaan serangan terbesar di era v4.
4. Refleksi
Dengan membandingkan mekanisme protokol dan serangan Cork, beberapa poin utama model keselamatan v4 Hook boleh diringkaskan:
(1) Jika fungsi panggilan balik Hook bergantung pada konteks panggilan yang disediakan oleh PoolManager, ia harus secara eksplisit dibatasi agar hanya boleh dipanggil oleh PoolManager. BaseHook tidak akan melakukan ini untuk pembangun, ini adalah perangkap reka bentuk yang paling bertentangan dengan pengalaman audit kontrak biasa dalam v4.
(2) Hubungan pengikatan Hook dengan kolam tidak dibatasi oleh PoolManager. Pembangun mesti mengimplementasikan senarai putih kolam atau pengikatan kolam tunggal secara sendiri dalam beforeInitialize.
(3) Kebenaran alamat Hook mesti serasi secara ketat dengan pelaksanaan fungsi. Alamat yang dikira sepatutnya mengandungi semua kebenaran yang mungkin diperluas pada masa depan secara awal.
(4) Hook Kurva Asinkron / Khusus pada dasarnya adalah implementasi swap yang sepenuhnya disesuaikan. Ia tidak memiliki perlindungan apa pun pada tingkat protokol Uniswap dan mesti diaudit mengikut piawaian "kontrak kewangan sepenuhnya autonomi".
(5) Delta akuntansi "konservasi" tidak sama dengan "betul". NonzeroDeltaCount == 0 hanya menjamin buku besar seimbang pada akhirnya, bukan menjamin kandungan buku besar tidak dimanipulasi secara jahat
(6) Kebingungan jenis token antar pasaran merupakan permukaan serangan baharu di era v4. Apabila protokol membenarkan pengguna mencipta pasaran, pengesahan semantik token adalah wajib dan tidak boleh bergantung semata-mata pada pemeriksaan antaramuka.
Setiap Hook adalah domain kepercayaan yang berasingan, dan keselamatan setiap kolam ditentukan oleh Hook yang terikat padanya. Kompleksiti audit keselamatan Hook oleh itu bukan lagi “mengaudit satu kod”, tetapi “mengaudit satu protokol sampingan yang lengkap” — perubahan ini bermakna peningkatan metodologi bagi pihak projek dan pihak audit.

