কন্ট্রাক্টের সাইজ লিমিট এড়াতে কন্ট্রাক্ট ছোট করা
কেন এই লিমিট বা সীমা রয়েছে?
22 নভেম্বর, 2016 (opens in a new tab)-এ Spurious Dragon হার্ড-ফর্ক EIP-170 (opens in a new tab) চালু করে, যা 24.576 kb-এর একটি স্মার্ট কন্ট্রাক্ট সাইজ লিমিট যোগ করে। একজন Solidity ডেভেলপার হিসেবে আপনার জন্য এর অর্থ হলো, যখন আপনি আপনার কন্ট্রাক্টে আরও বেশি ফাংশনালিটি যোগ করবেন, তখন এক পর্যায়ে আপনি এই লিমিটে পৌঁছে যাবেন এবং ডিপ্লয়মেন্ট করার সময় এই এররটি দেখতে পাবেন:
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 হিসেবে ডিক্লেয়ার করবেন না কারণ সেগুলো কম্পাইল করার সময় সরাসরি কন্ট্রাক্টে যোগ করা হবে (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-এর পার্থক্য তৈরি করে। সম্ভাবনা রয়েছে যে আপনি আপনার কন্ট্রাক্টগুলোতে এরকম অনেক পরিস্থিতি খুঁজে পেতে পারেন এবং সেগুলো একসাথে মিলে উল্লেখযোগ্য পরিমাণ সাইজ বাঁচাতে পারে।
এরর বার্তা ছোট করুন
দীর্ঘ রিভার্ট বার্তা এবং বিশেষ করে অনেকগুলো ভিন্ন ভিন্ন রিভার্ট বার্তা কন্ট্রাক্টের সাইজ বাড়িয়ে দিতে পারে। এর পরিবর্তে ছোট এরর কোড ব্যবহার করুন এবং আপনার কন্ট্রাক্টে সেগুলো ডিকোড করুন। একটি দীর্ঘ বার্তা অনেক ছোট হয়ে যেতে পারে:
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 করেন, তবে আপনি মূলত অপ্টিমাইজারকে প্রতিটি ফাংশন শুধুমাত্র একবার চালানোর জন্য অপ্টিমাইজ করতে বলছেন। শুধুমাত্র একবার চালানোর জন্য অপ্টিমাইজ করা ফাংশন মানে হলো এটি ডিপ্লয়মেন্ট-এর জন্যই অপ্টিমাইজ করা হয়েছে। সচেতন থাকুন যে এটি ফাংশনগুলো চালানোর জন্য গ্যাস খরচ বাড়িয়ে দেয়, তাই আপনি হয়তো এটি করতে চাইবেন না।
ছোট প্রভাব
ফাংশনে স্ট্রাক্ট (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);
}
ফাংশন এবং ভেরিয়েবলের জন্য সঠিক ভিজিবিলিটি ডিক্লেয়ার করুন
- যে ফাংশন বা ভেরিয়েবলগুলো শুধুমাত্র বাইরে থেকে কল করা হয়? সেগুলোকে
public-এর পরিবর্তেexternalহিসেবে ডিক্লেয়ার করুন। - যে ফাংশন বা ভেরিয়েবলগুলো শুধুমাত্র কন্ট্রাক্টের ভেতর থেকে কল করা হয়? সেগুলোকে
public-এর পরিবর্তেprivateবাinternalহিসেবে ডিক্লেয়ার করুন।
মডিফায়ারগুলো সরিয়ে ফেলুন
মডিফায়ারগুলো, বিশেষ করে যখন ব্যাপকভাবে ব্যবহার করা হয়, তখন কন্ট্রাক্টের সাইজের ওপর উল্লেখযোগ্য প্রভাব ফেলতে পারে। সেগুলো সরিয়ে ফেলার কথা বিবেচনা করুন এবং এর পরিবর্তে ফাংশন ব্যবহার করুন।
modifier checkStuff() {}
function doSomething() checkStuff {}
function checkStuff() private {}
function doSomething() { checkStuff(); }
এই টিপসগুলো আপনাকে কন্ট্রাক্টের সাইজ উল্লেখযোগ্যভাবে কমাতে সাহায্য করবে। আবারও বলছি, আমি এর ওপর যথেষ্ট জোর দিতে পারব না যে, সবচেয়ে বড় প্রভাবের জন্য সম্ভব হলে সর্বদা কন্ট্রাক্টগুলো ভাগ করার দিকে ফোকাস করুন।
পেজ সর্বশেষ আপডেট করা হয়েছে: 3 এপ্রিল, 2026