The Graph: Web3 veri sorgusunu düzeltme
Bu kez, geçen yıl merkeziyetsiz uygulamalar geliştirmek için standart yığının esasen bir parçası haline gelen The Graph'e daha yakından bakacağız. Önce geleneksel yöntemlerle bunları nasıl yapacağımızı görelim...
The Graph olmasaydı...
O hâlde, göstermek amacıyla basit bir örnekle devam edelim. Hepimiz oyunları severiz, bu yüzden kullanıcıların bahis oynadığı basit bir oyun hayal edin:
1pragma solidity 0.7.1;23contract Game {4 uint256 totalGamesPlayerWon = 0;5 uint256 totalGamesPlayerLost = 0;6 event BetPlaced(address player, uint256 value, bool hasWon);78 function placeBet() external payable {9 bool hasWon = evaluateBetForPlayer(msg.sender);1011 if (hasWon) {12 (bool success, ) = msg.sender.call{ value: msg.value * 2 }('');13 require(success, "Aktarım başarısız");14 totalGamesPlayerWon++;15 } else {16 totalGamesPlayerLost++;17 }1819 emit BetPlaced(msg.sender, msg.value, hasWon);20 }21}Tümünü gösterŞimdi diyelim ki merkeziyetsiz uygulamamızda, toplam bahisleri, kaybedilen/kazanılan toplam oyunları görüntülemek ve birisi tekrar oynadığında bunu güncellemek istiyoruz. Yaklaşım şöyle olurdu:
totalGamesPlayerWon'ı alın.totalGamesPlayerLost'u alın.BetPlacedolaylarına abone olun.
Sağda gösterildiği gibi Web3'teki olayı (opens in a new tab) dinleyebiliriz, ancak bu, epey bir durumu ele almayı gerektirir.
1GameContract.events.BetPlaced({2 fromBlock: 03}, function(error, event) { console.log(event); })4.on('data', function(event) {5 // olay tetiklendi6})7.on('changed', function(event) {8 // olay tekrar kaldırıldı9})10.on('error', function(error, receipt) {11 // işlem reddedildi12});Tümünü gösterBu durum, basit örneğimiz için hâlâ bir dereceye kadar kabul edilebilir. Ama diyelim ki şimdi sadece mevcut oyuncu için kaybedilen/kazanılan bahis miktarlarını görüntülemek istiyoruz. Pekala, şansımız yaver gitmedi, bu değerleri depolayan ve onları çeken yeni bir sözleşme dağıtmanız daha iyi olur. Ve şimdi çok daha karmaşık bir akıllı sözleşme ve merkeziyetsiz uygulama hayal edin, işler çabucak karışabilir.
Bunun neden optimal olmadığını görebilirsiniz:
- Halihazırda dağıtılmış sözleşmeler için çalışmaz.
- Bu değerleri depolamak için ek gaz maliyetleri.
- Bir Ethereum düğümünden veri çekmek için başka bir çağrı gerektirir.
Şimdi daha iyi bir çözüme bakalım.
GraphQL ile tanıştırayım
İlk olarak, Facebook tarafından tasarlanan ve uygulanan GraphQL'den bahsedelim. Geleneksel REST API modeline aşina olabilirsiniz. Şimdi bunun yerine, tam olarak istediğiniz veriler için bir sorgu yazabildiğinizi hayal edin:
Bu iki görsel, GraphQL'in özünü büyük ölçüde yansıtıyor. Sağdaki sorguyla tam olarak hangi verileri istediğimizi tanımlayabiliriz, böylece her şeyi tek bir istekte alırız ve ihtiyacımız olandan fazlasını da almayız. Bir GraphQL sunucusu, gerekli tüm verilerin çekilmesini yönetir, bu nedenle ön yüz tarafında kullanımı inanılmaz derecede kolaydır. İlgileniyorsanız, bu bağlantı (opens in a new tab) sunucunun bir sorguyu tam olarak nasıl ele aldığını güzel bir şekilde açıklıyor.
Şimdi bu bilgiyle, nihayet blokzincir alanına ve The Graph'e geçelim.
The Graph nedir?
Blokzincir merkeziyetsiz bir veritabanıdır, ancak alışılmışın aksine, bu veritabanı için bir sorgu dilimiz yoktur. Veri almak için çözümler ya zahmetlidir ya da tamamen imkansızdır. The Graph, blokzincir verilerini dizine eklemek ve sorgulamak için merkeziyetsiz bir protokoldür. Tahmin etmişsinizdir, sorgu dili olarak GraphQL kullanıyor.
Bir şeyi anlamanın en iyi yolu her zaman örneklerdir, bu yüzden GameContract örneğimiz için The Graph'i kullanalım.
Subgraph nasıl oluşturulur
Verilerin nasıl dizine ekleneceğinin tanımına subgraph denir. Üç bileşen gerektirir:
- Manifest (
subgraph.yaml) - Şema (
schema.graphql) - Eşleme (
mapping.ts)
Manifest (subgraph.yaml)
Manifest, yapılandırma dosyamızdır ve şunları tanımlar:
- hangi akıllı sözleşmelerin dizine ekleneceği (adres, ağ, ABI...)
- hangi olayların dinleneceği
- fonksiyon çağrıları veya bloklar gibi dinlenecek diğer şeyler
- çağrılan eşleme fonksiyonları (aşağıdaki
mapping.ts'ye bakın)
Burada birden fazla sözleşme ve işleyici tanımlayabilirsiniz. Tipik bir kurulum, Hardhat projesi içinde kendi deposu olan bir subgraph klasörüne sahip olacaktır. Ardından ABI'ye kolayca başvurabilirsiniz.
Kolaylık sağlamak için mustache gibi bir şablon aracı da kullanmak isteyebilirsiniz. Ardından bir subgraph.template.yaml oluşturur ve en son dağıtımlara göre adresleri eklersiniz. Daha gelişmiş bir örnek kurulum için, örneğin Aave subgraph deposuna (opens in a new tab) bakın.
Tüm belgelere buradan (opens in a new tab) ulaşabilirsiniz.
1specVersion: 0.0.12description: Placing Bets on Ethereum3repository: - GitHub link -4schema:5 file: ./schema.graphql6dataSources:7 - kind: ethereum/contract8 name: GameContract9 network: mainnet10 source:11 address: '0x2E6454...cf77eC'12 abi: GameContract13 startBlock: 617524414 mapping:15 kind: ethereum/events16 apiVersion: 0.0.117 language: wasm/assemblyscript18 entities:19 - GameContract20 abis:21 - name: GameContract22 file: ../build/contracts/GameContract.json23 eventHandlers:24 - event: PlacedBet(address,uint256,bool)25 handler: handleNewBet26 file: ./src/mapping.tsTümünü gösterŞema (schema.graphql)
Şema, GraphQL veri tanımıdır. Hangi varlıkların var olduğunu ve bunların türlerini tanımlamanıza olanak tanır. The Graph tarafından desteklenen türler şunlardır:
- Bayt
- ID
- String
- Boolean
- Int
- BigInt
- BigDecimal
İlişkileri tanımlamak için varlıkları tür olarak da kullanabilirsiniz. Örneğimizde, oyuncudan bahislere bire çok ilişki tanımlıyoruz. ! işareti, değerin boş olamayacağı anlamına gelir. Tüm belgelere buradan (opens in a new tab) ulaşabilirsiniz.
1type Bet @entity {2 id: ID!3 player: Player!4 playerHasWon: Boolean!5 time: Int!6}78type Player @entity {9 id: ID!10 totalPlayedCount: Int11 hasWonCount: Int12 hasLostCount: Int13 bets: [Bet]!14}Tümünü gösterEşleme (mapping.ts)
The Graph'teki eşleme dosyası, gelen olayları varlıklara dönüştüren işlevlerimizi tanımlar. TypeScript'in bir alt kümesi olan AssemblyScript ile yazılmıştır. Bu, eşlemenin daha verimli ve taşınabilir yürütülmesi için WASM'de (WebAssembly) derlenebileceği anlamına gelir.
subgraph.yaml dosyasında adı geçen her fonksiyonu tanımlamanız gerekecek, bu nedenle bizim durumumuzda yalnızca bir taneye ihtiyacımız var: handleNewBet. İlk önce gönderici adresinden Player varlığını kimlik olarak yüklemeye çalışıyoruz. Eğer mevcut değilse, yeni bir varlık yaratır ve onu başlangıç değerleri ile doldururuz.
Sonrasında yeni bir Bet varlığı oluştururuz. Bunun kimliği, her zaman benzersiz bir değer sağlayan event.transaction.hash.toHex() + "-" + event.logIndex.toString() olacaktır. Birisi bir akıllı sözleşme aracılığıyla placeBet fonksiyonunu bir işlemde birkaç kez çağırıyor olabileceğinden, yalnızca hash değerini kullanmak yeterli değildir.
Son olarak Player varlığını tüm verilerle güncelleyebiliriz. Dizilere doğrudan gönderim yapılamaz, ancak burada gösterildiği gibi güncellenmesi gerekir. Bahse başvurmak için kimliği kullanırız. Ve bir varlığı saklamak için sonunda .save() gereklidir.
Tüm belgelere buradan erişilebilir: https://thegraph.com/docs/en/developing/creating-a-subgraph/#writing-mappings (opens in a new tab). Ayrıca eşleme dosyasına günlük kaydı çıktısı da ekleyebilirsiniz, buraya (opens in a new tab) bakın.
1import { Bet, Player } from "../generated/schema"2import { PlacedBet } from "../generated/GameContract/GameContract"34export function handleNewBet(event: PlacedBet): void {5 let player = Player.load(event.transaction.from.toHex())67 if (player == null) {8 // henüz mevcut değilse oluştur9 player = new Player(event.transaction.from.toHex())10 player.bets = new Array<string>(0)11 player.totalPlayedCount = 012 player.hasWonCount = 013 player.hasLostCount = 014 }1516 let bet = new Bet(17 event.transaction.hash.toHex() + "-" + event.logIndex.toString()18 )19 bet.player = player.id20 bet.playerHasWon = event.params.hasWon21 bet.time = event.block.timestamp22 bet.save()2324 player.totalPlayedCount++25 if (event.params.hasWon) {26 player.hasWonCount++27 } else {28 player.hasLostCount++29 }3031 // diziyi bu şekilde güncelle32 let bets = player.bets33 bets.push(bet.id)34 player.bets = bets3536 player.save()37}Tümünü gösterÖn Yüzde Kullanımı
Apollo Boost gibi bir şey kullanarak The Graph'i React merkeziyetsiz uygulamanıza (veya Apollo-Vue) kolayca entegre edebilirsiniz. Özellikle React kancaları ve Apollo kullanırken veri almak, bileşeninize tek bir GraphQL sorgusu yazmak kadar basittir. Tipik bir kurulum şöyle görünebilir:
1// Tüm subgraph'ları gör: https://thegraph.com/explorer/2const client = new ApolloClient({3 uri: "{{ subgraphUrl }}",4})56ReactDOM.render(7 <ApolloProvider client={client}>8 <App />9 </ApolloProvider>,10 document.getElementById("root")11)Tümünü gösterVe şimdi örneğin şöyle bir sorgu yazabiliriz. Bu bize şunları getirecektir:
- mevcut kullanıcının kaç kez kazandığını
- mevcut kullanıcının kaç kez kaybettiğini
- önceki tüm bahisleriyle birlikte zaman damgalarının bir listesini
Hepsi GraphQL sunucusuna tek bir istekte.
1const myGraphQlQuery = gql`2 players(where: { id: $currentUser }) {3 totalPlayedCount4 hasWonCount5 hasLostCount6 bets {7 time8 }9 }10`1112const { loading, error, data } = useQuery(myGraphQlQuery)1314React.useEffect(() => {15 if (!loading && !error && data) {16 console.log({ data })17 }18}, [loading, error, data])Tümünü gösterAma yapbozun son bir parçası eksik: sunucu. Kendiniz çalıştırabilir veya barındırılan hizmeti kullanabilirsiniz.
The Graph sunucusu
Graph Explorer: Barındırılan hizmet
En kolay yol, barındırılan hizmeti kullanmaktır. Bir subgraph dağıtmak için buradaki (opens in a new tab) talimatları izleyin. Birçok proje için, mevcut subgraph'ları explorer (opens in a new tab) içinde bulabilirsiniz.
Kendi düğümünüzü çalıştırma
Alternatif olarak kendi düğümünüzü çalıştırabilirsiniz. Belgeler burada (opens in a new tab). Bunu yapmanın bir nedeni, barındırılan hizmet tarafından desteklenmeyen bir ağ kullanmak olabilir. Şu anda desteklenen ağlar burada bulunabilir (opens in a new tab).
Merkeziyetsiz gelecek
GraphQL, yeni gelen olaylar için akışları da destekler. Bunlar şu anda açık beta sürümünde olan Substreams (opens in a new tab) aracılığıyla grafikte desteklenmektedir.
The Graph, 2021 (opens in a new tab) yılında merkeziyetsiz bir dizin oluşturma ağına geçişine başladı. Bu merkeziyetsiz dizin oluşturma ağının mimarisi hakkında daha fazla bilgiyi buradan (opens in a new tab) okuyabilirsiniz.
İki temel unsur şunlardır:
- Kullanıcılar sorgular için dizin oluşturuculara ödeme yapar.
- Dizin oluşturucular, Graph Jetonlarını (GRT) stake eder.
Sayfanın son güncellenmesi: 26 Şubat 2026






