The Graph: Web3 ڈیٹا کیوری کو ٹھیک کرنا
اس بار ہم The Graph کا گہرائی سے جائزہ لیں گے جو پچھلے سال غیر مرکزی ایپلی کیشنز (dapps) تیار کرنے کے لیے معیاری اسٹیک کا حصہ بن گیا ہے۔ آئیے پہلے دیکھتے ہیں کہ ہم روایتی طریقے سے چیزیں کیسے کریں گے...
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 ڈیٹا کی تعریف ہے۔ یہ آپ کو یہ واضح کرنے کی اجازت دے گا کہ کون سی اینٹیٹیز موجود ہیں اور ان کی اقسام کیا ہیں۔ The Graph کی طرف سے تعاون یافتہ اقسام یہ ہیں
- Bytes
- ID
- String
- Boolean
- Int
- BigInt
- BigDecimal
آپ تعلقات کی وضاحت کرنے کے لیے اینٹیٹیز کو بطور قسم بھی استعمال کر سکتے ہیں۔ ہماری مثال میں ہم کھلاڑی سے شرطوں تک 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 میں میپنگ فائل ہمارے ان فنکشنز کی وضاحت کرتی ہے جو آنے والے ایونٹس کو اینٹیٹیز میں تبدیل کرتے ہیں۔ یہ AssemblyScript میں لکھی گئی ہے، جو TypeScript کا ایک ذیلی سیٹ ہے۔ اس کا مطلب ہے کہ اسے میپنگ کے زیادہ موثر اور پورٹیبل نفاذ کے لیے WASM (WebAssembly) میں مرتب کیا جا سکتا ہے۔
آپ کو subgraph.yaml فائل میں نامزد ہر فنکشن کی وضاحت کرنے کی ضرورت ہوگی، لہذا ہمارے معاملے میں ہمیں صرف ایک کی ضرورت ہے: handleNewBet۔ ہم سب سے پہلے بھیجنے والے کے پتے سے Player اینٹیٹی کو بطور id لوڈ کرنے کی کوشش کرتے ہیں۔ اگر یہ موجود نہیں ہے، تو ہم ایک نئی اینٹیٹی بناتے ہیں اور اسے ابتدائی اقدار سے بھرتے ہیں۔
پھر ہم ایک نئی 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 hooks اور 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 سرور
گراف ایکسپلورر: ہوسٹڈ سروس
سب سے آسان طریقہ ہوسٹڈ سروس کا استعمال کرنا ہے۔ سب گراف تعینات کرنے کے لیے یہاں (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) پڑھ سکتے ہیں۔
دو اہم پہلو یہ ہیں:
- صارفین کیوریز کے لیے انڈیکسرز کو ادائیگی کرتے ہیں۔
- انڈیکسرز گراف ٹوکنز (GRT) کو اسٹیک کرتے ہیں۔






