The Graph: Web3 डेटा क्वेरी करने की समस्या को ठीक करना
इस बार हम The Graph पर करीब से नज़र डालेंगे जो अनिवार्य रूप से पिछले साल विकेंद्रीकृत एप्लिकेशन (dapp) विकसित करने के लिए मानक स्टैक का हिस्सा बन गया है। आइए पहले देखें कि हम पारंपरिक तरीके से चीजों को कैसे करेंगे...
The Graph के बिना...
तो आइए उदाहरण के लिए एक सरल उदाहरण लेते हैं। हम सभी को गेम पसंद हैं, इसलिए एक ऐसे सरल गेम की कल्पना करें जिसमें उपयोगकर्ता दांव लगाते हैं:
pragma solidity 0.7.1;
contract Game {
uint256 totalGamesPlayerWon = 0;
uint256 totalGamesPlayerLost = 0;
event BetPlaced(address player, uint256 value, bool hasWon);
function placeBet() external payable {
bool hasWon = evaluateBetForPlayer(msg.sender);
if (hasWon) {
(bool success, ) = msg.sender.call{ value: msg.value * 2 }('');
require(success, "Transfer failed");
totalGamesPlayerWon++;
} else {
totalGamesPlayerLost++;
}
emit BetPlaced(msg.sender, msg.value, hasWon);
}
}
अब मान लीजिए कि हमारे dapp में, हम कुल दांव, हारे/जीते गए कुल गेम प्रदर्शित करना चाहते हैं और जब भी कोई फिर से खेलता है तो इसे अपडेट भी करना चाहते हैं। इसका तरीका यह होगा:
totalGamesPlayerWonप्राप्त करें।totalGamesPlayerLostप्राप्त करें।BetPlacedघटनाओं की सदस्यता लें।
हम दाईं ओर दिखाए गए अनुसार Web3 में घटना (opens in a new tab) को सुन सकते हैं, लेकिन इसके लिए काफी कुछ मामलों को संभालने की आवश्यकता होती है।
GameContract.events.BetPlaced({
fromBlock: 0
}, function(error, event) { console.log(event); })
.on('data', function(event) {
// घटना ट्रिगर हुई
})
.on('changed', function(event) {
// घटना फिर से हटा दी गई
})
.on('error', function(error, receipt) {
// लेन-देन अस्वीकार कर दिया गया
});
अब यह हमारे सरल उदाहरण के लिए अभी भी कुछ हद तक ठीक है। लेकिन मान लीजिए कि अब हम केवल वर्तमान खिलाड़ी के लिए हारे/जीते गए दांव की राशि प्रदर्शित करना चाहते हैं। खैर, हमारी किस्मत खराब है, बेहतर होगा कि आप एक नया अनुबंध तैनात करें जो उन मानों को संग्रहीत करता हो और उन्हें प्राप्त करता हो। और अब एक बहुत अधिक जटिल स्मार्ट अनुबंध और dapp की कल्पना करें, चीजें जल्दी ही अस्त-व्यस्त हो सकती हैं।
आप देख सकते हैं कि यह सबसे अच्छा तरीका क्यों नहीं है:
- पहले से तैनात अनुबंधों के लिए काम नहीं करता है।
- उन मानों को संग्रहीत करने के लिए अतिरिक्त गैस की लागत।
- इथेरियम नोड के लिए डेटा प्राप्त करने के लिए एक और कॉल की आवश्यकता होती है।
अब आइए एक बेहतर समाधान देखें।
आइए मैं आपको GraphQL से परिचित कराता हूँ
सबसे पहले आइए GraphQL के बारे में बात करते हैं, जिसे मूल रूप से फेसबुक द्वारा डिज़ाइन और कार्यान्वित किया गया था। आप पारंपरिक REST API मॉडल से परिचित हो सकते हैं। अब कल्पना करें कि इसके बजाय आप ठीक उसी डेटा के लिए एक क्वेरी लिख सकते हैं जो आप चाहते थे:
ये दोनों चित्र काफी हद तक GraphQL के सार को पकड़ते हैं। दाईं ओर की क्वेरी के साथ हम ठीक से परिभाषित कर सकते हैं कि हमें कौन सा डेटा चाहिए, इसलिए वहां हमें एक ही अनुरोध में सब कुछ मिल जाता है और हमें जो चाहिए उससे अधिक कुछ नहीं मिलता। एक GraphQL सर्वर आवश्यक सभी डेटा प्राप्त करने का काम संभालता है, इसलिए फ्रंटएंड उपभोक्ता पक्ष के लिए इसका उपयोग करना अविश्वसनीय रूप से आसान है। यदि आप रुचि रखते हैं तो यह एक अच्छी व्याख्या है (opens in a new tab) कि सर्वर वास्तव में किसी क्वेरी को कैसे संभालता है।
अब उस ज्ञान के साथ, आइए अंततः ब्लॉकचेन स्पेस और The Graph में कूदें।
The Graph क्या है?
ब्लॉकचेन एक विकेंद्रीकृत डेटाबेस है, लेकिन आमतौर पर जो होता है उसके विपरीत, हमारे पास इस डेटाबेस के लिए कोई क्वेरी भाषा नहीं है। डेटा प्राप्त करने के समाधान दर्दनाक या पूरी तरह से असंभव हैं। The Graph ब्लॉकचेन डेटा को इंडेक्स करने और क्वेरी करने के लिए एक विकेंद्रीकृत प्रोटोकॉल है। और जैसा कि आपने अनुमान लगाया होगा, यह क्वेरी भाषा के रूप में GraphQL का उपयोग कर रहा है।
किसी चीज़ को समझने के लिए उदाहरण हमेशा सबसे अच्छे होते हैं, इसलिए आइए अपने GameContract उदाहरण के लिए The Graph का उपयोग करें।
सबग्राफ कैसे बनाएं
डेटा को कैसे इंडेक्स करना है, इसकी परिभाषा को सबग्राफ कहा जाता है। इसके लिए तीन घटकों की आवश्यकता होती है:
- मेनिफेस्ट (
subgraph.yaml) - स्कीमा (
schema.graphql) - मैपिंग (
mapping.ts)
मेनिफेस्ट (subgraph.yaml)
मेनिफेस्ट हमारी कॉन्फ़िगरेशन फ़ाइल है और यह परिभाषित करती है:
- किन स्मार्ट अनुबंधों को इंडेक्स करना है (पता, नेटवर्क, ABI...)
- किन घटनाओं को सुनना है
- सुनने के लिए अन्य चीजें जैसे फ़ंक्शन कॉल या ब्लॉक
- कॉल किए जा रहे मैपिंग फ़ंक्शन (नीचे
mapping.tsदेखें)
आप यहां कई अनुबंधों और हैंडलर को परिभाषित कर सकते हैं। एक विशिष्ट सेटअप में Hardhat प्रोजेक्ट के अंदर अपनी स्वयं की रिपॉजिटरी के साथ एक सबग्राफ फ़ोल्डर होगा। फिर आप आसानी से ABI का संदर्भ दे सकते हैं।
सुविधा के कारणों से आप mustache जैसे टेम्प्लेट टूल का भी उपयोग करना चाह सकते हैं। फिर आप एक subgraph.template.yaml बनाते हैं और नवीनतम तैनाती के आधार पर पते सम्मिलित करते हैं। अधिक उन्नत उदाहरण सेटअप के लिए, उदाहरण के लिए आवे सबग्राफ रेपो (opens in a new tab) देखें।
और संपूर्ण दस्तावेज़ यहाँ (opens in a new tab) देखे जा सकते हैं।
specVersion: 0.0.1
description: Placing Bets on Ethereum
repository: - GitHub link -
schema:
file: ./schema.graphql
dataSources:
- kind: ethereum/contract
name: GameContract
network: mainnet
source:
address: '0x2E6454...cf77eC'
abi: GameContract
startBlock: 6175244
mapping:
kind: ethereum/events
apiVersion: 0.0.1
language: wasm/assemblyscript
entities:
- GameContract
abis:
- name: GameContract
file: ../build/contracts/GameContract.json
eventHandlers:
- event: PlacedBet(address,uint256,bool)
handler: handleNewBet
file: ./src/mapping.ts
स्कीमा (schema.graphql)
स्कीमा GraphQL डेटा परिभाषा है। यह आपको यह परिभाषित करने की अनुमति देगा कि कौन सी संस्थाएं (entities) मौजूद हैं और उनके प्रकार क्या हैं। The Graph द्वारा समर्थित प्रकार हैं:
- Bytes
- ID
- String
- Boolean
- Int
- BigInt
- BigDecimal
आप संबंधों को परिभाषित करने के लिए संस्थाओं (entities) का उपयोग प्रकार (type) के रूप में भी कर सकते हैं। हमारे उदाहरण में हम खिलाड़ी से दांव तक 1-से-अनेक (1-to-many) संबंध परिभाषित करते हैं। ! का अर्थ है कि मान खाली नहीं हो सकता। संपूर्ण दस्तावेज़ यहाँ (opens in a new tab) देखे जा सकते हैं।
type Bet @entity {
id: ID!
player: Player!
playerHasWon: Boolean!
time: Int!
}
type Player @entity {
id: ID!
totalPlayedCount: Int
hasWonCount: Int
hasLostCount: Int
bets: [Bet]!
}
मैपिंग (mapping.ts)
The Graph में मैपिंग फ़ाइल हमारे उन फ़ंक्शंस को परिभाषित करती है जो आने वाली घटनाओं को संस्थाओं (entities) में बदलते हैं। यह AssemblyScript में लिखा गया है, जो TypeScript का एक सबसेट है। इसका मतलब है कि मैपिंग के अधिक कुशल और पोर्टेबल निष्पादन के लिए इसे WASM (WebAssembly) में संकलित किया जा सकता है।
आपको subgraph.yaml फ़ाइल में नामित प्रत्येक फ़ंक्शन को परिभाषित करने की आवश्यकता होगी, इसलिए हमारे मामले में हमें केवल एक की आवश्यकता है: handleNewBet। हम सबसे पहले प्रेषक के पते से id के रूप में Player संस्था को लोड करने का प्रयास करते हैं। यदि यह मौजूद नहीं है, तो हम एक नई संस्था बनाते हैं और इसे शुरुआती मानों से भरते हैं।
फिर हम एक नई Bet संस्था बनाते हैं। इसके लिए id event.transaction.hash.toHex() + "-" + event.logIndex.toString() होगी जो हमेशा एक अद्वितीय मान सुनिश्चित करती है। केवल हैश का उपयोग करना पर्याप्त नहीं है क्योंकि कोई व्यक्ति स्मार्ट अनुबंध के माध्यम से एक ही लेन-देन में कई बार placeBet फ़ंक्शन को कॉल कर सकता है।
अंत में हम सभी डेटा के साथ Player संस्था को अपडेट कर सकते हैं। एरे (Arrays) को सीधे पुश नहीं किया जा सकता है, बल्कि उन्हें यहां दिखाए गए अनुसार अपडेट करने की आवश्यकता है। हम दांव को संदर्भित करने के लिए id का उपयोग करते हैं। और किसी संस्था को संग्रहीत करने के लिए अंत में .save() की आवश्यकता होती है।
संपूर्ण दस्तावेज़ यहाँ देखे जा सकते हैं: https://thegraph.com/docs/en/developing/creating-a-subgraph/#writing-mappings। (opens in a new tab) आप मैपिंग फ़ाइल में लॉगिंग आउटपुट भी जोड़ सकते हैं, यहाँ (opens in a new tab) देखें।
import { Bet, Player } from "../generated/schema"
import { PlacedBet } from "../generated/GameContract/GameContract"
export function handleNewBet(event: PlacedBet): void {
let player = Player.load(event.transaction.from.toHex())
if (player == null) {
// यदि अभी तक मौजूद नहीं है तो बनाएं
player = new Player(event.transaction.from.toHex())
player.bets = new Array<string>(0)
player.totalPlayedCount = 0
player.hasWonCount = 0
player.hasLostCount = 0
}
let bet = new Bet(
event.transaction.hash.toHex() + "-" + event.logIndex.toString()
)
bet.player = player.id
bet.playerHasWon = event.params.hasWon
bet.time = event.block.timestamp
bet.save()
player.totalPlayedCount++
if (event.params.hasWon) {
player.hasWonCount++
} else {
player.hasLostCount++
}
// ऐरे को इस तरह अपडेट करें
let bets = player.bets
bets.push(bet.id)
player.bets = bets
player.save()
}
फ्रंटएंड में इसका उपयोग करना
Apollo Boost जैसी किसी चीज़ का उपयोग करके, आप आसानी से अपने React dapp (या Apollo-Vue) में The Graph को एकीकृत कर सकते हैं। विशेष रूप से React हुक्स और Apollo का उपयोग करते समय, डेटा प्राप्त करना उतना ही सरल है जितना कि आपके घटक (component) में एक एकल GraphQL क्वेरी लिखना। एक विशिष्ट सेटअप कुछ इस तरह दिख सकता है:
// सभी सबग्राफ देखें: https://thegraph.com/explorer/
const client = new ApolloClient({
uri: "{{ subgraphUrl }}",
})
ReactDOM.render(
<ApolloProvider client={client}>
<App />
</ApolloProvider>,
document.getElementById("root")
)
और अब हम उदाहरण के लिए इस तरह की एक क्वेरी लिख सकते हैं। यह हमारे लिए प्राप्त करेगा:
- वर्तमान उपयोगकर्ता कितनी बार जीता है
- वर्तमान उपयोगकर्ता कितनी बार हारा है
- उसके सभी पिछले दांवों के साथ टाइमस्टैम्प की एक सूची
GraphQL सर्वर से एक ही अनुरोध में यह सब।
const myGraphQlQuery = gql`
players(where: { id: $currentUser }) {
totalPlayedCount
hasWonCount
hasLostCount
bets {
time
}
}
`
const { loading, error, data } = useQuery(myGraphQlQuery)
React.useEffect(() => {
if (!loading && !error && data) {
console.log({ data })
}
}, [loading, error, data])
लेकिन हम पहेली का एक आखिरी टुकड़ा भूल रहे हैं और वह है सर्वर। आप या तो इसे स्वयं चला सकते हैं या होस्ट की गई सेवा का उपयोग कर सकते हैं।
The Graph सर्वर
Graph Explorer: होस्ट की गई सेवा
सबसे आसान तरीका होस्ट की गई सेवा का उपयोग करना है। सबग्राफ तैनात करने के लिए यहाँ (opens in a new tab) दिए गए निर्देशों का पालन करें। कई प्रोजेक्ट्स के लिए आप वास्तव में एक्सप्लोरर (opens in a new tab) में मौजूदा सबग्राफ पा सकते हैं।
अपना स्वयं का नोड चलाना
वैकल्पिक रूप से आप अपना स्वयं का नोड चला सकते हैं। दस्तावेज़ यहाँ (opens in a new tab) हैं। ऐसा करने का एक कारण ऐसे नेटवर्क का उपयोग करना हो सकता है जो होस्ट की गई सेवा द्वारा समर्थित नहीं है। वर्तमान में समर्थित नेटवर्क यहाँ पाए जा सकते हैं (opens in a new tab)।
विकेंद्रीकृत भविष्य
GraphQL नई आने वाली घटनाओं के लिए स्ट्रीम का भी समर्थन करता है। ये ग्राफ़ पर Substreams (opens in a new tab) के माध्यम से समर्थित हैं जो वर्तमान में ओपन बीटा में हैं।
2021 (opens in a new tab) में The Graph ने विकेंद्रीकृत इंडेक्सिंग नेटवर्क में अपना संक्रमण शुरू किया। आप इस विकेंद्रीकृत इंडेक्सिंग नेटवर्क की वास्तुकला के बारे में यहाँ (opens in a new tab) अधिक पढ़ सकते हैं।
दो प्रमुख पहलू हैं:
- उपयोगकर्ता क्वेरी के लिए इंडेक्सर को भुगतान करते हैं।
- इंडेक्सर Graph Tokens (GRT) को स्टेक करते हैं।






