آموزش EIP-1167 در سالیدیتی

EIP-1167 در سالیدیتی که آن را به‌عنوان “قرارداد پراکسی مینیمال” نیز می‌شناسند، یکی از الگوهای پرکاربرد در زبان سالیدیتی است. توسعه دهندگان از این الگو برای ایجاد نسخه های مشابه از یک قرارداد با هزینه بسیار پایین استفاده می‌کنند.

اگر در پروژه‌ای لازم باشد که یک قرارداد یکسان یا بسیار مشابه چند بار روی شبکه مستقر شود، استفاده از این الگو روشی بهینه و کم‌هزینه خواهد بود.

برای مثال، پلتفرم Gnosis Safe هنگام ایجاد کیف پول جدید از الگوی clone استفاده می‌کند. زمانی که کاربران با Gnosis Safe کار می‌کنند، در واقع با یک نسخه‌ی clone شده تعامل دارند.

قرارداد clone نقش یک پراکسی را ایفا می‌کند، با این تفاوت که نمی‌توان آن را ارتقاء داد. چون اندازه این پراکسی ها بسیار کوچک‌تر از قرارداد اصلی است، هزینه استقرار آن‌ها نیز بسیار کمتر خواهد بود.

در این الگو، clone ها مانند پراکسی های معمولی تمام فراخوانی ها را به قرارداد اصلی منتقل می‌کنند. با این حال، هر clone داده های وضعیت خود را در فضای ذخیره سازی اختصاصی نگه می‌دارد.

برخلاف الگوی پراکسی معمول، در الگوی clone می‌توان چندین نسخه را به یک قرارداد پیاده سازی واحد متصل کرد. البته این نسخه ها قابلیت ارتقاء ندارند.

آدرس قرارداد اصلی در داخل bytecode ذخیره می‌شود. این کار مصرف گس را کاهش می‌دهد و همچنین مانع از تغییر مسیر clone به قراردادهای دیگر می‌شود.

طراحی این الگو هزینه استقرار را به‌طور چشم‌گیری کاهش می‌دهد، زیرا bytecode مربوط به clone بسیار کوچک‌تر از قرارداد اصلی است. در واقع، استاندارد EIP-1167 تنها 55 بایت حجم دارد که 45 بایت آن به زمان اجرا مربوط می‌شود. این مقدار شامل کد راه‌اندازی اولیه نیز هست.

البته در زمان اجرا، هر بار فراخوانی هزینه بیشتری دارد، چون همیشه یک delegatecall اضافی اجرا می‌شود.

در ادامه این مقاله، هم خود استاندارد EIP-1167 و هم نحوه استفاده از تابع مقداردهی اولیه برای جایگزینی پارامترهای سازنده (constructor) را توضیح می‌دهیم.

EIP-1167 در سالیدیتی چگونه کار میکند؟

در EIP-1167، قرارداد clone دقیقاً مانند یک پراکسی معمولی عمل می‌کند. ابتدا داده‌های تراکنش را از طریق فراخوانی (call) دریافت می‌کند. سپس این داده‌ها را به قرارداد پیاده‌سازی (implementation contract) منتقل می‌کند. اگر فراخوانی خارجی با موفقیت انجام شود، نتیجه را دریافت کرده و به کاربر بازمی‌گرداند. اما اگر در این فرآیند خطایی رخ دهد، تراکنش را با خطا (revert) متوقف می‌کند.

بایت‌کد قرارداد پراکسی مینیمال

قرارداد پراکسی مینیمال تنها ۵۵ بایت بایت‌کد دارد که بسیار فشرده و بهینه است. این بایت‌کد از بخش‌های زیر تشکیل شده است:

  • کد اولیه استقرار (init code)

  • کد زمان اجرا (runtime code) که شامل دستوراتی برای دریافت داده های تراکنش (calldata) است

  • آدرس ۲۰ بایتی قرارداد پیاده سازی

  • دستور اجرای delegatecall

  • و در نهایت دستور بازگرداندن نتیجه یا توقف اجرا در صورت بروز خطا (revert)

در زیر بایت‌کد کامل این پراکسی مینیمال را مشاهده می کنید:

در این بایت‌کد، آدرس فرضی 0xbebebebebebebebebebebebebebebebebebebebe به عنوان نمونه درج شده و هنگام ساخت واقعی قرارداد، با آدرس قرارداد پیاده سازی جایگزین می شود.

ما در ادامه، این بایت‌کد را بخش به بخش تحلیل می کنیم تا بهتر با ساختار آن آشنا شوید.

بایت‌کد قرارداد کلون به همراه بخش‌هایی از بایت‌کد که هایلایت شده‌اند

بخش init code

۱۰ بایت ابتدایی بایت کد به بخش init code اختصاص دارد. این بخش تنها یک بار اجرا می شود و وظیفه آن استقرار قرارداد پراکسی مینیمال بر بستر شبکه بلاکچین است.

در ادامه، مجموعه دستوراتی را بررسی خواهیم کرد که ماشین مجازی اتریوم (EVM) در این بخش اجرا می کند.

کپی کردن calldata

کد init در زمان استقرار قرارداد، وظیفه دارد بایت کد زمان اجرا (runtime bytecode) را از محل مشخصی در حافظه (از آفست 10 به بعد) کپی کرده و روی بلاکچین ذخیره کند. این بخش از بایت کد، همان قسمت مربوط به کپی calldata است.

پس از استقرار کامل یک پراکسی مینیمال، زمانی که تراکنشی به آن ارسال شود، ابتدا داده های تراکنش (calldata) را در حافظه کپی می کند. سپس آدرس 20 بایتی قرارداد پیاده سازی را در استک قرار می دهد و با استفاده از دستور delegatecall، کنترل را به قرارداد پیاده سازی منتقل می کند.

عملیات کپی کردن calldata با استفاده از مجموعه دستورات مشخصی در EVM انجام می شود که در ادامه به آن ها اشاره خواهیم کرد.

آدرس قرارداد پیاده سازی

پس از آنکه داده های تراکنش (calldata) در حافظه کپی شد، ماشین مجازی اتریوم (EVM) استک را برای اجرای دستور delegatecall آماده می کند. در این مرحله، آدرس ۲۰ بایتی قرارداد پیاده سازی در بالای استک قرار می گیرد.

همان طور که در بخش قبل اشاره شد، بایت کد با دستور PUSH20 پایان می یابد. بلافاصله پس از این دستور، آدرس قرارداد پیاده سازی قرار دارد که پراکسی تمام فراخوانی ها را به آن منتقل می کند.

بخش delegatecall

پس از آنکه داده های تراکنش (calldata) در حافظه کپی شد و آدرس قرارداد پیاده سازی در بالای استک قرار گرفت، پراکسی مینیمال آماده اجرای دستور delegatecall به قرارداد پیاده سازی می شود.

اگر نیاز دارید تا عملکرد دقیق delegatecall را مرور کنید، می توانید به آموزش اختصاصی ما درباره این دستور مراجعه کنید.

زمانی که دستور delegatecall اجرا شود، در صورتی که فراخوانی با موفقیت انجام شود، پراکسی مینیمال نتیجه آن را بازمی گرداند. اما اگر در جریان اجرا خطایی رخ دهد، تراکنش را متوقف کرده و عملیات را revert می کند.

دستورات اجرایی (opcodes) مربوط به بخش delegatecall در ادامه معرفی خواهند شد.

این بخش، نمای کلی از استاندارد EIP-1167 در سالیدیتی و نحوه عملکرد آن را ارائه می دهد.

برای درک بهتر، تصور کنید که قرارداد پیاده سازی یک توکن ERC20 باشد. در این صورت، نسخه clone شده دقیقاً همانند یک توکن ERC20 رفتار خواهد کرد و تمامی توابع و ویژگی های آن را به همان شکل ارائه می دهد.

پیاده سازی قرارداد هوشمند EIP-1167 همراه با مقداردهی اولیه (Initialization)

در برخی موارد، هنگام ایجاد یک clone نیاز داریم که برخی پارامترها را در لحظه استقرار تنظیم کنیم. برای نمونه، اگر در حال clone گرفتن از یک توکن ERC20 باشیم، تمام نسخه های clone شده دارای totalSupply یکسان خواهند بود که در بسیاری از سناریوها مطلوب نیست.

برای حل این مشکل، می توان از الگوی «clone همراه با مقداردهی اولیه (initialization)» استفاده کرد.

در ادامه می بینیم که چگونه می توان با استفاده از استاندارد EIP-1167، نسخه هایی از یک قرارداد را clone کرده و در لحظه استقرار، پارامترهای اولیه مورد نیاز را تنظیم کرد. این فرآیند مراحل ساده ای دارد:

  1. ابتدا قرارداد پیاده سازی (implementation contract) را ایجاد می کنیم.

  2. سپس با استفاده از استاندارد EIP-1167 از آن clone می گیریم.

  3. در مرحله بعد، clone را مستقر کرده و تابع مقداردهی اولیه (initialization function) را فراخوانی می کنیم. این تابع فقط یک بار قابل اجرا خواهد بود.

محدود بودن این تابع به یک بار اجرا ضروری است. در غیر این صورت، ممکن است پس از استقرار، شخصی مقدار یک پارامتر حیاتی مثل totalSupply را تغییر دهد.

در ادامه این مراحل را با یک مثال بررسی می کنیم.

قرارداد پیاده سازی که قرار است clone شود:

برای استقرار نسخه clone از قرارداد، از کد زیر استفاده می کنیم:

با استفاده از قرارداد MinimalProxyFactory، می توان تعداد نامحدودی clone بر اساس استاندارد EIP-1167 در سالیدیتی ایجاد کرد. با این حال، در این مثال صرفاً قصد داریم همان قرارداد پیاده سازی که پیش از این تعریف شده را مستقر کنیم و یک clone از آن بسازیم.

در ادامه یک اسکریپت ساده با استفاده از فریمورک Hardhat ارائه شده است که مراحل استقرار قراردادها و تعامل با یک clone مستقر شده را انجام می دهد:

در حال حاضر، قراردادهای مورد نظر با موفقیت روی شبکه Goerli مستقر شده اند و جزئیات تراکنش مربوط به سه قرارداد اصلی به شرح زیر است:

  1. قرارداد پیاده سازی (Implementation Contract)

  2. قرارداد کارخانه پراکسی مینیمال (Minimal Proxy Factory Contract)

  3. قرارداد پراکسی مینیمال (Minimal Proxy Contract)

نکته مهم این است که Etherscan به درستی تشخیص می دهد که قرارداد پراکسی، یک قرارداد معمولی نیست؛ بلکه فراخوانی های دریافتی را با استفاده از دستور delegatecall به قرارداد پیاده سازی منتقل می کند. این رفتار، یکی از ویژگی های اصلی استاندارد EIP-1167 در سالیدیتی است.

همچنین باید توجه داشت که نگهداری لیستی از clone های مستقرشده، صرفاً برای سهولت توسعه و بررسی در این پروژه انجام شده است. این قابلیت ضروری نیست و می توان آن را بسته به نیاز، حذف یا ساده تر کرد.

جمع بندی

استاندارد پراکسی مینیمال EIP-1167 در سالیدیتی روشی کارآمد و کم‌هزینه برای استقرار قراردادهایی است که دقیقاً رفتار یک قرارداد دیگر را تکرار می کنند. با استفاده از الگوی مقداردهی اولیه (initializer pattern)، می توان نسخه clone را طوری مستقر کرد که گویی دارای سازنده (constructor) با ورودی های دلخواه است. اگر به دنبال پیاده سازی عملی این الگو هستید، پیشنهاد می کنیم ابتدا مبانی آموزش برنامه نویسی قراردادهای هوشمند در سالیدیتی را مرور کنید.

هزینه اصلی این روش در این است که هر بار فراخوانی، با یک دستور delegatecall همراه می شود و این موضوع باعث افزایش اندک مصرف گس در زمان اجرا خواهد شد. با این حال، مزایای آن در صرفه جویی در گس هنگام استقرار و ساختار ساده، این هزینه را در بسیاری از موارد توجیه پذیر می کند.

به این مطلب امتیاز دهید

راستی! برای دریافت مطالب جدید در کانال تلگرام یا پیج اینستاگرام سورس باران عضو شوید.

دوره آموزش طراحی فروشگاه اینترنتی بدون کد نویسی در 8 ساعت
  • انتشار: ۱۷ تیر ۱۴۰۴

دسته بندی موضوعات

آخرین محصولات فروشگاه

مشاهده همه

نظرات

بازخوردهای خود را برای ما ارسال کنید