Skip to main content

Build

After having prepared Schema and Manifest file, let's dive into the build process

Prepare phase

Before building the subgraph, you need to run the codegen process

make prepare

Prepared files will be generated and place inside generated folder. It contains:

  • abi_helpers: utils functions to moving forward with the logic faster
  • eventhandlers: Defines interfaces for handling blockchain events
  • mappings: Defines mapping functions for each processing events defined in Manifest file
  • migration: Defines migration process for the subgraph, based on the schema
  • models: Defines models for the subgraph, based on the schema
  • query: Defines GraphQL queries for the subgraph
  • routers: Defines the route for the handlers

Run make system-migrate-up and make generated-migrate-up to apply the migration to database

Code generation also prepare basic query, and is placed in db/grapqldb

LayerG crawler value highly customizable crawler. Given the generated files, you can still customize the crawler to your needs, or optimize the query for better performance.

Build phase

For handlers, you need to place them in package handlers

Here is an example of a handler:

type TransferHandler struct {
*BaseHandler
}

func NewTransferHandler(queries *db.Queries, gqlQueries *graphqldb.Queries, chainID int32, logger *zap.SugaredLogger) *TransferHandler {
return &TransferHandler{
BaseHandler: &BaseHandler{
Queries: queries,
GQL: gqlQueries,
ChainID: chainID,
Logger: logger,
},
}
}

func (h *TransferHandler) HandleTransfer(ctx context.Context, event *eventhandlers.Transfer) error {
// Handle ERC721 transfer
fromUser, err := h.GQL.GetOrCreateUser(ctx, event.From.Hex())
if err != nil {
h.Logger.Errorw("Failed to ensure 'from' user exists",
"err", err,
"address", event.From.Hex(),
)
return fmt.Errorf("failed to ensure 'from' user exists: %w", err)
}
h.AddOperation("User", fromUser, event.Raw.BlockHash.Hex(), event.Raw.BlockNumber)

toUser, err := h.GQL.GetOrCreateUser(ctx, event.To.Hex())
if err != nil {
h.Logger.Errorw("Failed to ensure 'to' user exists",
"err", err,
"address", event.To.Hex(),
)
return fmt.Errorf("failed to ensure 'to' user exists: %w", err)
}
h.AddOperation("User", toUser, event.Raw.BlockHash.Hex(), event.Raw.BlockNumber)

tokenID := event.TokenId.String()

// Create or update item
item, err := h.GQL.GetItemByTokenId(ctx, tokenID)
if err != nil {
// Create new item if it doesn't exist
item, err = h.GQL.CreateItem(ctx, graphqldb.CreateItemParams{
ID: uuid.New().String(),
TokenID: tokenID,
TokenUri: "",
Standard: "ERC721",
})
if err != nil {
return fmt.Errorf("failed to create item: %w", err)
}
}

// Update balances
if event.From.Hex() != "0x0000000000000000000000000000000000000000" {
// Remove balance from sender
_, err = h.GQL.UpsertBalance(ctx, graphqldb.UpsertBalanceParams{
ID: uuid.New().String(),
ItemID: item.ID,
OwnerID: fromUser.ID,
Value: "0",
UpdatedAt: fmt.Sprintf("%d", event.Raw.BlockNumber),
Contract: event.Raw.Address.Hex(),
})
if err != nil {
return fmt.Errorf("failed to update sender balance: %w", err)
}
}

// Add balance to receiver
_, err = h.GQL.UpsertBalance(ctx, graphqldb.UpsertBalanceParams{
ID: uuid.New().String(),
ItemID: item.ID,
OwnerID: toUser.ID,
Value: "1",
UpdatedAt: fmt.Sprintf("%d", event.Raw.BlockNumber),
Contract: event.Raw.Address.Hex(),
})
if err != nil {
return fmt.Errorf("failed to update 721 receiver balance: %w", err)
}

h.Logger.Infow("Transfer event processed",
"tokenId", tokenID,
"from", fromUser.ID,
"to", toUser.ID,
"contract", event.Raw.Address.Hex(),
"tx", event.Raw.TxHash.Hex(),
)

if err := h.SubmitToDA(); err != nil {
h.Logger.Errorw("Failed to submit to DA", "error", err)
}

return nil
}


Run

Run make run to run the subgraph Run make query to run the query server for the subgraph

Deploy