راهنمای جامع توابع ERC20 در Compound V3

قرارداد Compound V3 دقیقاً مانند یک توکن ERC20 با موجودی متغیر خودکار عمل می‌کند. چنین توکن هایی، عرضه ثابتی ندارند و الگوریتمی، مقدار موجودی را تغییر می‌دهند. در این سیستم، توکن در واقع نشان‌دهنده ارزش فعلی موجودی مثبت USDC کاربران است. به زبان ساده، وام دهندگان می‌توانند همانند یک توکن ERC20، ارزش فعلی سرمایه خود را به آدرس‌های دیگر منتقل کنند.

از آنجایی که سرمایه اصلی معمولاً در طول زمان رشد می‌کند (به دلیل دریافت سود)، مقدار این توکن نیز به‌صورت خودکار افزایش پیدا می‌کند.

توسعه دهندگان Compound V3 تصمیم گرفتند برای ردیابی سهم کاربران از استخر وام دهی، از استانداردهای رایج خزانه داری مانند استاندارد ERC-4626 استفاده نکنند.

همان‌طور که پیش‌تر توضیح دادیم، اگر کاربری 100 USDC واریز کرده باشد، ممکن است با گذشت زمان، اعتبارش به 110 USDC برسد. این اختلاف به‌دلیل انباشته شدن سود به‌وجود می‌آید. در واقع، مقدار 110 نشان دهنده ارزش فعلی است و سیستم ERC20 در Compound V3 نیز با همین عدد کار می‌کند.

پیش‌نیازها

برای درک بهتر این مقاله، کاربران باید با مفاهیم نرخ بهره در دیفای (interest indexes)، ارزش فعلی (present value) و ارزش سرمایه اولیه (principal value) در Compound V3 آشنایی داشته باشند. در این مقاله، اصطلاحات Compound V3 و Comet به‌صورت مترادف به‌کار رفته‌اند، زیرا Comet نام قرارداد هوشمند اصلی در این پروتکل است که تمام توابع مورد بحث در این متن در آن پیاده سازی شده‌اند.

ساختار مقاله

در هر بخش از این مقاله، به بررسی یکی از توابع ERC20 می‌پردازیم که در Compound V3 پیاده سازی شده است. همچنین توضیح می‌دهیم که این پروتکل چگونه آن تابع را اجرا می‌کند. برخی از توابع مورد بحث، جزو استاندارد رسمی ERC20 نیستند، اما برای درک عملکرد کلی سیستم، نقش مهمی دارند.

totalSupply و totalBorrow در فایل Comet.sol

تابع totalBorrow همان‌طور که از نامش پیداست، مجموع کل USDC وام‌گرفته‌شده را نشان می‌دهد. به بیان دقیق‌تر، این عدد نمایانگر ارزش فعلی بدهی کاربران است. می‌توانیم این مقدار را هم از خروجی قرارداد هوشمند و هم از اطلاعات نمایش‌داده‌شده در رابط کاربری Compound تأیید کنیم. این عدد فقط نشان‌دهنده مقدار USDC برداشت‌شده نیست، بلکه شامل سودی نیز می‌شود که به مرور زمان بر روی وام‌ها انباشته شده است.

در تصویر زیر، رابط کاربری Compound V3 مقدار totalBorrow را نمایش می‌دهد. همچنین می‌بینیم که خروجی تابع totalBorrow() در Etherscan نیز همین مقدار را بازمی‌گرداند.

تابع totalBorrow در compound v3

به همین صورت، مقدار خروجی تابع totalSupply() نیز صرفاً نشان دهنده میزان USDC واریزشده توسط وام دهندگان در پلتفرم Compound نیست، بلکه نشان دهنده ارزش فعلی کل سپرده ها است.

در اسکرین شات کد زیر، می‌توانید توابع totalSupply و totalBorrow را مشاهده کنید که به‌روشنی ارتباط آن ها با مفهوم ارزش فعلی (Present Value) را نشان می‌دهند.

توابع totalSupply() و totalBrow()

در تصویر زیر، دو بار اجرای تابع totalSupply() را مشاهده می‌کنید. اگر به اسکرین شات سمت راست دقت کنید، می‌بینید که مقدار totalSupply نسبت به تصویر سمت چپ افزایش یافته است. این تغییر، نشان می‌دهد که ارزش فعلی سپرده ها در حال رشد بوده که معمولاً به‌دلیل انباشت سود اتفاق می‌افتد.

افزایش totalSupply()

تابع totalSupply() در Compound V3 دقیقاً مشابه همان تابع totalSupply() در استاندارد ERC20 عمل می‌کند و مقدار کل توکن های در گردش را نمایش می‌دهد.

از سوی دیگر، وام گیرندگان نمی‌توانند بدهی خود را به آدرس های دیگر منتقل کنند؛ بنابراین تابع totalBorrow در هیچ رابط شبیه به توکن مورد استفاده قرار نمی‌گیرد و فقط برای محاسبه مجموع بدهی فعلی کاربرد دارد.

balanceOf در فایل Comet.sol

پیش‌تر در بخش مربوط به سرمایه اصلی (principal) و ارزش فعلی (present value) درباره تابع balanceOf توضیح داده بودیم، بنابراین در این بخش نیازی به تکرار آن نیست.

transfer و transferFrom در فایل Comet.sol

توابع transfer و transferFrom، هر دو ارزش فعلی موجودی وام دهنده را منتقل می‌کنند. پارامتر amount در این توابع، بر پایه ارزش فعلی (present value) سنجیده می‌شود؛ نه سرمایه اولیه. درک این تفاوت برای هر کسی که وارد برنامه نویسی قراردادهای هوشمند می‌شود مهم است.

توابع transfer() و transferFrom()

هر دو تابع transfer و transferFrom در پشت صحنه، تابع داخلی transferInternal را فراخوانی می‌کنند.

در Compound V3، روش صحیح برای انتقال کل موجودی این است که مقدار uint256 بیشینه (max value) را به‌عنوان ورودی تابع وارد کنید. موجودی کاربران به‌صورت لحظه‌ای افزایش می‌یابد، چون سود به‌طور مداوم به آن اضافه می‌شود. به همین دلیل، تعیین دقیق کل موجودی کار ساده‌ای نیست.

تابع transferInternal()

توجه داشته باشید که وام گیرندگان نمی‌توانند وثیقه (Collateral) خود را به آدرس های دیگر منتقل کنند، زیرا تابع transferCollateral فقط به‌صورت داخلی (internal) تعریف شده است. این تابع صرفاً در فرآیند لیکوییدیشن (Liquidation) مورد استفاده قرار می‌گیرد.

توابع تکمیلی ERC20 در فایل CometExt.sol

به‌دلیل محدودیت ۲۴ کیلوبایتی برای استقرار قراردادهای هوشمند در اتریوم، قرارداد اصلی Comet بخشی از عملکرد خود را با استفاده از الگوی افزونه بازگشتی (fallback extension pattern) به فایل CometExt.sol منتقل کرده است. بیشتر توابعی که در CometExt پیاده سازی شده‌اند، به عملکردهای مربوط به استاندارد ERC20 مربوط می‌شوند.

approve در فایل CometExt.sol

عملکرد تابع approve() برای توکن cUSDCv3 غیراستاندارد است، چرا که فقط دو مقدار را می‌پذیرد: uint256.max یا صفر.

از آنجایی که موجودی حساب کاربران به‌صورت مداوم و خودکار در حال افزایش است (به‌دلیل ساختار توکن با موجودی متغیر خودکار)، امکان اینکه دقیقاً برای کل موجودی فعلی به کسی مجوز (allowance) بدهید وجود ندارد. از آنجا که سود به‌طور مداوم به موجودی افزوده می‌شود، هر عدد ثابت خیلی زود از مقدار واقعی موجودی عقب می‌ماند.

تابع approve()

تابع approve() در پشت‌صحنه، تابع داخلی allowInternal را فراخوانی می‌کند که بررسی آن به‌صورت جداگانه ارزشمند است.

allow() و allowInternal()

تابع allow() بخشی از استاندارد ERC20 نیست، اما عملکرد آن مشابه approve() است و به یک آدرس، اجازه دسترسی کامل (maximum allowance) می‌دهد. هر دو تابع approve() و allow()، در پشت‌صحنه از تابع داخلی allowInternal() برای تنظیم این سطح دسترسی استفاده می‌کنند.

از آنجا که تابع approve() در cUSDCv3 فقط دو حالت دارد (یا صفر یا حداکثر مقدار)، تابع allow() نیز رفتاری مشابه approve() دارد، با این تفاوت که به‌جای مقدار uint256، یک آرگومان بولی (boolean) دریافت می‌کند. این آرگومان مشخص می‌کند که آیا باید اجازه کامل صادر شود یا خیر.

توابع allow() و allowInternal()

متغیر ذخیره سازی مربوط به “مقدار مجاز” (allowances) در فایل CometStorage.sol قرار دارد و با نام isAllowed تعریف شده است.

متغیر ذخیره‌سازی isAllowed()‎

در Compound V3، مقدار مجاز (Allowance) فقط به‌صورت صفر یا کامل تعریف می‌شود و حالت میانه‌ای وجود ندارد. به همین دلیل، تابع approve تنها بیشینه مقدار uint256 را به‌عنوان ورودی می‌پذیرد.

برخلاف توکن های استاندارد ERC20، این نسخه از Compound هیچ متغیر ذخیره سازی برای نگهداری عددی مقدار مجاز ندارد. در نتیجه، امکان تعیین مقدار مشخصی به‌عنوان مجوز محدود وجود ندارد و تنها می‌توان دسترسی کامل یا هیچ دسترسی را تعریف کرد.

allowance در فایل CometExt.sol

در Compound V3، مقدار مجاز (Allowance) حالتی دودویی دارد؛ یعنی یک آدرس فقط می‌تواند یا مجوز کامل (uint256.max) داشته باشد یا هیچ مجوزی (صفر) نداشته باشد. اگر کاربر عددی غیر از این دو مقدار را وارد کند، تراکنش ریجکت (revert) می‌شود.

تابع hasPermission نیز به‌سادگی یک مقدار بولی (Boolean) برمی‌گرداند که مشخص می‌کند آیا یک آدرس، دسترسی کامل دارد یا هیچ دسترسی ندارد.

allowBySig() به‌عنوان نسخه غیر استاندارد تابع permit() در ERC20

تابع allowBySig() در Compound V3 جایگزین غیر استانداردی برای تابع permit() در استاندارد ERC20 محسوب می‌شود. این تابع همان هدف را دنبال می‌کند، اما توسعه دهندگان آن را با ساختاری متفاوت پیاده سازی کرده‌اند.

در این نسخه، فایل CometStorage به‌جای استفاده از تابع nonces(address owner) که در استاندارد EIP-2612 تعریف شده، متغیر userNonce را به‌صورت عمومی و از نوع mapping(address => uint256) در دسترس قرار داده است.

توابع name() و symbol() در فایل CometExt.sol

توابع name() و symbol() جزو توابع اختیاری استاندارد ERC20 هستند که یک رشته (string) را به‌عنوان خروجی برمی‌گردانند.

در فایل CometExt.sol، این مقادیر به‌جای آنکه به‌صورت مستقیم در متغیرهای string ذخیره شوند، در متغیرهای bytes32 تغییرناپذیر (immutable) نگهداری می‌شوند تا از نظر مصرف گس بهینه‌تر باشند.

از آنجایی که زبان سالیدیتی امکان تبدیل مستقیم bytes32 به string را فراهم نمی‌کند، سیستم این مقادیر را به‌صورت لحظه‌ای و با استفاده از کد خاصی به رشته تبدیل می‌کند.

نکته‌ای که کمتر مورد توجه قرار می‌گیرد این است که در سالیدیتی، انواع bytes1 تا bytes32 را می‌توان مانند آرایه‌های بایت، در سطح بایت (byte-level) ایندکس‌گذاری کرد. برای مثال:

قرارداد CometExt برای تبدیل نام و نماد توکن، ابتدا یک آرایه bytes را در حافظه (memory) ایجاد می‌کند، سپس کاراکترهای ذخیره شده در متغیرهای تغییرناپذیر name32 و symbol32 (از نوع bytes32) را به آن کپی می‌کند. در نهایت، این آرایه را به‌صورت لحظه‌ای به نوع string تبدیل می‌کند.

توابع name() و symbol()

فراخوانی توابع موجود در CometExt

رابط ABI که در Etherscan شناخته شده است، از وجود این توابع آگاهی ندارد؛ بنابراین توابع CometExt در لیست عمومی توابع قرارداد Comet نمایش داده نمی‌شوند. با این حال، امکان فراخوانی آن‌ها همچنان وجود دارد، زیرا این توابع از طریق مکانیسم fallback در قرارداد Comet شناسایی شده و به‌صورت خودکار به قرارداد CometExt ارجاع داده می‌شوند.

در ادامه، یک نمونه از فراخوانی توابع name() و symbol() با استفاده از ابزار cast در فریم ورک Foundry آورده شده است.

cast call foundry

نتیجه گیری

Comet V3 رفتاری شبیه به یک توکن ERC20 با موجودی متغیر خودکار دارد که نمایانگر موجودی مثبت وام دهندگان است. کاربران می‌توانند این موجودی مثبت را همانند یک توکن معمولی ERC20 به آدرس های دیگر منتقل کنند.

تابع approve() در این سیستم غیراستاندارد است و فقط دو حالت دارد: یا اجازه کامل (مقدار uint256.max) صادر می‌کند یا هیچ دسترسی نمی‌دهد. به‌همین صورت، تابع permit() که برای صدور مجوز بدون پرداخت گس استفاده می‌شود نیز از استاندارد ERC20 تبعیت نمی‌کند و ساختار متفاوتی دارد.

5/5 - (1 امتیاز)

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

پکیج آموزش پیشرفته ASP.NET Core + طراحی فروشگاه اینترنتی
  • انتشار: ۲۷ تیر ۱۴۰۴

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

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

مشاهده همه

نظرات

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