Skip to content

Commit f1d9d56

Browse files
mdtanrikulugskril
andauthored
migrate ponder indexer code to v0.12 (#9)
* migrate ponder indexer code to v0.10 * Bump and fix Ponder --------- Co-authored-by: Greg Skriloff <[email protected]>
1 parent e33c042 commit f1d9d56

File tree

7 files changed

+984
-900
lines changed

7 files changed

+984
-900
lines changed

apps/indexer/package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@
1111
"typecheck": "tsc"
1212
},
1313
"dependencies": {
14-
"@ponder/core": "0.5.0-next.1",
15-
"hono": "^4.4.7",
14+
"hono": "^4.9.4",
15+
"ponder": "^0.12.16",
1616
"shared": "workspace:*",
17-
"viem": "^1.19.3"
17+
"viem": "^2.8.2"
1818
},
1919
"devDependencies": {
2020
"@types/node": "^20.9.0",
@@ -25,4 +25,4 @@
2525
"engines": {
2626
"node": ">=18.14"
2727
}
28-
}
28+
}

apps/indexer/ponder-env.d.ts

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,15 @@
1-
// This file enables type checking and editor autocomplete for this Ponder project.
2-
// After upgrading, you may find that changes have been made to this file.
3-
// If this happens, please commit the changes. Do not manually edit this file.
4-
// See https://ponder.sh/docs/getting-started/installation#typescript for more information.
5-
6-
declare module "@/generated" {
7-
import type { Virtual } from "@ponder/core";
8-
9-
type config = typeof import("./ponder.config.ts").default;
10-
type schema = typeof import("./ponder.schema.ts").default;
1+
/// <reference types="ponder/virtual" />
112

12-
export const ponder: Virtual.Registry<config, schema>;
3+
declare module "ponder:internal" {
4+
const config: typeof import("./ponder.config.ts");
5+
const schema: typeof import("./ponder.schema.ts");
6+
}
137

14-
export type EventNames = Virtual.EventNames<config>;
15-
export type Event<name extends EventNames = EventNames> = Virtual.Event<
16-
config,
17-
name
18-
>;
19-
export type Context<name extends EventNames = EventNames> = Virtual.Context<
20-
config,
21-
schema,
22-
name
23-
>;
24-
export type IndexingFunctionArgs<name extends EventNames = EventNames> =
25-
Virtual.IndexingFunctionArgs<config, schema, name>;
26-
export type Schema = Virtual.Schema<schema>;
8+
declare module "ponder:schema" {
9+
export * from "./ponder.schema.ts";
2710
}
11+
12+
// This file enables type checking and editor autocomplete for this Ponder project.
13+
// After upgrading, you may find that changes have been made to this file.
14+
// If this happens, please commit the changes. Do not manually edit this file.
15+
// See https://ponder.sh/docs/requirements#typescript for more information.

apps/indexer/ponder.config.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
import { createConfig } from '@ponder/core'
1+
import { createConfig } from 'ponder'
22
import { erc20MultiDelegateContract } from 'shared/contracts'
33
import { http } from 'viem'
44

55
export default createConfig({
6-
networks: {
6+
chains: {
77
mainnet: {
8-
chainId: 1,
9-
transport: http(process.env.PONDER_RPC_URL_1),
8+
id: 1,
9+
rpc: process.env.PONDER_RPC_URL_1,
1010
},
1111
},
1212
contracts: {
1313
MultiDelegate: {
1414
...erc20MultiDelegateContract,
15-
network: 'mainnet',
15+
chain: 'mainnet',
1616
startBlock: erc20MultiDelegateContract.deployedBock,
1717
},
1818
},

apps/indexer/ponder.schema.ts

Lines changed: 40 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,45 @@
1-
import { createSchema } from '@ponder/core'
1+
import { onchainTable } from 'ponder'
22

3-
/*
4-
I want to be able to query the following:
5-
6-
{
7-
account ("0x123") {
8-
delegates {
9-
id
10-
amount
11-
}
12-
}
13-
}
14-
15-
or if that doesn't work, this is ok:
16-
17-
{
18-
account ("0x123") {
19-
delegates
20-
values
21-
}
22-
}
23-
24-
last option is a custom GET endpoint that returns something like this:
25-
26-
[
27-
{
28-
"delegate": "0x534631bcf33bdb069fb20a93d2fdb9e4d4dd42cf",
29-
"tokenId": "475411618940684652382658899876961866559843549903",
30-
"amount": "25000000000000000000"
31-
},
32-
{
33-
"delegate": "0xa7860e99e3ce0752d1ac53b974e309fff80277c6",
34-
"tokenId": "956391030522194004329440103514838893413546489798",
35-
"amount": "10000000000000000000"
36-
},
37-
]
38-
*/
39-
40-
export default createSchema((p) => ({
41-
Account: p.createTable({
42-
id: p.hex(),
43-
delegates: p.hex().list(),
44-
}),
3+
export const Account = onchainTable('Account', (t) => ({
4+
id: t.hex().primaryKey(),
5+
delegates: t.hex().array().notNull(),
6+
}))
457

46-
DelegationProcessedEvent: p.createTable({
47-
id: p.string(),
48-
from: p.hex(),
49-
to: p.hex(),
50-
amount: p.bigint(),
51-
}),
8+
export const DelegationProcessedEvent = onchainTable(
9+
'DelegationProcessedEvent',
10+
(t) => ({
11+
id: t.text().primaryKey(),
12+
timestamp: t.bigint().notNull(),
13+
owner: t.hex().notNull(),
14+
from: t.hex().notNull(),
15+
to: t.hex().notNull(),
16+
amount: t.bigint().notNull(),
17+
})
18+
)
19+
20+
export const ProxyDeployedEvent = onchainTable('ProxyDeployedEvent', (t) => ({
21+
id: t.text().primaryKey(),
22+
timestamp: t.bigint().notNull(),
23+
delegate: t.hex().notNull(),
24+
proxyAddress: t.hex().notNull(),
25+
}))
5226

53-
ProxyDeployedEvent: p.createTable({
54-
id: p.string(),
55-
delegate: p.hex(),
56-
proxyAddress: p.hex(),
57-
}),
27+
export const TransferBatchEvent = onchainTable('TransferBatchEvent', (t) => ({
28+
id: t.text().primaryKey(),
29+
timestamp: t.bigint().notNull(),
30+
operator: t.hex().notNull(),
31+
from: t.hex().notNull(),
32+
to: t.hex().notNull(),
33+
ids: t.bigint().array().notNull(),
34+
values: t.bigint().array().notNull(),
35+
}))
5836

59-
TransferBatchEvent: p.createTable({
60-
id: p.string(),
61-
operator: p.hex(),
62-
from: p.hex(),
63-
to: p.hex(),
64-
ids: p.bigint().list(),
65-
values: p.bigint().list(),
66-
}),
37+
export const TransferEvent = onchainTable('TransferEvent', (t) => ({
38+
key: t.text().primaryKey(),
39+
timestamp: t.bigint().notNull(),
40+
operator: t.hex().notNull(),
41+
from: t.hex().notNull(),
42+
to: t.hex().notNull(),
43+
id: t.bigint().notNull(),
44+
value: t.bigint().notNull(),
6745
}))

apps/indexer/src/api/index.ts

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,37 @@
1-
import { graphql } from '@ponder/core'
2-
import { cors } from 'hono/cors'
1+
import { Hono } from 'hono'
2+
import { eq, graphql } from 'ponder'
3+
import { db, publicClients } from 'ponder:api'
4+
import schema, { Account, DelegationProcessedEvent } from 'ponder:schema'
35
import { erc20MultiDelegateContract } from 'shared/contracts'
4-
import { createPublicClient, isAddress } from 'viem'
6+
import { isAddress } from 'viem'
57

6-
import { ponder } from '@/generated'
8+
const app = new Hono()
79

8-
import ponderConfig from '../../ponder.config'
10+
app.use('/', graphql({ db, schema }))
911

10-
ponder.use('*', cors())
12+
app.get('/:address', async (ctx) => {
13+
const { address } = ctx.req.param()
1114

12-
ponder.use('/', graphql())
15+
if (!isAddress(address)) {
16+
return ctx.json({ error: 'Invalid address' })
17+
}
1318

14-
ponder.get('/:address', async (c) => {
15-
const { address } = c.req.param()
16-
const { Account } = c.get('db')
17-
const client = createPublicClient(ponderConfig.networks.mainnet)
19+
const result = await db
20+
.select()
21+
.from(Account)
22+
.where(eq(Account.id, address))
23+
.limit(1)
1824

19-
if (!isAddress(address)) {
20-
return c.json({ error: 'Invalid address' })
25+
if (result.length === 0 || result[0]?.delegates?.length == null) {
26+
return ctx.json([])
2127
}
2228

23-
const delegates = (await Account.findUnique({ id: address }))?.delegates || []
24-
const tokenIds = delegates.map((item) => BigInt(item))
29+
const { delegates } = result[0]
30+
const tokenIds = delegates.map((item: string | number | bigint | boolean) =>
31+
BigInt(item)
32+
)
2533

26-
const balanceOf = await client.readContract({
34+
const balanceOf = await publicClients.mainnet.readContract({
2735
...erc20MultiDelegateContract,
2836
functionName: 'balanceOfBatch',
2937
args: [new Array(tokenIds.length).fill(address), tokenIds],
@@ -36,7 +44,7 @@ ponder.get('/:address', async (c) => {
3644
}))
3745

3846
// remove delegates with no balance
39-
return c.json(
47+
return ctx.json(
4048
data
4149
.filter((item) => item.amount !== '0')
4250
.map((item) => ({
@@ -45,3 +53,5 @@ ponder.get('/:address', async (c) => {
4553
}))
4654
)
4755
})
56+
57+
export default app

apps/indexer/src/index.ts

Lines changed: 58 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,79 @@
1-
import { toHex } from 'viem'
2-
3-
import { ponder } from '@/generated'
1+
import { ponder } from 'ponder:registry'
2+
import {
3+
Account,
4+
DelegationProcessedEvent,
5+
ProxyDeployedEvent,
6+
TransferBatchEvent,
7+
TransferEvent,
8+
} from 'ponder:schema'
9+
import { toHex, zeroAddress } from 'viem'
410

511
ponder.on('MultiDelegate:DelegationProcessed', async ({ event, context }) => {
6-
const { DelegationProcessedEvent } = context.db
7-
8-
await DelegationProcessedEvent.create({
9-
id: event.log.id,
10-
data: event.args,
12+
await context.db.insert(DelegationProcessedEvent).values({
13+
id: event.id,
14+
timestamp: event.block.timestamp,
15+
...event.args,
1116
})
1217
})
1318

1419
ponder.on('MultiDelegate:ProxyDeployed', async ({ event, context }) => {
15-
const { ProxyDeployedEvent } = context.db
16-
17-
await ProxyDeployedEvent.create({
18-
id: event.log.id,
19-
data: event.args,
20+
await context.db.insert(ProxyDeployedEvent).values({
21+
id: event.id,
22+
timestamp: event.block.timestamp,
23+
...event.args,
2024
})
2125
})
2226

2327
ponder.on('MultiDelegate:TransferBatch', async ({ event, context }) => {
24-
const { Account, TransferBatchEvent } = context.db
2528
const { to, ids, values } = event.args
2629
const delegates = ids.map((id) => toHex(id))
2730

28-
await TransferBatchEvent.create({
29-
id: event.log.id,
30-
data: {
31-
...event.args,
32-
ids: ids.map((id) => id),
33-
values: values.map((value) => value),
34-
},
31+
await context.db.insert(TransferBatchEvent).values({
32+
id: event.id,
33+
timestamp: event.block.timestamp,
34+
...event.args,
35+
ids: ids.map((id) => id),
36+
values: values.map((value) => value),
3537
})
3638

37-
if (to === '0x0000000000000000000000000000000000000000') return
39+
if (to === zeroAddress) return
3840

3941
// Store any address that an account has ever delegated to, even if it's currently not
4042
// TODO: Store `amount` here as well so we don't need a separate endpoint
41-
await Account.upsert({
42-
id: to,
43-
create: {
43+
await context.db
44+
.insert(Account)
45+
.values({
46+
id: to,
4447
delegates,
45-
},
46-
update: ({ current }) => ({
47-
delegates: Array.from(new Set([...current.delegates, ...delegates])),
48-
}),
48+
})
49+
.onConflictDoUpdate((current) => ({
50+
delegates: Array.from(
51+
new Set([...(current.delegates || []), ...delegates])
52+
),
53+
}))
54+
})
55+
56+
ponder.on('MultiDelegate:TransferSingle', async ({ event, context }) => {
57+
const { to, id, value } = event.args
58+
const delegate = toHex(id)
59+
60+
await context.db.insert(TransferEvent).values({
61+
key: event.id,
62+
timestamp: event.block.timestamp,
63+
...event.args,
4964
})
65+
66+
if (to === zeroAddress) return
67+
68+
// Store any address that an account has ever delegated to, even if it's currently not
69+
// TODO: Store `amount` here as well so we don't need a separate endpoint
70+
await context.db
71+
.insert(Account)
72+
.values({
73+
id: to,
74+
delegates: [delegate],
75+
})
76+
.onConflictDoUpdate((current) => ({
77+
delegates: Array.from(new Set([...(current.delegates || []), delegate])),
78+
}))
5079
})

0 commit comments

Comments
 (0)