Skip to content

Commit

Permalink
GoL2 Indexer
Browse files Browse the repository at this point in the history
  • Loading branch information
thecotne committed Jan 14, 2024
0 parents commit 24c5095
Show file tree
Hide file tree
Showing 26 changed files with 3,424 additions and 0 deletions.
9 changes: 9 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
root = true

[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
7 changes: 7 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
DATABASE_URL=postgres://indexer-user:indexer-password@localhost:5432/indexer-db?sslmode=disable

STARKNET_NETWORK_NAME=SN_GOERLI
CONTRACT_ADDRESS=0x06dc4bd1212e67fd05b456a34b24a060c45aad08ab95843c42af31f86c7bd093

# STARKNET_NETWORK_NAME=SN_MAIN
# CONTRACT_ADDRESS=0x06a05844a03bb9e744479e3298f54705a35966ab04140d3d8dd797c1f6dc49d0
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.DS_Store
dist
node_modules
gol2-indexer-0.0.0-source.tgz
package
13 changes: 13 additions & 0 deletions .kanelrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
require("dotenv/config");

const { generateKnexTablesModule } = require("kanel-knex");

/** @type {import('kanel').Config} */
module.exports = {
connection: process.env.DATABASE_URL,

preDeleteOutputFolder: true,
outputPath: "./src/schemas",

preRenderHooks: [generateKnexTablesModule],
};
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v20.11.0
31 changes: 31 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
FROM node:20.11.0-alpine as build

WORKDIR /build

COPY package.json pnpm-lock.yaml /build/

RUN corepack enable && pnpm install

COPY src /build/src/

COPY tsconfig.json /build/

RUN pnpm build && pnpm pack

FROM node:20.11.0-alpine as app

WORKDIR /app

RUN <<EOF
corepack enable
pnpm init
pnpm add dbmate@^2.10.0
EOF

COPY --from=build /build/gol2-indexer-0.0.0-source.tgz /app/

RUN pnpm add /app/gol2-indexer-0.0.0-source.tgz

COPY db /app/db/

CMD ["pnpm", "--shell-mode", "exec", "dbmate wait && dbmate up && gol2-indexer"]
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# GoL2 Indexer

## Quick start

- `source ./setup-install.sh` - install nvm, node and pnpm.
- `source ./setup-update.sh` - install up-to-date versions of nvm, node and pnpm.
- `pnpm dbmate up` - create the database and run migrations
- `pnpm build` - build the project
- `pnpm dev` - start the project
- `pnpm debug` - start the project with debug logs
- `pnpm debug:package` - build `dist`, `gol2-indexer-0.0.0-source.tgz` and `package`
- `pnpm clean` - remove `dist`, `gol2-indexer-0.0.0-source.tgz` and `package`
- `docker compose up --build` - start the project with docker
- `docker compose up -d` - start the project with docker in detached mode
- `docker compose up db -d` - start the database with docker
- `docker compose rm --stop` - stop the project with docker
- `pnpm dotenv -- pnpm -c exec 'pgcli "$DATABASE_URL"'` - connect to the database
- `pnpm kanel` - update the database schema

## Env variables
- `DATABASE_URL` - Database connection string
- `CONTRACT_ADDRESS`
- [0x06dc4bd1212e67fd05b456a34b24a060c45aad08ab95843c42af31f86c7bd093](https://testnet.starkscan.co/contract/0x06dc4bd1212e67fd05b456a34b24a060c45aad08ab95843c42af31f86c7bd093) for Goerli
- [0x06a05844a03bb9e744479e3298f54705a35966ab04140d3d8dd797c1f6dc49d0](https://starkscan.co/contract/0x06a05844a03bb9e744479e3298f54705a35966ab04140d3d8dd797c1f6dc49d0) for Mainnet
- `STARKNET_NETWORK_NAME` - Either `SN_MAIN` or `SN_GOERLI`
163 changes: 163 additions & 0 deletions db/migrations/20240113081901_init.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
-- migrate:up
CREATE TABLE public.event (
"txHash" character varying(65) NOT NULL,
"eventIndex" integer NOT NULL,
"txIndex" integer NOT NULL,
"blockIndex" integer NOT NULL,
name character varying NOT NULL,
content jsonb NOT NULL,
"createdAt" timestamp without time zone DEFAULT now() NOT NULL,
"blockHash" character varying(65)
);
CREATE MATERIALIZED VIEW public.balance AS WITH transfers AS (
SELECT (event.content->>'to')::numeric AS "to",
(event.content->>'from_')::numeric AS "from",
(event.content->>'value')::numeric AS value,
event."createdAt"
FROM public.event
WHERE (event.name = 'Transfer')
),
incoming AS (
SELECT transfers."to",
sum(transfers.value) AS incoming_credits,
min(transfers."createdAt") AS oldesttransaction,
max(transfers."createdAt") AS newesttransaction
FROM transfers
GROUP BY transfers."to"
),
outgoing AS (
SELECT transfers."from",
sum(transfers.value) AS outgoing_credits,
min(transfers."createdAt") AS oldesttransaction,
max(transfers."createdAt") AS newesttransaction
FROM transfers
GROUP BY transfers."from"
)
SELECT incoming."to" AS "userId",
(
incoming.incoming_credits - COALESCE(outgoing.outgoing_credits, (0)::numeric)
) AS balance,
CASE
WHEN (
incoming.oldesttransaction > outgoing.oldesttransaction
) THEN outgoing.oldesttransaction
ELSE incoming.oldesttransaction
END AS "createdAt",
CASE
WHEN (
incoming.newesttransaction < outgoing.newesttransaction
) THEN outgoing.newesttransaction
ELSE incoming.newesttransaction
END AS "updatedAt"
FROM (
incoming
LEFT JOIN outgoing ON ((incoming."to" = outgoing."from"))
)
WHERE (incoming."to" <> '0'::numeric) WITH NO DATA;
CREATE MATERIALIZED VIEW public.creator AS
SELECT event."txHash" AS "transactionHash",
event.name AS "transactionType",
event."eventIndex",
(event.content->>'user_id')::numeric AS "transactionOwner",
(event.content->>'game_id')::numeric AS "gameId",
(event.content->>'generation')::numeric AS "gameGeneration",
(event.content->>'state')::numeric AS "gameState",
CASE
WHEN (
(event.content->>'state')::numeric = (0)::numeric
) THEN true
ELSE false
END AS "gameOver",
event."createdAt"
FROM public.event
WHERE (
(
(event.name)::text = ANY (
(
ARRAY ['game_evolved'::character varying, 'game_created'::character varying]
)::text []
)
)
AND (
(event.content->>'game_id')::numeric <> '39132555273291485155644251043342963441664'::numeric
)
) WITH NO DATA;
CREATE MATERIALIZED VIEW public.infinite AS
SELECT event."txHash" AS "transactionHash",
event.name AS "transactionType",
event."eventIndex",
(event.content->>'user_id')::numeric AS "transactionOwner",
(event.content->>'generation')::numeric AS "gameGeneration",
(event.content->>'state')::numeric AS "gameState",
(event.content->>'cell_index')::numeric AS "revivedCellIndex",
CASE
WHEN (
(event.content->>'state')::numeric = (0)::numeric
) THEN true
ELSE false
END AS "gameExtinct",
event."createdAt"
FROM public.event
WHERE (
(
(
(event.name)::text = ANY (
(
ARRAY ['game_evolved'::character varying, 'game_created'::character varying]
)::text []
)
)
AND (
(event.content->>'game_id')::numeric = '39132555273291485155644251043342963441664'::numeric
)
)
OR ((event.name)::text = 'cell_revived'::text)
) WITH NO DATA;
CREATE TABLE public.transaction (
hash character varying(65) NOT NULL,
"errorContent" jsonb,
"blockHash" character varying(65),
status character varying NOT NULL,
"createdAt" timestamp without time zone DEFAULT now() NOT NULL,
"updatedAt" timestamp without time zone,
"functionName" character varying NOT NULL,
"functionCaller" numeric NOT NULL,
"functionInputCellIndex" integer,
"functionInputGameState" numeric,
"functionInputGameId" numeric
);
ALTER TABLE ONLY public.event
ADD CONSTRAINT "PK_168407df0cd2c71680bf4287000" PRIMARY KEY ("txHash", "eventIndex");
ALTER TABLE ONLY public.transaction
ADD CONSTRAINT "PK_de4f0899c41c688529784bc443f" PRIMARY KEY (hash);
CREATE INDEX "IDX_08f3024b3fad3c62274225faf9" ON public.transaction USING btree ("blockHash");
CREATE INDEX "IDX_0d681662b792661c819f8276a0" ON public.transaction USING btree ("functionInputGameId");
CREATE INDEX "IDX_253f6b005b632dbac80cff5020" ON public.transaction USING btree ("updatedAt");
CREATE INDEX "IDX_32ad9e0d62211b679ebca15104" ON public.transaction USING btree ("functionCaller");
CREATE INDEX "IDX_54dcd9578eb59a50d4095eae99" ON public.transaction USING btree ("functionName");
CREATE INDEX "IDX_63f749fc7f7178ae1ad85d3b95" ON public.transaction USING btree (status);
CREATE INDEX "IDX_7719a5dd0518e380f8911fb7ff" ON public.transaction USING btree ("functionInputGameState");
CREATE INDEX "IDX_7f40575a1b279607e73504117e" ON public.transaction USING btree ("functionInputCellIndex");
CREATE INDEX "IDX_83cb622ce2d74c56db3e0c29f1" ON public.transaction USING btree ("createdAt");
CREATE INDEX "IDX_b535fbe8ec6d832dde22065ebd" ON public.event USING btree (name);
CREATE UNIQUE INDEX "balance_userId_idx" ON public.balance USING btree ("userId");
CREATE INDEX "balance_userId_updatedAt_createdAt_idx" ON public.balance USING btree ("userId", "updatedAt", "createdAt");
CREATE UNIQUE INDEX "creator_transactionHash_eventIndex_idx" ON public.creator USING btree ("transactionHash", "eventIndex");
CREATE INDEX "creator_transactionType_transactionOwner_gameId_createdAt_g_idx" ON public.creator USING btree (
"transactionType",
"transactionOwner",
"gameId",
"createdAt",
"gameOver"
);
CREATE UNIQUE INDEX "infinite_transactionHash_eventIndex_idx" ON public.infinite USING btree ("transactionHash", "eventIndex");
CREATE INDEX "infinite_transactionType_transactionOwner_createdAt_gameExt_idx" ON public.infinite USING btree (
"transactionType",
"transactionOwner",
"createdAt",
"gameExtinct"
);
REFRESH MATERIALIZED VIEW public.balance;
REFRESH MATERIALIZED VIEW public.creator;
REFRESH MATERIALIZED VIEW public.infinite;
-- migrate:down
Loading

0 comments on commit 24c5095

Please sign in to comment.