यह लेख Solidity में यह निर्धारित करने के लिए तीन विधियों का वर्णन करता है कि क्या कोई address एक smart contract है:
- जांचें कि क्या
msg.sender == tx.originहै। यह एक अनुशंसित (recommended) तरीका नहीं है, लेकिन क्योंकि कई smart contracts इसका उपयोग करते हैं, इसलिए हम जानकारी पूरी करने के लिए इस विधि पर चर्चा करते हैं। - दूसरा (और अनुशंसित तरीका)
code.lengthका उपयोग करके address के bytecode आकार को मापना है। इस दृष्टिकोण में अभी भी कुछ सीमाएं हैं जिनका devs को समाधान करना पड़ता है। - तीसरा
codehashका उपयोग करना है और इसकी अनुशंसा नहीं की जाती है क्योंकि इसमें अतिरिक्त जटिलता के साथcode.lengthजैसी ही सीमाएं हैं।
हम इस ट्यूटोरियल में प्रत्येक विधि पर चर्चा करते हैं। अंत में, हम आपकी समझ का परीक्षण करने के लिए कुछ Solidity पहेलियाँ (puzzles) प्रदान करते हैं।
विधि 1: यह पता लगाने के लिए msg.sender == tx.origin का उपयोग करना कि क्या कोई address एक smart contract है
Global variable tx.origin वह वॉलेट है जिसने transaction शुरू किया था, जबकि msg.sender वह address है जिसने smart contract को कॉल किया था। यदि कोई वॉलेट सीधे किसी smart contract को कॉल करता है, तो tx.origin, msg.sender के समान होगा।
हालाँकि, मान लीजिए कि एक वॉलेट smart contract A को कॉल करता है जो फिर smart contract B को कॉल करता है।
contract B के दृष्टिकोण से, msg.sender, contract A है और वॉलेट tx.origin है। स्पष्ट रूप से, contract B के अंदर msg.sender, tx.origin के बराबर नहीं होगा। नीचे दिया गया आरेख (diagram) इस संबंध को दर्शाता है:

यह जांच कर कि क्या msg.sender == tx.origin है, smart contract यह पता लगा सकता है कि आने वाली कॉल किसी smart contract से है या वॉलेट से।
require(msg.sender == tx.origin) एक antipattern है
Account abstraction को अपनाए जाने के साथ ही एक smart contract का वॉलेट के रूप में उपयोग तेजी से लोकप्रिय हो रहा है, जैसे कि ERC-4337 और multisignature वॉलेट (जैसे Gnosis Safe) के लिए smart contracts का उपयोग करना।
किसी smart contract में require(msg.sender == tx.origin) जोड़ने का अर्थ है कि account abstraction वॉलेट और multisignature वॉलेट उस smart contract के साथ इंटरैक्ट नहीं कर सकते हैं।
यह तकनीक केवल यह जांच सकती है कि msg.sender एक कॉन्ट्रैक्ट है या नहीं। यह किसी भी यादृच्छिक (arbitrary) address का परीक्षण नहीं कर सकती है।
विधि 2: code.length के साथ यह पता लगाना कि क्या कोई address एक smart contract है
किसी smart contract के लिए यह जांचने का अनुशंसित तरीका कि क्या कोई address एक smart contract है, उसके bytecode के आकार को मापना है।
यदि किसी address में bytecode है, तो वह एक smart contract है।
निम्नलिखित कोड पर विचार करें:
contract TestAddress {
function test(
address target
)
public
view
returns (bool isContract) {
if (target.code.length == 0) {
isContract = false;
} else {
isContract = true;
}
}
}
यद्यपि सभी smart contracts में bytecode होता है और वॉलेट addresses में नहीं होता है, लेकिन कुछ बातों (“gotchas”) को ध्यान में रखना चाहिए:
- एक address जिसमें अभी कोई bytecode नहीं है, भविष्य में वहां bytecode आ सकता है यदि उस address पर कोई smart contract डिप्लॉय (deploy) किया जाता है।
- यह पता लगाने के लिए कि क्या कोई incoming (आने वाली) कॉल smart contract से है,
msg.sender.code.length == 0का उपयोग करना कोई विश्वसनीय तरीका नहीं है। यदि कोई smart contract कंस्ट्रक्टर (constructor) से कॉल करता है, तो उसने अभी तक अपना bytecode डिप्लॉय नहीं किया है औरmsg.sender.code.length, 0 होगा। जब constructor निष्पादित (execute) हो रहा होता है, तब तक smart contract का bytecode डिप्लॉय नहीं हुआ होता है। इसलिए,code.lengthशून्य (zero) होगा। - EVM चेन्स जो
selfdestructका समर्थन करती हैं, वहां अतीत मेंtargetपर एक smart contract हो सकता है, लेकिन वह smart contract नष्ट (self destructed) हो गया हो।
code.length के साथ msg.sender का परीक्षण करना
यदि कोई वॉलेट किसी कॉन्ट्रैक्ट को कॉल करता है, तो यह सुनिश्चित है कि msg.sender.code.length, 0 होगा।
यदि कोई कॉन्ट्रैक्ट किसी अन्य कॉन्ट्रैक्ट को कॉल करता है, तो msg.sender.code.length 0 होगा यदि उसे constructor से कॉल किया गया है, और यदि इसे किसी अन्य smart contract function से कॉल किया गया है तो यह गैर-शून्य (non-zero) होगा।
code.length के साथ एक address (msg.sender नहीं) का परीक्षण करना
यदि कोई smart contract किसी target पर address(target).code.length परीक्षण का उपयोग करता है, और वह target एक smart contract है, तो यह सुनिश्चित है कि address(target).code.length गैर-शून्य (non-zero) होगा।
डेवलपर (dev) को यह ध्यान रखना चाहिए कि यदि कॉन्ट्रैक्ट self destruct हो जाता है (यह मानते हुए कि चेन selfdestruct का समर्थन करती है और कॉन्ट्रैक्ट में selfdestruct होने की क्षमता है), तो code.length बाद में 0 हो सकता है।
यदि कोई smart contract किसी target पर address(target).code.length परीक्षण का उपयोग करता है, और वह target एक वॉलेट है, तो यह सुनिश्चित है कि address(target.code.length), 0 होगा।
हालाँकि, सिर्फ इसलिए कि address(target).code.length अभी 0 है, इसका मतलब यह नहीं है कि यह हमेशा शून्य रहेगा। वहां बाद में एक smart contract डिप्लॉय किया जा सकता है। मान लीजिए कि मैं आपको एक address देता हूं। आप इसे अभी address(target).code.length के साथ मापते हैं और यह 0 लौटाता है। वह माप उस क्षण सटीक होगा जब आपने इसे मापा था, लेकिन यह संभव है कि मैं बाद की तारीख में उस address (target) पर एक कॉन्ट्रैक्ट डिप्लॉय कर दूं और यदि आप इसे फिर से address(target).code.length के साथ मापते हैं तो यह गैर-शून्य (non-zero) होगा।
यह जांचने के लिए सामान्य उपयोग का मामला (use case) कि क्या कोई address एक smart contract है
यदि किसी टोकन को ऐसे smart contract में ट्रांसफर किया जाता है जिसमें टोकन को बाहर भेजने की कार्यक्षमता (functionality) नहीं है, तो वे टोकन हमेशा के लिए उसी कॉन्ट्रैक्ट के पास फंस जाएंगे।
इसलिए, कुछ टोकन मानक (token standards) ऐसा होने से रोकने के लिए कदम उठाते हैं।
उदाहरण के लिए, safeTransferFrom फ़ंक्शन के साथ ERC-721 यह जांच करेगा कि जिस address पर टोकन ट्रांसफर किए जा रहे हैं, क्या वह एक smart contract है (code.length ट्रिक का उपयोग करके)।

(मूल कोड)
यदि यह है, तो वे यह पूछने के लिए कॉन्ट्रैक्ट पर एक विशेष फ़ंक्शन को कॉल करने का प्रयास करते हैं कि क्या कॉन्ट्रैक्ट ERC-721 टोकन का समर्थन करता है। यदि वह फ़ंक्शन वहां नहीं है, तो उसे पता चल जाता है कि टोकन फंस जाएंगे और वह ट्रांसफर को ब्लॉक कर देता है।
विधि 3: Codehash यह परीक्षण करने का एक खराब तरीका है कि क्या कोई address एक कॉन्ट्रैक्ट है
codehash किसी address के bytecode का keccak256 लौटाता है।
इसका व्यवहार (behavior) निम्नलिखित है:
- यदि address में कोई Ethereum बैलेंस नहीं है और कोई bytecode नहीं है, तो हैश करने के लिए कुछ भी नहीं है और यह
bytes32(0)लौटाता है। - यदि address में Ethereum बैलेंस है लेकिन कोई bytecode नहीं है, तो यह खाली डेटा
keccak256("")काkeccak256लौटाता है जो0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470के बराबर है। - यदि address में bytecode है (बैलेंस की परवाह किए बिना) तो यह कॉन्ट्रैक्ट के bytecode का
keccak256लौटाता है।
codehash के सटीक व्यवहार का वर्णन codehash पर Etherum क्लाइंट टिप्पणियों (comments) में किया गया है।
कुछ कॉन्ट्रैक्ट्स ने गलती से codehash का उपयोग यह परीक्षण करने के लिए किया है कि किसी address में bytecode है या नहीं। यह एक अच्छा विचार नहीं है क्योंकि यदि हम बिना bytecode वाले कॉन्ट्रैक्ट पर codehash का उपयोग करते हैं, तो हमें या तो bytes32(0) या keccak256("") वापस मिलेगा और हमें दोनों संभावनाओं की जांच करनी होगी।
यदि किसी address a में कोई bytecode नहीं है और कोई ईथर (ether) नहीं है, तो address(a).codehash, bytes32(0) या सभी शून्य (zero) वाले 32 बाइट्स लौटाता है। हालाँकि, यदि कोई उस address पर Ether ट्रांसफर करता है, तो एक वॉलेट और smart contract न होने के बावजूद, codehash, keccak256("") बन जाएगा।
आप codehash का व्यवहार देखने के लिए Remix में नीचे दिए गए कोड का परीक्षण कर सकते हैं:
contract TestHash {
function getHash()
external
view
returns (bytes32) {
// random address with no balance or code
return address(101).codehash;// returns 0x000...000
}
function hashOfNonEmptyWallet()
external
view
returns (bytes32) {
// tx.origin has a non-zero ether balance
return tx.origin.codehash;
// returns a non-zero hash
}
// observe that `keccakNil` and `hashOfNonEmptyWallet`
// return the same value
function keccakNil()
external
pure
returns (bytes32) {
return keccak256("");
}
// Deploy SomeTestContract and put its address in
// codeHashOtherContract to test it
function codeHashOtherContract(
address _a
)
external
view
returns (bool) {
// returns true because the codehash
// of another contract
// is equal to the `keccak256` of its bytecode
return a.codehash == keccak256(a.code);
}
}
contract SomeTestContract {
function someFunction()
external
pure
returns (uint256) {
return 5;
}
}
bytecode की उपस्थिति की जांच करके यह निर्धारित करने के लिए codehash और code.length दोनों का उपयोग किया जा सकता है कि क्या कोई address एक smart contract है; हालाँकि, codehash bytecode को हैश करके अनावश्यक जटिलता (complexity) का परिचय देता है, जिसके परिणामस्वरूप तीन संभावित परिणाम मिलते हैं, जबकि हमें केवल यह जांचने की आवश्यकता है कि code.length शून्य है या नहीं।
code.length की जांच करना कहीं अधिक सरल है।
आपके ज्ञान का परीक्षण करने के लिए पहेली (Puzzle)
पहेली 1
क्या आप ऐसा कर सकते हैं कि जब puzzle को कॉल किया जाए तो निम्नलिखित कॉन्ट्रैक्ट true लौटाए और रिवर्ट (revert) न हो?
contract Puzzle {
function puzzle()
external
view
returns (bool success) {
require(msg.sender != tx.origin);
require(msg.sender.code.length == 0);
success = true;
}
}
पहेली 2
tx.origin.code.length को क्या लौटाना चाहिए? क्या यह हमेशा समान मान (value) लौटाता है?
RareSkills के साथ और अधिक जानें
यदि आप Solidity में नए हैं तो हमारा Solidity course देखें। यदि आपके पास पहले से कुछ अनुभव है तो हमारा Solidity bootcamp देखें। पढ़ने के लिए धन्यवाद!
मूल रूप से 5 अप्रैल, 2024 को प्रकाशित