মূল কন্টেন্টে যান

The Graph: ওয়েব3 ডাটা কুয়েরিং ঠিক করা

Solidity
স্মার্ট কন্ট্রাক্ট
কুয়েরিং
the graph
React
ইন্টারমিডিয়েট
মার্কাস ওয়াস
6 সেপ্টেম্বর, 2020
8 মিনিট পড়া

এবার আমরা The Graph সম্পর্কে আরও বিস্তারিত জানব, যা গত বছর ডিএ্যাপস (dapps) ডেভেলপ করার জন্য স্ট্যান্ডার্ড স্ট্যাকের একটি অপরিহার্য অংশ হয়ে উঠেছে। চলুন প্রথমে দেখি কীভাবে আমরা ঐতিহ্যবাহী উপায়ে কাজগুলো করতাম...

The Graph ছাড়া...

বোঝার সুবিধার্থে চলুন একটি সহজ উদাহরণ দিয়ে শুরু করি। আমরা সবাই গেম পছন্দ করি, তাই কল্পনা করুন এমন একটি সাধারণ গেম যেখানে ব্যবহারকারীরা বাজি ধরছে:

1pragma solidity 0.7.1;
2
3contract Game {
4 uint256 totalGamesPlayerWon = 0;
5 uint256 totalGamesPlayerLost = 0;
6 event BetPlaced(address player, uint256 value, bool hasWon);
7
8 function placeBet() external payable {
9 bool hasWon = evaluateBetForPlayer(msg.sender);
10
11 if (hasWon) {
12 (bool success, ) = msg.sender.call{ value: msg.value * 2 }('');
13 require(success, "Transfer failed");
14 totalGamesPlayerWon++;
15 } else {
16 totalGamesPlayerLost++;
17 }
18
19 emit BetPlaced(msg.sender, msg.value, hasWon);
20 }
21}

এখন ধরুন আমাদের ডিএ্যাপ-এ, আমরা মোট বাজি, মোট হারা/জেতা গেমের সংখ্যা দেখাতে চাই এবং যখনই কেউ আবার খেলবে তখন এটি আপডেট করতে চাই। এর পদ্ধতিটি হবে:

  1. totalGamesPlayerWon ফেচ করা।
  2. totalGamesPlayerLost ফেচ করা।
  3. BetPlaced ইভেন্টগুলোতে সাবস্ক্রাইব করা।

আমরা ডানদিকে দেখানো event in Web3 (opens in a new tab)-এর মতো ইভেন্ট শুনতে পারি, তবে এর জন্য বেশ কয়েকটি কেস হ্যান্ডেল করতে হয়।

1GameContract.events.BetPlaced({
2 fromBlock: 0
3}, function(error, event) { console.log(event); })
4.on('data', function(event) {
5 // ইভেন্ট ফায়ার হয়েছে
6})
7.on('changed', function(event) {
8 // ইভেন্টটি আবার মুছে ফেলা হয়েছে
9})
10.on('error', function(error, receipt) {
11 // ট্রানজ্যাকশন বাতিল হয়েছে
12});

আমাদের সাধারণ উদাহরণের জন্য এটি এখনও কিছুটা ঠিক আছে। কিন্তু ধরুন আমরা এখন শুধুমাত্র বর্তমান খেলোয়াড়ের হারা/জেতা বাজির পরিমাণ দেখাতে চাই। এক্ষেত্রে আমাদের ভাগ্য খারাপ, আপনাকে এমন একটি নতুন কন্ট্রাক্ট ডিপ্লয় করতে হবে যা এই মানগুলো স্টোর করে এবং সেগুলো ফেচ করে। আর এখন আরও জটিল একটি স্মার্ট কন্ট্রাক্ট এবং ডিএ্যাপ কল্পনা করুন, পরিস্থিতি খুব দ্রুতই বিশৃঙ্খল হয়ে যেতে পারে।

One Does Not Simply Query

আপনি দেখতে পাচ্ছেন কেন এটি সর্বোত্তম নয়:

  • ইতিমধ্যে ডিপ্লয় করা কন্ট্রাক্টগুলোর জন্য কাজ করে না।
  • এই মানগুলো স্টোর করার জন্য অতিরিক্ত গ্যাস খরচ হয়।
  • একটি ইথিরিয়াম নোড থেকে ডাটা ফেচ করার জন্য আরেকটি কলের প্রয়োজন হয়।

Thats not good enough

এখন চলুন একটি ভালো সমাধান দেখি।

চলুন GraphQL-এর সাথে পরিচয় করিয়ে দিই

প্রথমে চলুন GraphQL নিয়ে কথা বলি, যা মূলত Facebook দ্বারা ডিজাইন এবং বাস্তবায়িত হয়েছিল। আপনি হয়তো ঐতিহ্যবাহী REST API মডেলের সাথে পরিচিত। এখন কল্পনা করুন এর পরিবর্তে আপনি ঠিক যে ডাটাটি চান তার জন্যই একটি কুয়েরি লিখতে পারবেন:

GraphQL API vs. REST API

Animated demonstration of a GraphQL query in The Graph playground

এই দুটি ছবি মূলত GraphQL-এর মূল বিষয়বস্তু তুলে ধরে। ডানদিকের কুয়েরি দিয়ে আমরা ঠিক কী ডাটা চাই তা নির্ধারণ করতে পারি, তাই আমরা একটি রিকোয়েস্টেই সবকিছু পেয়ে যাই এবং আমাদের যা প্রয়োজন তার চেয়ে বেশি কিছু পাই না। একটি GraphQL সার্ভার প্রয়োজনীয় সব ডাটা ফেচ করার কাজ পরিচালনা করে, তাই ফ্রন্টএন্ড কনজিউমার সাইডের জন্য এটি ব্যবহার করা অবিশ্বাস্যভাবে সহজ। আপনি যদি আগ্রহী হন, তবে সার্ভার কীভাবে একটি কুয়েরি পরিচালনা করে তার একটি চমৎকার ব্যাখ্যা এখানে রয়েছে (opens in a new tab)

এখন এই জ্ঞান নিয়ে, চলুন অবশেষে ব্লকচেইন স্পেস এবং The Graph-এ প্রবেশ করি।

The Graph কী?

ব্লকচেইন হলো একটি ডিসেন্ট্রালাইজড ডাটাবেস, কিন্তু সাধারণত যেমনটা হয় তার বিপরীতে, এই ডাটাবেসের জন্য আমাদের কোনো কুয়েরি ল্যাঙ্গুয়েজ নেই। ডাটা রিট্রিভ করার সমাধানগুলো কষ্টদায়ক বা সম্পূর্ণ অসম্ভব। The Graph হলো ব্লকচেইন ডাটা ইনডেক্সিং এবং কুয়েরি করার জন্য একটি ডিসেন্ট্রালাইজড প্রটোকল। এবং আপনি হয়তো অনুমান করতে পেরেছেন, এটি কুয়েরি ল্যাঙ্গুয়েজ হিসেবে GraphQL ব্যবহার করে।

The Graph

কোনো কিছু বোঝার জন্য উদাহরণ সবসময়ই সেরা, তাই চলুন আমাদের GameContract উদাহরণের জন্য The Graph ব্যবহার করি।

কীভাবে একটি সাবগ্রাফ (Subgraph) তৈরি করবেন

কীভাবে ডাটা ইনডেক্স করতে হবে তার সংজ্ঞাকে সাবগ্রাফ বলা হয়। এর জন্য তিনটি উপাদানের প্রয়োজন:

  1. ম্যানিফেস্ট (subgraph.yaml)
  2. স্কিমা (schema.graphql)
  3. ম্যাপিং (mapping.ts)

ম্যানিফেস্ট (subgraph.yaml)

ম্যানিফেস্ট হলো আমাদের কনফিগারেশন ফাইল এবং এটি নির্ধারণ করে:

  • কোন স্মার্ট কন্ট্রাক্টগুলো ইনডেক্স করতে হবে (এডড্রেস, নেটওয়ার্ক, ABI...)
  • কোন ইভেন্টগুলো শুনতে হবে
  • ফাংশন কল বা ব্লকস-এর মতো অন্যান্য যেসব বিষয় শুনতে হবে
  • কল করা ম্যাপিং ফাংশনগুলো (নিচে mapping.ts দেখুন)

আপনি এখানে একাধিক কন্ট্রাক্ট এবং হ্যান্ডলার সংজ্ঞায়িত করতে পারেন। একটি সাধারণ সেটআপে Hardhat প্রজেক্টের ভেতরে নিজস্ব রিপোজিটরি সহ একটি সাবগ্রাফ ফোল্ডার থাকবে। তারপর আপনি সহজেই ABI রেফারেন্স করতে পারবেন।

সুবিধার জন্য আপনি mustache-এর মতো একটি টেমপ্লেট টুলও ব্যবহার করতে চাইতে পারেন। এরপর আপনি একটি subgraph.template.yaml তৈরি করবেন এবং সর্বশেষ ডিপ্লয়মেন্টের ওপর ভিত্তি করে এডড্রেসগুলো ইনসার্ট করবেন। আরও উন্নত উদাহরণ সেটআপের জন্য, উদাহরণস্বরূপ Aave subgraph repo (opens in a new tab) দেখতে পারেন।

এবং সম্পূর্ণ ডকুমেন্টেশন এখানে (opens in a new tab) দেখা যেতে পারে।

1specVersion: 0.0.1
2description: Placing Bets on Ethereum
3repository: - GitHub link -
4schema:
5 file: ./schema.graphql
6dataSources:
7 - kind: ethereum/contract
8 name: GameContract
9 network: mainnet
10 source:
11 address: '0x2E6454...cf77eC'
12 abi: GameContract
13 startBlock: 6175244
14 mapping:
15 kind: ethereum/events
16 apiVersion: 0.0.1
17 language: wasm/assemblyscript
18 entities:
19 - GameContract
20 abis:
21 - name: GameContract
22 file: ../build/contracts/GameContract.json
23 eventHandlers:
24 - event: PlacedBet(address,uint256,bool)
25 handler: handleNewBet
26 file: ./src/mapping.ts

স্কিমা (schema.graphql)

স্কিমা হলো GraphQL ডাটা ডেফিনিশন। এটি আপনাকে কোন এনটিটিগুলো বিদ্যমান এবং তাদের ধরন সংজ্ঞায়িত করতে দেয়। The Graph থেকে সমর্থিত ধরনগুলো হলো

  • Bytes
  • ID
  • String
  • Boolean
  • Int
  • BigInt
  • BigDecimal

সম্পর্ক সংজ্ঞায়িত করতে আপনি এনটিটিগুলোকে টাইপ হিসেবেও ব্যবহার করতে পারেন। আমাদের উদাহরণে আমরা প্লেয়ার থেকে বাজির (bets) একটি 1-to-many সম্পর্ক সংজ্ঞায়িত করেছি। ! মানে হলো মানটি খালি হতে পারবে না। সম্পূর্ণ ডকুমেন্টেশন এখানে (opens in a new tab) দেখা যেতে পারে।

1type Bet @entity {
2 id: ID!
3 player: Player!
4 playerHasWon: Boolean!
5 time: Int!
6}
7
8type Player @entity {
9 id: ID!
10 totalPlayedCount: Int
11 hasWonCount: Int
12 hasLostCount: Int
13 bets: [Bet]!
14}

ম্যাপিং (mapping.ts)

The Graph-এর ম্যাপিং ফাইলটি আমাদের সেই ফাংশনগুলোকে সংজ্ঞায়িত করে যা আগত ইভেন্টগুলোকে এনটিটিতে রূপান্তরিত করে। এটি Typescript-এর একটি সাবসেট AssemblyScript-এ লেখা হয়। এর মানে হলো ম্যাপিংয়ের আরও দক্ষ এবং পোর্টেবল এক্সিকিউশনের জন্য এটিকে WASM (WebAssembly)-এ কম্পাইল করা যেতে পারে।

আপনাকে subgraph.yaml ফাইলে নাম দেওয়া প্রতিটি ফাংশন সংজ্ঞায়িত করতে হবে, তাই আমাদের ক্ষেত্রে শুধুমাত্র একটি প্রয়োজন: handleNewBet। আমরা প্রথমে সেন্ডার এডড্রেস থেকে id হিসেবে Player এনটিটি লোড করার চেষ্টা করি। যদি এটি না থাকে, তবে আমরা একটি নতুন এনটিটি তৈরি করি এবং এটিকে প্রারম্ভিক মান দিয়ে পূরণ করি।

এরপর আমরা একটি নতুন Bet এনটিটি তৈরি করি। এর id হবে event.transaction.hash.toHex() + "-" + event.logIndex.toString() যা সবসময় একটি ইউনিক মান নিশ্চিত করে। শুধুমাত্র হ্যাস ব্যবহার করা যথেষ্ট নয় কারণ কেউ হয়তো একটি স্মার্ট কন্ট্রাক্ট-এর মাধ্যমে এক লেনদেন-এ একাধিকবার placeBet ফাংশন কল করতে পারে।

সবশেষে আমরা সমস্ত ডাটা দিয়ে Player এনটিটি আপডেট করতে পারি। অ্যারেগুলোতে সরাসরি পুশ করা যায় না, তবে এখানে দেখানো উপায়ে আপডেট করতে হয়। আমরা বাজিটিকে রেফারেন্স করতে id ব্যবহার করি। এবং একটি এনটিটি স্টোর করার জন্য শেষে .save() প্রয়োজন।

সম্পূর্ণ ডকুমেন্টেশন এখানে দেখা যেতে পারে: https://thegraph.com/docs/en/developing/creating-a-subgraph/#writing-mappings। (opens in a new tab) আপনি ম্যাপিং ফাইলে লগিং আউটপুটও যোগ করতে পারেন, এখানে (opens in a new tab) দেখুন।

1import { Bet, Player } from "../generated/schema"
2import { PlacedBet } from "../generated/GameContract/GameContract"
3
4export function handleNewBet(event: PlacedBet): void {
5 let player = Player.load(event.transaction.from.toHex())
6
7 if (player == null) {
8 // যদি এখনও না থাকে তবে তৈরি করুন
9 player = new Player(event.transaction.from.toHex())
10 player.bets = new Array<string>(0)
11 player.totalPlayedCount = 0
12 player.hasWonCount = 0
13 player.hasLostCount = 0
14 }
15
16 let bet = new Bet(
17 event.transaction.hash.toHex() + "-" + event.logIndex.toString()
18 )
19 bet.player = player.id
20 bet.playerHasWon = event.params.hasWon
21 bet.time = event.block.timestamp
22 bet.save()
23
24 player.totalPlayedCount++
25 if (event.params.hasWon) {
26 player.hasWonCount++
27 } else {
28 player.hasLostCount++
29 }
30
31 // অ্যারেটি এভাবে আপডেট করুন
32 let bets = player.bets
33 bets.push(bet.id)
34 player.bets = bets
35
36 player.save()
37}

ফ্রন্টএন্ডে এটি ব্যবহার করা

Apollo Boost-এর মতো কিছু ব্যবহার করে, আপনি সহজেই আপনার React ডিএ্যাপ-এ (বা Apollo-Vue) The Graph ইন্টিগ্রেট করতে পারেন। বিশেষ করে যখন React হুকস এবং Apollo ব্যবহার করা হয়, তখন ডাটা ফেচ করা আপনার কম্পোনেন্টে একটি একক GraphQL কুয়েরি লেখার মতোই সহজ। একটি সাধারণ সেটআপ দেখতে এমন হতে পারে:

1// সমস্ত সাবগ্রাফ দেখুন: https://thegraph.com/explorer/
2const client = new ApolloClient({
3 uri: "{{ subgraphUrl }}",
4})
5
6ReactDOM.render(
7 <ApolloProvider client={client}>
8 <App />
9 </ApolloProvider>,
10 document.getElementById("root")
11)

এবং এখন আমরা উদাহরণস্বরূপ এমন একটি কুয়েরি লিখতে পারি। এটি আমাদের জন্য ফেচ করবে

  • বর্তমান ব্যবহারকারী কতবার জিতেছে
  • বর্তমান ব্যবহারকারী কতবার হেরেছে
  • তার আগের সমস্ত বাজির টাইমস্ট্যাম্পের একটি তালিকা

সবকিছু GraphQL সার্ভারে একটি মাত্র রিকোয়েস্টের মাধ্যমে।

1const myGraphQlQuery = gql`
2 players(where: { id: $currentUser }) {
3 totalPlayedCount
4 hasWonCount
5 hasLostCount
6 bets {
7 time
8 }
9 }
10`
11
12const { loading, error, data } = useQuery(myGraphQlQuery)
13
14React.useEffect(() => {
15 if (!loading && !error && data) {
16 console.log({ data })
17 }
18}, [loading, error, data])

Magic

তবে আমাদের পাজলের শেষ একটি অংশ বাকি আছে আর তা হলো সার্ভার। আপনি চাইলে এটি নিজে রান করতে পারেন অথবা হোস্টেড সার্ভিস ব্যবহার করতে পারেন।

The Graph সার্ভার

Graph Explorer: হোস্টেড সার্ভিস

সবচেয়ে সহজ উপায় হলো হোস্টেড সার্ভিস ব্যবহার করা। একটি সাবগ্রাফ ডিপ্লয় করতে এখানকার (opens in a new tab) নির্দেশিকা অনুসরণ করুন। অনেক প্রজেক্টের জন্য আপনি আসলে এক্সপ্লোরার (opens in a new tab)-এ বিদ্যমান সাবগ্রাফগুলো খুঁজে পেতে পারেন।

The Graph-Explorer

নিজের নোড রান করা

বিকল্প হিসেবে আপনি নিজের নোড রান করতে পারেন। ডক্স এখানে (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)

দুটি মূল দিক হলো:

  1. ব্যবহারকারীরা কুয়েরির জন্য ইনডেক্সারদের পেমেন্ট করে।
  2. ইনডেক্সাররা Graph Tokens (GRT) স্টেক করে।

পেজ সর্বশেষ আপডেট: 3 মার্চ, 2026

এই টিউটোরিয়ালটি কি সহায়ক ছিল?