मुख्य सामग्री पर जाएँ

अनुबंध आकार सीमा से निपटने के लिए अनुबंधों का आकार छोटा करना

Solidity
स्मार्ट अनुबंध
स्टोरेज
माध्यमिक
मार्कस वास
26 जून 2020
7 मिनट का पठन

एक सीमा क्यों है?

22 नवंबर, 2016 (opens in a new tab) को Spurious Dragon हार्ड-फोर्क ने EIP-170 (opens in a new tab) पेश किया जिसने 24.576 kb की स्मार्ट अनुबंध आकार सीमा जोड़ी। एक सॉलिडिटी डेवलपर के रूप में आपके लिए इसका मतलब है कि जब आप अपने अनुबंध में अधिक से अधिक कार्यक्षमता जोड़ते हैं, तो किसी बिंदु पर आप सीमा तक पहुंच जाएंगे और डिप्लॉय करते समय आपको यह त्रुटि दिखाई देगी:

चेतावनी: अनुबंध कोड का आकार 24576 बाइट्स से अधिक है (Spurious Dragon में शुरू की गई एक सीमा)। यह अनुबंध मेननेट पर डिप्लॉय करने योग्य नहीं हो सकता है। ऑप्टिमाइज़र को सक्षम करने पर विचार करें (कम "रन" मान के साथ!), रिवर्ट स्ट्रिंग्स को बंद करें, या लाइब्रेरी का उपयोग करें।

यह सीमा डिनायल-ऑफ-सर्विस (DOS) हमलों को रोकने के लिए शुरू की गई थी। गैस के हिसाब से किसी अनुबंध पर कोई भी कॉल अपेक्षाकृत सस्ता है। हालांकि, एथेरियम नोड्स के लिए एक अनुबंध कॉल का प्रभाव बुलाए गए अनुबंध कोड के आकार के आधार पर असंगत रूप से बढ़ जाता है (डिस्क से कोड पढ़ना, कोड को पूर्व-संसाधित करना, मर्कल प्रूफ में डेटा जोड़ना)। जब भी आपके पास ऐसी स्थिति होती है जहां हमलावर को दूसरों के लिए बहुत काम करने के लिए कुछ संसाधनों की आवश्यकता होती है, तो आपको DOS हमलों की संभावना मिलती है।

मूल रूप से यह कम समस्या थी क्योंकि एक प्राकृतिक अनुबंध आकार सीमा ब्लॉक गैस सीमा है। जाहिर है, एक अनुबंध को एक लेनदेन के भीतर डिप्लॉय किया जाना चाहिए जिसमें अनुबंध का पूरा बाइटकोड होता है। यदि आप उस एक लेनदेन को एक ब्लॉक में शामिल करते हैं, तो आप उस सारी गैस का उपयोग कर सकते हैं, लेकिन यह अनंत नहीं है। लंदन अपग्रेड के बाद से, ब्लॉक गैस सीमा नेटवर्क मांग के आधार पर 15M और 30M यूनिट के बीच भिन्न हो सकी है।

निम्नलिखित में हम उनके संभावित प्रभाव के अनुसार क्रमबद्ध कुछ तरीकों को देखेंगे। इसे वजन घटाने के संदर्भ में सोचें। किसी के लिए अपने लक्ष्य वजन (हमारे मामले में 24kb) तक पहुंचने के लिए सबसे अच्छी रणनीति पहले बड़े प्रभाव वाले तरीकों पर ध्यान केंद्रित करना है। ज्यादातर मामलों में सिर्फ अपने आहार को ठीक करने से आप वहां पहुंच जाएंगे, लेकिन कभी-कभी आपको थोड़ा और चाहिए होता है। फिर आप कुछ व्यायाम (मध्यम प्रभाव) या यहां तक कि पूरक (छोटा प्रभाव) भी जोड़ सकते हैं।

बड़ा प्रभाव

अपने अनुबंधों को अलग करें

यह हमेशा आपका पहला तरीका होना चाहिए। आप अनुबंध को कई छोटे अनुबंधों में कैसे अलग कर सकते हैं? यह आम तौर पर आपको अपने अनुबंधों के लिए एक अच्छी वास्तुकला के साथ आने के लिए मजबूर करता है। कोड पठनीयता के दृष्टिकोण से छोटे अनुबंध हमेशा पसंद किए जाते हैं। अनुबंधों को विभाजित करने के लिए, अपने आप से पूछें:

  • कौन से फंक्शन एक साथ हैं? फंक्शन का प्रत्येक सेट अपने स्वयं के अनुबंध में सबसे अच्छा हो सकता है।
  • कौन से फंक्शन को अनुबंध की स्टेट पढ़ने या स्टेट के केवल एक विशिष्ट उपसमूह की आवश्यकता नहीं होती है?
  • क्या आप भंडारण और कार्यक्षमता को विभाजित कर सकते हैं?

लाइब्रेरी

भंडारण से कार्यक्षमता कोड को दूर ले जाने का एक सरल तरीका लाइब्रेरी (opens in a new tab) का उपयोग करना है। लाइब्रेरी फंक्शन को आंतरिक के रूप में घोषित न करें क्योंकि वे संकलन के दौरान सीधे अनुबंध में जोड़े जाएंगे (opens in a new tab)। लेकिन यदि आप सार्वजनिक फंक्शन का उपयोग करते हैं, तो वे वास्तव में एक अलग लाइब्रेरी अनुबंध में होंगे। लाइब्रेरी के उपयोग को और अधिक सुविधाजनक बनाने के लिए using for (opens in a new tab) पर विचार करें।

प्रॉक्सी

एक अधिक उन्नत रणनीति एक प्रॉक्सी प्रणाली होगी। लाइब्रेरी बैक में DELEGATECALL का उपयोग करती हैं जो केवल कॉलिंग अनुबंध की स्टेट के साथ दूसरे अनुबंध के फंक्शन को निष्पादित करता है। प्रॉक्सी सिस्टम के बारे में अधिक जानने के लिए यह ब्लॉग पोस्ट (opens in a new tab) देखें। वे आपको अधिक कार्यक्षमता देते हैं, उदाहरण के लिए, वे अपग्रेडेबिलिटी को सक्षम करते हैं, लेकिन वे बहुत अधिक जटिलता भी जोड़ते हैं। मैं केवल अनुबंध के आकार को कम करने के लिए उन्हें नहीं जोड़ूंगा जब तक कि किसी भी कारण से यह आपका एकमात्र विकल्प न हो।

मध्यम प्रभाव

फंक्शन हटाएं

यह स्पष्ट होना चाहिए। फंक्शन एक अनुबंध के आकार को काफी बढ़ा देते हैं।

  • बाहरी: अक्सर हम सुविधा के कारणों से बहुत सारे व्यू फंक्शन जोड़ते हैं। यह तब तक पूरी तरह से ठीक है जब तक आप आकार की सीमा तक नहीं पहुंच जाते। तब आप बिल्कुल ज़रूरी फंक्शन के अलावा बाकी सभी को हटाने के बारे में सोचना चाहेंगे।
  • आंतरिक: आप आंतरिक/निजी फंक्शन भी हटा सकते हैं और जब तक फंक्शन को केवल एक बार कॉल किया जाता है, तब तक कोड को इनलाइन कर सकते हैं।

अतिरिक्त वेरिएबल से बचें

1function get(uint id) returns (address,address) {
2 MyStruct memory myStruct = myStructs[id];
3 return (myStruct.addr1, myStruct.addr2);
4}
1function get(uint id) returns (address,address) {
2 return (myStructs[id].addr1, myStructs[id].addr2);
3}

इस तरह के एक साधारण बदलाव से 0.28kb का अंतर आता है। संभावना है कि आप अपने अनुबंधों में कई समान स्थितियां पा सकते हैं और वे वास्तव में महत्वपूर्ण मात्रा में जुड़ सकती हैं।

त्रुटि संदेश छोटा करें

लंबे रिवर्ट संदेश और विशेष रूप से कई अलग-अलग रिवर्ट संदेश अनुबंध को फुला सकते हैं। इसके बजाय छोटे त्रुटि कोड का उपयोग करें और उन्हें अपने अनुबंध में डीकोड करें। एक लंबा संदेश बहुत छोटा हो सकता है:

1require(msg.sender == owner, "Only the owner of this contract can call this function");
1require(msg.sender == owner, "OW1");

त्रुटि संदेशों के बजाय कस्टम त्रुटियों का उपयोग करें

सॉलिडिटी 0.8.4 (opens in a new tab) में कस्टम त्रुटियों को पेश किया गया है। वे आपके अनुबंधों के आकार को कम करने का एक शानदार तरीका हैं, क्योंकि वे ABI-एन्कोडेड चयनकर्ताओं के रूप में होते हैं (जैसे फंक्शन होते हैं)।

1error Unauthorized();
2
3if (msg.sender != owner) {
4 revert Unauthorized();
5}

ऑप्टिमाइज़र में कम रन मान पर विचार करें

आप ऑप्टिमाइज़र सेटिंग्स भी बदल सकते हैं। 200 का डिफ़ॉल्ट मान का मतलब है कि यह बाइटकोड को अनुकूलित करने की कोशिश कर रहा है जैसे कि एक फ़ंक्शन को 200 बार कॉल किया जाता है। यदि आप इसे 1 में बदलते हैं, तो आप मूल रूप से ऑप्टिमाइज़र को प्रत्येक फ़ंक्शन को केवल एक बार चलाने के मामले के लिए अनुकूलित करने के लिए कहते हैं। केवल एक बार चलने के लिए एक अनुकूलित फ़ंक्शन का मतलब है कि यह स्वयं डिप्लॉयमेंट के लिए अनुकूलित है। ध्यान रखें कि यह फंक्शन चलाने के लिए गैस लागत को बढ़ाता है, इसलिए आप ऐसा नहीं करना चाह सकते हैं।

छोटा प्रभाव

फंक्शन को स्ट्रक्चर पास करने से बचें

यदि आप ABIEncoderV2 (opens in a new tab) का उपयोग कर रहे हैं, तो यह किसी फ़ंक्शन में स्ट्रक्चर पास न करने में मदद कर सकता है। पैरामीटर को स्ट्रक्चर के रूप में पास करने के बजाय, आवश्यक पैरामीटर सीधे पास करें। इस उदाहरण में हमने एक और 0.1kb बचाया।

1function get(uint id) returns (address,address) {
2 return _get(myStruct);
3}
4
5function _get(MyStruct memory myStruct) private view returns(address,address) {
6 return (myStruct.addr1, myStruct.addr2);
7}
1function get(uint id) returns(address,address) {
2 return _get(myStructs[id].addr1, myStructs[id].addr2);
3}
4
5function _get(address addr1, address addr2) private view returns(address,address) {
6 return (addr1, addr2);
7}

फंक्शन और वेरिएबल के लिए सही विज़िबिलिटी घोषित करें

  • फंक्शन या वेरिएबल जो केवल बाहर से कॉल किए जाते हैं? उन्हें public के बजाय external के रूप में घोषित करें।
  • फंक्शन या वेरिएबल केवल अनुबंध के भीतर से कॉल किए जाते हैं? उन्हें public के बजाय private या internal के रूप में घोषित करें।

मॉडिफ़ायर निकालें

मॉडिफ़ायर, विशेष रूप से जब गहन रूप से उपयोग किए जाते हैं, अनुबंध के आकार पर महत्वपूर्ण प्रभाव डाल सकते हैं। उन्हें हटाने और इसके बजाय फंक्शन का उपयोग करने पर विचार करें।

1modifier checkStuff() {}
2
3function doSomething() checkStuff {}
1function checkStuff() private {}
2
3function doSomething() { checkStuff(); }

ये टिप्स आपको अनुबंध के आकार को काफी कम करने में मदद करेंगे। एक बार फिर, मैं इस पर पर्याप्त जोर नहीं दे सकता, सबसे बड़े प्रभाव के लिए यदि संभव हो तो हमेशा अनुबंधों को विभाजित करने पर ध्यान केंद्रित करें।

पेज का अंतिम अपडेट: 25 फ़रवरी 2026

क्या यह ट्यूटोरियल सहायक था?