कंत्राटाच्या आकाराच्या मर्यादेला सामोरे जाण्यासाठी कंत्राटे लहान करणे
मर्यादा का आहे?
22 नोव्हेंबर, 2016 (opens in a new tab) रोजी Spurious Dragon हार्ड-फोर्कने EIP-170 (opens in a new tab) सादर केले, ज्याने 24.576 kb ची स्मार्ट कंत्राट आकार मर्यादा जोडली. एक Solidity विकसक म्हणून तुमच्यासाठी याचा अर्थ असा आहे की, जेव्हा तुम्ही तुमच्या कंत्राटात अधिकाधिक कार्यक्षमता जोडता, तेव्हा एका क्षणी तुम्ही मर्यादेपर्यंत पोहोचता आणि तैनात करताना तुम्हाला खालील त्रुटी दिसेल:
चेतावणी: कंत्राट कोडचा आकार 24576 बाइट्सपेक्षा जास्त आहे (Spurious Dragon मध्ये सादर केलेली मर्यादा). हे कंत्राट कदाचित Mainnet वर तैनात करण्यायोग्य नसेल. ऑप्टिमायझर सक्षम करण्याचा विचार करा (कमी "runs" मूल्यांसह!), रिव्हर्ट स्ट्रिंग बंद करा, किंवा लायब्ररी वापरा.
ही मर्यादा डिनायल-ऑफ-सर्व्हिस (DOS) हल्ले रोखण्यासाठी सुरू करण्यात आली. गॅसच्या दृष्टीने कंत्राटाला कोणताही कॉल तुलनेने स्वस्त असतो. तथापि, Ethereum नोड्ससाठी कंत्राट कॉलचा परिणाम, कॉल केलेल्या कंत्राट कोडच्या आकारावर अवलंबून (डिस्कवरून कोड वाचणे, कोडवर पूर्व-प्रक्रिया करणे, मर्केल प्रूफमध्ये डेटा जोडणे) अवाजवी प्रमाणात वाढतो. जेव्हाही अशी परिस्थिती येते की आक्रमणकर्त्याला इतरांसाठी खूप काम निर्माण करण्यासाठी कमी संसाधनांची आवश्यकता असते, तेव्हा 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, "फक्त या कंत्राटाचा मालक हे कार्य कॉल करू शकतो");1require(msg.sender == owner, "OW1");त्रुटी संदेशांऐवजी कस्टम त्रुटी वापरा
कस्टम त्रुटी Solidity 0.8.4 (opens in a new tab) मध्ये सादर करण्यात आल्या आहेत. तुमच्या कंत्राटांचा आकार कमी करण्याचा हा एक उत्तम मार्ग आहे, कारण ते निवडक (selectors) म्हणून ABI-एनकोड केलेले आहेत (जसे कार्ये असतात).
1error Unauthorized();23if (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}45function _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}45function _get(address addr1, address addr2) private view returns(address,address) {6 return (addr1, addr2);7}कार्ये आणि व्हेरिएबल्ससाठी योग्य दृश्यमानता घोषित करा
- फक्त बाहेरून कॉल केली जाणारी कार्ये किंवा व्हेरिएबल्स? त्यांना
publicऐवजीexternalम्हणून घोषित करा. - फक्त कंत्राटातूनच कॉल केली जाणारी कार्ये किंवा व्हेरिएबल्स? त्यांना
publicऐवजीprivateकिंवाinternalम्हणून घोषित करा.
मॉडिफायर्स काढा
मॉडिफायर्स, विशेषतः जेव्हा ते जास्त प्रमाणात वापरले जातात, तेव्हा कंत्राटाच्या आकारावर महत्त्वपूर्ण परिणाम करू शकतात. त्यांना काढण्याचा विचार करा आणि त्याऐवजी कार्ये वापरा.
1modifier checkStuff() {}23function doSomething() checkStuff {}1function checkStuff() private {}23function doSomething() { checkStuff(); }या टिप्स तुम्हाला कंत्राटाचा आकार लक्षणीयरीत्या कमी करण्यास मदत करतील. पुन्हा एकदा, मी हे पुरेसे जोर देऊन सांगू इच्छितो की, सर्वात मोठ्या परिणामासाठी शक्य असल्यास नेहमी कंत्राटे विभागण्यावर लक्ष केंद्रित करा.
पृष्ठ अखेरचे अद्यतन: २५ फेब्रुवारी, २०२६