Migrate a subgraph

Migrate a subgraph

Tips for migrating mapping functions

Functions run in Node.js, not WebAssembly

You can import NPM packages, debug with console.log, and use normal JavaScript types like string and number.

Entities & contracts are injected, not imported

mapping.ts (Graph Protocol)
import { Token, Wallet } from "../generated/schema";
import { MyNftContract } from "../generated/MyNftContract/MyNftContract";
 
export function handleTransfer(event) {
  // Get an entity object
  const token = Token.load(event.params.id);
 
  // ...
}
src/MyNftContract.ts
import { ponder } from "@/generated";
 
ponder.on("MyNftContract:Transfer", async ({ event, context }) => {
  const { Token } = context.entities;
 
  // Get an entity object
  const token = await Token.findUnique({ id: event.params.id });
 
  // ...
});

Tips for migrating schema

Ponder uses TypeScript to define the schema of the underlying database.

Start

The createSchema() function allows for static schema validation, as well as type hints.

schema.graphql
# No initial infrastructure is needed
ponder.schema.ts
import { p } from "@ponder/core";
 
export const schema = p.createSchema({
  // schema definition here
});

Tables

Ponder aims to have a schema format more similiar to the underlying database format.

schema.graphql
type ExampleTable @entity {
  # table definition here
}
ponder.schema.ts
import { p } from "@ponder/core";
 
export const schema = p.createSchema({
  ExampleTable: p.createTable({
    // table definition here
  }),
});

Enums

schema.graphql
enum Color {
  ORANGE
  BLACK
}
 
type Cat @entity {
  color: Color!
}
ponder.schema.ts
import { p } from "@ponder/core";
 
export const schema = p.createSchema({
  Color: p.createEnum(["ORANGE", "BLACK"]),
  Cat: p.createTable({
    color: p.enum("Color"),
  }),
});

Scalar Columns

Columns are non-optional by default in the TypeScript schema

schema.graphql
type Account @entity {
  id: Bytes!
  daiBalance: BigInt!
  totalUsdValue: Float!
  lastActiveAt: Int!
  isAdmin: Boolean!
  graffiti: String!
}
ponder.schema.ts
import { p } from "@ponder/core";
 
export const schema = p.createSchema({
  Account: p.createTable({
    id: p.bytes(),
    daiBalance: p.bigint(),
    totalUsdValue: p.float(),
    lastActiveAt: p.int(),
    isAdmin: p.boolean(),
    graffiti: p.string(),
  }),
});

Optional

Columns can be made options with the .optional() modifier.

schema.graphql
type User @entity {
  id: String!
  highScore: Int
  middleName: String
}
ponder.schema.ts
import { p } from "@ponder/core";
 
export const schema = p.createSchema({
  User: p.createTable({
    id: p.string(),
    highScore: p.int().optional(),
    middleName: p.string().optional(),
  }),
});

List

schema.graphql
type FancyCat @entity {
  id: String!
  favoriteNumbers: [Int!]!
}
ponder.schema.ts
import { p } from "@ponder/core";
 
export const schema = p.createSchema({
  FancyCat: p.createTable({
    id: p.string(),
    favoriteNumbers: p.int().list(),
  }),
});

Reference Columns

Reference column names must end with "Id" in the TypeScript schema. Query fields on the GraphQL endpoint remove this suffix to more closely represent the return type of the query.

TypeScript schema is much closer to the actual database layout, where the owner column is a secondary key that holds the primary key of the "Person" table.

schema.graphql
type Person @entity {
  id: String!
  age: Int!
}
 
type Dog @entity {
  id: String!
  owner: Person!
}
ponder.schema.ts
import { p } from "@ponder/core";
 
export const schema = p.createSchema({
  Person: p.createTable({
    id: p.string(),
    age: p.int(),
  }),
  Dog: p.createTable({
    id: p.string(),
    ownerId: p.string().references("Person.id"),
  }),
});

Virutal/Derived Columns

Virtual columns do not exist in the database, they are entirely used for the GraphQL endpoint.

schema.graphql
type Person @entity {
  id: String!
  dogs: [Dog!]! @derivedFrom(field: "owner")
}
 
type Dog @entity {
  id: String!
  owner: Person!
}
ponder.schema.ts
import { p } from "@ponder/core";
 
export const schema = p.createSchema({
  Person: p.createTable({
    id: p.string(),
    dogs: p.virtual("Dog.ownerId"),
  }),
  Dog: p.createTable({
    id: p.string(),
    ownerId: p.string().references("Person.id"),
  }),
});