कॉन्ट्रॅक्ट आकार मर्यादेशी लढण्यासाठी कॉन्ट्रॅक्ट्सचा आकार कमी करणे
मर्यादा का आहे?
22 नोव्हेंबर 2016 (opens in a new tab) रोजी Spurious Dragon हार्ड-फोर्कने EIP-170 (opens in a new tab) सादर केले, ज्याने 24.576 kb ची स्मार्ट कॉन्ट्रॅक्ट आकार मर्यादा जोडली. एक Solidity डेव्हलपर म्हणून तुमच्यासाठी याचा अर्थ असा आहे की जेव्हा तुम्ही तुमच्या कॉन्ट्रॅक्टमध्ये अधिकाधिक कार्यक्षमता जोडता, तेव्हा एका टप्प्यावर तुम्ही मर्यादेपर्यंत पोहोचाल आणि प्रस्थापना (deploying) करताना तुम्हाला ही त्रुटी दिसेल:
Warning: Contract code size exceeds 24576 bytes (a limit introduced in Spurious Dragon). This contract may not be deployable on Mainnet. Consider enabling the optimizer (with a low "runs" value!), turning off revert strings, or using libraries.
ही मर्यादा डिनायल-ऑफ-सर्व्हिस (DOS) हल्ले रोखण्यासाठी लागू करण्यात आली होती. कॉन्ट्रॅक्टला केलेला कोणताही कॉल गॅसच्या दृष्टीने तुलनेने स्वस्त असतो. तथापि, इथेरियम नोड्ससाठी कॉन्ट्रॅक्ट कॉलचा प्रभाव कॉल केलेल्या कॉन्ट्रॅक्ट कोडच्या आकारानुसार विषम प्रमाणात वाढतो (डिस्कवरून कोड वाचणे, कोडवर पूर्व-प्रक्रिया करणे, मर्केल पुराव्यामध्ये डेटा जोडणे). जेव्हा जेव्हा अशी परिस्थिती असते जिथे हल्लेखोराला इतरांसाठी खूप काम निर्माण करण्यासाठी कमी संसाधनांची आवश्यकता असते, तेव्हा DOS हल्ल्यांची शक्यता निर्माण होते.
सुरुवातीला ही फार मोठी समस्या नव्हती कारण ब्लॉक गॅस मर्यादा ही एक नैसर्गिक कॉन्ट्रॅक्ट आकार मर्यादा आहे. साहजिकच, कॉन्ट्रॅक्ट अशा व्यवहारामध्ये प्रस्थापित केले जाणे आवश्यक आहे ज्यामध्ये कॉन्ट्रॅक्टचा सर्व बाइटकोड असतो. जर तुम्ही ब्लॉकमध्ये फक्त तो एकच व्यवहार समाविष्ट केला, तर तुम्ही तो सर्व गॅस वापरू शकता, परंतु तो अमर्याद नाही. London Upgrade पासून, नेटवर्कच्या मागणीनुसार ब्लॉक गॅस मर्यादा 15M आणि 30M युनिट्सच्या दरम्यान बदलू शकते.
खाली आपण त्यांच्या संभाव्य प्रभावानुसार क्रमवारी लावलेल्या काही पद्धती पाहणार आहोत. याचा विचार वजन कमी करण्याच्या संदर्भात करा. एखाद्या व्यक्तीने त्यांचे लक्ष्यित वजन (आपल्या बाबतीत 24kb) गाठण्यासाठी सर्वोत्तम रणनीती म्हणजे प्रथम मोठ्या प्रभावाच्या पद्धतींवर लक्ष केंद्रित करणे. बहुतेक प्रकरणांमध्ये फक्त तुमचा आहार सुधारल्याने तुम्ही तिथे पोहोचाल, परंतु काहीवेळा तुम्हाला थोड्या अधिक प्रयत्नांची आवश्यकता असते. मग तुम्ही काही व्यायाम (मध्यम प्रभाव) किंवा सप्लिमेंट्स (कमी प्रभाव) जोडू शकता.
मोठा प्रभाव
तुमचे कॉन्ट्रॅक्ट्स वेगळे करा
हा नेहमी तुमचा पहिला दृष्टिकोन असावा. तुम्ही कॉन्ट्रॅक्टला अनेक लहान कॉन्ट्रॅक्ट्समध्ये कसे विभागू शकता? हे सामान्यतः तुम्हाला तुमच्या कॉन्ट्रॅक्ट्ससाठी एक चांगले आर्किटेक्चर तयार करण्यास भाग पाडते. कोड वाचनीयतेच्या दृष्टिकोनातून लहान कॉन्ट्रॅक्ट्सना नेहमीच प्राधान्य दिले जाते. कॉन्ट्रॅक्ट्स विभाजित करण्यासाठी, स्वतःला विचारा:
- कोणती फंक्शन्स एकत्र येतात? फंक्शन्सचा प्रत्येक संच त्याच्या स्वतःच्या कॉन्ट्रॅक्टमध्ये सर्वोत्तम असू शकतो.
- कोणत्या फंक्शन्सना कॉन्ट्रॅक्ट स्थिती वाचण्याची आवश्यकता नाही किंवा फक्त स्थितीचा एक विशिष्ट उपसंच आवश्यक आहे?
- तुम्ही स्टोरेज आणि कार्यक्षमता विभाजित करू शकता का?
लायब्ररी
स्टोरेजपासून कार्यक्षमता कोड दूर हलवण्याचा एक सोपा मार्ग म्हणजे लायब्ररी (opens in a new tab) वापरणे. लायब्ररी फंक्शन्सना internal म्हणून घोषित करू नका कारण ती संकलनादरम्यान (compilation) थेट कॉन्ट्रॅक्टमध्ये जोडली जातील (opens in a new tab). परंतु जर तुम्ही public फंक्शन्स वापरत असाल, तर ती प्रत्यक्षात एका वेगळ्या लायब्ररी कॉन्ट्रॅक्टमध्ये असतील. लायब्ररीचा वापर अधिक सोयीस्कर करण्यासाठी using for (opens in a new tab) वापरण्याचा विचार करा.
प्रॉक्सी
प्रॉक्सी प्रणाली ही एक अधिक प्रगत रणनीती असेल. लायब्ररी बॅकग्राउंडमध्ये DELEGATECALL वापरतात जे कॉलिंग कॉन्ट्रॅक्टच्या स्थितीसह दुसऱ्या कॉन्ट्रॅक्टचे फंक्शन कार्यान्वित करते. प्रॉक्सी प्रणालींबद्दल अधिक जाणून घेण्यासाठी ही ब्लॉग पोस्ट (opens in a new tab) पहा. ते तुम्हाला अधिक कार्यक्षमता देतात, उदा., ते अपग्रेड करण्याची क्षमता सक्षम करतात, परंतु ते खूप गुंतागुंत देखील वाढवतात. कोणत्याही कारणास्तव तो तुमचा एकमेव पर्याय असल्याशिवाय मी फक्त कॉन्ट्रॅक्टचा आकार कमी करण्यासाठी ते जोडणार नाही.
मध्यम प्रभाव
फंक्शन्स काढून टाका
हे स्पष्ट असले पाहिजे. फंक्शन्स कॉन्ट्रॅक्टचा आकार बऱ्यापैकी वाढवतात.
- External: बऱ्याच वेळा आपण सोयीसाठी अनेक view फंक्शन्स जोडतो. जोपर्यंत तुम्ही आकार मर्यादेपर्यंत पोहोचत नाही तोपर्यंत ते अगदी ठीक आहे. त्यानंतर तुम्हाला अत्यंत आवश्यक असलेली फंक्शन्स वगळता इतर सर्व काढून टाकण्याचा खरोखर विचार करावा लागेल.
- Internal: तुम्ही internal/private फंक्शन्स देखील काढून टाकू शकता आणि जोपर्यंत फंक्शन फक्त एकदाच कॉल केले जाते तोपर्यंत कोड इनलाइन करू शकता.
अतिरिक्त व्हेरिएबल्स टाळा
function get(uint id) returns (address,address) {
MyStruct memory myStruct = myStructs[id];
return (myStruct.addr1, myStruct.addr2);
}
function get(uint id) returns (address,address) {
return (myStructs[id].addr1, myStructs[id].addr2);
}
यासारखा एक साधा बदल 0.28kb चा फरक करतो. तुमच्या कॉन्ट्रॅक्ट्समध्ये तुम्हाला अशा अनेक समान परिस्थिती सापडण्याची शक्यता आहे आणि त्या खरोखरच लक्षणीय प्रमाणात भर घालू शकतात.
त्रुटी संदेश लहान करा
लांब पूर्ववत करणे (revert) संदेश आणि विशेषतः अनेक भिन्न पूर्ववत करणे संदेश कॉन्ट्रॅक्टचा आकार वाढवू शकतात. त्याऐवजी लहान त्रुटी कोड वापरा आणि त्यांना तुमच्या कॉन्ट्रॅक्टमध्ये डीकोड करा. एक लांब संदेश खूप लहान होऊ शकतो:
require(msg.sender == owner, "Only the owner of this contract can call this function");
require(msg.sender == owner, "OW1");
त्रुटी संदेशांऐवजी कस्टम त्रुटी वापरा
Solidity 0.8.4 (opens in a new tab) मध्ये कस्टम त्रुटी सादर केल्या गेल्या आहेत. तुमच्या कॉन्ट्रॅक्ट्सचा आकार कमी करण्याचा हा एक उत्तम मार्ग आहे, कारण ते सिलेक्टर्स म्हणून ABI-एनकोड केलेले असतात (जसे फंक्शन्स असतात).
error Unauthorized();
if (msg.sender != owner) {
revert Unauthorized();
}
ऑप्टिमायझरमध्ये कमी रन व्हॅल्यूचा विचार करा
तुम्ही ऑप्टिमायझर सेटिंग्ज देखील बदलू शकता. 200 च्या डीफॉल्ट मूल्याचा अर्थ असा आहे की ते बाइटकोड ऑप्टिमाइझ करण्याचा प्रयत्न करत आहे जसे की फंक्शन 200 वेळा कॉल केले जाते. जर तुम्ही ते 1 मध्ये बदलले, तर तुम्ही मुळात ऑप्टिमायझरला प्रत्येक फंक्शन फक्त एकदाच चालवण्याच्या केससाठी ऑप्टिमाइझ करण्यास सांगता. फक्त एकदाच चालवण्यासाठी ऑप्टिमाइझ केलेले फंक्शन म्हणजे ते प्रस्थापनेसाठीच (deployment) ऑप्टिमाइझ केलेले आहे. लक्षात ठेवा की यामुळे फंक्शन्स चालवण्यासाठी गॅस खर्च वाढतो, त्यामुळे तुम्हाला कदाचित ते करायचे नसेल.
कमी प्रभाव
फंक्शन्सना स्ट्रक्ट्स (structs) पास करणे टाळा
जर तुम्ही ABIEncoderV2 (opens in a new tab) वापरत असाल, तर फंक्शनला स्ट्रक्ट्स पास न करणे उपयुक्त ठरू शकते. पॅरामीटर स्ट्रक्ट म्हणून पास करण्याऐवजी, आवश्यक पॅरामीटर्स थेट पास करा. या उदाहरणात आपण आणखी 0.1kb वाचवले.
function get(uint id) returns (address,address) {
return _get(myStruct);
}
function _get(MyStruct memory myStruct) private view returns(address,address) {
return (myStruct.addr1, myStruct.addr2);
}
function get(uint id) returns(address,address) {
return _get(myStructs[id].addr1, myStructs[id].addr2);
}
function _get(address addr1, address addr2) private view returns(address,address) {
return (addr1, addr2);
}
फंक्शन्स आणि व्हेरिएबल्ससाठी योग्य दृश्यमानता (visibility) घोषित करा
- फंक्शन्स किंवा व्हेरिएबल्स जे फक्त बाहेरून कॉल केले जातात? त्यांना
publicऐवजीexternalम्हणून घोषित करा. - फंक्शन्स किंवा व्हेरिएबल्स जे फक्त कॉन्ट्रॅक्टच्या आतून कॉल केले जातात? त्यांना
publicऐवजीprivateकिंवाinternalम्हणून घोषित करा.
मॉडिफायर्स काढून टाका
मॉडिफायर्स, विशेषतः जेव्हा मोठ्या प्रमाणावर वापरले जातात, तेव्हा कॉन्ट्रॅक्टच्या आकारावर लक्षणीय प्रभाव टाकू शकतात. ते काढून टाकण्याचा आणि त्याऐवजी फंक्शन्स वापरण्याचा विचार करा.
modifier checkStuff() {}
function doSomething() checkStuff {}
function checkStuff() private {}
function doSomething() { checkStuff(); }
या टिप्स तुम्हाला कॉन्ट्रॅक्टचा आकार लक्षणीयरीत्या कमी करण्यात मदत करतील. पुन्हा एकदा, मी यावर पुरेसा भर देऊ शकत नाही की, सर्वात मोठ्या प्रभावासाठी शक्य असल्यास नेहमी कॉन्ट्रॅक्ट्स विभाजित करण्यावर लक्ष केंद्रित करा.
पृष्ठ शेवटचे अपडेट: 3 एप्रिल, 2026