آموزش نرخ بهره در Compound V3

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

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

این مقاله یک راهنمای عملی سالیدیتی است و برای کسانی که به دنیای برنامه نویسی قراردادهای هوشمند علاقه‌مند هستند، مثال بسیار مناسبی محسوب می‌شود. در این راهنما، مرحله‌به‌مرحله توضیح می‌دهیم که چگونه میزان استفاده از منابع، نرخ بهره را تغییر می‌دهد.

پارامترهای نرخ بهره اتراسکن

تغییرات مهم نسبت به Compound V2

در نسخه دوم Compound (و همچنین در AAVE V3)، نرخ بهره پرداختی به تامین کنندگان (Supply Interest Rate)، حاصل‌ضرب نرخ بهره وام گیرنده ها در میزان استفاده (Utilization) است. اما در Compound V3 این فرمول تغییر کرده است.

در نسخه سوم، نرخ بهره برای تامین کننده ها مستقیماً به میزان استفاده وابسته است و دیگر از نرخ وام گیری مشتق نمی‌شود. از طرف دیگر، نرخ بهره وام گیرنده ها مسیر مستقل خود را از طریق یک منحنی مشخص دنبال می‌کنند.

نام متغیرها در مدل نرخ بهره

در AAVE V3 مفهومی به نام “بهینه ترین میزان استفاده” یا optimal utilization وجود دارد. اما در Compound V3 همین مفهوم با نام “kink” شناخته می‌شود.

متغیر borrowPerSecondInterestRateBase در واقع نقطه تقاطع منحنی (intercept) است. مقدار فعلی این متغیر برابر با ۳۱۷۰۹۷۹۱۹ می‌باشد. هدف ما در این بخش این است که نشان دهیم این مقدار معادل با نرخ سالانه (APY) یک درصد است. به عبارت دیگر، زمانی که میزان استفاده صفر باشد، وام گیرنده ها بر اساس پارامترهای فعلی (که ممکن است توسط حاکمیت تغییر کنند)، باید سالانه ۱ درصد بهره پرداخت کنند.

بهره وام در هر ثانیه برجسته شده است

در هر سال معمولی، بدون در نظر گرفتن سال‌های کبیسه یا تفاوت‌های تقویم میلادی، ۳۱,۵۳۶,۰۰۰ ثانیه وجود دارد (SECONDS_PER_YEAR). حالا اگر این عدد را در مقدار borrowInterestRatePerSecond یعنی ۳۱۷۰۹۷۹۱۹ ضرب کنیم، نتیجه‌ای در حدود 0.01e18 یا به عبارتی 1e16 به دست می‌آید. در مقیاسی که عدد ۱ معادل با 1e18 در نظر گرفته می‌شود، این مقدار نشان دهنده نرخ بهره وام گیری معادل ۱ درصد سالانه است.

می‌توانیم این موضوع را با مراجعه به بازار USDC در پروتکل Compound روی شبکه Ethereum نیز تأیید کنیم.

در رابط کاربری این پلتفرم، اگر ماوس را روی بخش Interest Rate Model قرار دهید، می‌توانید نرخ بهره پیش‌بینی‌شده را در سطوح مختلف از میزان استفاده مشاهده کنید. البته این رابط اجازه نمی‌دهد مستقیماً روی ۰ درصد استفاده قرار بگیرید، اما از روی شیب تغییرات می‌توان نرخ را تخمین زد. مثلاً برای هر ۱ درصد افزایش در میزان استفاده، نرخ بهره تقریباً ۰.۰۳ درصد افزایش پیدا می‌کند. بنابراین، نرخ بهره در نقطه صفر استفاده، همان ۱ درصد خواهد بود. برای مشاهده دقیق‌تر، می‌توانید به انیمیشن زیر مراجعه کنید.

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

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

در انیمیشن بالا می‌بینیم که نرخ بهره مورد انتظار برای تامین‌کنندگان سرمایه، که با عنوان Earn APR نمایش داده می‌شود، در میزان استفاده صفر برابر با ۰ درصد است. این موضوع با اطلاعاتی که در Etherscan نیز وجود دارد کاملاً تطابق دارد. در مورد نرخ بهره برای تامین‌کنندگان، متغیری به نام supplyPerSecondInterestRateBase وجود دارد که مقدار آن تعیین‌کننده نقطه شروع منحنی است. هم نمودار رابط کاربری و هم داده های Etherscan نشان می‌دهند که مقدار این متغیر برابر با صفر است.

مقدار supplyPerSecondInterestBase etherscan برابر با ۰ است.

چطور مطمئن می‌شویم که مقدار 0.01e18 معادل 1 درصد است؟

در مثال قبلی، استنتاج کردیم که عدد 0.01e18 معادل نرخ بهره 1 درصد سالانه است. اما برای اثبات این موضوع، باید کدهای اصلی پروتکل را بررسی کنیم. در ادامه، طی پنج مرحله توضیح می‌دهیم که این تبدیل چگونه انجام می‌شود و چرا مقدار 0.01e18 در مقیاس سالانه دقیقاً به معنای 1% نرخ بهره است.

مرحله ۱: معرفی ثابت FACTOR_SCALE برابر با 1e18

در فایل CometCore.sol و در خط ۵۷، مشاهده می‌کنیم که پروتکل Compound از ثابتی به نام FACTOR_SCALE با مقدار 1e18 استفاده می‌کند (در تصویر زیر با کادر آبی مشخص شده است). همچنین در همین بخش، ثابت SECONDS_PER_YEAR نیز تعریف شده است (کادر قرمز)، که در بخش قبلی مقاله به آن اشاره کردیم. این ثابت نشان می‌دهد که مبنای زمانی محاسبات سالانه در این پروتکل، برابر با ۳۱,۵۳۶,۰۰۰ ثانیه در نظر گرفته شده است.

با استفاده از این دو مقدار، پروتکل می‌تواند نرخ‌های بهره را به صورت دقیق و با دقت بالا در مقیاس‌های مختلف (مانند ثانیه یا سال) محاسبه کند. مقدار 1e18 به عنوان مقیاس ثابت برای دقت محاسبات در بسیاری از قراردادهای سالیدیتی رایج است، چون امکان محاسبه درصدها و اعداد اعشاری را بدون استفاده از ممیز شناور فراهم می‌کند.

ثابت‌های seconds_per_year و factor_scale

مرحله ۲: میزان استفاده با مقیاس FACTOR_SCALE یعنی 1e18 اندازه‌گیری می‌شود

برای درک دقیق‌تر، بیایید تابع getUtilization() را در فایل Comet.sol بررسی کنیم.

تابع ()getUtilization

در این تابع، دو متغیر totalSupply_ (کادر آبی) و totalBorrow_ (کادر سبز) به‌کار رفته‌اند. در این مرحله هنوز نمی‌دانیم که این متغیرها دقیقاً از چه مقیاسی استفاده می‌کنند، اما منطقی است فرض کنیم که هر دو دارای تعداد رقم اعشار (Decimals) یکسان هستند. بنابراین مقیاس آن‌ها در صورت و مخرج ساده می‌شود و اثری در نتیجه نهایی ندارد. در خط بازگشتی تابع (خط ۴۶۴)، صورت کسر در مقدار FACTOR_SCALE (کادر قرمز) ضرب شده است. بنابراین، نتیجه نهایی تابع getUtilization() با دقت ۱۸ رقم اعشار محاسبه می‌شود.

حالا بیایید مقدار فعلی تابع getUtilization() را در Etherscan با آنچه در اپلیکیشن رسمی Compound نمایش داده می‌شود مقایسه کنیم.

مقدار فعلی getUtilization رسم شده است

در حال حاضر، مقدار getUtilization() برابر با عدد زیر است:

904869679838357231

اگر این عدد را بر FACTOR_SCALE (یعنی 1e18) تقسیم کنیم و نتیجه را تا دو رقم اعشار گرد کنیم، به 90.49 درصد می‌رسیم. این عدد با مقدار نمایش داده شده در اپلیکیشن کامپوند مطابقت کامل دارد. بنابراین به‌وضوح مشخص است که مقدار بازگشتی این تابع با دقت ۱۸ رقم اعشار محاسبه شده است.

در این مرحله انتظار نداریم که مخاطب با توابع presentValueSupply و presentValueBorrow آشنایی کامل داشته باشد. اما برای ساده سازی مفهوم، می‌توان این دو تابع را این‌گونه در نظر گرفت:

  • presentValueSupply: ارزش دلاری کل سرمایه ای که در حال حاضر برای وام دهی در دسترس است.

  • presentValueBorrow: ارزش دلاری کل وام هایی که کاربران تاکنون دریافت کرده‌اند.

این توابع نقش مهمی در تعیین نرخ‌های بهره و میزان استفاده واقعی از منابع ایفا می‌کنند.

مرحله ۳: مقدار “kink” یا میزان استفاده بهینه، عددی در بازه [0 تا 1e18] است

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

در پروتکل Compound V3 به این نقطه اصطلاحاً “kink” گفته می‌شود. از این نقطه به بعد، نرخ‌های بهره با شیب تندتری افزایش پیدا می‌کنند. بنابراین، اصطلاح‌های kink در Compound و optimal utilization در سایر پروتکل‌ها (مثل AAVE) هر دو به همان مفهوم واحد اشاره دارند.

در زمان نگارش این مقاله، مقدار kink برای بازار USDC روی شبکه Ethereum برابر با ۹۳ درصد است که در تصویر زیر نیز نمایش داده شده است.

borrowKink و supplyKink

از آنجا که میزان استفاده (Utilization) با عدد ثابت اعشاری ۱۸ رقمی (Fixed Point با مقیاس 1e18) اندازه‌گیری می‌شود، متغیرهای borrowKink و supplyKink نیز با همین مقیاس ۱۸ رقمی تعریف شده‌اند.

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

مرحله ۴: تابع mulFactor()

برنامه نویسانی که با تابع mulDivDown در کتابخانه های عدد اعشاری ثابت (Fixed Point) آشنا هستند، به‌راحتی می‌توانند منطق تابع mulFactor() را درک کنند.

این تابع، دو عدد اعشاری با مقیاس ثابت (۱۸ رقم اعشار) را به‌عنوان ورودی دریافت می‌کند و حاصل‌ضرب آن‌ها را به صورت یک عدد اعشاری ۱۸ رقمی برمی‌گرداند.

تابع mulFactor

از آنجایی که دو عدد با مقیاس 1e18 (یعنی ۱۸ رقم اعشار) در یکدیگر ضرب می‌شوند، حاصل اولیه دارای ۳۶ رقم اعشار خواهد بود. برای جلوگیری از این وضعیت، تابع نتیجه ضرب را بر FACTOR_SCALE تقسیم می‌کند تا مقیاس خروجی دوباره به ۱۸ رقم اعشار بازگردد.

می‌توان این تابع را به‌صورت خلاصه این‌گونه توصیف کرد:

«تابع mulFactor() دو عدد اعشاری با مقیاس ۱۸ رقمی را در هم ضرب می‌کند و نتیجه را به‌صورت یک عدد اعشاری ۱۸ رقمی برمی‌گرداند که حاصل‌ضرب دقیق آن‌ها را نمایش می‌دهد.»

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

مرحله ۵: تابع getSupplyRate() نرخ بهره را با مقیاس FACTOR_SCALE (یعنی 1e18) برمی‌گرداند

اکنون آماده‌ایم تا نتیجه‌گیری کنیم که پروتکل Compound V3 نرخ‌های بهره را با مقیاس 1e18 اندازه‌گیری می‌کند.

همان‌طور که در مقاله قبلی درباره نرخ‌های بهره توضیح دادیم، این پروتکل از تابع خطی تکه‌ای (piecewise linear) برای محاسبه نرخ بهره استفاده می‌کند. تابع getSupplyRate() نرخ بهره فعلی را که تامین کنندگان (وام دهندگان) دریافت می‌کنند محاسبه و بازمی‌گرداند. این نرخ کاملاً وابسته به مقدار فعلی استفاده (Utilization Rate) است. از آنجا که می‌دانیم mulFactor(...) خروجی را در مقیاس FACTOR_SCALE (یعنی با ۱۸ رقم اعشار) تولید می‌کند، می‌توانیم نتیجه بگیریم که متغیر supplyPerSecondInterestRate (دایره زرد در تصویر) نیز باید دقیقاً در همین مقیاس ۱۸ رقمی باشد. در غیر این صورت، جمع دو مقدار با مقیاس متفاوت منجر به خطای عددی می‌شود. در نتیجه، تابع getSupplyRate() خروجی با دقت ۱۸ رقم اعشار بازمی‌گرداند و با بقیه ساختارهای محاسباتی پروتکل هم‌راستا باقی می‌ماند.

تابع getSupplyRate

تابع getBorrowRate() که نرخ بهره برای وام گیرنده ها را محاسبه می‌کند، دقیقاً به همین شکل عمل می‌کند. به همین دلیل، نیازی به ارائه جداگانه آن در اینجا نیست.

تمام پارامترهای مربوط به منحنی نرخ بهره شامل نقطه شروع (intercept)، شیب‌ها (slopes)، نقطه kink (یا همان میزان استفاده بهینه) همگی از طریق سازوکار حاکمیتی پروتکل (Governance) قابل تغییر هستند. این قابلیت به جامعه کاربران اجازه می‌دهد که سیاست‌های مالی پروتکل را متناسب با شرایط بازار به‌روزرسانی کنند.

محاسبه نرخ های بهره فرضی

برای محاسبه نرخ های بهره بر اساس میزان استفاده (Utilization)، کاربر می‌تواند از تابع getUtilization() مقدار فعلی استفاده را دریافت کرده و آن را در توابع getSupplyRate() و getBorrowRate() جایگذاری کند.

در مثال زیر، یک قرارداد ساده با نام GetCurrentRatesComet تعریف شده که این محاسبات را انجام می‌دهد:

انباشت نرخ بهره (Interest Rate Accrual)

در این مقاله، فقط نحوه محاسبه نرخ بهره در یک لحظه خاص را بررسی کردیم. در مقاله بعدی، به‌طور کامل توضیح خواهیم داد که پروتکل Compound چگونه بهره را در طول زمان انباشت و ترکیب (compound) می‌کند.

جمع‌بندی نهایی

  • واحد زمانی محاسبه نرخ بهره در Compound برابر با ثانیه است.

  • تمام نرخ‌ها با دقت ۱۸ رقم اعشار ثبت می‌شوند (Fixed Point 1e18).

  • متغیر Utilization نیز دقیقاً با همین دقت ۱۸ رقمی اندازه‌گیری می‌شود.

این ساختار یکپارچه باعث می‌شود که محاسبات مالی در این پروتکل هم دقیق باشند و هم به‌راحتی با سایر بخش‌های سیستم ترکیب شوند.

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

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

دوره صفر تا صد آموزش بین المللی لینوکس
  • انتشار: ۲۶ تیر ۱۴۰۴

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

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

مشاهده همه

نظرات

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