Skip to content

Commit

Permalink
#505 Init indexer v2 for dao factory (#514)
Browse files Browse the repository at this point in the history
* feat: indexer for dao factory (wip: need fix on apibara lib with their drizzle plugin)

* chore: add all config files
  • Loading branch information
ncoquelet authored Feb 19, 2025
1 parent 17fa19e commit 5fa4987
Show file tree
Hide file tree
Showing 22 changed files with 490 additions and 318 deletions.
2 changes: 2 additions & 0 deletions apps/indexer-v2/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
POSTGRES_CONNECTION_STRING=postgres://postgres:postgres@localhost:5432/postgres
DNA_TOKEN_ADDRESS=fdfsfsd
142 changes: 142 additions & 0 deletions apps/indexer-v2/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
*.lcov

# nyc test coverage
.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# Snowpack dependency directory (https://snowpack.dev/)
web_modules/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional stylelint cache
.stylelintcache

# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local

# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache

# Next.js build output
.next
out
next-env.d.ts

# Nuxt.js build / generate output
.nuxt
dist

# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public

# vuepress build output
.vuepress/dist

# vuepress v2.x temp and cache directory
.temp

# Docusaurus cache and generated files
.docusaurus

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# TernJS port file
.tern-port

# Stores VSCode versions used for testing VSCode extensions
.vscode-test

# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

# next
.next

# prisma
.prisma

# apibara
.apibara

# drizzle
.drizzle
40 changes: 40 additions & 0 deletions apps/indexer-v2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Indexer V2

## How to run

```
docker compose up -d
```

Run indexer

```
docker run -it --env-file ./.env dao-indexer run /app/main.ts --tls-accept-invalid-certificates=true --allow-env-from-env POSTGRES_CONNECTION_STRING,AUTH_TOKEN
```

```
docker run -it --env-file ./.env dao-indexer run /app/main.ts --tls-accept-invalid-certificates=true --allow-env-from-env POSTGRES_CONNECTION_STRING
```

## Install

Example:
https://github.com/apibara/typescript-sdk/tree/main/examples/cli

# Todo

- Fix script in Typescript: indexer script operation failed
├╴at /build/source/script/src/script.rs:339:22
├╴failed to run indexer event loop
╰╴error: TypeError: No such file or di


- Saved DAO AA Created in db
- Saved DAO Proposal Created in db
- Saved DAO Proposal Voted in db
- Saved DAO Proposal Executed in db
- Saved DAO Proposal Whitelisted in db
- Saved DAO Proposal Ended in db
- Saved DAO Proposal Failed in db
- Saved DAO Proposal Succeeded in db
18 changes: 18 additions & 0 deletions apps/indexer-v2/apibara.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import typescript from '@rollup/plugin-typescript';
import { defineConfig } from 'apibara/config';

export default defineConfig({
runtimeConfig: {
pgLiteDBPath: 'memory://persistence',
},
presets: {
dev: {
runtimeConfig: {
pgLiteDBPath: './.persistence',
},
},
},
rollupConfig: {
plugins: [typescript()],
},
});
26 changes: 26 additions & 0 deletions apps/indexer-v2/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
version: "3"
services:
postgres:
image: timescale/timescaledb-ha:pg14-latest
restart: always
environment:
POSTGRES_PASSWORD: postgres
ports:
- "5432:5432"
volumes:
- timescaledb_data:/var/lib/postgresql/data

dao-indexer:
environment:
- AUTH_TOKEN=${AUTH_TOKEN}
- POSTGRES_CONNECTION_STRING=${POSTGRES_CONNECTION_STRING}
image: quay.io/apibara/sink-postgres:latest
command: 'run ./src/main.ts --allow-env-from-env --allow-env AUTH_TOKEN,POSTGRES_CONNECTION_STRING -A ${AUTH_TOKEN}'
volumes:
- ./src:/app
depends_on:
- postgres
restart: on-failure

volumes:
timescaledb_data:
13 changes: 13 additions & 0 deletions apps/indexer-v2/drizzle.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { Config } from 'drizzle-kit';

const connectionString =
process.env.POSTGRES_CONNECTION_STRING ?? 'postgres://postgres:postgres@localhost:5434/indexer';

export default {
schema: './lib/schema.ts',
out: './.drizzle',
dialect: 'postgresql',
dbCredentials: {
url: connectionString,
},
} satisfies Config;
88 changes: 88 additions & 0 deletions apps/indexer-v2/indexers/dao-factory.indexer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { defineIndexer } from '@apibara/indexer';
import { useLogger } from '@apibara/indexer/plugins';
import { drizzleStorage, useDrizzleStorage } from '@apibara/plugin-drizzle';
import { StarknetStream } from '@apibara/starknet';
import { hash } from 'starknet';
import { ApibaraRuntimeConfig } from 'apibara/types';
import { db } from '../lib/db';
import { daoCreation } from '../lib/schema';

const DAO_AA_CREATED = hash.getSelectorFromName('DaoAACreated') as `0x${string}`;
const PROPOSAL_CREATED = hash.getSelectorFromName('ProposalCreated') as `0x${string}`;
const PROPOSAL_VOTED = hash.getSelectorFromName('ProposalVoted') as `0x${string}`;
const PROPOSAL_CANCELED = hash.getSelectorFromName('ProposalCanceled') as `0x${string}`;
const PROPOSAL_RESOLVED = hash.getSelectorFromName('ProposalResolved') as `0x${string}`;

export default function (config: ApibaraRuntimeConfig) {
return defineIndexer(StarknetStream)({
streamUrl: 'https://starknet-sepolia.preview.apibara.org',
startingCursor: {
orderKey: 500_000n,
},
filter: {
events: [
{
address: '0x67abfcaab98916c954c6a82260f61f566488dd72d94a6b1fb6be0cd2462703e', // DAO Factory to deploy
keys: [DAO_AA_CREATED],
},
],
},
plugins: [drizzleStorage({ db })],
async factory({ block: { events }, context }) {
const logger = useLogger();
const { db } = useDrizzleStorage();

if (events.length === 0) {
return {};
}

const daoCreationEvents = events.map((event) => {
const daoAddress = event.keys[1];

logger.log('Factory: new DAO Address : ', `\x1b[35m${daoAddress}\x1b[0m`);
return {
address: daoAddress,
keys: [PROPOSAL_CREATED, PROPOSAL_VOTED, PROPOSAL_CANCELED, PROPOSAL_RESOLVED],
};
});

const daoCreationData = events.map((event) => {
const daoAddress = event.keys[0];
const creator = event.data[0];
const tokenAddress = event.data[1];
const starknetAddress = event.data[2];

return {
number: event.eventIndex,
hash: event.address,
contractAddress: daoAddress,
creator,
tokenAddress,
starknetAddress,
};
});

await db.insert(daoCreation).values(daoCreationData).onConflictDoNothing().execute();

return {
filter: {
events: daoCreationEvents,
},
};
},
async transform({ block }) {
const logger = useLogger();
//const { db } = useDrizzleStorage();
const { events, header } = block;
logger.log(`Block number ${header?.blockNumber}`);

if (events.length === 0) {
logger.log(`No events found in block ${header?.blockNumber}`);
}

for (const event of events) {
logger.log(`Event ${event.eventIndex} tx=${event.transactionHash}`);
}
},
});
}
15 changes: 15 additions & 0 deletions apps/indexer-v2/lib/db.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { drizzle, type NodePgDatabase } from 'drizzle-orm/node-postgres';
import pg from 'pg';

import * as schema from './schema';

const connectionString =
process.env.POSTGRES_CONNECTION_STRING ?? 'postgres://postgres:postgres@localhost:5434/postgres';

const pool = new pg.Pool({
connectionString,
});

export const db = drizzle(pool, { schema });

export type Database = NodePgDatabase<typeof schema>;
11 changes: 11 additions & 0 deletions apps/indexer-v2/lib/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { bigint, pgTable, text, uuid } from 'drizzle-orm/pg-core';

export const daoCreation = pgTable('dao_creation', {
_id: uuid('_id').primaryKey().defaultRandom(),
number: bigint('number', { mode: 'number' }),
hash: text('hash'),
creator: text('creator'),
tokenAddress: text('token_address'),
contractAddress: text('contract_address'),
starknetAddress: text('starknet_address'),
});
Loading

0 comments on commit 5fa4987

Please sign in to comment.