ইউনিসোয়াপ v4-এর মেইননেট লঞ্চের পর, হুক মেকানিজম DeFi-এর সবচেয়ে আকর্ষণীয় উদ্ভাবনগুলির মধ্যে একটি হয়ে উঠেছে। বেস চেইনের memecoin লঞ্চ প্ল্যাটফর্ম ফ্লঞ্চ হুক ব্যবহার করে ফিক্সড প্রিসেল প্রাইস এবং অটোমেটিক লিকুইডেশন লঞ্চ মেকানিজম বাস্তবায়ন করেছে; লিকুইডিটি প্রোটোকল বুনি v2 প্রোগ্রামযোগ্য লিকুইডিটি এবং রি-মর্টগেজ মডেল তৈরি করতে হুক ব্যবহার করেছে; এই বছর SATO, uPEG (Unipeg), Slonks-এর মতো হুক-ভিত্তিক টোকেনগুলি সংক্ষিপ্ত সময়ের মধ্যে দশগুণেরও বেশি বৃদ্ধি অর্জন করেছে।
হুক ইকোসিস্টেমের প্রস্পের অন্য দিকে, হুক বাস্তবায়নের ত্রুটির উপর ভিত্তি করে আক্রমণগুলি উল্লেখযোগ্যভাবে বৃদ্ধি পাচ্ছে। এই নিবন্ধটি Uniswap v4-এর হুক মেকানিজম থেকে শুরু করে, এর কোর কল স্ট্যাক ধাপে ধাপে বিশ্লেষণ করবে, যাতে প্রকল্পগুলি এতে সম্ভাব্য ভালোবাসা বুঝতে পারে।
ইউনিসৱ্যাপ v4 হুক নিরাপদ
1. পরিচিতি
Uniswap v4-এর v3 এর চেয়ে সবচেয়ে উল্লেখযোগ্য আর্কিটেকচারাল পরিবর্তন হল Hook (হুক) মেকানিজমের চালু: যা ডেভেলপারদের লিকুইডিটি পুলের লাইফসাইকেল ইভেন্টগুলির উপর কাস্টম কনট্রাক্ট যুক্ত করতে এবং swap, লিকুইডিটি যোগ/বিয়োগ, ইনিশিয়ালাইজেশন ইত্যাদি পয়েন্টগুলিতে যেকোনো লজিক ইনজেক্ট করতে সক্ষম করে।
v4-এর কয়েকটি প্রধান পরিবর্তন নিম্নরূপ:
- সিঙ্গলটন প্যাটার্ন: প্রতিটি পুলের অবস্থা একটি একক PoolManager চুক্তি দ্বারা কেন্দ্রীয়ভাবে পরিচালিত হয়, প্রতিটি পুলের জন্য আলাদা চুক্তি বাস্তবায়ন করা হয় না
- ফ্ল্যাশ অ্যাকাউন্টিং: ট্রেডিং প্রক্রিয়ার মধ্যবর্তী ব্যালেন্স পরিবর্তনগুলি শুধুমাত্র transient storage-এ রেকর্ড করা হয় এবং শুধুমাত্র ট্রেডিং শেষে এককভাবে সেটেল করা হয়
- হুক মেকানিজম: প্রতিটি পুল একটি হুক কন্ট্রাক্টের সাথে বাঁধা যেতে পারে, যেখানে PoolManager কী পয়েন্টগুলিতে (beforeInitialize, beforeSwap, afterAddLiquidity ইত্যাদি) এই কন্ট্রাক্টকে কলব্যাক করে।
- হুক পরিবর্তনযোগ্য নয়: পুল ইনিশিয়ালাইজেশন সম্পন্ন হওয়ার পরে, বাঁধা হুক ঠিকানা চিরস্থায়ীভাবে স্থির হয়ে যায় (পুলের সাথে বাঁধা হুক ঠিকানা পরিবর্তন করা যায় না, তবে হুক স্মার্ট কনট্রাক্টটি আপগ্রেডযোগ্য কিনা তা এর বাস্তবায়নের উপর নির্ভর করে)

v3 পর্যায়ে, ডেভেলপারদের শুধুমাত্র Uniswap প্রোটোকলের উপর বিশ্বাস রাখতে হত; কিন্তু v4 পর্যায়ে, প্রতিটি পুলের নিরাপত্তা এটির সাথে বাঁধা Hook-এর উপর নির্ভর করে। Hook-এর মাধ্যমে AMM একটি নির্দিষ্ট আর্থিক প্রিমিটিভ থেকে একটি প্রোগ্রামযোগ্য আর্থিক অবকাঠামোতে পরিণত হয়েছে, কিন্তু নিরাপত্তা মডেলটি “প্রোটোকল-লেভেল” থেকে “পুল-লেভেল”-এ বিভক্ত হয়েছে।
2. হুক আর্কিটেকচার
2.1 পুলম্যানেজার এবং আনলক/কলব্যাক মডেল
v4-এর কোর স্মার্ট কন্ট্রাক্ট হল একক পুল ম্যানেজার। পুলের অবস্থা পরিবর্তনের যেকোনো অপারেশন (সুইপ, তরলতা যোগ বা বিয়োগ) এর আগে PoolManager.unlock() কল করতে হবে, একবারের জন্য কলব্যাক অনুমতি পাওয়ার পর unlockCallback() এর মধ্যে সংশ্লিষ্ট কাজ সম্পন্ন করতে হবে। পুরো প্রক্রিয়া শেষে, PoolManager লেজ ভারসাম্যপূর্ণ কিনা তা যাচাই করবে:

যখন NonzeroDeltaCount != 0, তখন সরাসরি revert করুন, এটি v4 flash accounting-এর মূল সীমাবদ্ধতা। যেকোনো Hook এক্সিকিউশনের সময় অস্থায়ীভাবে অ্যাকাউন্টকে অসমতা করতে পারে, কিন্তু ট্রানজেকশন শেষ হওয়ার আগে এটিকে নিজেই settle করতে হবে, অন্যথায় সম্পূর্ণ ট্রানজেকশনটি রিভার্ট হয়ে যাবে।
প্রতিটি পুল PoolKey স্ট্রাকচার দ্বারা অনন্যভাবে চিহ্নিত হয়, যার মধ্যে hooks ফিল্ড রয়েছে:

PoolId কেকাক256(PoolKey) দ্বারা গণনা করা হয়, তাই ভিন্ন ভিন্ন hooks ঠিকানা ভিন্ন ভিন্ন পুল তৈরি করে। এটি একইসাথে বোঝায় যে, PoolManager কোনো Hook ঠিকানার সত্যতা যাচাই করে না যে এটি আগে অন্য কোনো পুলের জন্য ব্যবহৃত হয়েছে কিনা; একই Hook কন্ট্রাক্টকে একাধিক পুল একসাথে বাঁধা যেতে পারে।

2.2 হুক পারমিশন বিট এনকোডিং ঠিকানায়
v4-এর একটি অপ্রত্যাশিত ডিজাইন হল: হুকের অনুমতি কোনো কন্ট্রাক্টের ভিতরের কোনো ভেরিয়েবল দ্বারা নির্ধারিত হয় না, বরং হুক কন্ট্রাক্টের ডিপ্লয় ঠিকানা দ্বারা নির্ধারিত হয়।
PoolManager হুক ঠিকানার নিম্ন 14 বিট পরীক্ষা করে বুঝতে পারে যে কোন জীবনচক্রের বিন্দুতে এই হুকটি কল করা হবে কিনা:

উদাহরণস্বরূপ, BEFORE_SWAP_FLAG = 1 << 7। যদি Hook ঠিকানার 7ম বিট 1 হয়, তবে PoolManager swap-এর আগে এই Hook-এর beforeSwap() কল করবে; অন্যথায়, যদিও Hook স্মার্ট চুক্তিটি beforeSwap() বাস্তবায়ন করে, তবুও PoolManager কখনই এটিকে কল করবে না।
এর অর্থ হলো Hook-কে ডিপ্লয় করার সময় CREATE2 + salt ব্যবহার করে একটি ঠিকানা গণনা করতে হবে, যাতে লো-বিটগুলি লক্ষ্য অনুমতির সাথে সম্পূর্ণরূপে মিলে যায়। এই উদ্দেশ্যে Uniswap অফিসিয়াল HookMiner টুল প্রদান করেছে:

অনুমতি বিট এবং ফাংশন বাস্তবায়নের মধ্যে অসামঞ্জস্যতা হলে দুটি ধরনের সমস্যা দেখা দেয়:
(1) একটি hook ফাংশন বাস্তবায়িত হয়েছে, কিন্তু ঠিকানাটি সংশ্লিষ্ট অনুমতি বিটগুলির সাথে এনকোড করা হয়নি—PoolManager কখনই এই ফাংশনটি কল করবে না, যার ফলে যুক্তি অর্থহীন হয়ে যায়
(2) ঠিকানাটি কোনো অনুমতি বিট কোড করেছে, কিন্তু hook সংশ্লিষ্ট ফাংশনটি বাস্তবায়ন করেনি—PoolManager কলব্যাকের সময় revert হতে পারে, যা DOS বাস্তবায়ন বা রিটার্ন ভ্যালু যাচাইয়ের ব্যর্থতা ঘটায়, ফলে সংশ্লিষ্ট অপারেশনগুলি বাস্তবায়িত হয় না।
এটি হুক আপগ্রেডের একটি প্রাকৃতিক বাধা হিসেবেও কাজ করে: যদি হুক প্রক্সির মাধ্যমে আপগ্রেড করা যায়, তাহলে ডিপ্লয়মেন্ট ঠিকানা আপগ্রেডের সময় অপরিবর্তিত থাকে, ফলে আপগ্রেডের পরে শুধুমাত্র বিদ্যমান হুক ফাংশনগুলির বাস্তবায়নই পরিবর্তন করা যায়, নতুন হুক টাইপ যোগ করা যায় না। ভবিষ্যতের বিস্তারের জন্য প্রস্তুতি রাখতে, প্রাথমিক ডিপ্লয়মেন্টের সময় সমস্ত সম্ভাব্য অনুমতি বিটগুলি পূর্বেই সংরক্ষণ করা প্রয়োজন।
2.3 বেসহুক এবং একটি সাধারণত উপেক্ষিত অ্যাক্সেস নিয়ন্ত্রণ ফাঁদ
ইউনিসৱ্যাপ v4 পেরিফারির পুরনো সংস্করণে প্রদানকৃত BaseHook অ্যাবস্ট্রাক্ট কনট্রাক্টটি ডেভেলপাররা ব্যবহার করতে পারেন কাস্টম হুক বাস্তবায়নের জন্য। BaseHook-এর একটি গুরুত্বপূর্ণ ভূমিকা হল unlockCallback() ফাংশনের জন্য onlyPoolManager মডিফায়ার প্রদান করা:

কিন্তু—এখানে একটি অত্যন্ত উপেক্ষিত ডিজাইন ফাঁদ রয়েছে—BaseHook-এর প্রাথমিক সংস্করণগুলিতে শুধুমাত্র unlockCallback-এর জন্য onlyPoolManager যোগ করা হয়েছিল, অন্যান্য hook কলব্যাকগুলির (beforeSwap, afterSwap, beforeAddLiquidity ইত্যাদি) জন্য কোনও সুরক্ষা ছিল না। এই ফাংশনগুলির অ্যাক্সেস নিয়ন্ত্রণ অবশ্যই Hook ডেভেলপারদের দ্বারা স্বয়ং প্রকাশ্যে যোগ করা হতে হবে।
3. হুক লাইফসাইকেল কোড রিভিউ
একটি exact-input swap-এর উদাহরণ হিসেবে, ব্যবহারকারী ট্রেড শুরু করে থেকে সেটেলমেন্ট পর্যন্ত সম্পূর্ণ কল স্ট্যাক নিচে বিশ্লেষণ করা হল।
3.1 পুল ইনিশিয়ালাইজেশন এবং হুক বাইনিং
যেকোনো ব্যক্তি PoolManager.initialize() কল করে নতুন পুল তৈরি করতে পারেন:

isValidHookAddress শুধুমাত্র ঠিকানার অনুমতি বিট এবং ফি ক্ষেত্রের সামঞ্জস্যতা যাচাই করে, এটি যাচাই করে না যে Hook অন্য কোনো পুলে ইতিমধ্যে ব্যবহার করা হয়েছে কিনা বা Hook এই PoolKey গ্রহণ করতে চায় কিনা। যদি Hook-এর ডিজাইনে beforeInitialize-এ সাদা তালিকা বা একক পুল বাইন্ডিং লজিক যোগ না করা হয়, তবে যেকোনো ব্যক্তি একই Hook ব্যবহার করে, কিন্তু token pair-এর যেকোনো সংমিশ্রণ সহ একটি নতুন পুল তৈরি করতে পারবে এবং Hook-এর পরবর্তী সমস্ত কলব্যাক ট্রিগার করতে পারবে।
3.2 beforeSwap এবং BeforeSwapDelta
সুইপ প্রক্রিয়ার প্রবেশদ্বার হল PoolManager.swap(), যা মূল সুইপ লজিক সম্পাদনের আগে Hooks.beforeSwap() কল করে:

beforeSwap-এর রিটার্ন মান একটি ট্রাইপল (bytes4, BeforeSwapDelta, uint24):
- bytes4: IHooks.beforeSwap.selector এর সমান হতে হবে, অন্যথায় PoolManager সরাসরি revert করবে
- BeforeSwapDelta: এই swap-এর জন্য নির্দিষ্ট টোকেন এবং অনির্দিষ্ট টোকেনের জন্য ডেল্টা সমায়োজন
- uint24: ডায়নামিক এলপি ফি রেট কভার ভ্যালু (শুধুমাত্র পুলটি ডায়নামিক ফি সক্ষম করলে প্রযোজ্য)
BeforeSwapDelta হল int256-এর একটি অ্যালিয়াস, যার উচ্চ 128 বিট হল specified token-এর ডেল্টা (অর্থাৎ ব্যবহারকারী দ্বারা নির্দিষ্ট পরিমাণের টোকেন), এবং নিম্ন 128 বিট হল unspecified token-এর ডেল্টা:

দ্রষ্টব্য, BeforeSwapDelta-এর অর্থ হলো, হুক ফি আদায় করলে ধনাত্মক মান ফেরত দিতে হবে এবং হুক টোকেন ফেরত দিলে ঋণাত্মক মান ফেরত দিতে হবে। ডেভেলপাররা সহজেই সাইনটি ভুল করে ফেলেন; এছাড়াও, specified এবং unspecified-এর সংশ্লিষ্টতা params.zeroForOne এবং amountSpecified-এর সাইনের উপর নির্ভর করে, যা সামান্য ভুলেই token-এর অবস্থান বিকৃত করে দিতে পারে।
PoolManager সরাসরি beforeSwap দ্বারা ফেরত প্রাপ্ত specifiedDelta কে amountToSwap-এর সাথে যোগ করে:

এই লাইনটি একটি গুরুত্বপূর্ণ অর্থ নিহিত রাখে: হুক সোয়াপ পরিমাণ ধরে রাখতে পারে। যখন hookDeltaSpecified -params.amountSpecified-এর সমান হয়, তখন amountToSwap সরাসরি শূন্য হয়ে যায়, যা হুকের দ্বারা এই সোয়াপটি সম্পূর্ণরূপে নিয়ন্ত্রিত হওয়ার সমতুল্য—এটিকে Async Hook বা Custom Curve Hook বলা হয়।
Async Hook হল v4-এর সবচেয়ে ঝুঁকিপূর্ণ ডিজাইন প্যাটার্ন: এটি মূলত Uniswap-এর swap লজিককে Hook-এর নিজস্ব লজিক দিয়ে প্রতিস্থাপন করে। যদি Hook-এ কোনো দুর্বলতা থাকে বা এটি নিজেই ক্ষতিকর হয়, তাহলে ব্যবহারকারীর ফান্ড Uniswap-এর মূল মূল্যনির্ধারণ লজিকের দ্বারা সীমাবদ্ধ থাকবে না, বরং মূলত Hook-এর নিজস্ব বাস্তবায়নের সঠিকতার উপর নির্ভরশীল হবে।
3.3 ডেল্টা সেটেলমেন্ট এবং NonzeroDeltaCount
beforeSwap এবং afterSwap দ্বারা ফেরত পাঠানো ডেল্টা তাৎক্ষণিকভাবে ট্রান্সফার ট্রিগার করে না, বরং এটি PoolManager-এর অভ্যন্তরীণ বইয়ে রেকর্ড করা হয়:

যখন কোনো টোকেনের কুমুলেটিভ ডেল্টা শূন্য থেকে অশূন্যে পরিবর্তিত হয়, তখন NonzeroDeltaCount বৃদ্ধি পায়; যখন এটি আবার শূন্যে ফিরে আসে, তখন এটি হ্রাস পায়। 2.1 অনুসারে, unlock() শেষে যদি NonzeroDeltaCount != 0 হয়, তবে সম্পূর্ণ ট্রানজেকশন revert হবে।
হুক সেটেল() (পুলম্যানেজারে ট্রান্সফার করে) এবং টেক() (পুলম্যানেজার থেকে তুলে নিয়ে) দুটি ক্রিয়ার মাধ্যমে তার ডেল্টা ভারসাম্যপূর্ণ করে:

এই মেকানিজমটির নিরাপত্তা অর্থ পরিষ্কার: সবাইকে চূড়ান্তভাবে তাদের হিসাব শূন্য করতে হবে। কিন্তু এটি শুধুমাত্র “হিসাবের সংরক্ষণ” নিশ্চিত করে, “হিসাবের সঠিকতা” নয়। যদি Hook beforeSwap-এ একটি কৃত্রিমভাবে তৈরি করা ডেল্টা ফেরত দেয়, তবে PoolManager সেই ডেল্টা অনুযায়ী হিসাব রাখবে, যতক্ষণ চূড়ান্তভাবে settle করা হয়, ট্রানজেকশনটি সফল হবে—এমনকি যদি এটির অর্থ হয় যে Hook কৃত্রিম ব্যবসায়িক অবস্থা ব্যবহার করে সিস্টেমকে এই ধারণা দিতে পারে যে আক্রমণকারীর কাছে কিছু সম্পদের অধিকার রয়েছে, যখন PoolManager এই ব্যবসায়িক-স্তরের ভুলকে চিনতে পারে না।
আগের কর্ক প্রোটোকল সুরক্ষা ঘটনাটি তার হুকে দুর্বলতা থাকার কারণে ঘটেছিল, এবং আক্রমণের আগে এটি চারটি অডিট কোম্পানির দ্বারা অডিট করা হয়েছিল। ঘটনার পরে পুনর্বিশ্লেষণে আমরা দেখেছি:
চারটি অডিটের মধ্যে তিনটির স্কোপে কর্কহুক কন্ট্রাক্ট অন্তর্ভুক্ত নেই
কর্কহুকের একমাত্র অডিট করা প্রতিষ্ঠানটি কিছু কোড সমস্যা শনাক্ত করেছিল এবং উন্নতির প্রস্তাব জমা দিয়েছিল, তবে অ্যাক্সেস নিয়ন্ত্রণ সমস্যাগুলির সম্পূর্ণ কভারেজ করেনি।
একটি অন্যান্য অডিটর তাদের রিপোর্টে স্পষ্টভাবে সুপারিশ করেছে: “একটি আকর্ষণীয় ফলো-আপ এনগেজমেন্ট হবে এই এনগেজমেন্টের পরিসরে যাচাইকৃত বিভিন্ন কম্পোনেন্টগুলি দ্বারা কল করা CorkHook ফাংশনগুলির জন্য ইনভ্যারিয়ান্টস প্রমাণ করা।” এই সুপারিশটি পরবর্তী বিশ্লেষণের দৃষ্টিকোণ থেকে অত্যন্ত সংশ্লিষ্ট।
এটি v4 হুক যুগের একটি নতুন অডিট অন্ধবিন্দুকে প্রকাশ করে: প্রোটোকলের জটিলতার বিস্ফোরণ হওয়ায়, স্কোপ নির্ধারণ নিজেই একটি নিরাপত্তা সিদ্ধান্ত হয়ে উঠেছে। হুক এবং প্রোটোকলের অন্যান্য স্মার্ট কনট্রাক্টগুলির মধ্যে ইন্টারঅপারেশনের লিঙ্ক খুব দীর্ঘ; শুধুমাত্র হুক কনট্রাক্টটি অডিট করলে কনট্রাক্ট-পারস্পরিক কম্বিনেটরিয়াল সমস্যাগুলি শনাক্ত করা যায় না; অন্যদিকে, পাশের কনট্রাক্টগুলি অডিট করেও হুককে স্কোপের বাইরে রাখলে v4 যুগের সবচেয়ে বড় আক্রমণের পৃষ্ঠভাগটি এড়িয়ে যাওয়া যায়।
৪. প্রতিফলন
প্রোটোকল মেকানিজম এবং Cork আক্রমণকে পাশাপাশি পুনর্বিশ্লেষণ করে, v4 Hook সিকিউরিটি মডেলের কয়েকটি মূল বিষয় উপস্থাপন করা যায়:
(1) যদি Hook কলব্যাক ফাংশনটি PoolManager দ্বারা প্রদানকৃত কল কনটেক্সটের উপর নির্ভর করে, তাহলে এটিকে স্পষ্টভাবে শুধুমাত্র PoolManager দ্বারা কল করার জন্য সীমাবদ্ধ করা উচিত। BaseHook ডেভেলপারদের জন্য এটি করবে না, এটি v4 এবং সাধারণ স্মার্ট কনট্রাক্ট অডিটের অভিজ্ঞতার মধ্যে সবচেয়ে বেশি সংঘর্ষের কারণ হওয়া ডিজাইন ফাঁদ।
(2) হুক এবং পুলের বাইন্ডিং সম্পর্ক PoolManager দ্বারা সীমাবদ্ধ নয়। ডেভেলপারদের অবশ্যই beforeInitialize-এ পুল ওয়াইটলিস্ট বা একক পুল বাইন্ডিং বাস্তবায়ন করতে হবে।
(3) হুক ঠিকানার অনুমতি বিটগুলি ফাংশন বাস্তবায়নের সাথে কঠোরভাবে মেলা উচিত। গণনা করা ঠিকানাটিতে ভবিষ্যতে সম্ভাব্য প্রসারিত সমস্ত অনুমতি বিটগুলি পূর্বেই অন্তর্ভুক্ত করা উচিত।
(4) অ্যাসিঙ্ক / কাস্টম কার্ভ হুক মূলত সম্পূর্ণরূপে কাস্টমাইজড সুইপ বাস্তবায়ন। এটি কোনও ইউনিসুয়াপ প্রোটোকল লেভেলের সুরক্ষা বহন করে না, এবং এটিকে “সম্পূর্ণ স্বায়ত্তশাসিত ফাইন্যান্সিয়াল কনট্রাক্ট” মানদণ্ডে অডিট করা প্রয়োজন।
(5) ডেল্টা হিসাবের “সংরক্ষণ” মানে হল “সঠিক” নয়। NonzeroDeltaCount == 0 শুধুমাত্র এটি নিশ্চিত করে যে হিসাব চূড়ান্তভাবে ভারসাম্যপূর্ণ, কিন্তু এটি নিশ্চিত করে না যে হিসাবের বিষয়বস্তুকে কোনো দুষ্টুমির মাধ্যমে পরিবর্তন করা হয়নি।
(6) ক্রস-মার্কেট টোকেন টাইপ বিভ্রান্তি হল v4 যুগের একটি নতুন আক্রমণের ক্ষেত্র। যখন প্রোটোকল ব্যবহারকারীদের মার্কেট তৈরি করার অনুমতি দেয়, তখন টোকেনের সেমান্টিক যাচাই অপরিহার্য, শুধুমাত্র ইন্টারফেস চেকের উপর নির্ভর করা যাবে না।
প্রতিটি হুক একটি স্বতন্ত্র বিশ্বাসের ডোমেইন, এবং প্রতিটি পুলের নিরাপত্তা এর সংযুক্ত হুক দ্বারা নির্ধারিত হয়। এই পরিবর্তনের ফলে হুক নিরাপত্তা অডিটের জটিলতা এখন “একটি কোড অডিট” নয়, বরং “একটি সম্পূর্ণ সাব-প্রোটোকল অডিট” — এই পরিবর্তনটি প্রকল্প প্রতিষ্ঠান এবং অডিটরদের উভয়ের জন্য পদ্ধতিগত উন্নতির অর্থবহ।

