Compound लेंडर्स (lenders) और बॉरोअर्स (borrowers) को COMP टोकन में रिवॉर्ड (rewards) जारी करता है, जो मार्केट की लेंडिंग और बॉरोइंग में उनके हिस्से (share) के अनुपात में होता है।
यह एल्गोरिदम MasterChef Staking Algorithm के काफी समान है, इसलिए पाठक को पहले उससे परिचित हो जाना चाहिए।
Compound V3 rewards का हाई-लेवल ओवरव्यू
MasterChef के समान ही, Compound V3 रिवॉर्ड कॉन्ट्रैक्ट यह ट्रैक करता है कि शुरुआत से लेकर अब तक एक काल्पनिक “staked” USDC ने कितना कमाया है। यहाँ “staking” का मतलब बॉरोइंग या लेंडिंग दोनों हो सकता है — दोनों गतिविधियों के लिए रिवॉर्ड दिया जाता है।
Compound V3 के baseSupplyIndex या MasterChef के rewardPerTokenAcc के अनुरूप, Compound rewards में trackingSupplyIndex और trackingBorrowIndex होते हैं जो शुरुआत से लेकर अब तक एक USDC (lent या borrowed) के लिए रिवॉर्ड को ट्रैक करते हैं।
MasterChef की तरह, जब अधिक USDC को “stake” किया जाता है, तो एक सिंगल USDC द्वारा कलेक्ट किए जाने वाले रिवॉर्ड की मात्रा “diluted” (कम) हो जाती है और इसके विपरीत भी ऐसा ही होता है।
MasterChef के विपरीत, प्रति यूनिट समय में मिलने वाला रिवॉर्ड immutable वेरिएबल्स baseSupplyTrackingSupplySpeed और baseTrackingBorrowSpeed के साथ सेट किया जाता है। डिस्ट्रीब्यूट किए जाने वाले रिवॉर्ड्स को “rescale” करने का विकल्प Governance के पास होता है।
यूज़र्स Comet Rewards से रिवॉर्ड क्लेम करते हैं जो मुख्य Comet लेंडिंग कॉन्ट्रैक्ट से अलग एक सेपरेट कॉन्ट्रैक्ट है।
यदि कुल बॉरो किया गया अमाउंट या कुल लेंड किया गया अमाउंट एक निश्चित थ्रेशोल्ड से कम है, तो Compound रिवॉर्ड जारी नहीं करता है, इसके कारणों पर हम आगे चर्चा करेंगे।
MasterChef की तरह, रिवॉर्ड्स आटोमेटिक रूप से डिस्ट्रीब्यूट नहीं होते हैं, उन्हें एक अलग ट्रांज़ैक्शन में क्लेम करना पड़ता है। नीचे दिया गया स्क्रीनशॉट एकत्रित COMP टोकन को क्लेम करने के लिए फ्रंटएंड दिखाता है, जिसमें yellow circle (पीले घेरे) में टोकन क्लेम करने का एक्शन है।

Comet Rewards Contract को कभी-कभी Top Up करने की आवश्यकता होती है
Comet Rewards कॉन्ट्रैक्ट COMP टोकन मिंट (mint) नहीं करता है, यह Governance द्वारा इसे टोकन ट्रांसफर करने पर निर्भर करता है। सर्कुलेशन में कुल 10 मिलियन COMP टोकन की सप्लाई है, और ये सभी पहले ही मिंट किए जा चुके हैं। सप्लाई का एक महत्वपूर्ण हिस्सा governance के पास है। समय-समय पर, COMP टोकन को governance ट्रेज़री से रिवॉर्ड कॉन्ट्रैक्ट में ट्रांसफर किया जाता है। आप निम्नलिखित governance ट्रांज़ैक्शन्स को देख सकते हैं जो रिवॉर्ड कॉन्ट्रैक्ट को “top up” करते हैं।
https://compound.finance/governance/proposals/194 (Nov 21, 2023)
https://compound.finance/governance/proposals/164 (June 29, 2023)
रिवॉर्ड्स कॉन्ट्रैक्ट का मेननेट एड्रेस है:
0x1B0e765F6224C21223AeA2af16c1C46E38885a40
क्योंकि सप्लाई फिक्स्ड है, इकोसिस्टम में भाग लेने के लिए COMP रिवॉर्ड्स तब तक अनिश्चित काल (indefinitely) के लिए जारी नहीं रह सकते जब तक कि governance ओपन मार्केट से COMP टोकन न खरीदे।
नीचे दिए गए Etherscan स्क्रीनशॉट में हम देखते हैं कि कॉन्ट्रैक्ट के साथ अधिकांश ट्रांज़ैक्शन्स COMP टोकन को क्लेम करने के लिए हैं (blue box), और कॉन्ट्रैक्ट के पास वर्तमान में लगभग 73,000 COMP टोकन होल्ड हैं (blue arrow)।

trackingSupplyIndex और trackingBorrowIndex, rewardPerTokenAcc की तरह व्यवहार करते हैं
नीचे दिया गया प्लॉट MasterChef से परिचित लगना चाहिए। जितना अधिक USDC “staked” किया जाता है, प्रत्येक टोकन को उतना ही कम रिवॉर्ड मिलता है क्योंकि हर पीरियड में केवल एक निश्चित अमाउंट ही जारी किया जाता है (trackingSupplyIndex और trackingBorrowIndex द्वारा निर्धारित)।
एक ध्यान देने योग्य बदलाव यह है कि यदि सप्लाई या बॉरो किए गए USDC का अमाउंट (pink line) baseMinForRewards (red text and dashed line) थ्रेशोल्ड से नीचे है, तो USDC पर रिवॉर्ड्स एक्युमुलेट (accumulate) नहीं होते हैं, और उस स्टेट अपडेट के लिए trackingSupplyIndex (या trackingBorrowIndex) नहीं बढ़ता है।

ये वेरिएबल्स पब्लिक नहीं हैं, लेकिन इन्हें CometExt में मौजूद totalsBasic() पब्लिक फंक्शन के ज़रिए प्राप्त किया जा सकता है। चूँकि CometExt एक सेपरेट कॉन्ट्रैक्ट है जिसे Comet delegatecalls जारी करता है, इसलिए हम Etherscan के माध्यम से इन वैल्यूज़ को प्राप्त नहीं कर सकते हैं। इसके बजाय हम उन्हें प्राप्त करने के लिए Foundry से cast का उपयोग करते हैं, जैसा कि नीचे दिया गया स्क्रीनशॉट दिखाता है।

baseMinForRewards
baseMinForRewards को line 86 in Comet.sol में डिफाइन किया गया है।

यदि 1 मिलियन डॉलर (1e12 USDC, क्योंकि USDC में 6 डेसीमल्स होते हैं) से कम अमाउंट लेंड किया गया है, तो Compound लेंडर्स या बॉरोअर्स को रिवॉर्ड जारी नहीं करता है। इसी तरह, बॉरो किया गया USDC COMP रिवॉर्ड्स एक्युमुलेट नहीं करेगा यदि बॉरो किया गया अमाउंट 1 मिलियन डॉलर से कम है।

accumulator overflow को रोकने के लिए baseMinRewards मौजूद है
याद रखें कि प्रति टोकन एक्युमुलेटेड रिवॉर्ड स्टेक किए गए टोकन की मात्रा के इन्वर्सली प्रोपोर्शनल (inversely proportional) होता है। यदि स्टेक किए गए टोकन की कुल सप्लाई कम है, तो एक्युमुलेटर (accumulator) तेज़ी से एक्युमुलेट करेगा, जिससे संभवतः यह बहुत जल्दी ओवरफ्लो हो सकता है।
यदि आप एक ऑडिटर हैं, तो यह काफी हद तक नज़रअंदाज़ की गई एक मीडियम वल्नरेबिलिटी (medium vulnerability) हो सकती है क्योंकि टेस्ट आसानी से एक्युमुलेटर ओवरफ्लो को नहीं पकड़ पाते हैं। आपको यह सुनिश्चित करने की आवश्यकता है कि एक्युमुलेटर कई वर्षों तक ओवरफ्लो नहीं होगा और इसका अर्थ है कि या तो रिवॉर्ड रेट को छोटा होना चाहिए या स्टेक किए गए अमाउंट को बड़ा होना चाहिए।
accrueInternal() पर फिर से विचार (revisited)
जब भी accrueInternal() को कॉल किया जाता है तो trackingSupplyIndex और trackingBorrowIndex अपडेट हो जाते हैं।
नीचे दिया गया कोड ऊपर के सेक्शन में बताए गए लॉजिक को इम्प्लीमेंट करता है। red boxes (लाल बॉक्स) में दी गई if कंडीशन trackingSupplyIndex या trackingBorrowIndex को अधिक रिवॉर्ड एक्युमुलेट करने से रोकती हैं यदि सप्लाई या बॉरो अमाउंट baseMinForRewards से कम है। baseTrackingSupplySpeed और baseTrackingBorrowSpeed (नीले बॉक्स) immutable वेरिएबल्स हैं, इसलिए इंडेक्स जिस अमाउंट से बढ़ते हैं वह केवल timeElapsed और (इनवर्सली) totalSupplyBase (या totalBorrowBase) पर निर्भर करता है।

आप baseTrackingSupplySpeed और baseTrackingBorrowSpeed को “प्रति यूनिट समय रिवॉर्ड” के रूप में सोच सकते हैं। जब इसे timeElapsed से गुणा किया जाता है, तो यह सिंगल पार्टिसिपेटिंग USDC के लिए एक्युमुलेटेड रिवॉर्ड की मात्रा की गणना करता है। अंत में, उस रिज़ल्ट को totalSupplyBase या totalBorrowBase से विभाजित करने पर कुल अमाउंट के आधार पर वह USDC डाइल्यूट (dilute) हो जाता है।
baseSupplyTrackingSpeed और baseTrackingBorrowSpeed
ये वेरिएबल्स MasterChef के rewardPerBlock के समान हैं। वे यह स्पेसिफाई करते हैं कि ऊपर के सेक्शन में बताए गए एक्युमुलेटर्स कितनी तेज़ी से बढ़ते हैं।
हम उनकी वैल्यूज़ को Comet Etherscan Proxy contract से प्राप्त कर सकते हैं।

ये दोनों वेरिएबल्स अपने डेसीमल्स के लिए trackingIndexScale का उपयोग करते हैं, और per Etherscan, trackingIndexScale is 1e15:

चूँकि वे 15 डेसीमल fixed point numbers हैं, इसलिए उनकी वैल्यूज़ इस प्रकार हैं:
baseTrackingSupplySpeed = 2.979166666666e-03
baseTrackingBorrowSpeed = 4.414467592592e-03
Comet.sol से वेरिएबल डेफिनिशन्स (स्केल के बारे में कमैंट्स के साथ) का स्क्रीनशॉट नीचे दिया गया है

User-level rewards को ट्रैक करना: baseTrackingAccrued और baseTrackingIndex
MasterChef की तरह, Compound Rewards किसी अकाउंट में रिवॉर्ड्स तब एक्युमुलेट करता है जब वह अकाउंट कोई स्टेट-चेंजिंग ट्रांज़ैक्शन (state-changing transaction) करता है। और MasterChef की तरह ही, यूज़र जो रिवॉर्ड्स एक्युमुलेट करता है, वह उनके बैलेंस और इस बात के प्रोपोर्शनल (proportional) होता है कि पिछली बार यूज़र द्वारा स्टेट-चेंजिंग ऑपरेशन करने के बाद से “index” या “accumulator” में कितना बदलाव आया है।
आइए यूज़र struct को फिर से देखें

baseTrackingIndex, UserBasic स्टोरेज struct के अंतिम बार अपडेट होने के समय trackingSupplyIndex या trackingBorrowIndex की वैल्यू है, जो इस पर निर्भर करता है कि अकाउंट लेंडर है या बॉरोअर है। करंट trackingSupplyIndex (या trackingBorrowIndex) और यूज़र के baseTrackingIndex की स्टोर्ड वैल्यू के बीच का डेल्टा यह निर्धारित करता है कि वे उस ट्रांज़ैक्शन के लिए कितने रिवॉर्ड्स एक्युमुलेट करेंगे। नीचे दिए गए प्लॉट पर विचार करें:

जब भी कोई यूज़र ऐसा कुछ करता है जिससे उनका प्रिंसिपल (principal) बदल जाएगा, तो इंटरनल फंक्शन updateBasePrincipal() को कॉल किया जाता है। यह फंक्शन यह निर्धारित करेगा कि पिछले अपडेट के बाद से trackingSupplyIndex या trackingBorrowIndex में कितना बदलाव आया है और उसी के अनुसार यूज़र के baseTrackingAccrued में रिवॉर्ड एक्युमुलेट करेगा। फंक्शन नीचे दिखाया गया है

संक्षेप में baseTrackingIndex इंडेक्स की वह वैल्यू है जब यूज़र ने आख़िरी बार अपडेट किया था। baseTrackingAccrued यूज़र के प्रोटोकॉल में भाग लेने के बाद से उन्हें मिलने वाला कुल रिवॉर्ड है, भले ही उनके पिछले क्लेम कुछ भी रहे हों जिन्हें रिवॉर्ड कॉन्ट्रैक्ट में ट्रैक किए गए रिवॉर्ड डेट (reward debt) के साथ नेगेट (negate) कर दिया जाता है।
accrualDescaleFactor क्या है?
ऊपर दिए गए कोड में, हम देखते हैं कि यूज़र के एक्युमुलेटेड रिवॉर्ड्स को accrualDescaleFactor से विभाजित किया गया है।
यह ETH और USDC दोनों को एक ही स्केल पर ट्रैक करने की अनुमति देता है। चूँकि ETH में 18 डेसीमल्स होते हैं और USDC में 6 डेसीमल्स होते हैं, इसलिए ETH के baseTrackingAccrued को 1e12 से विभाजित किया जाता है ताकि यह प्रभावी रूप से USDC के समान “डेसीमल्स” की संख्या रख सके। यह baseTrackingAccrued को दोनों एसेट्स को एक ही स्केल पर ट्रैक करने की अनुमति देता है।
रिवॉर्ड्स क्लेम करना (Claiming rewards)
रिवॉर्ड क्लेम करने के लिए, एक यूज़र बस CometReward.sol में claim() फंक्शन को कॉल करता है। rewardsClaimed मैपिंग (red box) MasterChef के rewardDebt की तरह व्यवहार करती है।

shouldAccrue आर्ग्यूमेंट किसके लिए है?
यदि कोई ट्रांज़ैक्शन में एकमात्र एक्शन के रूप में रिवॉर्ड क्लेम कर रहा है, तो shouldAccrue (green box) को true होना चाहिए। हालाँकि, यदि यह अन्य फंक्शन कॉल्स के बाद हो रहा है, तो अन्य स्टेट-चेंजिंग फंक्शन कॉल्स accrueAccount() को कॉल करेंगे, जिससे एक और कॉल अनावश्यक हो जाएगा।
getRewardAccrued() (CometRewards.sol)
ऊपर दिए गए blue box में, getRewardAccrued यह निर्धारित करता है कि यूज़र को कितना भुगतान करना है। यह बस Comet में यूज़र struct से baseTrackingAccrued को क्वेरी (query) करता है। फिर CometRewards इसे उनके रिवॉर्ड डेट (rewardsClaimed) से घटा देता है और यूज़र को बचा हुआ अमाउंट (difference) पे करता है।

स्वयं COMP टोकन में Quirks (ख़ासियतें)
रिवॉर्ड्स कॉन्ट्रैक्ट जिस COMP token को डिस्ट्रीब्यूट करता है, वह अधिकांश ERC 20 टोकन्स की तरह बैलेंस को uint256 के रूप में स्टोर नहीं करता है, बल्कि uint96 के रूप में स्टोर करता है।

यदि आप uint96 की मैक्सिमम वैल्यू से अधिक अमाउंट को transfer या approve करने का प्रयास करते हैं, तो ट्रांज़ैक्शन रिवर्ट (revert) हो जाएगा।

RareSkills के साथ और जानें
अधिक एडवांस्ड स्मार्ट कॉन्ट्रैक्ट डेवलपमेंट सीखने के लिए कृपया हमारा Solidity bootcamp देखें।
मूल रूप से 10 जनवरी, 2024 को पब्लिश किया गया