noDelegateCall modifier एक कॉन्ट्रैक्ट में delegatecall को भेजे जाने से रोकता है। हम सबसे पहले इसे पूरा करने की कार्यप्रणाली दिखाएंगे और फिर बाद में इस बात पर चर्चा करेंगे कि कोई ऐसा क्यों करना चाहेगा।
नीचे, हमने मूल रूप से Uniswap V3’s noDelegateCall द्वारा बनाए गए noDelegateCall modifier को सरल बनाया है:
contract NoDelegateCallExample {
address immutable private originalAddress;
constructor() {
originalAddress = address(this);
}
modifier noDelegateCall() {
require(address(this) == originalAddress, "no delegate call");
_;
}
}
execution environment के आधार पर address(this) बदल जाएगा, लेकिन originalAddress हमेशा उस कोड का deployed address रहेगा जो noDelegateCall का उपयोग करता है। इसलिए यदि कोई अन्य कॉन्ट्रैक्ट noDelegateCall modifier वाले किसी फ़ंक्शन पर delegatecall करता है, तो address(this), originalAddress के बराबर नहीं होगा और transaction revert हो जाएगा। यह अत्यंत महत्वपूर्ण है कि original address एक immutable वेरिएबल हो, अन्यथा delegatecall जारी करने वाला कॉन्ट्रैक्ट रणनीतिक रूप से उस स्लॉट में noDelegateCall का उपयोग करने वाले कॉन्ट्रैक्ट का address डाल सकता है और require स्टेटमेंट को बायपास कर सकता है।
noDelegateCall की टेस्टिंग
नीचे हम noDelegateCall को टेस्ट करने के लिए कोड प्रदान कर रहे हैं।
contract noDelegateCall {
address immutable private originalAddress;
constructor() {
originalAddress = address(this);
}
modifier noDelegateCall() {
require(address(this) == originalAddress, "no delegate call");
_;
}
}
contract A is noDelegateCall {
uint256 public x;
function increment() noDelegateCall public {
x++;
}
}
contract B {
uint256 public x; // this variable does not increment
function tryDelegatecall(address a) external {
(bool ok, ) = a.delegatecall(
abi.encodeWithSignature("increment()")
);// ignore ok
}
}
कॉन्ट्रैक्ट B, A को एक delegatecall करता है जो noDelegateCall modifier का उपयोग कर रहा है। यद्यपि B.tryDelegatecall का transaction revert नहीं होगा क्योंकि low level call की रिटर्न वैल्यू को इग्नोर कर दिया जाता है, फिर भी स्टोरेज वेरिएबल x इन्क्रीमेंट (increment) नहीं होगा क्योंकि delegatecall के कॉन्टेक्स्ट के अंदर transaction revert हो जाता है।
noDelegateCall का उद्देश्य
Uniswap V2 इतिहास में सबसे अधिक फोर्क (forked) किया गया DeFi प्रोटोकॉल है। Uniswap V2 प्रोटोकॉल को उन प्रोजेक्ट्स से प्रतिस्पर्धा का सामना करना पड़ा जो सोर्स कोड को लाइन-दर-लाइन कॉपी कर रहे थे और नए प्रोडक्ट को Uniswap V2 के विकल्प के रूप में मार्केट कर रहे थे, और कभी-कभी एयरड्रॉप (airdrops) प्रदान करके यूज़र्स को प्रोत्साहित कर रहे थे।
ऐसा होने से रोकने के लिए, Uniswap टीम ने Uniswap V3 को Business Source License के तहत लाइसेंस दिया — कोई भी कोड कॉपी कर सकता है लेकिन इसका उपयोग व्यावसायिक उद्देश्यों (commercial purposes) के लिए तब तक नहीं किया जा सकता था जब तक कि अप्रैल 2023 में लाइसेंस समाप्त (expire) नहीं हो गया।
हालाँकि, यदि कोई Uniswap V3 की “कॉपी” बनाना चाहता था, तो वे बस एक clone proxy बना सकते थे और इसे Uniswap V3 के एक इंस्टेंस (instance) पर पॉइंट कर सकते थे — फिर उस स्मार्ट कॉन्ट्रैक्ट को Uniswap V3 के विकल्प के रूप में मार्केट कर सकते थे। noDelegateCall modifier ऐसा होने से रोकता है।
मूल रूप से 11 मई को प्रकाशित