در نگاه اول، سادهترین روش برای دنبال کردن سپرده های وام دهندگان این است که مقدار USDC و زمان واریز آن را مستقیماً ثبت کنیم. اما Compound V3 این مسیر را انتخاب نمیکند.
این پروتکل، مشابه الگوریتم استیکینگ MasterChef در SushiSwap، سود فرضی حاصل از وام دادن 1 دلار از ابتدای راهاندازی پروتکل را محاسبه میکند. اگر با الگوریتم MasterChef آشنا نیستید، بهتر است ابتدا منبع معرفیشده را مطالعه کنید.
متغیر baseSupplyIndex در فایل CometStorage.sol
این سود فرضی را نشان میدهد. این متغیر عملکردی شبیه به rewardPerTokenAccumulator
در SushiSwap دارد. مقدار اولیه آن برابر 1.0 است و هر زمان که یکی از عملیاتهای تغییردهنده وضعیت مانند سپردهگذاری، برداشت یا وامگیری اتفاق بیفتد، مقدار آن با توجه به زمان سپریشده و نرخ بهره دوره افزایش پیدا میکند.
برای مثال، فرض کنید 100 ثانیه گذشته باشد و نرخ بهره برابر با 0.001 در هر ثانیه باشد (مقداری اغراقآمیز اما مناسب برای درک سادهتر). در این شرایط، مقدار baseSupplyIndex
به 1.1 میرسد. فرمول زیر این محاسبه را انجام میدهد:
1 |
baseSupplyIndex += supplyInterestRatePerSecond(utilization) × secondsElapsed |
baseSupplyIndex
را تغییر میدهد: خط 403 از فایل Comet.sol
در داخل تابع accruedInterestIndices
.
از آنجایی که نرخ بهره مستقیماً به میزان استفاده (Utilization) وابسته است، شاخص baseSupplyIndex
در زمانهایی که استفاده بالا است سریعتر رشد میکند و در دورههایی که استفاده پایین است، با سرعت کمتری افزایش مییابد.
نمودار فرضی زیر، نحوه افزایش baseSupplyIndex
و baseBorrowIndex
را در شرایط مختلف استفاده نشان میدهد. به طور کلی، وامگیرندگان بهره بیشتری پرداخت میکنند نسبت به بهرهای که وامدهندگان دریافت میکنند؛ به همین دلیل، شاخص baseBorrowIndex
با سرعت بیشتری رشد میکند.
مثال زیر نشان میدهد که این متغیر چگونه مورد استفاده قرار میگیرد.
مثال و اصطلاحات کلیدی
فرض کنید “آلیس” مبلغ 1000 دلار را در زمانی سپرده گذاری میکند که مقدار baseSupplyIndex
برابر با 2.5 است. در این حالت، سیستم Compound بهجای ثبت مستقیم عدد 1000 دلار برای او، مقدار 400 دلار را به عنوان سپرده در نظر میگیرد؛ زیرا سپرده آلیس بر اساس شاخص فعلی تقسیم میشود (1000 دلار ÷ 2.5 = 400 دلار).
در نتیجه، حساب آلیس یک “ارزش اصلی” (principal value) معادل 400 دلار ثبت میکند (در کادر زرد). این عدد همان مقداری است که قرارداد هوشمند Compound در فایل CometStorage.sol برای کاربران ذخیره میکند.
اگر آلیس بلافاصله پس از سپردهگذاری تصمیم به برداشت بگیرد، سیستم Compound موجودی او را برابر با حاصلضرب مقدار “ارزش اصلی” (400 دلار) در شاخص فعلی baseSupplyIndex
محاسبه میکند؛ یعنی 400 × 2.5 = 1000 دلار. بنابراین، او میتواند همان مبلغ اولیه را برداشت کند. Compound اصطلاح «ارزش فعلی» (present value) را برای حاصلضرب ارزش اصلی در شاخص baseSupplyIndex
به کار میبرد.
نکته مهم اینجاست که Compound V3 هرگز مقدار واقعی سپرده اولیه را بهصورت مستقیم ذخیره نمیکند. این مقدار تنها از طریق شاخص قابل استنباط است. چون شاخص در حال حاضر 2.5 است و مقدار ثبتشده برای سپرده آلیس 400 دلار میباشد، بهطور ضمنی مشخص است که سپرده اولیه او 1000 دلار بوده است.
مقداری که Compound V3 در قرارداد ذخیره میکند، یک نسخه «کوچکشده» یا «مقیاسپایینرفته» از سپرده واقعی است و با عنوان “ارزش اصلی” (principal value) شناخته میشود. زمانی که این مقدار را در baseSupplyIndex
ضرب کنیم، به «ارزش فعلی» (present value) میرسیم؛ که در واقع معادل روز سپرده فرد است.
اگر مخاطب با مفاهیم سنتی مالی آشنا باشد، ممکن است استفاده Compound از اصطلاحات “ارزش اصلی” و “ارزش فعلی” برایش گیجکننده باشد. پیشنهاد میکنیم این مفاهیم را صرفاً در بستر تعریفشده توسط Compound درک کنید و آنها را با معانی رایج در مالی سنتی مقایسه نکنید.
حال اگر آلیس منتظر بماند تا مقدار baseSupplyIndex
به 3.0 برسد، مقدار “ارزش اصلی” او همچنان برابر با 400 دلار باقی میماند، اما “ارزش فعلی” به 1200 دلار افزایش پیدا میکند؛ چون 400 × 3.0 = 1200 دلار.
در فایل CometCore.sol
، دو رابطه اصلی تعریف شدهاند:
-
برای محاسبه “ارزش اصلی”، باید “ارزش فعلی” را بر
baseSupplyIndex
تقسیم کنیم -
برای محاسبه “ارزش فعلی”، باید “ارزش اصلی” را در
baseSupplyIndex
ضرب کنیم
بنابراین، برای جمع بندی مفاهیم کلیدی:
ارزش اصلی (Principal Value)
ارزش اصلی برابر است با مقدار USDC واریزشده تقسیم بر مقدار baseSupplyIndex
در زمان واریز. این عدد در یک متغیر ذخیره سازی مخصوص حساب کاربر نگهداری میشود و فقط زمانی تغییر میکند که کاربر سپرده جدیدی اضافه کند یا برداشت انجام دهد. معمولاً ارزش اصلی کمتر از مقدار واقعی سپرده است، چون مقدار baseSupplyIndex
همیشه برابر یا بزرگتر از 1 است.
ارزش فعلی (Present Value)
ارزش فعلی از حاصلضرب ارزش اصلی در مقدار فعلی baseSupplyIndex
بهدست میآید. این مقدار در هیچ جایی ذخیره نمیشود و هر بار بهصورت لحظهای (on the fly) محاسبه میگردد.
درک درست از دو مفهوم ارزش اصلی و ارزش فعلی برای فهم عملکرد پروتکل Compound V3 کاملاً ضروری است.
فقط متغیر “ارزش اصلی” (Principal) برای وام دهندگان دنبال میشود
برای روشنتر شدن موضوع، بیایید بار دیگر به ساختار userBasic
که در بخش قبل به آن اشاره شد، نگاهی بیندازیم:
در این ساختار:
-
متغیر
baseTrackingAccrued
توسط ماژولCometRewards
استفاده میشود تا مشخص کند چه مقدار توکن COMP به خاطر مشارکت کاربر در پروتکل به او تعلق میگیرد. -
متغیر
baseTrackingIndex
به این فرآیند مرتبط است اما در حال حاضر کاربردی ندارد. -
متغیر
assetsIn
فقط در سمت وام گیرندگان استفاده میشود تا نشان دهد چه داراییهایی بهعنوان وثیقه فعال شدهاند. -
متغیر
_reserved
نیز در حال حاضر هیچ کاربردی ندارد.
بنابراین، تنها متغیر کلیدی برای محاسبه وضعیت وام دهندگان، همان principal
یا ارزش اصلی است. نکته مهم اینجاست که این متغیر از نوع signed (علامتدار) تعریف شده است — یعنی برای وام گیرندگان مقدار آن منفی در نظر گرفته میشود.
در مورد وامگیرندگان، علاوه بر principal
باید مقدار وثیقه هایی که سپرده گذاری کردهاند نیز ثبت شود؛ اما این موضوع در مقالهای جداگانه بررسی خواهد شد.
تابع balanceOf()
— بررسی موجودی مثبت وام دهنده
برای درک بهتر اینکه Compound V3 فقط مقدار “ارزش اصلی” (principal value) را ذخیره میکند ولی هنگام نمایش موجودی، از “ارزش فعلی” (present value) استفاده میکند، کافی است به نحوه عملکرد تابع balanceOf()
در فایل Comet.sol
توجه کنیم.
در این تابع که از نوع view
است، ابتدا مقدار بهروزشده baseSupplyIndex
خوانده میشود، بدون آنکه تغییری در وضعیت قرارداد ایجاد کند. سپس مقدار principal
یا همان موجودی اصلی وام دهنده خوانده میشود و در مقدار baseSupplyIndex
ضرب میشود تا موجودی فعلی بهدست آید.
از آنجا که سود حاصل از سپرده تابعی از زمان و نرخ استفاده (utilization) است، در صورتی که utilization صفر نباشد، هر بار که تابع balanceOf()
فراخوانی شود، مقدار بازگشتی آن نسبت به دفعات قبلی بیشتر خواهد بود.
اسکرینشات زیر رابطه بین موجودی وام دهنده در رابط کاربری اپلیکیشن غیرمتمرکز Compound V3 (dapp) و مقدار بازگشتی تابع balanceOf
در Etherscan را برای همان آدرس نشان میدهد. هر دو عدد با یک کادر نارنجی مشخص شدهاند تا مطابقت کامل آنها قابل مشاهده باشد.
مثال تصویری از رفتار baseSupplyIndex
زمانی که یک وام دهنده سپرده گذاری میکند، موجودی USDC او بر baseSupplyIndex
در آن لحظه تقسیم میشود تا مقدار ارزش اصلی (principal value) محاسبه شود — این همان مقداری است که در قرارداد ذخیره میشود. هرچه زمان سپرده گذاری دیرتر باشد، مقدار baseSupplyIndex
بزرگتر خواهد بود و در نتیجه، کاربر اعتبار کمتری بهعنوان ارزش اصلی دریافت میکند.
ارزش اصلی عددی ثابت است (مگر آنکه کاربر سپرده جدیدی وارد کند یا برداشت انجام دهد)، اما ارزش فعلی (present value) از ضرب ارزش اصلی در baseSupplyIndex
بهدست میآید و این مقدار بهصورت مداوم در حال افزایش است.
در نمودار زیر:
-
آلیس مبلغ 1 دلار را زمانی سپرده گذاری کرد که
baseSupplyIndex
برابر با 1.01 بود. بنابراین، ارزش اصلی او برابر است با: 1 ÷ 1.01 = 0.99 -
باب نیز مبلغ 1 دلار سپرده گذاری کرد، اما در زمان دیرتری که مقدار
baseSupplyIndex
به 1.03 رسیده بود. بنابراین، ارزش اصلی او برابر است با: 1 ÷ 1.03 = 0.97
در نتیجه، با اینکه آلیس و باب هر دو مقدار یکسانی (1 دلار) واریز کردند، باب بهدلیل سپرده گذاری در زمان دیرتر و رشد شاخص، مقدار کمتری بهعنوان ارزش اصلی دریافت کرد.
توابع supplyBase()
و withdrawBase()
زمانی که یک کاربر دارایی پایه (base asset) را سپرده گذاری یا برداشت میکند، مقدار ارزش اصلی (principal value) او دوباره محاسبه و بهروزرسانی میشود. چنین توابعی در قراردادهای هوشمند، پایه ای ترین مفاهیم آموزش برنامه نویسی در حوزه دیفای بهشمار میروند و درک نحوه عملکرد آنها برای توسعه دهندگان کاملاً ضروری است.
فرض کنید آلیس 10 دلار را در زمانی سپرده گذاری کرده است که baseSupplyIndex
برابر با 10 بوده. در این شرایط، ارزش اصلی او معادل 1 دلار خواهد بود (10 ÷ 10 = 1). حالا تصور کنید که با گذشت زمان، شاخص به عدد 20 افزایش پیدا میکند؛ بنابراین، ارزش فعلی (present value) او به 20 دلار میرسد (1 × 20).
در ادامه، آلیس تصمیم میگیرد 10 دلار دیگر هم واریز کند. پس ارزش فعلی جدید حساب او باید 30 دلار باشد (20 دلار قبلی + 10 دلار جدید).
حالا این سؤال مطرح میشود:
ارزش اصلی جدید او باید چقدر باشد؟
(کمی فکر کنید!)
در حال حاضر baseSupplyIndex
برابر با 20 است و ارزش فعلی حساب برابر با 30 دلار است. پس ارزش اصلی جدید باید برابر باشد با:
30 ÷ 20 = 1.5
با همین منطق، تابع supplyBase()
در فایل Comet.sol
(در خط 829) دقیقاً همین فرآیند را هنگام سپرده گذاری یک وام دهنده انجام میدهد: ابتدا ارزش فعلی را محاسبه میکند، سپس با توجه به شاخص فعلی، مقدار ارزش اصلی جدید را محاسبه و ذخیره میکند.
زمانی که آلیس سپرده گذاری میکند، ابتدا مقدار ارزش اصلی فعلی او از ذخیره سازی خوانده میشود (کادر نارنجی). سپس این مقدار به ارزش فعلی تبدیل میشود (کادر سبز). بعد، مقدار جدیدی که کاربر هماکنون واریز کرده به آن اضافه میشود (کادر آبی).
مجموع بهدستآمده که بیانگر ارزش فعلی جدید است، مجدداً با استفاده از شاخص baseSupplyIndex
به ارزش اصلی جدید تبدیل میشود. این مقدار جدید در متغیری به نام dstPrincipalNew
(کادر زرد) ذخیره میشود و در نهایت، سیستم آن را بهعنوان ارزش اصلی بهروزشده کاربر در ساختار داده ذخیره میکند (کادر قرمز).
تابع updateBasePrincipal()
(کادر قرمز) وظیفه دارد مقدار UserBasic.principal
مربوط به حساب کاربر را با مقدار جدید dstPrincipalNew
جایگزین کند.
در سمت مقابل، تابع withdrawBase()
(در خط 1051 فایل Comet.sol
) همین منطق را بهصورت معکوس پیاده سازی میکند: ابتدا ارزش اصلی را به ارزش فعلی تبدیل میکند، سپس مقدار برداشتشده را کم میکند و مقدار باقیمانده را مجدداً به عنوان ارزش اصلی جدید ذخیره مینماید.
شاخص baseBorrowIndex
چیست؟
همانطور که baseSupplyIndex
رشد سود یک دلار وام داده شده را از ابتدای راه اندازی پروتکل دنبال میکند، شاخص baseBorrowIndex
نیز انباشت بدهی یک دلار وام گرفته شده را از همان نقطه آغاز، ردیابی میکند.
از آنجا که نرخ های بهره برای وام دهندگان و وام گیرندگان متفاوت است و هر کدام منحنی بهره مستقل خود را دارند، لازم است برای محاسبه دقیق، از دو شاخص مجزا استفاده شود:
-
baseSupplyIndex
برای وام دهندگان -
baseBorrowIndex
برای وام گیرندگان
این تفکیک باعث میشود سیستم بتواند بدهی و سود را بهطور مجزا و دقیق محاسبه کند، بدون اینکه تداخل یا خطای محاسباتی بهوجود آید.
آیا ممکن است شاخص ها از حد مجاز عبور کنند؟ (Overflow)
هر دو شاخص baseSupplyIndex
و baseBorrowIndex
بهصورت اعداد ممیز ثابت با 15 رقم اعشار در نظر گرفته میشوند. بهعبارت دیگر، مقدار 1e15
معادل عدد 1.0 در این سیستم است.
بیشترین مقداری که یک عدد علامتدار 104 بیتی میتواند نگه دارد برابر است با 1.014e31. در نتیجه، با احتساب 15 رقم اعشار، شاخص تجمیعی (accumulator) حداکثر میتواند تا مقدار 1.014e16 افزایش یابد.
حال اگر فرض کنیم که نرخ بهره در پروتکل وام دهی هرگز از 100 درصد سالانه (APR) فراتر نرود، در آن صورت 53 سال طول میکشد تا شاخص دچار overflow شود. اگر نرخ بهره منطقیتری مانند 10 درصد سالانه را در نظر بگیریم، رسیدن یک دلار به مقدار 10 تریلیون دلار از طریق سود مرکب، 386 سال زمان میبرد.
با این حساب، میتوان با اطمینان گفت که احتمالاً نسخه چهارم پروتکل Compound خیلی زودتر از آن روز منتشر خواهد شد.
راستی! برای دریافت مطالب جدید در کانال تلگرام یا پیج اینستاگرام سورس باران عضو شوید.
- انتشار: ۲۶ تیر ۱۴۰۴
دسته بندی موضوعات
- آموزش ارز دیجیتال
- آموزش برنامه نویسی
- آموزش متنی برنامه نویسی
- اطلاعیه و سایر مطالب
- پروژه برنامه نویسی
- دوره های تخصصی برنامه نویسی
- رپورتاژ
- فیلم های آموزشی
- ++C
- ADO.NET
- Adobe Flash
- Ajax
- AngularJS
- apache
- ARM
- Asp.Net
- ASP.NET MVC
- AVR
- Bootstrap
- CCNA
- CCNP
- CMD
- CSS
- Dreameaver
- EntityFramework
- HTML
- IOS
- jquery
- Linq
- Mysql
- Oracle
- PHP
- PHPMyAdmin
- Rational Rose
- silver light
- SQL Server
- Stimulsoft Reports
- Telerik
- UML
- VB.NET&VB6
- WPF
- Xml
- آموزش های پروژه محور
- اتوکد
- الگوریتم تقریبی
- امنیت
- اندروید
- اندروید استودیو
- بک ترک
- بیسیک فور اندروید
- پایتون
- جاوا
- جاوا اسکریپت
- جوملا
- دلفی
- دوره آموزش Go
- دوره های رایگان پیشنهادی
- زامارین
- سئو
- ساخت CMS
- سی شارپ
- شبکه و مجازی سازی
- طراحی الگوریتم
- طراحی بازی
- طراحی وب
- فتوشاپ
- فریم ورک codeigniter
- فلاتر
- کانستراکت
- کریستال ریپورت
- لاراول
- معماری کامپیوتر
- مهندسی اینترنت
- هوش مصنوعی
- یونیتی
- کتاب های آموزشی
- Android
- ASP.NET
- AVR
- LINQ
- php
- Workflow
- اچ تی ام ال
- بانک اطلاعاتی
- برنامه نویسی سوکت
- برنامه نویسی موبایل
- پاسکال
- پایان نامه
- پایتون
- جاوا
- جاوا اسکریپت
- جی کوئری
- داده کاوی
- دلفی
- رباتیک
- سئو
- سایر کتاب ها
- سخت افزار
- سی اس اس
- سی پلاس پلاس
- سی شارپ
- طراحی الگوریتم
- فتوشاپ
- مقاله
- مهندسی نرم افزار
- هک و امنیت
- هوش مصنوعی
- ویژوال بیسیک
- نرم افزار و ابزار برنامه نویسی
- وردپرس