पिछले अध्यायों में, हमने एक सेगमेंट में दो कीमतों के बीच टोकन X और Y के वास्तविक रिज़र्व की गणना करने के लिए फॉर्मूले प्राप्त किए थे। इन फॉर्मूलों का उपयोग प्रोटोकॉल द्वारा तब किया जाता है जब उपयोगकर्ता लिक्विडिटी जोड़ते या हटाते हैं, साथ ही स्वैप (swaps) के दौरान भी किया जाता है।
इस अध्याय में, हम दिखाएंगे कि ये फॉर्मूले कोडबेस में कहाँ स्थित हैं। चूंकि प्रोटोकॉल कीमतों के वर्गमूल (square roots) को Q64.96 फॉर्मेट में स्टोर करता है, इसलिए इस फॉर्मेट का उपयोग करने के लिए फॉर्मूलों को एडजस्ट करना आवश्यक है।
कोडबेस में मात्रा (amounts) और की गणना करना
कोडबेस में निरंतर लिक्विडिटी (constant liquidity) पर दो कीमतों के बीच टोकन X और Y की मात्रा की गणना करने के लिए हेल्पर फ़ंक्शंस (helper functions) शामिल हैं। ये फ़ंक्शंस SqrtPriceMath.sol लाइब्रेरी में निम्नलिखित नामों के साथ परिभाषित किए गए हैं:
getAmount0Delta: दी गई लिक्विडिटी के लिए दो कीमतों के बीच X में वास्तविक रिज़र्व की गणना करता है।getAmount1Delta: दी गई लिक्विडिटी के लिए दो कीमतों के बीच Y में वास्तविक रिज़र्व की गणना करता है।
वास्तविक रिज़र्व के लिए ये फॉर्मूले वास्तव में कीमतों के वर्गमूल पर निर्भर करते हैं, न कि स्वयं कीमतों पर। हम वैल्यू का वर्ग (squaring) करके उसके वर्गमूल से कीमत वापस प्राप्त कर सकते हैं—लेकिन यह केवल ऑफ-चेन (off-chain) किया जाता है; ऑन-चेन (on-chain) इसकी आवश्यकता नहीं है।
संक्षिप्तता के लिए, हम कीमतों का उल्लेख करना जारी रखेंगे, लेकिन पाठक को इस बात की जानकारी होनी चाहिए कि प्रोटोकॉल केवल कीमतों के वर्गमूल का उपयोग करता है। उदाहरण के लिए, यदि प्रोटोकॉल को 100 और 200 की कीमतों के बीच वास्तविक रिज़र्व की मात्रा की गणना करने की आवश्यकता है, तो यह 100 और 200 के बजाय इन फ़ंक्शंस को और तर्कों (arguments) के साथ कॉल करेगा, दोनों Q64.96 फॉर्मेट में होंगे।
फ़ंक्शंस getAmount0Delta और getAmount1Delta
SqrtPriceMath लाइब्रेरी में getAmount0Delta नाम के दो फ़ंक्शंस हैं, जिनमें से एक का सिग्नेचर नीचे दिया गया है, दूसरे पर बाद के अध्याय में चर्चा की जाएगी।

यह Q64.96 फॉर्मेट (sqrtRatioAX96 और sqrtRatioBX96) में कीमतों के दो वर्गमूल, लिक्विडिटी, और एक बूलियन (roundUp) प्राप्त करता है जो यह निर्दिष्ट करता है कि अंतिम मात्रा को राउंड अप (round up) किया जाना चाहिए या राउंड डाउन (round down)।
जब यह प्रोटोकॉल से उपयोगकर्ता को भुगतान का प्रतिनिधित्व करता है—जैसे कि स्वैप में या लिक्विडिटी हटाते समय—तो मात्रा को राउंड डाउन किया जाता है और जब यह उपयोगकर्ता से प्रोटोकॉल में जमा का प्रतिनिधित्व करता है—जैसे कि स्वैप में या लिक्विडिटी प्रदान करते समय—तो इसे राउंड अप किया जाता है। राउंडिंग (Rounding) हमेशा प्रोटोकॉल के पक्ष में और उपयोगकर्ता के नुकसान में होती है।
यह फ़ंक्शन दी गई लिक्विडिटी के लिए sqrtRatioAX96 और sqrtRatioBX96 के बीच टोकन X में वास्तविक रिज़र्व की मात्रा की गणना करता है और उसे रिटर्न करता है, यह मानते हुए कि इस सेगमेंट में लिक्विडिटी पूरी तरह से टोकन X में है।
प्रोटोकॉल में दो getAmount1Delta फ़ंक्शंस भी हैं, जिनमें से एक का सिग्नेचर getAmount0Delta के बहुत समान है, दूसरे पर बाद के अध्याय में चर्चा की जाएगी। इसका उद्देश्य दी गई लिक्विडिटी के लिए sqrtRatioAX96 और sqrtRatioBX96 के बीच टोकन Y में वास्तविक रिज़र्व की मात्रा की गणना करना है, यह मानते हुए कि इस सेगमेंट में लिक्विडिटी पूरी तरह से टोकन Y में है।

इस फ़ंक्शन पर भी getAmount0Delta के समान ही राउंडिंग व्यवहार लागू होता है।
नीचे दिया गया इंटरैक्टिव टूल दी गई लिक्विडिटी के साथ एक मूल्य अंतराल (price interval) के लिए getAmount0Delta और getAmount1Delta के समान ही गणना करता है। इसका उपयोग करने के लिए, sqrtRatioAX96 और sqrtRatioBX96 को एडजस्ट करने के लिए नीले और बैंगनी स्लाइडर्स को घुमाएं, और लिक्विडिटी को एडजस्ट करने के लिए नारंगी स्लाइडर को घुमाएं। amount0/amount1 रेडियो बटन यह दर्शाते हैं कि कौन सा फ़ंक्शन निष्पादित (execute) किया जाना चाहिए। परिणाम को राउंड अप करने के लिए, roundUp चेकबॉक्स चुनें।
ध्यान दें कि ये फ़ंक्शंस यह मानकर चलते हैं कि, दो मूल्य बिंदुओं के बीच, लिक्विडिटी निरंतर है और इसमें केवल एक ही रिज़र्व शामिल है।
हमें इन फ़ंक्शंस का उपयोग करने से पहले इस जानकारी का पहले से पता होना चाहिए क्योंकि इन फ़ंक्शंस को संभावित कीमतों की पूरी रेंज में लिक्विडिटी के वितरण का कोई ज्ञान नहीं होता है।
फ़ंक्शन getAmount0Delta
फ़ंक्शन getAmount0Delta निम्नलिखित फॉर्मूले की गणना करता है, जिसे हमने real reserves वाले अध्याय में प्राप्त किया था:
लेकिन sqrtRatioAX96 और sqrtRatioBX96 द्वारा दिए गए Q64.96 फॉर्मेट में कीमतों के वर्गमूल का उपयोग करके।
चूँकि और sqrtRatioX96 के बीच का संबंध है
हम के फॉर्मूले में इस संबंध को इस प्रकार प्रतिस्थापित (substitute) कर सकते हैं
अंतिम चरण सख्ती से गणितीय रूप से आवश्यक नहीं है, लेकिन कोड में जहां विभाजन (division) राउंडिंग त्रुटि (rounding error) का कारण बनता है, यह सटीकता (precision) को बढ़ाता है क्योंकि हम दो के बजाय केवल एक ही विभाजन करते हैं।
यह फॉर्मूला कोडबेस में फ़ंक्शन एनोटेशन (annotations) में पाया जा सकता है ( के बिना)

और इसे getAmount0Delta फ़ंक्शन में लागू किया गया है:

उपरोक्त छवि में नारंगी और हरे रंग के बॉक्स उस फॉर्मूले के नारंगी और हरे भागों से मेल खाते हैं जिसे हमने अभी प्राप्त किया है, जो अंश (numerator) बनाते हैं। नीले बॉक्स में पूरा व्यंजक (expression) होता है, जिसमें अंश के पदों को sqrtRatioAX96 और sqrtRatioBX96 दोनों से गुणा और विभाजित किया जाता है।
mulDiv फ़ंक्शन पहले दो तर्कों को गुणा करता है और गुणनफल (product) को तीसरे से विभाजित करता है। mulDivRoundingUp फ़ंक्शन भी ऐसा ही करता है, लेकिन यह अंतिम परिणाम को राउंड अप करता है।
divRoundingUp पूर्णांक विभाजन (integer division) करता है जो राउंड अप होता है।
जैसा कि समझाया गया है, roundUp पैरामीटर यह इंगित करता है कि अंतिम मात्रा को राउंड अप किया जाना चाहिए या नहीं।
फ़ंक्शंस getAmount1Delta
इस फ़ंक्शन का लक्ष्य निम्नलिखित फॉर्मूले की गणना करना है
लेकिन और के बजाय sqrtRatioAX96 और sqrtRatioBX96 के संदर्भ में।
याद रखें कि और sqrtRatioX96 के बीच का संबंध है
तो हम के फॉर्मूले में इस संबंध का उपयोग इस प्रकार कर सकते हैं
एक बार फिर, हम केवल एक विभाजन प्राप्त करने के लिए फॉर्मूले को पुनर्व्यवस्थित (rearrange) करते हैं, जो सटीकता को बढ़ाता है और कोडबेस में जो पाया जाता है उससे मेल खाता है।
नीचे दी गई छवि getAmount1Delta फ़ंक्शन दिखाती है।

Python में getAmount0Delta और getAmount1Delta फ़ंक्शंस
इस खंड में, हम Python में getAmount0Delta और getAmount1Delta फ़ंक्शंस लिखेंगे, जिनका उपयोग किसी सेगमेंट के वास्तविक रिज़र्व की गणना करने के लिए किया जाएगा।
सरलता के लिए, हम स्क्रिप्ट में राउंडिंग त्रुटियों (rounding errors) के बारे में चिंता नहीं करेंगे।
इन्हें इस प्रकार लिखा जा सकता है:
import math
def getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, liquidity, roundUp):
numerator1 = liquidity*2**96
numerator2 = sqrtRatioBX96 - sqrtRatioAX96
if roundUp:
return math.ceil(numerator1*numerator2 / (sqrtRatioBX96*sqrtRatioAX96))
else:
return math.floor(numerator1*numerator2 / (sqrtRatioBX96*sqrtRatioAX96))
def getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, liquidity, roundUp):
if roundUp:
return math.ceil(liquidity*(sqrtRatioBX96 - sqrtRatioAX96)/2**96)
else:
return math.floor(liquidity*(sqrtRatioBX96 - sqrtRatioAX96)/2**96)
मान लीजिए कि हम टिक (ticks) -10 और 10 के बीच वास्तविक रिज़र्व की गणना करना चाहते हैं, जहां वर्तमान कीमत (current price) बिल्कुल टिक शून्य (zero) पर है, जैसा कि नीचे दर्शाया गया है। टोकन Y में वास्तविक रिज़र्व निचले टिक (-10) और वर्तमान कीमत (0) के बीच स्थित हैं, जबकि टोकन X में वास्तविक रिज़र्व वर्तमान कीमत (0) और ऊपरी टिक (10) के बीच स्थित हैं।

इन मानों (values) को प्राप्त करने के लिए हम getAmount0Delta और getAmount1Delta फ़ंक्शंस का उपयोग कर सकते हैं।
यह निर्धारित करने के लिए कि राउंड अप करना है या राउंड डाउन, हमें गणना के उद्देश्य पर विचार करने की आवश्यकता है। यदि गणना लिक्विडिटी प्रदान करने के लिए है, तो परिणाम को राउंड अप किया जाएगा, और लिक्विडिटी हटाने के लिए इसके विपरीत (round down) किया जाएगा। आइए ये गणनाएं लिक्विडिटी प्रदान करते समय करें, जहां राउंड अप करने की आवश्यकता होती है।
गणनाएं और उनके परिणामी मान नीचे दिए गए कोड में दिखाए गए हैं।
def getSqrtRatioAtTick(i):
return math.sqrt(1.0001 ** i) * 2**96;
current_price = getSqrtRatioAtTick(0)
upper_price = getSqrtRatioAtTick(10)
lower_price = getSqrtRatioAtTick(-10)
print(getAmount0Delta(current_price, upper_price, 1000000000, True)) # 499851
print(getAmount1Delta(lower_price, current_price, 1000000000, True)) # 499851
अभ्यास (Exercise): निम्नलिखित मामलों में टिक -10 और 20 के बीच वास्तविक रिज़र्व की गणना करें:
- वर्तमान कीमत टिक -20 पर है,
- वर्तमान कीमत टिक 0 पर है,
- वर्तमान कीमत टिक 20 पर है।
निष्कर्ष (Conclusion)
- प्रोटोकॉल दो मनमानी (arbitrary) कीमतों के बीच वास्तविक रिज़र्व और उनके बीच दी गई निरंतर लिक्विडिटी की गणना करने के लिए फ़ंक्शंस प्रदान करता है। ये फ़ंक्शंस
getAmount0Delta(टोकन X के लिए) औरgetAmount1Delta(टोकन Y के लिए) हैं। - ये फ़ंक्शंस
sqrtRatioAX96औरsqrtRatioBX96नामक वेरिएबल्स में, Q64.96 फॉर्मेट में वर्गमूल के रूप में कीमतें प्राप्त करते हैं। - वास्तविक रिज़र्व की गणना की गई मात्रा को राउंड अप किया जाता है यदि यह प्रोटोकॉल द्वारा प्राप्त की जाने वाली मात्रा का प्रतिनिधित्व करती है, और यदि यह प्रोटोकॉल द्वारा भुगतान की जाने वाली मात्रा का प्रतिनिधित्व करती है तो इसे राउंड डाउन किया जाता है।
यह लेख Uniswap V3 पर आधारित श्रृंखला का हिस्सा है।