MasterChef और Synthetix स्टेकिंग एल्गोरिदम stakers के बीच एक निश्चित reward pool को उनके time-weighted योगदान के अनुसार वितरित करते हैं। गैस (gas) बचाने के लिए, ये एल्गोरिदम टोकन-स्तर के reward के एक संचयी काउंटर (cumulative counter) का उपयोग करते हैं और reward वितरण को स्थगित (defer) कर देते हैं।
कल्पना करें कि हमारे पास 100,000 REWARD टोकन का एक निश्चित पूल है जिसे हम ब्लॉक 1 से 100 तक stakers को निष्पक्ष रूप से वितरित करना चाहते हैं।
हमारा लक्ष्य हर ब्लॉक में 1,000 REWARD वितरित करना है, जिसे stakers के बीच उनके stake के अनुसार बांटा गया हो।
उदाहरण के लिए, यदि किसी विशेष ब्लॉक पर, कॉन्ट्रैक्ट में स्टेकिंग बैलेंस इस प्रकार थे:
| स्टेक की गई राशि | पूल का % | |
|---|---|---|
| Alice | 100 | 25% |
| Bob | 100 | 25% |
| Chad | 200 | 50% |
तो उस ब्लॉक में वितरित किए गए 1,000 REWARD इस प्रकार होंगे:
| स्टेक की गई राशि | पूल का % | वितरित किया गया Reward | |
|---|---|---|---|
| Alice | 100 | 25% | 250 |
| Bob | 100 | 25% | 250 |
| Chad | 200 | 50% | 500 |
टोकन और रिवॉर्ड के लिए शब्दावली (Terminology)
स्टेक किया गया टोकन और स्टेकिंग रिवॉर्ड एक ही करेंसी (currency) हो भी सकते हैं और नहीं भी। स्पष्टता के लिए हम उन्हें टोकन (token) और रिवॉर्ड (reward) कहेंगे – कभी-कभी TOKEN और REWARD के रूप में।
रिवॉर्ड वितरित करने के लिए हर ब्लॉक में एक ट्रांज़ैक्शन भेजना अव्यावहारिक है
इसका एक साधारण समाधान यह होगा कि एक ऑफ-चेन (off-chain) बॉट हर ब्लॉक में ट्रांज़ैक्शन भेजे, जो कॉन्ट्रैक्ट में मौजूद हर staker के TOKEN बैलेंस को पढ़े और पूल में उनके प्रतिशत के अनुसार उन्हें REWARD मिंट (mint) करे।
हालाँकि, हर ब्लॉक में ट्रांज़ैक्शन शामिल करवाने का कोई विश्वसनीय तरीका नहीं है। यदि बॉट एक भी ब्लॉक मिस कर देता है, तो यूज़र्स को उनकी उम्मीद से कम रिवॉर्ड मिलेगा।
इस रणनीति में बहुत अधिक ट्रांज़ैक्शन फीस भी लगेगी।
हर ब्लॉक में ट्रांज़ैक्शन भेजना अनावश्यक है, हम उन ब्लॉक्स के लिए “catch up” रिवॉर्ड जारी कर सकते हैं जहां रिवॉर्ड वितरित नहीं किए गए थे
मान लीजिए कि हमने एक वेरिएबल lastUpdateBlockNumber रखा है जो यह ट्रैक करता है कि हमने पिछली बार रिवॉर्ड कब जारी किया था। हम पिछले रिवॉर्ड के बाद से अब तक के ब्लॉक्स की संख्या की गणना block.number - lastUpdateBlockNumber के रूप में करेंगे।
फिर हम कुछ ब्लॉक्स को छोड़ (skip) सकते हैं जहाँ हम रिवॉर्ड वितरित करते हैं और जब हम वास्तव में रिवॉर्ड वितरित करते हैं तो “catch up” (कवर) कर सकते हैं।
नीचे दिया गया ग्राफ इसे दर्शाता है।

हालाँकि, अभी भी हमारे पास उन रिवॉर्ड्स को सभी stakers के बीच उनके स्टेक प्रतिशत के अनुसार वितरित करने का कोई अच्छा तरीका नहीं है जिन्हें हमने अभी मिंट किया है।
साथ ही, हम यह नहीं जानते कि पिछले रिवॉर्ड वितरण के बाद से पिछले अंतराल (interval) के दौरान स्टेक किए गए टोकन का बैलेंस स्थिर (constant) था या नहीं। उदाहरण के लिए, क्या होगा यदि Chad को पता था कि हम ब्लॉक 100 पर बैलेंस मापने वाले हैं और उसने रिवॉर्ड का बड़ा हिस्सा पाने के लिए ब्लॉक 99 पर एक बड़ा डिपॉजिट (deposit) कर दिया?
यह समस्या सुलझाने में आसान साबित होती है।
मुख्य सिद्धांत (Key invariant): कोई ट्रांज़ैक्शन नहीं, कोई बैलेंस बदलाव नहीं
हर 20 ब्लॉक या उसके आस-पास एक बॉट से ट्रांज़ैक्शन फायर करवाने के बजाय, हम केवल किसी यूज़र के कॉन्ट्रैक्ट के साथ deposit() या withdraw() जैसे state changing फंक्शन के ज़रिए इंटरैक्ट करने का इंतज़ार कर सकते हैं।
इन फंक्शन्स को कॉल किए जाने के बीच, हम यह सुनिश्चित कर सकते हैं कि किसी का भी बैलेंस नहीं बदला है।
उदाहरण के लिए, यदि ब्लॉक 10 और ब्लॉक 15 के बीच Alice के पास 50% स्टेक था और Bob के पास 50% स्टेक था, तो हम 5,000 REWARD (5 ब्लॉक्स गुणा 1,000) जारी कर सकते हैं और उनमें से प्रत्येक को 50% दे सकते हैं।
Chad या Bob के लिए बीच में “कूदने” (jump in) और अपना बैलेंस बढ़ाने का कोई तरीका नहीं है क्योंकि जब वे deposit() कॉल करेंगे, तो वे एक रिवॉर्ड वितरण को ट्रिगर कर देंगे। और रिवॉर्ड वितरण फंक्शन को इस तरह प्रोग्राम किया गया है कि इसमें उनके हालिया डिपॉजिट को शामिल नहीं किया जाता है।
समय के साथ बैलेंस में होने वाले बदलावों को दिखाने वाले नीचे दिए गए ग्राफ पर विचार करें। इन बदलावों के होने का एकमात्र तरीका यह है कि स्मार्ट कॉन्ट्रैक्ट के साथ कोई ट्रांज़ैक्शन हुआ हो।

हालाँकि, यह समाधान स्केल (scale) नहीं करता है।
सभी stakers पर लूप (Looping) चलाना गैस-इंटेंसिव (gas-intensive) है
जब भी कोई deposit() या withdraw() कॉल करता है, तो हर staker को उनका REWARD वितरित करना बहुत गैस खर्चीला (gas expensive) होगा यदि वहाँ दर्जनों stakers हों। ERC-20 टोकन को ट्रांसफर करना सस्ता नहीं है, और इसे एक लूप में दर्जनों बार करना वर्जित (prohibitive) है।
इस स्टेकिंग को कुशलतापूर्वक (efficiently) करने के लिए, लोगों को केवल तभी रिवॉर्ड ट्रांसफर किया जा सकता है जब वे कोई state changing ट्रांज़ैक्शन शुरू करते हैं। जो लोग अपना रिवॉर्ड क्लेम (claim) नहीं करते हैं, उनके रिवॉर्ड स्थगित (deferred) कर दिए जाते हैं। रिवॉर्ड कॉन्ट्रैक्ट में ही रहते हैं और उनके द्वारा क्लेम किए जाने का इंतज़ार करते हैं।
यह हमें एक साथ बहुत सारे ERC-20 ट्रांसफर करने से बचाएगा।
एक कुशल समाधान प्राप्त करने के लिए:
- हम केवल उस अकाउंट से जुड़े अकाउंट वेरिएबल्स को अपडेट कर सकते हैं जो ट्रांज़ैक्शन शुरू कर रहा है
- हम केवल एक सिंगल ग्लोबल वेरिएबल को अपडेट कर सकते हैं जो बाकी सभी के बढ़े हुए रिवॉर्ड एलोकेशन (allocation) को ट्रैक करता है, हम स्पष्ट रूप से प्रत्येक अकाउंट को अपडेट नहीं कर सकते
अकाउंट्स में रिवॉर्ड accruals को ट्रैक करने के बजाय, हम एक सिंगल स्टेक किए गए टोकन के लाभ (gains) को ट्रैक करते हैं
मान लीजिए कि हम सटीक रूप से यह ट्रैक कर सकते हैं कि एक सिंगल स्टेक किए गए टोकन ने “शुरुआत से” (जब से कॉन्ट्रैक्ट ने रिवॉर्ड वितरित करना शुरू किया) कितना रिवॉर्ड संचित (accumulate) किया है।
यदि ऐसा है, तो किसी अकाउंट ने कितना रिवॉर्ड अर्जित (accrue) किया है, इसे ट्रैक करने के लिए बस उनके टोकन बैलेंस को इस बात से गुणा करना होता है कि शुरुआत से लेकर अब तक एक सिंगल टोकन ने कितना रिवॉर्ड अर्जित किया है।
मान लीजिए कि हम जानते हैं कि शुरुआत से लेकर वर्तमान समय तक स्टेक किए गए एक टोकन ने 12 रिवॉर्ड जमा किए हैं। यदि Alice का स्टेक 100 है, तो उसे 1,200 रिवॉर्ड मिलने चाहिए।
यह कुछ इस तरह कहने जैसा है कि “हमारे बैंक में बचाए गए एक डॉलर ने बैंक खुलने के बाद से $0.40 का ब्याज कमाया है। यदि आपने तब अकाउंट खोला था जब हमने बैंक खोला था, और तब से न तो कुछ जमा किया है और न ही निकाला है, तो आपने 40% ब्याज कमाया है।”
इससे हमारे सामने दो प्रश्न आते हैं:
-
हम शुरुआत से लेकर अब तक एक सिंगल टोकन के लिए रिवॉर्ड accruals को कैसे ट्रैक करते हैं।
-
क्या होगा यदि Alice शुरुआत से स्टेकिंग नहीं कर रही है, बल्कि उसने हाल ही में डिपॉजिट किया है
हम शुरुआत से लेकर अब तक एक सिंगल टोकन के लिए रिवॉर्ड accruals को कैसे ट्रैक करते हैं
चूंकि हर ब्लॉक में एक निश्चित संख्या में रिवॉर्ड जारी किए जाते हैं (हमारे वर्तमान उदाहरण में 1,000), जितने अधिक stakers होंगे, वे निश्चित 1,000 रिवॉर्ड का उतना ही छोटा हिस्सा कमाएंगे। इससे कोई फर्क नहीं पड़ता कि कितने लोग स्टेक करते हैं, केवल कॉन्ट्रैक्ट में स्टेक किए गए टोकन की कुल सप्लाई मायने रखती है।
निम्नलिखित काल्पनिक उदाहरण पर विचार करें।
| प्रति ब्लॉक जारी किए गए रिवॉर्ड | स्टेक किए गए टोकन की सप्लाई | प्रति ब्लॉक प्रति टोकन रिवॉर्ड | |
|---|---|---|---|
| blocks 1-5 | 1,000 | 100 | 10 |
| blocks 6-13 | 1,000 | 200 | 5 |
| blocks 14-15 | 1,000 | 100 | 10 |
| blocks 16-20 | 1,000 | 500 | 2 |
जितने अधिक टोकन स्टेक किए जाते हैं, प्रति ब्लॉक प्रति टोकन रिवॉर्ड उतना ही कम होता है। बड़े स्टेक्स रिवॉर्ड को डाइल्यूट (dilute) कर देते हैं, और परिणामस्वरूप एक सिंगल टोकन कम कमाता है।
इस टेबल को नीचे विज़ुअली प्लॉट किया गया है। लाल प्लॉट स्टेक किए गए टोकन की सप्लाई है। बैंगनी रेखा उस रिवॉर्ड की राशि है जो उस ब्लॉक में एक सिंगल टोकन में जमा (accrue) होती है। x-एक्सिस पर ब्लॉक्स दाईं ओर बढ़ते हैं। दोनों वेरिएबल्स के बीच का विपरीत संबंध (inverse relationship) स्पष्ट होना चाहिए।

यहाँ मुख्य बात यह है:
हर बार जब हमारे पास एक state changing ट्रांज़ैक्शन होता है, तो हम पीछे मुड़कर देखते हैं कि कितने ब्लॉक्स बीत चुके हैं, उसे प्रति ब्लॉक रिवॉर्ड से गुणा करते हैं, और फिर उसे स्टेक की गई कुल सप्लाई से विभाजित (divide) करते हैं। यह वह रिवॉर्ड है जो एक टोकन ने उस अंतराल (interval) में अर्जित (accrue) किया है। इसके बाद हम इस वैल्यू को एक ग्लोबल एक्युमुलेटर (global accumulator) में जोड़ देते हैं जो शुरुआत में शून्य से शुरू हुआ था। यदि हम हर बार ट्रांज़ैक्शन आने पर इस प्रक्रिया को दोहराते रहें, तो हमें पता चल जाएगा कि शुरुआत से लेकर अब तक एक सिंगल टोकन ने रिवॉर्ड्स में कितना संचित (accumulate) किया है।
यहाँ एक्युमुलेटर (accumulator) जोड़े जाने के साथ वही प्लॉट दिया गया है।

और यहाँ उन्हीं वैल्यूज़ को दर्शाने वाली एक टेबल है:
| प्रति ब्लॉक जारी किए गए रिवॉर्ड | स्टेक किए गए टोकन की सप्लाई | प्रति ब्लॉक प्रति टोकन रिवॉर्ड | अंतराल में ब्लॉक्स की संख्या | अंतराल में जारी किए गए रिवॉर्ड | प्रति टोकन संचित (Accumulated) रिवॉर्ड | |
|---|---|---|---|---|---|---|
| block 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| blocks 1-5 | 1,000 | 100 | 10 | 5 | 50 | 50 |
| blocks 6-13 | 1,000 | 200 | 5 | 8 | 40 | 90 |
| blocks 14-15 | 1,000 | 100 | 10 | 2 | 20 | 110 |
| blocks 16-20 | 1,000 | 500 | 2 | 5 | 10 | 120 |
कहने का तात्पर्य यह है कि हमारे प्लॉट के दौरान स्टेक किए गए एक सिंगल टोकन ने 120 रिवॉर्ड संचित कर लिया है।
टेस्ट केस (Test case): Alice को रिवॉर्ड देना जो शुरुआत से स्टेकिंग कर रही है
आइए एक बहुत ही सरल उदाहरण पर विचार करें। हम फिर से प्रति ब्लॉक 1,000 रिवॉर्ड जारी कर रहे हैं। 20 ब्लॉक्स के दौरान, 20,000 रिवॉर्ड जारी किए जाएंगे।
Alice और Bob यह करते हैं:
- Alice ने ब्लॉक 1 से ब्लॉक 20 तक 100 टोकन स्टेक किए हैं।
- ब्लॉक 10 पर, Bob 100 टोकन स्टेक करता है।
- ब्लॉक 20 पर, Alice को उस समय तक जारी किए गए सभी रिवॉर्ड्स का 75% मिलना चाहिए, जो कि 15,000 रिवॉर्ड होता है।
विज़ुअली, प्रति ब्लॉक स्टेक पूल का हिस्सा निम्नलिखित जैसा दिखेगा।

ब्लॉक 1 से ब्लॉक 10 तक, प्रति ब्लॉक प्रति टोकन रिवॉर्ड 10 (1,000 ÷ 100) था। 10 ब्लॉक्स के उस अंतराल में, प्रत्येक टोकन ने 100 रिवॉर्ड संचित किए (10 रिवॉर्ड प्रति टोकन प्रति ब्लॉक x 10 ब्लॉक्स)।
लेकिन जब Bob ने ब्लॉक 10 पर डिपॉजिट किया, तो प्रति ब्लॉक प्रति टोकन रिवॉर्ड डाइल्यूट होकर 5 (1,000 ÷ 200) हो गया। अगले 10 ब्लॉक के अंतराल (ब्लॉक 11 से 20) में, प्रत्येक टोकन ने 50 रिवॉर्ड संचित किए।
इसलिए, ब्लॉक 1 से ब्लॉक 10 तक एक टोकन द्वारा संचित कुल वैल्यू 100 थी, और 11 से 20 तक संचित वैल्यू 50 थी। अतः, एक टोकन द्वारा संचित कुल वैल्यू 100 + 50 = 150 है।
चूंकि Alice के पास 100 टोकन डिपॉजिट हैं, और प्रत्येक टोकन ने 150 रिवॉर्ड संचित किए हैं, इसलिए उसे 15,000 रिवॉर्ड जारी किए जाएंगे, जो वास्तव में जारी किए गए कुल रिवॉर्ड्स का 75% है।
क्या होगा यदि कोई शुरुआत से स्टेकिंग नहीं कर रहा है?
उपरोक्त उदाहरण में एक स्पष्ट कॉर्नर केस (corner case) (और जिसकी भविष्यवाणी हमने पहले के सेक्शन में की थी) यह है कि यदि Bob रिवॉर्ड क्लेम करता है, तो उसे भी 15,000 रिवॉर्ड मिलेगा क्योंकि ब्लॉक 20 पर उसका स्टेक 100 है, ठीक Alice की तरह।
इसे हल करने के लिए, हम केवल यह चाहते हैं कि एक्युमुलेटर Bob के लिए उसी क्षण से गिनती शुरू करे जब Bob ने डिपॉजिट किया था।
इसका सहज (intuitive) समाधान उस ब्लॉक नंबर को स्टोर करना है जहाँ Bob ने डिपॉजिट किया था और इसे बाद में सही करना है।
हालाँकि, उन रिवॉर्ड्स की मात्रा की गणना करना और भी आसान है जो उसे ब्लॉक 10 पर डिपॉजिट करने और तुरंत रिवॉर्ड क्लेम करने पर जारी किए गए होते। उदाहरण के लिए, ब्लॉक 10 पर, प्रति टोकन संचित रिवॉर्ड 100 था। चूंकि Bob ने 100 टोकन डिपॉजिट किए, इसलिए वह सैद्धांतिक (theoretically) रूप से तुरंत 10,000 रिवॉर्ड क्लेम कर सकता था।
ऐसा होने से रोकने के लिए, हमारे पास Bob के लिए एक वेरिएबल है जिसे हम “reward debt” कहते हैं। जैसे ही वह डिपॉजिट करता है, हम reward debt को डिपॉजिट किए गए बैलेंस गुणा प्रति टोकन एक्युमुलेटर रिवॉर्ड के बराबर सेट कर देते हैं। यह उसे तुरंत रिवॉर्ड क्लेम करने से रोकेगा क्योंकि उस समय, उसके लिए देय रिवॉर्ड शून्य होगा (current rewards माइनस reward debt)।
हमारे पास Bob के लिए “reward debt” या “rewards already issued” नाम का एक अलग वेरिएबल है और हम इसे उस काल्पनिक रिवॉर्ड राशि पर असाइन कर देते हैं। ब्लॉक 10 पर, प्रति टोकन संचित रिवॉर्ड 100 था, और Bob का डिपॉजिट 100 था, इसलिए उसका reward debt 10,000 है।
यदि Bob ब्लॉक 20 पर रिवॉर्ड क्लेम करता है, तो हम 15,000 रिवॉर्ड में से 10,000 के reward debt को घटा देते हैं। Bob ब्लॉक 20 पर केवल 5,000 रिवॉर्ड क्लेम कर पाएगा।
MasterChef के लिए Pseudocode
नीचे हम MasterChef एल्गोरिदम का एक संक्षिप्त (stripped down) वर्ज़न प्रस्तुत कर रहे हैं। हमने स्पष्टता के लिए ओरिजिनल कॉन्ट्रैक्ट से वेरिएबल नामों को बदलने की कुछ छूट ली है। हम इवेंट्स और टोकन डेसिमल्स की स्केलिंग से जुड़े इम्प्लीमेंटेशन डिटेल्स को भी छोड़ रहे हैं।

Synthetix और MasterChef के बीच अंतर
Synthetix और MasterChef दोनों स्टेक की गई राशि के आधार पर प्रति टोकन रिवॉर्ड संचित करने के लिए समान तंत्र (mechanism) का उपयोग करते हैं। मुख्य अंतर यह है कि reward debt को ट्रैक करने के बजाय, जब यूज़र ने पिछली बार कॉन्ट्रैक्ट के साथ इंटरैक्ट किया था, तब Synthetix रिवॉर्ड एक्युमुलेटर का एक स्नैपशॉट (snapshot) स्टोर करता है। वर्तमान रिवॉर्ड एक्युमुलेटर और स्नैपशॉट के बीच के अंतर का उपयोग यूज़र के अकाउंट में रिवॉर्ड की गणना करने के लिए किया जाता है।
यह अंतर एक per-user rewards मैपिंग में जोड़ा जाता है और वहां तब तक जमा होता रहता है जब तक यूज़र getRewards() कॉल नहीं करता। यह अतिरिक्त बुककीपिंग (bookkeeping) Synthetix एल्गोरिदम को कम कुशल (less efficient) बनाती है।
बाकी के अंतर काफी मामूली (minor) हैं:
- MasterChef में
deposit()औरwithdraw()हैं।- Synthetix में
stake(),withdraw(), औरgetReward()हैं।
- Synthetix में
- MasterChef समय की इकाई के रूप में ब्लॉक्स का उपयोग करता है।
- Synthetix टाइमस्टैम्प (timestamp) का उपयोग करता है।
- MasterChef खुद के लिए रिवॉर्ड मिंट करता है, जैसा कि ऊपर दिए गए सेक्शन में बताया गया है।
- Synthetix यह मानकर चलता है कि एडमिन ने पहले ही कॉन्ट्रैक्ट में रिवॉर्ड ट्रांसफर कर दिए हैं और यह रिवॉर्ड मिंट नहीं करता है।
- MasterChef एक कॉन्फिगरेबल
startBlockसेlastRewardBlockतक रिवॉर्ड वितरित करता है।- Synthetix में यह हार्डकोड (hardcoded) किया गया है कि एडमिन के समय शुरू करने के बाद यह एक सप्ताह की अवधि में रिवॉर्ड वितरित करेगा। Synthetix ज़रूरी नहीं कि कॉन्ट्रैक्ट में रिवॉर्ड का पूरा बैलेंस वितरित करे, बल्कि एडमिन द्वारा निर्दिष्ट राशि ही वितरित करेगा।
- MasterChef यूज़र को रिवॉर्ड ट्रांसफर कर देता है जब भी वे नॉन-जीरो (non-zero) अमाउंट के साथ
deposit()याwithdraw()कॉल करते हैं।- Synthetix यूज़र के देय रिवॉर्ड को rewards नामक मैपिंग में संचित करता है, लेकिन जब तक वे स्पष्ट रूप से
getRewards()कॉल नहीं करते, तब तक इसे यूज़र को ट्रांसफर नहीं करता।
- Synthetix यूज़र के देय रिवॉर्ड को rewards नामक मैपिंग में संचित करता है, लेकिन जब तक वे स्पष्ट रूप से
- MasterChef एक ही कॉन्ट्रैक्ट के भीतर कई पूल्स को सपोर्ट करता है, और रिवॉर्ड्स को पूल के वज़न के हिसाब से बांटता है।
- Synthetix में केवल एक पूल है।
रुचि रखने वाले पाठक SushiSwap MasterChef Staking के कोड देख सकते हैं।
Synthetix के लिए Pseudocode
नीचे दिया गया ग्राफिक Synthetix के बुककीपिंग सबरूटीन (bookkeeping subroutine) को दिखाता है जिसे deposit(), withdraw(), या getRewards() के दौरान कॉल किया जाता है। विशेष रूप से, यह deposit या withdraw में बैलेंस अपडेट होने या रिवॉर्ड वितरण से पहले किया जाता है।
नीचे दिए गए ग्राफिक में lastUpdateTime वह अंतिम समय है जब किसी यूज़र ने तीन में से किसी एक फंक्शन को कॉल किया था। नीचे दिए गए उदाहरण में, जो यूज़र रिवॉर्ड क्लेम कर रहा है वह वह यूज़र नहीं है जिसने पहले कॉन्ट्रैक्ट के साथ इंटरैक्ट किया था। प्राइम ’ (prime) मार्कर का अर्थ है सबरूटीन पूरा होने के बाद वेरिएबल की वैल्यू।

रुचि रखने वाले पाठक स्वयं Synthetix स्टेकिंग कोड देख सकते हैं।
RareSkills के साथ और जानें
अधिक उन्नत तकनीकी Web3 विषयों को जानने के लिए कृपया हमारा ब्लॉकचेन बूटकैंप (blockchain bootcamp) देखें।
मूल रूप से 21 नवंबर, 2023 को प्रकाशित