Setting up a Loot Subgraph on The Graph
What is a Subgraph?
Subgraph are exposed to developers via GraphQL APIs, allowing users to query the transaction data (Events) happening on their contract in real time. Subgraphs are especially beneficial for developers of complex, custom smart contracts that need to have robust frontend interfaces the display Events emitted based on their contracts.
Step 1: Define the Info and what I want to query
This looks like the Etherscan link for the Contract:
https://etherscan.io/address/0xff9c1b15b16263c61d017ee9f65c50e4ae0113d7
Using the Contract address 0xFF9C1b15B16263C61d017ee9F65C50e4AE0113D7
I also checked on Miniscan.xyz:
The Loot contract minted Loot NFTs (ERC-721) that should have on-chain randomly generated attributes.
Here is an example:
https://etherscan.io/nft/0xff9c1b15b16263c61d017ee9f65c50e4ae0113d7/2352
The content is not hosted on IPFS as a .png
but it's .svg
:
<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet" viewBox="0 0 350 350"><style>.base { fill: white; font-family: serif; font-size: 14px; }</style><rect width="100%" height="100%" fill="black" /><text x="10" y="20" class="base">Short Sword</text><text x="10" y="40" class="base">Plate Mail of Vitriol</text><text x="10" y="60" class="base">Full Helm</text><text x="10" y="80" class="base">"Storm Glow" Heavy Belt of Rage</text><text x="10" y="100" class="base">Greaves</text><text x="10" y="120" class="base">Studded Leather Gloves</text><text x="10" y="140" class="base">Pendant</text><text x="10" y="160" class="base">Silver Ring</text></svg>
NOTE: although I'm starting with my goal based on what would be interesting given the project, this isn't what a subgraph is designed for. I'll amend it to reflect this, but I'm writing this from my perspective of what I'd want to do without knowing the limitations, and then exploring the solution space.
Functions can return a response for a given tokenId
(but, as noted above, subgraphs were designed to provide real-time responses of Events, so we'll see how to change the goal and then explore how to solve this.)
function getWeapon(uint256 tokenId) public view returns (string memory) {
1470 return pluck(tokenId, "WEAPON", weapons);
1471 }
1472
1473 function getChest(uint256 tokenId) public view returns (string memory) {
1474 return pluck(tokenId, "CHEST", chestArmor);
1475 }
1476
1477 function getHead(uint256 tokenId) public view returns (string memory) {
1478 return pluck(tokenId, "HEAD", headArmor);
1479 }
1480
1481 function getWaist(uint256 tokenId) public view returns (string memory) {
1482 return pluck(tokenId, "WAIST", waistArmor);
1483 }
1484
1485 function getFoot(uint256 tokenId) public view returns (string memory) {
1486 return pluck(tokenId, "FOOT", footArmor);
1487 }
1488
1489 function getHand(uint256 tokenId) public view returns (string memory) {
1490 return pluck(tokenId, "HAND", handArmor);
1491 }
1492
1493 function getNeck(uint256 tokenId) public view returns (string memory) {
1494 return pluck(tokenId, "NECK", necklaces);
1495 }
1496
1497 function getRing(uint256 tokenId) public view returns (string memory) {
1498 return pluck(tokenId, "RING", rings);
1499 }
So what is it that I want to query?
- How many NFTs have a given value for a given attribute?
- Edit:
For example:
- How many tokens will return for
getChest
Plate Mail of Vitriol
?
Step 2: Install the Graph CLI
npm install -g @graphprotocol/graph-cli
Run graph
to verify.
Run graph init
to initialize the package's CLI wizard and select the appropriate options:
- protocol:
ethereum
- product:
hosted-service
- subgraph name:
timfong888/loot-nft
- ethereum network:
mainnet
- contract address:
0xFF9C1b15B16263C61d017ee9F65C50e4AE0113D7
My first pass said: Failed to fetch contract creation transaction hash
and gave me the option to try again, which is nice.
It filled out the start block for me.
- contract name:
Loot
- Index contract events as entities:
Y
- Do I want to index another contract?
n
Cool! Lift off:
Step 3: Review and Tweak Generated Files
Here's what's in the new folder loot-nft
(after I cd loot-nft
):
- schema.graphql: defines entities which are contract Events indexed
- Entity example:
Transfer
with fieldsid
,fromAddress
,toAddress
,tokenId
, andtimestamp
- Entity example:
- subgraph.yaml: the manifest -- looks like what I entered from CLI
- loot.ts: This file (which will be
*.ts
) contains the function handlers for your entities. When your subgraph indexes a new event, it will run the function, defined in this file, (and mapped to the entity in the subgraph.yaml file) to that entity.
Step 4: Evaluate Available Entities to Your Goal
So...my query is based on functions, not entities.
This means that the output for a given function may not be available at all...unless I can query via historical Events.