Knowledge Bank

Understanding subgraphs

Guide to Subgraphs on SettleMint platform

🧾 subgraph project structure – deep dive for mycontract.sol

This document provides a complete, line-by-line breakdown of a subgraph project folder designed to index events from an Ethereum smart contract named MyContract.sol. It follows the conventions used by The Graph Protocol and tooling like SettleMint Subgraph Generator.


πŸ“ folder structure

subgraph/
β”‚
β”œβ”€β”€ subgraph.config.json                 # Global generator settings
β”‚
β”œβ”€β”€ datasources/
β”‚   β”œβ”€β”€ mycontract.gql.json              # GraphQL schema definition (JSON-based)
β”‚   β”œβ”€β”€ mycontract.ts                    # AssemblyScript event handlers for MyContract
β”‚   └── mycontract.yaml                  # Subgraph manifest (datasource-level config)
β”‚
└── fetch/
    └── mycontractdata.ts               # Utility functions for entity fetch/create

πŸ”§ subgraph.config.json

βœ… purpose:

This file is consumed by SettleMint's CLI or The Graph CLI (graph codegen, graph build) to bootstrap subgraph code and configuration.

πŸ” breakdown:

{
  "output": "generated/scs.",
  "chain": "localhost",
  "datasources": [
    {
      "name": "MyContract",
      "address": "0x0000000000000000000000000000000000000000",
      "startBlock": 0,
      "module": ["mycontract"]
    }
  ]
}
FieldDescription
outputDirectory for generated TypeScript types (generated/ folder). Often linked to schema.graphql, ABI, and handler typing.
chainBlockchain environment to index (e.g., mainnet, localhost, polygon, arbitrum-one).
datasourcesArray of contract modules to be indexed. Each maps to a folder and .yaml config inside datasources/.
nameLogical name of this contract or module (used in code and schema).
addressOn-chain deployed address of MyContract.sol. Must be accurate for indexing to work.
startBlockFirst block number to index from (skip historical blocks for faster sync).
moduleName of folder containing *.yaml, *.ts, and *.gql.json files for this contract.

πŸ’‘ Tip: Use the block number where your contract was deployed to avoid unnecessary indexing of irrelevant historical data.


πŸ“„ datasources/mycontract.gql.json

βœ… purpose:

This is a JSON-encoded GraphQL schema, which defines the shape of your indexed data. Each type here corresponds to an entity stored in the subgraph’s database.

🧾 example:

[
  {
    "name": "MyEntity",
    "description": "Stores state emitted from MyContract events",
    "fields": [
      { "name": "id", "type": "ID!" },
      { "name": "creator", "type": "Bytes!" },
      { "name": "status", "type": "String!" },
      { "name": "timestamp", "type": "BigInt!" }
    ]
  }
]

πŸ” field types:

  • ID!: Required unique identifier (string format, often tx hash or entity composite key).
  • Bytes: EVM-style address or 32-byte values.
  • BigInt: For uint256, block numbers, timestamps.
  • String, Boolean, Int: Basic GraphQL types.
  • @derivedFrom: Used for reverse relationships (foreign keys) but only in .graphql, not .gql.json.

πŸ’‘ Use id as a combination of transactionHash-logIndex for uniqueness in event-based entities.


πŸ“„ datasources/mycontract.yaml

βœ… purpose:

This is the manifest file that connects your smart contract to its:

  • Deployed address
  • ABI definition
  • Events you want to index
  • GraphQL schema
  • Event handler functions

🧾 example:

specVersion: 0.0.2
schema:
  file: mycontract.gql.json
 
dataSources:
  - kind: ethereum/contract
    name: MyContract
    network: localhost
    source:
      address: "0x0000000000000000000000000000000000000000"
      abi: MyContract
      startBlock: 0
    mapping:
      kind: ethereum/events
      language: wasm/assemblyscript
      apiVersion: 0.0.5
      entities:
        - MyEntity
      abis:
        - name: MyContract
          file: "../../abis/MyContract.json"
      eventHandlers:
        - event: MyEvent(indexed uint256,string)
          handler: handleMyEvent
      file: mycontract.ts

πŸ” key fields:

SectionFieldDescription
dataSourcesnameLogical label for contract.
source.addressDeployed contract address.
source.abiRefers to name under abis:.
startBlockBlock number from which to start processing.
mapping.entitiesList of entities affected by this contract.
abis.filePath to .json ABI file of contract (must match source).
eventHandlersevent:Signature of the Solidity event being indexed.
handler:Name of exported function from .ts file that handles this event.

⚠️ event: must exactly match the ABI event signature, including indexed fields.


πŸ“„ datasources/mycontract.ts

βœ… purpose:

This file contains AssemblyScript handler functions that transform on-chain events into GraphQL entities. These handlers are executed by the subgraph runtime when matching events appear on-chain.

🧾 example:

import { BigInt } from "@graphprotocol/graph-ts";
import { MyEvent as MyEventType } from "../generated/MyContract/MyContract";
import { MyEntity } from "../generated/schema";
import { fetchMyEntity } from "../fetch/mycontractdata";
 
export function handleMyEvent(event: MyEventType): void {
  let entity = fetchMyEntity(event.params.id);
  entity.creator = event.transaction.from;
  entity.status = event.params.status;
  entity.timestamp = event.block.timestamp;
  entity.save();
}

πŸ” what's happening:

  • Event is imported from auto-generated MyContract.ts (compiled ABI).
  • A helper is called to load or create an entity (fetchMyEntity).
  • Event parameters and context (tx sender, timestamp) are assigned to fields.
  • Entity is stored using .save().

🧩 Best practice: use fetch* functions to reduce duplication and enforce default values.


πŸ“„ fetch/mycontractdata.ts

βœ… purpose:

Contains utility functions to fetch an entity from the store or create it if it doesn't exist. Useful for enforcing schema defaults and ensuring consistent object initialization.

🧾 example:

import { MyEntity } from "../../generated/schema";
import { BigInt } from "@graphprotocol/graph-ts";
 
export function fetchMyEntity(id: BigInt): MyEntity {
  let key = id.toString();
  let entity = MyEntity.load(key);
 
  if (!entity) {
    entity = new MyEntity(key);
    entity.creator = Bytes.empty();
    entity.status = "PENDING";
    entity.timestamp = BigInt.zero();
  }
 
  return entity;
}

πŸ” why it's important:

  • Prevents null pointer errors in .ts handler files.
  • Centralizes logic for initializing new entities.
  • Keeps handler functions small and readable.

βœ… summary

FilePurpose
subgraph.config.jsonGlobal configuration (network, modules, output path).
mycontract.gql.jsonGraphQL schema defining the structure of data to be stored.
mycontract.yamlContract address, ABI, events, and mapping file linkage.
mycontract.tsEvent-to-entity transformation logic in AssemblyScript.
mycontractdata.tsReusable logic to load or create GraphQL entities safely.

πŸ› οΈ best practices

  • πŸ§ͺ Always test your subgraph locally with a dev chain before deploying.
  • 🧱 Use composite IDs (txHash-logIndex) for events to avoid collisions.
  • βš™οΈ Keep fetch/ files modular to reduce logic in event handlers.
  • πŸ“Š Use BigInt, Bytes, and Boolean types consistently with EVM data.

πŸ” This setup is modular, clean, and scalable for multi-contract projects, cross-chain indexing, or advanced state machine tracking.