Tutorial: How to access transactions of PEPE ($PEPE) coin using The Graph subgraphs and ChatGPT prompts

Kirill Balakhonov | Chainstack
6 min readMay 27, 2023

--

I am going to show how to use The Graph subgraphs to easily access the data of the PEPE smart contract and ChatGPT prompting to write powerful GraphQL queries. Of course, you can use this technology to build your own subgraph and use its data in your DApp, trading/MEV bot, research, or analytics.

What is The Graph?

The Graph is an indexing protocol for organizing blockchain data and making it easily accessible with GraphQL. But we don’t always need the entire protocol. To get access to some data we can just do the following steps:

  1. Generate a subgraph code for a specific smart contract
  2. Add some values to indexing if needed (by editing the subgraph code)
  3. Deploy a subgraph into any hosted service (for faster syncing you can use Chainstack Subgraphs) and wait indexing.
  4. Make queries using GraphQL

In some cases, you don’t even need to write code.

How to create a subgraph

To install the libraries run

npm install -g @graphprotocol/graph-cli

or

yarn global add @graphprotocol/graph-cli

Then if you run the command:

graph init --allow-simple-name

The command line will respond with a few questions. Choose the parameters according to my example:

✔ Protocol · ethereum
✔ Product for which to initialize · hosted-service
✔ Subgraph name · pepe
✔ Directory to create the subgraph in · pepe
✔ Ethereum network · mainnet
✔ Contract address · 0x6982508145454Ce325dDbE47a25d4ec3d2311933
✔ Fetching ABI from Etherscan
✖ Failed to fetch Start Block: Failed to fetch contract creation transaction hash

✔ Start Block · 17046105
✔ Contract Name · Contract
✔ Index contract events as entities (Y/n) · true

It means that the code will be generated for a contract on Ethereum Mainnet; “hosted-service” means that you are not going to deploy your code into the decentralized network, but into a hosted service that is much cheaper; the name of the folder and subgraph will be “pepe”, the address of the smart contract is “0x6982508145454Ce325dDbE47a25d4ec3d2311933” that is found on Coinmarketcap:

Also, the ABI (Application Binary Interface) file has been extracted from Etherscan automatically. But the start block for the indexing we have to find manually. To do so, we go to the Etherscan, search this smart contract by address, clicking on the transaction with the deployment of this smart contract:

and getting the start block number:

The last line of the “graph init” command input

Index contract events as entities (Y/n) · true

means that we expect a boilerplate code for this subgraph which collects all the data emitted by events in the smart contract.

What we get after the code generation

Overall, we get the project folder with some source files. But we need to understand only three of them:

  1. subgraph.yaml — the manifest file with some general parameters like network, start block, names of events, etc.
  2. schema.graphql — the description of tables that store the data in the internal database
  3. src/contract.ts — the data collection logic which is written in AssemblyScript (similar to TypeSript programming language)

In this case, with PEPE smart contract we are not going to write any custom code, because ERC-20 contracts have already good contents in the events so the generated code of a subgraph will cover most of the required applications. We just briefly look at the sources to understand what we can modify.

Let’s see what the sources are containing

The subgraph.yaml file looks like this:

specVersion: 0.0.5
schema:
file: ./schema.graphql
dataSources:
- kind: ethereum
name: Contract
network: mainnet
source:
address: "0x6982508145454Ce325dDbE47a25d4ec3d2311933"
abi: Contract
startBlock: 17046105
mapping:
kind: ethereum/events
apiVersion: 0.0.7
language: wasm/assemblyscript
entities:
- Approval
- OwnershipTransferred
- Transfer
abis:
- name: Contract
file: ./abis/Contract.json
eventHandlers:
- event: Approval(indexed address,indexed address,uint256)
handler: handleApproval
- event: OwnershipTransferred(indexed address,indexed address)
handler: handleOwnershipTransferred
- event: Transfer(indexed address,indexed address,uint256)
handler: handleTransfer
file: ./src/contract.ts

You can easily remove some events (to skip them while indexing), change startBlock, migrate to another chain/network, change the smart contract address, etc.

The schema.graphql file contains the following tables’ descriptions:

type Approval @entity(immutable: true) {
id: Bytes!
owner: Bytes! # address
spender: Bytes! # address
value: BigInt! # uint256
blockNumber: BigInt!
blockTimestamp: BigInt!
transactionHash: Bytes!
}

type OwnershipTransferred @entity(immutable: true) {
id: Bytes!
previousOwner: Bytes! # address
newOwner: Bytes! # address
blockNumber: BigInt!
blockTimestamp: BigInt!
transactionHash: Bytes!
}

type Transfer @entity(immutable: true) {
id: Bytes!
from: Bytes! # address
to: Bytes! # address
value: BigInt! # uint256
blockNumber: BigInt!
blockTimestamp: BigInt!
transactionHash: Bytes!
}

You can remove some tables or fields, or you can add new ones if needed. You can also read more about it in the Explaining subgraph schemas manual.

The last file is called “src/contract.ts” and looks like this:

import {
Approval as ApprovalEvent,
OwnershipTransferred as OwnershipTransferredEvent,
Transfer as TransferEvent
} from "../generated/Contract/Contract"
import { Approval, OwnershipTransferred, Transfer } from "../generated/schema"

export function handleApproval(event: ApprovalEvent): void {
let entity = new Approval(
event.transaction.hash.concatI32(event.logIndex.toI32())
)
entity.owner = event.params.owner
entity.spender = event.params.spender
entity.value = event.params.value

entity.blockNumber = event.block.number
entity.blockTimestamp = event.block.timestamp
entity.transactionHash = event.transaction.hash

entity.save()
}

export function handleOwnershipTransferred(
event: OwnershipTransferredEvent
): void {
let entity = new OwnershipTransferred(
event.transaction.hash.concatI32(event.logIndex.toI32())
)
entity.previousOwner = event.params.previousOwner
entity.newOwner = event.params.newOwner

entity.blockNumber = event.block.number
entity.blockTimestamp = event.block.timestamp
entity.transactionHash = event.transaction.hash

entity.save()
}

export function handleTransfer(event: TransferEvent): void {
let entity = new Transfer(
event.transaction.hash.concatI32(event.logIndex.toI32())
)
entity.from = event.params.from
entity.to = event.params.to
entity.value = event.params.value

entity.blockNumber = event.block.number
entity.blockTimestamp = event.block.timestamp
entity.transactionHash = event.transaction.hash

entity.save()
}

It shows how the data from the event in the smart contract will be stored in the database tables that we saw above. The code is pretty straightforward, but you can do a lot of complex transformations including updating the existing rows in the database and adding the data from the transaction, block, transaction receipt, or transaction log. This can be not easy to figure out for a fresh user of subgraphs, but you can join our small subgraph developer community and ask any questions there.

Now it’s time to deploy a subgraph. The deployment command usually looks like this:

graph deploy 
--node https://api.graph-eu.p2pify.com/3a57099edc73d24c2807cafeefaa82e1/deploy
--ipfs https://api.graph-eu.p2pify.com/3a57099edc36a35c2807cafeefaa82e1/ipfs
pepe_subgraph

But better to copy&paste the deployment command from a hosted service subgraph page. The order of actions will be similar to this.

After the successful deployment, you need to wait until the subgraph will collect all the data from “startBlock” to the current chain head block. Usually, you can see the indication on the subgraph page on a hosted service.

When the subgraph has been synced, you can do queries using the GraphQL interface. It is pretty easy to master GraphQL using this video, but you can hack it by asking ChatGPT to create a GraphQL query for your schema😅

Let’s check if it works. You can also use the standard GUI to query the data:

Works perfectly! You can also ask for more complex queries and iteratively figure out how to fix them. In this case, it can be helpful to use this prompt

For this graphql.schema

{here goes your schema}

of just generated using “graph init” subgraph (The Graph), write a GraphQL query {here goer the description of your query}.

This can help to switch CharGPR to The Graph version of GraphQL. Although you can find your own prompts that could work better.

That’s it! I hope you enjoyed this subgraph tutorial, and if you have any questions regarding this manual or any other questions, join our small subgraph developer community and ask any questions there.

--

--

Kirill Balakhonov | Chainstack

Product Lead @ chainstack.com / Data Science Expert - Building Web3 Data APIs / Blockchain Indexing Solutions / Subgraphs