Skip to content

Commit

Permalink
feat(hackathon): ponder setup
Browse files Browse the repository at this point in the history
  • Loading branch information
tremarkley committed Oct 2, 2024
1 parent 2726950 commit 65c4820
Show file tree
Hide file tree
Showing 15 changed files with 1,158 additions and 90 deletions.
24 changes: 24 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,30 @@ EXPOSE 42069

CMD [ "pnpm", "start" ]


########################################
# STEP 4: INTEROP-INDEXER
########################################

FROM node:18.19-bullseye-slim AS interop-indexer

RUN apt-get update \
&& apt-get install -y libvips libxtst6 libxss1 curl \
git ca-certificates python3 pkg-config build-essential --no-install-recommends

COPY ./ ./monorepo
WORKDIR /monorepo/apps/interop-indexer

RUN npm install pnpm --global
RUN pnpm install --frozen-lockfile

ENV NODE_ENVIRONMENT=production
ENV NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/extra-ca-certificates.crt

EXPOSE 42070

CMD [ "pnpm", "start" ]

########################################
# STEP 4: DAPP-CONSOLE-API
########################################
Expand Down
3 changes: 3 additions & 0 deletions apps/interop-indexer/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "ponder"
}
18 changes: 18 additions & 0 deletions apps/interop-indexer/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Dependencies
/node_modules

# Debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# Misc
.DS_Store

# Env files
.env*.local

# Ponder
/generated/
/.ponder/
1 change: 1 addition & 0 deletions apps/interop-indexer/abis/ExampleContractAbi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const ExampleContractAbi = [] as const;
33 changes: 33 additions & 0 deletions apps/interop-indexer/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
version: '3.8'

services:
postgres:
image: postgres:14-alpine
environment:
- POSTGRES_HOST_AUTH_METHOD=trust
ports:
- '5432:5432'
volumes:
- ./setup-local-db.sql:/docker-entrypoint-initdb.d/setup-local-db.sql
healthcheck:
test: ['CMD-SHELL', 'pg_isready -q -U local-db-user -d interop-indexer']

indexer:
build:
context: ../../
dockerfile: Dockerfile
target: interop-indexer
env_file:
- .env.local
environment:
- DATABASE_URL=postgresql://local-db-user:@postgres:5432/interop-indexer
volumes:
- ../../certs/extra-ca-certificates.crt:/usr/local/share/ca-certificates/extra-ca-certificates.crt
ports:
- "42070:42070"
depends_on:
postgres:
condition: service_healthy

volumes:
db:
31 changes: 31 additions & 0 deletions apps/interop-indexer/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "@eth-optimism/interop-indexer",
"version": "0.0.1",
"private": true,
"type": "module",
"scripts": {
"dev": "ponder dev",
"docker:up": "docker compose up",
"docker:down": "docker compose down",
"start": "ponder start",
"codegen": "ponder codegen",
"lint": "eslint \"**/*.{ts,tsx}\" && pnpm prettier --check \"**/*.{ts,tsx}\"",
"lint:fix": "eslint \"**/*.{ts,tsx}\" --fix --quiet && pnpm prettier \"**/*.{ts,tsx}\" --write --loglevel=warn",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@eth-optimism/viem": "workspace:^",
"@ponder/core": "^0.6.4",
"hono": "^4.5.0",
"viem": "^2.21.3"
},
"devDependencies": {
"@types/node": "^20.9.0",
"eslint": "^8.53.0",
"eslint-config-ponder": "^0.6.4",
"typescript": "^5.2.2"
},
"engines": {
"node": ">=18.14"
}
}
28 changes: 28 additions & 0 deletions apps/interop-indexer/ponder-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// This file enables type checking and editor autocomplete for this Ponder project.
// After upgrading, you may find that changes have been made to this file.
// If this happens, please commit the changes. Do not manually edit this file.
// See https://ponder.sh/docs/getting-started/installation#typescript for more information.

declare module "@/generated" {
import type { Virtual } from "@ponder/core";

type config = typeof import("./ponder.config.ts").default;
type schema = typeof import("./ponder.schema.ts").default;

export const ponder: Virtual.Registry<config, schema>;

export type EventNames = Virtual.EventNames<config>;
export type Event<name extends EventNames = EventNames> = Virtual.Event<
config,
name
>;
export type Context<name extends EventNames = EventNames> = Virtual.Context<
config,
schema,
name
>;
export type ApiContext = Virtual.Drizzle<schema>;
export type IndexingFunctionArgs<name extends EventNames = EventNames> =
Virtual.IndexingFunctionArgs<config, schema, name>;
export type Schema = Virtual.Schema<schema>;
}
34 changes: 34 additions & 0 deletions apps/interop-indexer/ponder.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { createConfig } from '@ponder/core'
import { http } from 'viem'
import {
supersimL2A,
supersimL2B,
l2ToL2CrossDomainMessengerABI,
contracts,
} from '@eth-optimism/viem'

export default createConfig({
networks: {
opchainA: {
chainId: supersimL2A.id,
transport: http(process.env.SUPERSIM_L2A_RPC_URL),
},
opchainB: {
chainId: supersimL2B.id,
transport: http(process.env.SUPERSIM_L2B_RPC_URL),
},
},
contracts: {
L2toL2CDM: {
network: {
opchainA: {
address: contracts.l2ToL2CrossDomainMessenger.address,
},
opchainB: {
address: contracts.l2ToL2CrossDomainMessenger.address,
},
},
abi: l2ToL2CrossDomainMessengerABI,
},
},
})
25 changes: 25 additions & 0 deletions apps/interop-indexer/ponder.schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { createSchema } from '@ponder/core'

export default createSchema((p) => ({
MessageStatus: p.createEnum(['pending', 'failed', 'relayed']),
XChainMessage: p.createTable(
{
id: p.string(),
txHash: p.hex(),
destinationChainId: p.bigint(),
target: p.hex(),
sender: p.hex(),
chainId: p.bigint(),
messagePayload: p.hex(),
messageNonce: p.bigint(),
blockNumber: p.bigint(),
logIndex: p.int(),
timestamp: p.bigint(),
msgHash: p.hex(),
status: p.enum('MessageStatus'),
},
{
msgHashIndex: p.index('msgHash'),
},
),
}))
33 changes: 33 additions & 0 deletions apps/interop-indexer/setup-local-db.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
CREATE USER "local-db-user";

CREATE ROLE read_only;
CREATE ROLE read_write;

GRANT read_write TO postgres;
GRANT read_write TO "local-db-user";

CREATE DATABASE "interop-indexer";

-- read_only
GRANT CONNECT ON DATABASE "interop-indexer" TO read_only;

GRANT USAGE ON SCHEMA public TO read_only;

GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only;

ALTER DEFAULT PRIVILEGES GRANT SELECT ON TABLES TO read_only;

-- read_write
GRANT CREATE, CONNECT ON DATABASE "interop-indexer" TO read_write;

GRANT USAGE, CREATE ON SCHEMA public TO read_write;

GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO read_write;

ALTER DEFAULT PRIVILEGES GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO read_write;

GRANT USAGE ON ALL SEQUENCES IN SCHEMA public TO read_write;

ALTER DEFAULT PRIVILEGES GRANT USAGE ON SEQUENCES TO read_write;

ALTER DEFAULT PRIVILEGES FOR ROLE read_write GRANT SELECT ON TABLES TO read_only;
116 changes: 116 additions & 0 deletions apps/interop-indexer/src/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { ponder } from '@/generated'
import { graphql } from '@ponder/core'
import { encodeAbiParameters, keccak256 } from 'viem'

ponder.use('/graphql', graphql())
ponder.use('/', graphql())
ponder.on('L2toL2CDM:SentMessage', async ({ event, context }) => {
const { db, network } = context
const { XChainMessage } = db
const { log, block, transaction, args } = event

const { chainId } = network
const { logIndex } = log
const { timestamp, number: blockNumber } = block
const { hash: txHash } = transaction
const { destination, target, messageNonce, sender, message } = args
const msgHash = hashL2toL2CrossDomainMessage(
destination,
BigInt(chainId),
messageNonce,
sender,
target,
message,
)

await XChainMessage.create({
id: msgHash,
data: {
chainId: BigInt(chainId),
txHash,
logIndex,
destinationChainId: destination,
target,
sender,
messagePayload: executingMessagePayloadBytes(event.log),
blockNumber,
timestamp,
messageNonce,
msgHash: msgHash,
status: 'pending',
},
})
})

ponder.on('L2toL2CDM:RelayedMessage', async ({ event, context }) => {
const { db } = context
const { XChainMessage } = db

const { messageHash } = event.args

await XChainMessage.update({
id: messageHash,
data: {
status: 'relayed',
},
})
})

ponder.on('L2toL2CDM:FailedRelayedMessage', async ({ event, context }) => {
const { db } = context
const { XChainMessage } = db

const { messageHash } = event.args

await XChainMessage.update({
id: messageHash,
data: {
status: 'failed',
},
})
})

function executingMessagePayloadBytes(log: {
topics: string[]
data: string
}): `0x${string}` {
let msg = ''

// Concatenate each topic (removing the "0x" prefix from each)
for (const topic of log.topics) {
msg += topic.startsWith('0x') ? topic.slice(2) : topic
}

// Concatenate log.data (removing the "0x" prefix if it exists)
const logData = log.data.startsWith('0x') ? log.data.slice(2) : log.data

// Return the concatenated result prefixed with "0x"
return ('0x' + msg + logData) as `0x${string}`
}

function hashL2toL2CrossDomainMessage(
destination: bigint,
source: bigint,
nonce: bigint,
sender: `0x${string}`,
target: `0x${string}`,
message: `0x${string}`,
) {
// Encoding the parameters as per ABI specification
const encodedData = encodeAbiParameters(
[
{ name: '_destination', type: 'uint256' },
{ name: '_source', type: 'uint256' },
{ name: '_nonce', type: 'uint256' },
{ name: '_sender', type: 'address' },
{ name: '_target', type: 'address' },
{ name: '_message', type: 'bytes' },
],
[destination, source, nonce, sender, target, message],
)

// Hash the encoded data using keccak256
const hash = keccak256(encodedData)

return hash
}
1 change: 1 addition & 0 deletions apps/interop-indexer/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import { ponder } from "@/generated";
26 changes: 26 additions & 0 deletions apps/interop-indexer/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"compilerOptions": {
// Type checking
"strict": true,
"noUncheckedIndexedAccess": true,

// Interop constraints
"verbatimModuleSyntax": false,
"esModuleInterop": true,
"isolatedModules": true,
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,

// Language and environment
"moduleResolution": "bundler",
"module": "ESNext",
"noEmit": true,
"lib": ["ES2022"],
"target": "ES2022",

// Skip type checking for node modules
"skipLibCheck": true
},
"include": ["./**/*.ts"],
"exclude": ["node_modules"]
}
Loading

0 comments on commit 65c4820

Please sign in to comment.