Uniswap V2 में, प्रोटोकॉल टोकन रिज़र्व को ट्रैक करता है और स्पॉट प्राइस (spot price), , तथा कुल लिक्विडिटी (total liquidity), , प्राप्त करता है, जहाँ और क्रमशः टोकन X और Y के रिज़र्व हैं।
इसके विपरीत, Uniswap V3 वर्तमान प्राइस और लिक्विडिटी को ट्रैक करता है, और रिज़र्व प्राप्त करता है। यह कैलकुलेशन जटिल है और इसे बाद के अध्यायों में कवर किया जाएगा।
वास्तव में Uniswap V3 प्राइस के बजाय प्राइस का स्क्वायर रूट (square root), , स्टोर करता है। यह दृष्टिकोण गैस एफिशिएंसी (gas efficiency) में सुधार करता है, जैसा कि हम बाद के एक अध्याय में विस्तार से देखेंगे। ऐसा करने से सटीकता (accuracy) का कोई नुकसान नहीं होता है, क्योंकि प्राइस हमेशा इसके स्क्वायर रूट से प्राप्त किया जा सकता है।
इस अध्याय का लक्ष्य यह चर्चा करना है कि Uniswap V3 प्राइस का स्क्वायर रूट, , कहाँ और कैसे स्टोर करता है, और यह इस समस्या को कैसे संभालता है कि टोकन का प्राइस एक दशमलव (decimal) मान हो सकता है, जबकि Solidity में कोई float या decimal नंबर टाइप नहीं होता है।
वेरिएबल sqrtPriceX96
प्राइस का स्क्वायर रूट pool’s contract में struct slot0 के फील्ड वेरिएबल sqrtPriceX96 में स्टोर किया जाता है, जैसा कि नीचे दी गई इमेज में दिखाया गया है।

Struct slot0 अन्य वेरिएबल्स को भी स्टोर करता है, जैसे tick, जो वर्तमान टिक (tick) को दर्शाता है, जैसा कि हमने chapter on ticks में देखा था। slot0 में मौजूद अन्य वेरिएबल्स ऑरेकल (oracle), फीस (fees), या कॉन्ट्रैक्ट सिक्योरिटी (contract security) से संबंधित हैं और उनका परीक्षण बाद में किया जाएगा।
वेरिएबल का नाम sqrtPriceX96 पहले से ही इंगित करता है कि स्टोर किया गया मान Q96 नंबर फॉर्मेट (X96), विशेष रूप से Q64.96 फॉर्मेट में, प्राइस का स्क्वायर रूट (sqrtPrice) है।
पिछले अध्याय में Q number format की विस्तृत व्याख्या प्रदान की गई थी। हम अगले सेक्शन में Uniswap V3 में इसके उपयोग की संक्षेप में समीक्षा करेंगे।
फिक्स्ड-पॉइंट वैल्यू के रूप में प्राइस का स्क्वायर रूट,
Uniswap V3 प्राइस के स्क्वायर रूट को फिक्स्ड-पॉइंट नंबर (fixed-point number) के रूप में स्टोर करता है।
समीक्षा के तौर पर, फिक्स्ड-पॉइंट नंबर हमें गैस-एफिशिएंट तरीके से फ्रैक्शनल नंबर्स (fractional numbers) को दर्शाने की अनुमति देते हैं। उदाहरण के लिए, मान लें कि हमें 1.0050122696230506 स्टोर करने की आवश्यकता है, लेकिन हम केवल इंटिजर्स (integers) स्टोर कर सकते हैं। एक दृष्टिकोण यह है कि मान को एक बड़ी संख्या से गुणा किया जाए, जैसे , जिसके परिणामस्वरूप:
फिर हम दशमलव भाग को हटा देते हैं और 79625275426524700982079509374 स्टोर करते हैं।
इस प्रकार, किसी संख्या के फिक्स्ड-पॉइंट रिप्रजेंटेशन Q64.96 और (मूल) संख्या के बीच का संबंध है:
मूल मान (original value) को पुनः प्राप्त करने के लिए, हम फिक्स्ड-पॉइंट रिप्रजेंटेशन को सीधे से विभाजित करते हैं, जैसे:
हमारे उदाहरण में, मान 1.0050122696230506 को एक फिक्स्ड-पॉइंट नंबर के रूप में दर्शाया गया है: 79625275426524700982079509374। मूल मान को पुनः प्राप्त करने के लिए, हम इस मान को से विभाजित करते हैं, जिससे लगभग 1.0050122696230507 प्राप्त होता है, जो मूल संख्या के बहुत करीब है।
ठीक यही Uniswap V3 इस तथ्य से निपटने के लिए करता है कि Solidity में कोई float या decimal टाइप नहीं है। वह वेरिएबल जो टोकन प्राइस का स्क्वायर रूट रखता है, sqrtPriceX96, एक फिक्स्ड-पॉइंट नंबर है जिसे प्राइस के वास्तविक, संभवतः फ्रैक्शनल स्क्वायर रूट को से गुणा करके प्राप्त किया जाता है।
और sqrtPriceX96 के बीच संबंध
और sqrtPriceX96 के बीच संबंध यह है कि प्राइस का वास्तविक स्क्वायर रूट है, जबकि sqrtPriceX96 Q64.96 फॉर्मेट में किसी टोकन के प्राइस के स्क्वायर रूट को दर्शाता है।
इस संबंध को एक सरल सूत्र (formula) के साथ व्यक्त किया जा सकता है। को sqrtPriceX96 में बदलने के लिए, हम उपयोग करते हैं:
sqrtPriceX96 को वापस में बदलने के लिए, हम उपयोग करते हैं:
Python का उपयोग करके sqrtPriceX96 को कैलकुलेट करना
एक प्राइस को sqrtPriceX96 में बदलने और फिर मूल प्राइस को पुनः प्राप्त करने के लिए Python कोड नीचे दिया गया है:
from decimal import Decimal, getcontext
getcontext().prec = 100
price = Decimal('1.0050122696230506')
sqrtPriceX96 = price * Decimal(2) ** 96 # Decimal('79625275426524700982079509374.6667867672150016')
original_price = sqrtPriceX96 / 2 ** 96 # Decimal('1.0050122696230506')
कैलकुलेशन की सटीकता (precision) बढ़ाने के लिए decimal लाइब्रेरी का उपयोग किया गया था।
sqrtPriceX96 के मानों के उदाहरण
आइए कुछ उदाहरणों पर गौर करें।
- यदि किसी टोकन के प्राइस का वर्तमान स्क्वायर रूट 100 है, तो यह मान वेरिएबल
sqrtPriceX96में इस प्रकार स्टोर किया जाएगा:
- यदि प्राइस का वर्तमान स्क्वायर रूट 323.002 है, तो इसे
sqrtPriceX96में इस प्रकार स्टोर किया जाएगा:
मूल (और वास्तविक) मानों को पुनः प्राप्त करने के लिए, बस उपरोक्त मानों को से विभाजित करें।
किसी पूल का प्राइस ज्ञात करने के लिए slot0 में sqrtPriceX96 को क्वेरी करना
Base में ETH:DAI पूल
ETH:DAI pool in Base के लिए वर्तमान sqrtPriceX96 मान को नीचे दी गई इमेज में 4552234755200983230583166215033 के रूप में देखा जा सकता है।

sqrtPriceX96 से में बदलने के लिए, हम प्राप्त मान को से विभाजित करते हैं। इसलिए:
प्राइस के स्क्वायर रूट से, हम मान का वर्ग (square) करके वास्तविक प्राइस प्राप्त करते हैं:
यह उस समय DAI के संदर्भ में Ether के मूल्य को दर्शाता है जब टेक्स्ट का यह भाग लिखा गया था।
मेननेट (mainnet) में USDC:ETH पूल
एक अन्य उदाहरण के तौर पर, आइए USDC:ETH pool on mainnet में sqrtPriceX96 प्राप्त करें। यह उदाहरण पिछले वाले से दो कारणों से अलग है: पहला, यह प्राइस स्टेबलकॉइन (stablecoin) के संदर्भ में Ether के बजाय Ether के संदर्भ में USDC के लिए है। दूसरा, Ether में 18 डेसीमल प्लेसेस (decimal places) होते हैं, जबकि USDC में केवल 6 होते हैं, जो पिछले उदाहरण के विपरीत है जहाँ दोनों टोकन में 18 डेसीमल प्लेसेस थे।
जैसा कि इमेज में देखा जा सकता है, मान 1506673274302120988651364689808458 है।

के लिए मान को इस प्रकार कैलकुलेट किया जा सकता है:
प्राइस को इस प्रकार कैलकुलेट किया जा सकता है:
चूँकि यह एक USDC:ETH पूल है, इसलिए हमारे पास ETH के संदर्भ में USDC का प्राइस है। USDC के संदर्भ में ETH का प्राइस, प्राप्त प्राइस के व्युत्क्रम (inverse) द्वारा दिया जाता है:
अंततः, USDC में 6 डेसीमल प्लेसेस हैं, जबकि ETH में 18 हैं। हमें इस अंतर को ध्यान में रखना होगा; USDC के संदर्भ में ETH के प्राइस को कैलकुलेट करने के लिए, हमें उपरोक्त मान को से गुणा करना होगा।
यह उस समय USDC के संदर्भ में Ether के मूल्य को दर्शाता है जब टेक्स्ट का यह भाग लिखा गया था। हमारे लिखते समय ETH अत्यधिक अस्थिर (volatile) रहा है।
प्रोटोकॉल में अधिकतम प्राइस
चूँकि प्राइस का स्क्वायर रूट Q64.96 फॉर्मेट नंबर में स्टोर होता है, इसलिए यह सबसे बड़ी पूर्ण संख्या (whole number) जो स्टोर कर सकता है वह लगभग है। इस स्क्वायर रूट के अनुरूप प्राइस को इस सबसे बड़ी पूर्ण संख्या का वर्ग करके प्राप्त किया जा सकता है।
इस प्रकार, प्राइस का सबसे बड़ा स्क्वायर रूट जिसके साथ प्रोटोकॉल काम कर सकता है वह लगभग है, और इससे संबंधित सबसे बड़ा प्राइस से थोड़ा कम है।
प्रोटोकॉल में न्यूनतम प्राइस
से प्राप्त एक फिक्स्ड-पॉइंट नंबर जितने छोटे फ्रैक्शन (fractions) को दर्शा सकता है। ऐसा इसलिए है क्योंकि को जब से गुणा किया जाता है, तो परिणाम 1 होता है, जिसे एक इंटीजर के रूप में स्टोर किया जा सकता है।
से छोटे मान आउट ऑफ रेंज (out of range) होते हैं। उदाहरण के लिए, , जब से गुणा करके फिक्स्ड-पॉइंट में बदला जाता है, तो यह , या 0.5 हो जाता है। चूँकि केवल इंटीजर भाग ही रखा जाता है, इसे 0 पर राउंड डाउन कर दिया जाता है, जिससे मूल मान की जानकारी खो जाती है।
इसलिए, सिद्धांत रूप में, सबसे कम प्राइस जिसके साथ प्रोटोकॉल काम कर सकता है वह या है। हालाँकि, जैसा कि हम अगले अध्याय में देखेंगे, यह इतने कम प्राइस की अनुमति नहीं देता है।
प्रोटोकॉल किसी टोकन द्वारा ग्रहण किए जा सकने वाले प्राइस के सबसे बड़े स्क्वायर रूट, जो है, और टोकन द्वारा ग्रहण किए जा सकने वाले प्राइस के सबसे छोटे स्क्वायर रूट, जिसे होने के लिए लागू किया गया है, के बीच एक समरूपता (symmetry) लागू करता है। यह समरूपता तर्कसंगत है, क्योंकि Y के सापेक्ष टोकन X का मान, X के सापेक्ष टोकन Y के मान का व्युत्क्रम (inverse) होता है।
प्राइस का स्क्वायर रूट स्टोर करने के लिए Q64.96 का उपयोग क्यों करें
इस प्रश्न का उत्तर देना आसान नहीं है, और प्रोटोकॉल टीम किसी अन्य Q नंबर फॉर्मेट को भी चुन सकती थी। चूँकि उन्होंने प्राइस के स्क्वायर रूट को टिक (tick) और अन्य जानकारी के साथ एक सिंगल 256-बिट स्टोरेज स्लॉट में पैक करने का निर्णय लिया, इसलिए प्राइस के स्क्वायर रूट के लिए बची हुई जगह 160 बिट्स थी।
अगले सेक्शन में, हम दिखाएंगे कि कैसे पूर्ण संख्या (whole number) वाले भाग के लिए 64 बिट्स का उपयोग करना वास्तविक दुनिया के परिदृश्य में प्राइस को समायोजित करने के लिए पर्याप्त है, जिससे प्रोटोकॉल एक ऐसे पूल को सपोर्ट करने में सक्षम होता है जहाँ एक कॉइन का मूल्य ट्रिलियन डॉलर (trillions of dollars) हो जबकि दूसरे का मूल्य सेंट (cent) का केवल एक अंश हो।
वास्तविक दुनिया के परिदृश्य में किसी टोकन की प्राइस लिमिट्स
जैसा कि हमने देखा है, प्रोटोकॉल जिस उच्चतम प्राइस को हैंडल कर सकता है वह लगभग () है, या परिमाण के क्रम (order of magnitude) में लगभग है। चूँकि टोकन के प्राइस हमेशा किसी अन्य टोकन के सापेक्ष होते हैं, इसलिए एक पूल में दो टोकन के बीच का प्राइस अंतर (price difference) ऑर्डर्स ऑफ़ मैग्नीट्यूड तक फैल सकता है।
प्राइस अंतर को कैलकुलेट करते समय हमें डेसीमल्स को भी सावधानीपूर्वक शामिल करना चाहिए। उदाहरण के लिए, 18 डेसीमल प्लेसेस वाले टोकन को इसके कॉन्ट्रैक्ट में इकाइयों के रूप में स्टोर किया जाता है, जबकि 8 डेसीमल प्लेसेस वाले टोकन को इकाइयों के रूप में स्टोर किया जाता है। इसलिए, यदि इन दोनों टोकन का डॉलर में समान मूल्य है, तो पूल में उनके प्राइस अंतर में केवल डेसीमल प्लेसेस के कारण पहले से ही 10 ऑर्डर्स ऑफ़ मैग्नीट्यूड का अंतर होगा।
आइए एक वास्तविक दुनिया के उदाहरण, WBTC:PEPE पूल पर विचार करें। वर्तमान में, 1 WBTC का मूल्य लगभग 100,000 () डॉलर है, जबकि 1 PEPE का मूल्य लगभग 0.00001 डॉलर है, जो 10 ऑर्डर्स ऑफ़ मैग्नीट्यूड का अंतर है।
हालाँकि, हमें डेसीमल प्लेसेस में अंतर पर भी विचार करना चाहिए। WBTC में 8 डेसीमल प्लेसेस हैं, जबकि PEPE में 18 डेसीमल प्लेसेस हैं, जिसके परिणामस्वरूप प्राइस अंतर के कारण 10-ऑर्डर-ऑफ़-मैग्नीट्यूड अंतर के अतिरिक्त 10-ऑर्डर-ऑफ़-मैग्नीट्यूड का अंतर आता है।
इस प्रकार, WBTC टोकन की सबसे छोटी इकाई (एक सतोशी, या WBTC) का मूल्य PEPE की सबसे छोटी इकाई (जिसका कोई नाम नहीं है लेकिन यह PEPE को दर्शाती है) से (या एक सौ क्विंटिलियन) गुना अधिक है।
बीस ऑर्डर्स ऑफ़ मैग्नीट्यूड एक महत्वपूर्ण अंतर है, लेकिन Uniswap V3 पूल 38 ऑर्डर्स ऑफ़ मैग्नीट्यूड तक के अंतर की अनुमति देता है। इसलिए, Uniswap V3 की लिमिट तक पहुँचने से पहले PEPE के सापेक्ष WBTC का प्राइस 18 और ऑर्डर्स ऑफ़ मैग्नीट्यूड बढ़ सकता है।
यह मानते हुए कि PEPE अपने वर्तमान प्राइस (0.00001 डॉलर) पर बना रहता है, Uniswap V3 प्राइस लिमिट तक पहुँचने से पहले Bitcoin का प्राइस एक सौ सेक्सटिलियन (one hundred sextillion) डॉलर तक पहुँचना चाहिए। इसी तरह, यह मानते हुए कि Bitcoin 1 ट्रिलियन डॉलर तक पहुँच जाता है, प्रोटोकॉल की प्राइस लिमिट तक पहुँचने से पहले PEPE का प्राइस 0.0000000000000001 डॉलर (एक डॉलर का दसवां-क्वाड्रिलियनवां हिस्सा) जितना कम हो सकता है।
इन लिमिट्स को प्राप्त करना (Deriving) पाठक के लिए एक अभ्यास (exercise) है।
सारांश
- Uniswap V3 किसी प्राइस के स्क्वायर रूट को Q64.96 नंबर फॉर्मेट में स्टोर करता है। यह ऐसा इसलिए करता है क्योंकि Solidity में फ्लोटिंग-पॉइंट नंबर्स (floating-point numbers) नहीं होते हैं, और बाइनरी में फिक्स्ड-पॉइंट नंबर्स के साथ काम करना गैस-एफिशिएंट (gas-efficient) होता है।
- प्रोटोकॉल किसी टोकन का जो उच्चतम प्राइस स्टोर कर सकता है वह लगभग है। समरूपता बनाए रखने के लिए, जो न्यूनतम प्राइस यह स्टोर कर सकता है वह है। पूल में टोकन प्राइस इन मानों से अधिक नहीं हो सकते।