Snapshot

In this tutorial, we will show you how to index Snapshot data using the Blockflow CLI. Blockflow provides a seamless and efficient solution for building "Serverless Backends" through our Managed Databases, Instances, and Custom APIs.

Snapshot Labs is an innovative platform that facilitates decentralized governance for blockchain projects. It lets token holders easily vote on proposals without paying gas fees, making the process super efficient. With its user-friendly interface, anyone can join in and have a say in decision-making. The platform also supports various blockchain protocols, making it flexible and perfect for diverse communities looking to manage their projects collaboratively.


Project Overview

The purpose of this tutorial is to teach you how to index Snapshot data by creating subgraphs with the aid of Blockflow and its utilities. A short breakdown for the project is -

  • Create a project directory and cd into it. Use the command blockflow init through the terminal and provide with the relevant contract address to be tracked.

  • Provide with appropriate schema to the studio.schema.ts file depending upon the data emitted by the events to be tracked.

  • Write the handler functions for tracking each event.

  • Test the project using the comand blockflow test and verify the data stored over the database.


Setting up the project

  • Download the Blockflow CLI using the command:

    npm i -g @blockflow-labs/cli

    We can remove the -g tag if global installation is not required.

  • Create a project directory and use the cd command over it to make it the working directory.

  • Use the blockflow init command to initiate the project.

  • Below are the fields that need to be entered for the project. You can also let the default values for each field by pressing [ENTER]. The last field requires you to select the instance trigger type with 4 choices. You can also add more than one trigger for a project.


Setting up the schema

The below mentioned schema is appropriate for indexing the events emitted by the Snapshot contract:

export interface Delegation {
  id: String;
  delegator: string;
  space: string;
  delegate: string;
  timestamp: string;
}

export interface Block {
  id: String;
  number: Number;
  timestamp: string;
}

export interface Sig {
  id: String;
  account: string;
  messageHash: string;
  timestamp: string;
}

On completing the schema, use the command blockflow typegen to generate types.


Writing the handler function

Now we move onto writing the handler functions but first we remove the unwanted event triggers in our studio.yaml file otherwise unnecessary handlers would be generated in our /src directory. Below is the studio.yaml:

name: Project Apollo
description: A top-secret research project to the moon
startBlock: latest
userId: XXXXXXXX-XXXX-XXXX-XXXXXXXX-XXXXXXXX
projectId: XXXXXXXX-XXXX-XXXX-XXXXXXXX-XXXXXXXX
network: Ethereum
user: Jane-doe
schema:
  file: ./studio.schema.ts
execution: parallel
Resources:
  - Name: delegateregistry
    Abi: src/abis/delegateregistry.json
    Type: contract/event
    Address: "0x469788fE6E9E9681C6ebF3bF78e7Fd26Fc015446"
    Triggers:
      - Event: ClearDelegate(address indexed,bytes32 indexed,address indexed)
        Handler: src/handlers/delegateregistry/ClearDelegate.ClearDelegateHandler
      - Event: SetDelegate(address indexed,bytes32 indexed,address indexed)
        Handler: src/handlers/delegateregistry/SetDelegate.SetDelegateHandler
  - Name: v100
    Abi: src/abis/v100.json
    Type: contract/event
    Address: "0x12302fE9c02ff50939BaAaaf415fc226C078613C"
    Triggers:
      - Event: ProxyCreation(address)
        Handler: src/handlers/v100/ProxyCreation.ProxyCreationHandler
  - Name: v111
    Abi: src/abis/v111.json
    Type: contract/event
    Address: "0x76E2cFc1F5Fa8F6a5b3fC4c8F4788F0116861F9B"
    Triggers:
      - Event: ProxyCreation(address)
        Handler: src/handlers/v111/ProxyCreation.ProxyCreationHandler
  - Name: v130
    Abi: src/abis/v130.json
    Type: contract/event
    Address: "0xa6B71E26C5e0845f74c812102Ca7114b6a896AB2"
    Triggers:
      - Event: ProxyCreation(address,address)
        Handler: src/handlers/v130/ProxyCreation.ProxyCreationHandler
  - template:
    Name: GnosisSafe
    Abi: src/abis/v111.json
    Type: contract/event
    Triggers:
      - Event: SignMsg(address)
        Handler: src/handlers/SignMsg.SignMsgHandler

Use the command blockflow codegen to generate handler templates in the /src/handlers directory.

The handlers have been generated as such so we now move onto writing handler logic and updating the databases. In the /src/handlers/delegateregistry directory, we find two files - ClearDelegate.ts and SetDelegate.ts. We proceed with writing the ClearDelegate handler. The boilerplate code is already present there. We now import the Delegation and other imp classes from the types/schema and bind the database connections.

import { Delegation, IDelegation, Block, IBlock} from "../../types/schema";

const delegationDB = bind(Delegation);
const blockDB = bind(Block);
let Id = `${delegator}-${id}-${delegate}`;
let blockId = `${block.block_number}`;

We create a delegation variable and use the await method to create the document in delegationDB and blockDB as follows.

let delegation = await delegationDB.create({
    id: Id,
    delegator: delegator,
    space: id,
    delegate: delegate
  });
 
  let blockData = await blockDB.create({ 
    id: blockId,
    number: block.block_number,
    timestamp: block.block_timestamp,
  });
};

To fill the fields, we use the emitted event data and blockflow's utils. Similarly we can write the handler logic for the SetDelegate.ts file as follows

const { event, transaction, block, log } = context;
  const { delegator, id, delegate } = event;

  const delegationDB = bind(Delegation);
  const blockDB = bind(Block);

  let Id = `${delegator}-${id}-${delegate}`;
  let blockId = `${block.block_number}`;

  let delegation = await delegationDB.create({
    id: Id,
    delegator: delegator,
    space: id,
    delegate: delegate,
    timestamp: block.block_timestamp,
  });

  let blockData = await blockDB.create({ 
    id: blockId,
    number: block.block_number,
    timestamp: block.block_timestamp,
  });
};

Testing

We use the below command to test if the handler logic is correct and the data gets stored on our specified collection in Mongo.

blockflow instance-test --startBlock <block number> --clean --range 10 --uri <connection string>

The <block number> can be put of any range, but we can put it to latest if no opinion provided. The — uri holds the MongoDB connection URL which is tested locally shall be filled with mongodb://localhost:27017/blockflow_studio.

Last updated