Uniswap v4'ün ana ağda başlatılmasından sonra, Hook mekanizması DeFi'nin en çok dikkat çeken yeniliklerinden biri haline geldi. Base zincirindeki memecoin fırlatma platformu Flaunch, Hook'u sabit ön satış fiyatı ve otomatik halka arz mekanizması oluşturmak için kullandı; likidite protokolü Bunni v2, Hook'u programlanabilir likidite ve yeniden çarpan modeli oluşturmak için kullandı; bu yıl SATO, uPEG (Unipeg), Slonks gibi Hook oyunlarına dayalı tokenlar kısa sürede onlarca kat artış kaydetti.
Hook ekosisteminin gelişimine paralel olarak, Hook implementasyon hatalarına yönelik saldırılar da belirgin bir şekilde artıyor. Bu makale, Uniswap v4 Hook mekanizmasından başlayarak temel çağrı yığınını adım adım analiz ederek projelerin olası açıkları anlamasına yardımcı olacak.
Uniswap v4 Hook Güvenliği
1. Giriş
Uniswap v4'ün v3'e göre en önemli mimari değişikliği, Hook (bağlantı noktası) mekanizmasının eklenmesidir: Geliştiriciler, özelleştirilmiş sözleşmeleri likidite havuzlarının yaşam döngüsü olaylarına bağlayabilir ve swap, likidite ekleme/çıkarma, başlatma gibi noktalarda herhangi bir mantık ekleyebilir.
v4'ün birkaç ana değişikliği şunlardır:
Tekil desen: Tüm havuzların durumu, her havuz için ayrı ayrı sözleşme dağıtmak yerine, tek bir PoolManager sözleşmesi tarafından merkezi olarak yönetilir.
- Anlık muhasebe: İşlem sırasında ortaya çıkan geçici bakiye değişiklikleri yalnızca geçici depolamada kaydedilir ve işlem sonunda tek seferde hesaplanır.
- Hook mekanizması: Her havuz, bir Hook sözleşmesiyle bağlanabilir; PoolManager, kritik noktalarda (beforeInitialize, beforeSwap, afterAddLiquidity vb.) bu sözleşmeyi geri çağırır.
- Bağlantı noktası değiştirilemez: Havuz başlatıldığında, ilişkilendirilen bağlantı noktası adresi kalıcı olarak sabitlenir (havuzla ilişkilendirilen bağlantı noktası adresi değiştirilemez, ancak bağlantı noktası sözleşmesinin kendisinin yükseltilebilir olup olmadığı, uygulama şekline bağlıdır).

v3 döneminde geliştiriciler yalnızca Uniswap protokolüne güvenmek zorundaydı; ancak v4 döneminde her havuzun güvenliği, bağlı olduğu Hook'a bağlıdır. Hook, AMM'yi sabit bir finansal primitive'den programlanabilir bir finansal altyapıya dönüştürür, ancak güvenlik modeli "protokol seviyesinden" "havuz seviyesine" parçalanır.
2. Tutucu Yapı
2.1 PoolManager ve açma/geri çağırma modeli
v4'nin çekirdek sözleşmesi, tekil bir PoolManager'dır. Havuzun durumunda yapılan herhangi bir değişiklik (swap, likidite ekleme/çıkarma), önce PoolManager.unlock() çağrısı yapılarak tek seferlik geri çağırma izni alınmalı ve ardından unlockCallback() içinde ilgili işlem tamamlanmalıdır. Süreç sonunda, PoolManager hesapların dengede olup olmadığını doğrular:

NonzeroDeltaCount != 0 olduğunda doğrudan revert edilir; bu, v4 flash muhasebenin temel kısıtlamasıdır. Herhangi bir Hook, işlem sırasında hesap dengesizliğini geçici olarak oluşturabilir, ancak işlem sonlanmadan önce kendi kendine settle etmelidir; aksi takdirde tüm işlem geri alınır.
Her havuz, hooks alanını içeren bir PoolKey yapısıyla benzersiz şekilde tanımlanır:

PoolId, keccak256(PoolKey) ile hesaplanır, bu nedenle farklı hooks adresleri farklı havuzlar oluşturur. Bu aynı zamanda, PoolManager'ın bir hook adresinin daha önce başka bir havuzda kullanılıp kullanılmadığını doğrulamadığı anlamına gelir; aynı hook sözleşmesi birden fazla havuz tarafından aynı anda bağlanabilir.

2.2 Bağlantı izni kodlaması adres içinde
v4'ün bir doğrudan zıtlığı şudur: Hook'un izinleri, sözleşmenin içindeki bir değişken tarafından değil, Hook sözleşmesinin dağıtım adresi tarafından belirlenir.
PoolManager, Hook adresinin son 14 bitini kontrol ederek bu Hook'un belirli bir yaşam döngüsü noktasında çağrılması gerekip gerekmediğini belirler:

Örneğin, BEFORE_SWAP_FLAG = 1 << 7. Hook adresinin 7. biti 1 ise, PoolManager swap öncesi bu Hook'un beforeSwap() fonksiyonunu çağırır; aksi halde, Hook sözleşmesi beforeSwap() u uygulasa bile, PoolManager tarafından asla çağrılmaz.
Bu, Hook'un dağıtımı sırasında CREATE2 + salt ile adresin hesaplanmasını ve hedef izne tamamen uygun bir düşük adresin oluşturulmasını gerektirir. Uniswap, bu amaç için HookMiner aracını sunmaktadır:

Yetki bitleri ile fonksiyon uygulamaları uyumsuz olduğunda iki tür sorun oluşur:
(1) Bir hook fonksiyonu uygulandı, ancak adres uygun izin bitleriyle kodlanmadı — PoolManager bu fonksiyonu asla çağırmaz, mantık boşuna yapılmıştır.
(2) Adres, belirli bir izin bitini kodlamıştır, ancak hook ilgili işlevi uygulamamıştır—PoolManager, geri çağırma sırasında revert olabilir, bu da DOS'u gerçekleştirebilir veya dönüş değerini doğrulama başarısız olabilir ve ilgili işlemlerin yürütülmesini engelleyebilir.
Bu aynı zamanda Hook'un yükseltilmesi için doğal bir engeldir: Hook, bir aracılıkla yükseltilebilirse, dağıtım adresi yükseltme sırasında değişmez, bu nedenle yükseltmeden sonra yalnızca mevcut Hook işlevlerinin uygulamalarını değiştirebilir, yeni Hook türleri ekleyemezsiniz. Gelecekteki genişletilebilirliği korumak için, tüm olası izin bitleri ilk dağıtımda önceden ayarlanmalıdır.
2.3 BaseHook ve Genellikle Gözden Kaçırılan Bir Erişim Kontrol Tuzağı
Uniswap v4 periphery, eski sürümde sağlanan BaseHook soyut sözleşmesini, geliştiricilerin özelleştirilmiş Hook uygulamak için miras almasını sağlar. BaseHook'un önemli bir işlevi, unlockCallback() fonksiyonuna onlyPoolManager dekoratörünü sağlamaktır:

Ancak — burada çok kolayca gözden kaçırılan bir tasarım tuzağı var — BaseHook'un erken sürümleri yalnızca unlockCallback için onlyPoolManager ekledi, diğer hook geri çağırma fonksiyonları (beforeSwap, afterSwap, beforeAddLiquidity vb.) için hiçbir koruma sağlamadı. Bu fonksiyonların erişim denetimi, Hook geliştiricisi tarafından açıkça eklenmelidir.
3. Hook Yaşam Döngüsü Kod İncelemesi
Bir exact-input swap örneğiyle, kullanıcı tarafından işlem başlatılmasından varışa kadar olan tam çağrı yığını analiz edilmiştir.
3.1 Havuz başlatma ve Hook bağlama
Herkes PoolManager.initialize() yöntemini çağırarak yeni bir havuz oluşturabilir:

isValidHookAddress, adres yetki bitini ve fee alanının uyumluluğunu yalnızca doğrular; Hook'un başka bir havuzda zaten kullanılıp kullanılmadığını veya Hook'un bu PoolKey'yi kabul etmek isteyip istemediğini doğrulamaz. Hook, beforeInitialize içinde beyaz liste veya tek havuz bağlantısı mantığı eklenmeden tasarlanırsa, herhangi biri aynı Hook'u kullanan ancak token çifti herhangi bir şey olan yeni bir havuz oluşturabilir ve Hook'un sonraki tüm geri aramalarını tetikleyebilir.
3.2 beforeSwap ve BeforeSwapDelta
Swap işlem giriş noktası PoolManager.swap() fonksiyonudur ve temel swap mantığını çalıştırmadan önce Hooks.beforeSwap() çağırır:

beforeSwap'in dönüş değeri bir üçlüdür (bytes4, BeforeSwapDelta, uint24):
- bytes4: IHooks.beforeSwap.selector ile eşit olmalıdır, aksi halde PoolManager doğrudan revert eder
- BeforeSwapDelta: Hook, bu takas sırasında belirtilen token ve belirtilmeyen token için delta ayarlaması yapar
- uint24: Dinamik LP ücret oranı kapsama değeri (havuz dinamik ücret etkinleştirildiğinde geçerlidir)
BeforeSwapDelta, int256'in bir takma adıdır; üst 128 bit, belirtilen token'ın delta'sını (kullanıcının belirttiği miktardaki token) temsil eder, alt 128 bit ise belirtilmeyen token'ın delta'sını temsil eder:

Önemli not: BeforeSwapDelta'nın anlamı, Hook'un ücreti alması durumunda pozitif, token iade etmesi durumunda negatif değer döndürmesidir. Geliştiriciler işaretleri kolayca karıştırabilir; aynı zamanda, specified ve unspecified arasındaki ilişki, params.zeroForOne ve amountSpecified'in işaretlerine bağlıdır; küçük bir yazım hatası token karışıklığına neden olabilir.
PoolManager, beforeSwap tarafından döndürülen specifiedDelta değerini doğrudan amountToSwap üzerine ekleyecektir:

Bu satır, Hook'un swap miktarını tutabileceğini ima eder. HookDeltaSpecified, -params.amountSpecified'e eşit olduğunda, amountToSwap doğrudan sıfırlanır, bu da Hook'un bu swap'i tamamen ele aldığını gösterir—buna Async Hook veya Custom Curve Hook denir.
Async Hook, v4'teki en yüksek riskli tasarım desenidir: Temel olarak, Hook'un kendi mantığını Uniswap'in swap mantığıyla değiştirir. Hook'ta bir açığa veya kendi kendine zararlı bir yapı varsa, kullanıcı fonları Uniswap'in yerel fiyatlandırma mantığından değil, Hook'un kendi implementasyonunun doğruluğuna bağlı kalır.
3.3 Delta Settlemesi ve NonzeroDeltaCount
beforeSwap ve afterSwap tarafından döndürülen delta, hemen bir transfer tetiklemeyecek, bunun yerine PoolManager'ın dahili defterine kaydedilecektir:

Bir token'un birikmiş delta'sı sıfırdan sıfır olmayan bir değere değiştirdiğinde NonzeroDeltaCount artırılır; sıfıra döndüğünde azaltılır. 2.1'de belirtildiği gibi, unlock() işlevi bittiğinde NonzeroDeltaCount != 0 ise, tüm işlem geri alınır.
Hook, delta'sini dengelemek için settle() (PoolManager'e transfer etme) ve take() (PoolManager'dan çekme) işlemlerini gerçekleştirir:

Bu mekanizma tarafından sağlanan güvenlik anlamları nettir: herkes sonunda hesaplarını kapatmak zorundadır. Ancak bu yalnızca “hesapların korunması” garantisi verir, “hesapların doğru olması” garantisi vermez. Eğer Hook, beforeSwap sırasında kötü niyetli bir delta döndürürse, PoolManager bu delta’ya sadık kalır ve hesap kapatıldığında işlem başarılı sayılır—hatta Hook, sahte iş durumları oluşturarak sistemin, saldırganın bazı varlık haklarına sahip olduğunu yanlışlıkla düşünebilmesine neden olsa bile, PoolManager bu iş düzeyindeki hataları tanıyamaz.
Daha önce Cork Protokolü'nün güvenlik olayı, Hook'unda bir açığın bulunmasından kaynaklanmıştı ve saldırıdan önce dört adet denetim şirketinden denetimden geçmişti. Olayın ardından yapılan incelemede şunu fark ettik:
Dört denetimden üçünün kapsamı CorkHook sözleşmesini içermiyor.
CorkHook'ın tek denetimini yapan kurum, bazı kod sorunlarını tespit edip iyileştirme önerilerinde bulundu, ancak erişim kontrol sorunlarını tamamen kapsamadı.
Başka bir denetim firması raporunda açıkça şunu öneriyor: “Bu kapsamda doğrulanmış farklı bileşenler tarafından çağrılan CorkHook işlevlerinin sabitlerini kanıtlamak, ilginç bir takip etkinliği olurdu.” Bu öneri, olay sonrası değerlendirme açısından oldukça hedefe yönelik görünmektedir.
Bu, v4 Hook dönemi için yeni bir denetim boşluğunu ortaya koyuyor: protokol karmaşıklığının patlayıcı artışının, kapsam belirleme işleminin kendisini bir güvenlik kararı haline getirmesi. Hook ile protokolün diğer sözleşmeleri arasındaki etkileşim zinciri çok uzundur; Hook sözleşmesini yalnızca incelemek, sözleşmeler arası kombinasyon sorunlarını tespit etmek için yeterli değildir; tersine, çevresel sözleşmeleri inceleyip Hook'u kapsam dışına almak, v4 dönemi için en büyük saldırı yüzeyini kaçırmaktır.
4. Yansıma
Protokol mekanizması ve Cork saldırısını yan yana incelediğinde, v4 Hook güvenlik modelinin birkaç temel noktası ortaya çıkar:
(1) Hook geri çağırma işlevi, PoolManager tarafından sağlanan çağırma bağlamına bağlıysa, bunun yalnızca PoolManager tarafından çağrılmasını açıkça sınırlamalısınız. BaseHook, bu işi geliştirici adına yapmaz; bu, v4 ile genel sözleşme denetimi deneyimi arasındaki en yaygın çatışma noktasıdır.
(2) Bağlantı, PoolManager tarafından sınırlanmaz. Geliştiriciler, havuz beyaz listesi veya tek havuz bağlantısını beforeInitialize içinde kendileri uygulamalıdır.
(3) Bağlantı adresinin izin bitleri, fonksiyon uygulamasıyla tam olarak uyumlu olmalıdır. Hesaplanan adres, gelecekteki olası genişlemeler için tüm izin bitlerini önceden içermelidir.
(4) Async / Özel Eğri Bağlantısı, tamamen özelleştirilmiş bir takas uygulamasıdır. Uniswap protokol seviyesinde herhangi bir korumaya sahip değildir ve "tamamen özerk finansal sözleşme" standartlarına göre denetlenmelidir.
(5) Delta muhasebesindeki “korumalılık” “doğruluk” anlamına gelmez. NonzeroDeltaCount == 0, hesabın son durumunun dengeli olduğunu garanti eder, ancak hesabın içeriğinin kasıtlı olarak manipüle edilmediğini garanti etmez.
(6) Çapraz pazar token türü karıştırması, v4 dönemi için yeni bir saldırı yüzeyidir. Protokol kullanıcıların pazar oluşturmasına izin verdiğinde, tokenlar için semantik doğrulama zorunludur ve yalnızca arayüz denetimine dayalı olunmamalıdır.
Her Hook, bağımsız bir güvenirlik alanıdır ve her havuzun güvenliği, bağlı olduğu Hook tarafından belirlenir. Bu nedenle Hook güvenlik denetiminin karmaşıklığı, “bir kod dosyasını denetlemek”ten “tam bir alt protokolü denetlemek” haline gelir—bu değişiklik, hem proje sahipleri hem de denetim firmaları için yöntemsel bir yükselişi ifade eder.

