├── src ├── model │ ├── index.ts │ └── generated │ │ ├── index.ts │ │ ├── comoBalance.model.ts │ │ ├── vote.model.ts │ │ ├── objekt.model.ts │ │ ├── transfer.model.ts │ │ ├── collection.model.ts │ │ └── marshal.ts ├── http │ ├── gravity-api │ │ ├── env.ts │ │ ├── server.ts │ │ └── handlers │ │ │ └── poll-votes.ts │ └── drizzle-proxy │ │ ├── env.ts │ │ ├── server.ts │ │ ├── handlers │ │ ├── processor-status.ts │ │ ├── process-query.ts │ │ ├── set-band.ts │ │ └── rescan-metadata.ts │ │ └── db.ts ├── env │ ├── base.ts │ └── processor.ts ├── constants.ts ├── util.ts ├── processor.ts ├── cosmo.ts ├── parser.ts ├── main.ts └── abi │ ├── como.ts │ ├── objekt.ts │ └── gravity.ts ├── .dockerignore ├── .vscode └── settings.json ├── .gitignore ├── docker ├── gravity-api.Dockerfile ├── drizzle-proxy.Dockerfile └── processor.Dockerfile ├── db └── migrations │ ├── 1741083439703-Data.js │ ├── 1763018906000-Data.js │ ├── 1736335176223-Data.js │ ├── 1749046626919-Data.js │ ├── 1755862697000-Data.js │ ├── 1753624821000-Data.js │ ├── 1741083743969-Data.js │ ├── 1739771974876-Data.js │ ├── 1741780963552-Data.js │ ├── 1745716395199-Data.js │ ├── 1721359520022-Data.js │ └── 1721359520021-Data.js ├── tsconfig.json ├── squid.yaml ├── .env.example ├── package.json ├── LICENSE.md ├── .gitpod.yaml ├── schema.graphql ├── docker-compose.yml ├── README.md └── commands.json /src/model/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./generated" 2 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | /.git 2 | /node_modules 3 | /lib 4 | /*Versions.json 5 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /lib 3 | /builds 4 | 5 | /**Versions.json 6 | 7 | # IDE files 8 | /.idea 9 | 10 | .env 11 | .env.* -------------------------------------------------------------------------------- /src/model/generated/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./collection.model" 2 | export * from "./transfer.model" 3 | export * from "./objekt.model" 4 | export * from "./comoBalance.model" 5 | export * from "./vote.model" 6 | -------------------------------------------------------------------------------- /src/http/gravity-api/env.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { baseEnvSchema } from "../../env/base"; 3 | 4 | const envSchema = z 5 | .object({ 6 | GRAVITY_HTTP_PORT: z.coerce.number().positive(), 7 | GRAVITY_CORS: z.string().min(1), 8 | }) 9 | .merge(baseEnvSchema); 10 | 11 | export const env = envSchema.parse(process.env); 12 | -------------------------------------------------------------------------------- /src/env/base.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | 3 | export const baseEnvSchema = z.object({ 4 | DB_NAME: z.string().min(1), 5 | DB_USER: z.string().min(1), 6 | DB_PASS: z.string().min(1), 7 | DB_PORT: z.coerce.number().positive(), 8 | DB_HOST: z.string().min(1), 9 | DB_READ_USER: z.string().min(1), 10 | DB_READ_PASS: z.string().min(1), 11 | }); 12 | -------------------------------------------------------------------------------- /docker/gravity-api.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM oven/bun 2 | 3 | WORKDIR /usr/src/app 4 | 5 | COPY package.json ./ 6 | RUN bun install 7 | COPY . . 8 | 9 | ENV NODE_ENV production 10 | 11 | RUN bun build ./src/http/gravity-api/server.ts --outdir ./dist --target bun 12 | RUN mv ./dist/server.js ./dist/gravity-api-server.js 13 | CMD ["bun", "run", "dist/gravity-api-server.js"] -------------------------------------------------------------------------------- /docker/drizzle-proxy.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM oven/bun 2 | 3 | WORKDIR /usr/src/app 4 | 5 | COPY package.json ./ 6 | RUN bun install 7 | COPY . . 8 | 9 | ENV NODE_ENV production 10 | 11 | RUN bun build ./src/http/drizzle-proxy/server.ts --outdir ./dist --target bun 12 | RUN mv ./dist/server.js ./dist/drizzle-proxy-server.js 13 | CMD ["bun", "run", "dist/drizzle-proxy-server.js"] -------------------------------------------------------------------------------- /src/http/drizzle-proxy/env.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { baseEnvSchema } from "../../env/base"; 3 | 4 | const envSchema = z 5 | .object({ 6 | PROXY_HTTP_PORT: z.coerce.number().positive(), 7 | PROXY_KEY: z.string().min(1), 8 | PROXY_CACHE_MAX_AGE: z.coerce.number().positive().default(60), 9 | }) 10 | .merge(baseEnvSchema); 11 | 12 | export const env = envSchema.parse(process.env); 13 | -------------------------------------------------------------------------------- /db/migrations/1741083439703-Data.js: -------------------------------------------------------------------------------- 1 | module.exports = class Data1741083439703 { 2 | name = "Data1741083439703"; 3 | 4 | async up(db) { 5 | // improves initial profile load performance 6 | await db.query( 7 | `CREATE INDEX "IDX_objekt_owner_received_at" ON "objekt" ("owner", "received_at") ` 8 | ); 9 | } 10 | 11 | async down(db) { 12 | await db.query(`DROP INDEX "public"."IDX_objekt_owner_received_at"`); 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /src/constants.ts: -------------------------------------------------------------------------------- 1 | import { addr } from "./util"; 2 | 3 | export const COSMO_START_BLOCK = 6363806; 4 | export const Addresses = { 5 | BURN: addr("0x0000000000000000000000000000000000000000"), 6 | OBJEKT: addr("0x99Bb83AE9bb0C0A6be865CaCF67760947f91Cb70"), 7 | COMO: addr("0xd0EE3ba23a384A8eeFd43f33A957dED60eD12706"), 8 | GRAVITY: addr("0xF1A787da84af2A6e8227aD87112a21181B7b9b39"), 9 | SPIN: addr("0xD3D5f29881ad87Bb10C1100e2c709c9596dE345F"), 10 | } as const; 11 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "NodeNext", 4 | "target": "es2022", 5 | "outDir": "lib", 6 | "rootDir": "src", 7 | "strict": true, 8 | "declaration": false, 9 | "sourceMap": true, 10 | "esModuleInterop": true, 11 | "experimentalDecorators": true, 12 | "emitDecoratorMetadata": true, 13 | "skipLibCheck": true, 14 | "resolveJsonModule": true, 15 | "moduleResolution": "NodeNext" 16 | }, 17 | "include": ["src"], 18 | "exclude": ["node_modules"] 19 | } 20 | -------------------------------------------------------------------------------- /db/migrations/1763018906000-Data.js: -------------------------------------------------------------------------------- 1 | module.exports = class Data1763018906000 { 2 | name = "Data1763018906000"; 3 | 4 | async up(db) { 5 | // add front_media column to collection table 6 | await db.query( 7 | `ALTER TABLE "public"."collection" ADD COLUMN "front_media" varchar(255);` 8 | ); 9 | } 10 | 11 | async down(db) { 12 | // drop front_media column from collection table 13 | await db.query( 14 | `ALTER TABLE "public"."collection" DROP COLUMN "front_media";` 15 | ); 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /src/http/gravity-api/server.ts: -------------------------------------------------------------------------------- 1 | import { Hono } from "hono"; 2 | import { cors } from "hono/cors"; 3 | import { env } from "./env"; 4 | import { pollVotes } from "./handlers/poll-votes"; 5 | 6 | const app = new Hono(); 7 | 8 | app.use( 9 | cors({ 10 | origin: env.GRAVITY_CORS || "*", 11 | }) 12 | ); 13 | 14 | app.get("/poll/:pollId", pollVotes); 15 | 16 | console.log(`[gravity-api] Listening on port ${env.GRAVITY_HTTP_PORT}`); 17 | 18 | export default { 19 | port: env.GRAVITY_HTTP_PORT, 20 | fetch: app.fetch, 21 | }; 22 | -------------------------------------------------------------------------------- /db/migrations/1736335176223-Data.js: -------------------------------------------------------------------------------- 1 | module.exports = class Data1736335176223 { 2 | name = "Data1736335176223"; 3 | 4 | // profiles initially load using the owner and received_at columns, so creating a covering index should optimize that query 5 | async up(db) { 6 | await db.query( 7 | `CREATE INDEX "IDX_463f5339e811c02da943075d43" ON "objekt" ("owner", "received_at" DESC) ` 8 | ); 9 | } 10 | 11 | async down(db) { 12 | await db.query(`DROP INDEX "public"."IDX_463f5339e811c02da943075d43"`); 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /db/migrations/1749046626919-Data.js: -------------------------------------------------------------------------------- 1 | module.exports = class Data1749046626919 { 2 | name = "Data1749046626919"; 3 | 4 | async up(db) { 5 | // 1. add an index on owner and collection_id for initial load 6 | await db.query( 7 | `CREATE INDEX IF NOT EXISTS idx_objekt_owner_collection_id ON objekt (owner, collection_id);` 8 | ); 9 | } 10 | 11 | async down(db) { 12 | // 1. drop the index on owner and collection_id 13 | await db.query(`DROP INDEX IF EXISTS idx_objekt_owner_collection_id;`); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /db/migrations/1755862697000-Data.js: -------------------------------------------------------------------------------- 1 | module.exports = class Data1755862697000 { 2 | name = "Data1755862697000"; 3 | 4 | async up(db) { 5 | // add band_image_url column to collection table 6 | await db.query( 7 | `ALTER TABLE "public"."collection" ADD COLUMN "band_image_url" varchar(255);` 8 | ); 9 | } 10 | 11 | async down(db) { 12 | // drop band_image_url column from collection table 13 | await db.query( 14 | `ALTER TABLE "public"."collection" DROP COLUMN "band_image_url";` 15 | ); 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /src/util.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * lowercases the incoming address 3 | * necessary due to archives returning addresses in lowercase 4 | */ 5 | export const addr = (address: string) => address.toLowerCase(); 6 | 7 | /** 8 | * Chunk an array into chunks of a given size. 9 | */ 10 | export async function chunk( 11 | arr: T[], 12 | chunkSize: number, 13 | callback: (chunk: T[]) => Promise 14 | ) { 15 | for (let i = 0; i < arr.length; i += chunkSize) { 16 | const chunk = arr.slice(i, i + chunkSize); 17 | await callback(chunk); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /squid.yaml: -------------------------------------------------------------------------------- 1 | manifestVersion: subsquid.io/v0.1 2 | name: cosmo-db 3 | version: 1 4 | description: A squid indexer generated from an ABI template 5 | secrets: 6 | - ENABLE_OBJEKTS 7 | - ENABLE_GRAVITY 8 | build: 9 | deploy: 10 | addons: 11 | postgres: 12 | processor: 13 | cmd: 14 | - node 15 | - lib/main 16 | api: 17 | cmd: 18 | - npx 19 | - squid-graphql-server 20 | - "--dumb-cache" 21 | - in-memory 22 | - "--dumb-cache-ttl" 23 | - "1000" 24 | - "--dumb-cache-size" 25 | - "100" 26 | - "--dumb-cache-max-age" 27 | - "1000" 28 | -------------------------------------------------------------------------------- /src/env/processor.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { baseEnvSchema } from "./base"; 3 | 4 | const envSchema = z 5 | .object({ 6 | RPC_RATE_LIMIT: z.coerce.number().positive().default(2), 7 | RPC_FINALITY: z.coerce.number().positive().default(60), 8 | RPC_ENDPOINT: z.string().url(), 9 | SQD_ENDPOINT: z.string().url(), 10 | ENABLE_OBJEKTS: z.preprocess((x) => x === "true", z.coerce.boolean()), 11 | ENABLE_GRAVITY: z.preprocess((x) => x === "true", z.coerce.boolean()), 12 | COSMO_PARALLEL_COUNT: z.coerce.number().positive().default(500), 13 | }) 14 | .merge(baseEnvSchema); 15 | 16 | export const env = envSchema.parse(process.env); 17 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | # base options 2 | DB_HOST=db 3 | DB_NAME=apollo-objekts 4 | DB_USER=postgres 5 | DB_PASS=postgres 6 | DB_PORT=5432 7 | DB_READ_USER=postgres_read 8 | DB_READ_PASS=postgres_read 9 | 10 | # processor options 11 | RPC_RATE_LIMIT=10 12 | RPC_FINALITY=60 13 | RPC_ENDPOINT="https://api.mainnet.abs.xyz" 14 | SQD_ENDPOINT="https://v2.archive.subsquid.io/network/abstract-mainnet" 15 | ENABLE_OBJEKTS=true 16 | ENABLE_GRAVITY=false 17 | COSMO_PARALLEL_COUNT=50 18 | FORCE_PRETTY_LOGGER=1 19 | 20 | # drizzle http proxy 21 | PROXY_HTTP_PORT=8080 22 | PROXY_KEY=key 23 | PROXY_CACHE_MAX_AGE=60 24 | 25 | # http server for gravity info 26 | GRAVITY_HTTP_PORT=8081 27 | GRAVITY_CORS="*" -------------------------------------------------------------------------------- /db/migrations/1753624821000-Data.js: -------------------------------------------------------------------------------- 1 | module.exports = class Data1753624821000 { 2 | name = "Data1753624821000"; 3 | 4 | async up(db) { 5 | // 1. drop the candidate_id column from the vote table 6 | await db.query(`ALTER TABLE vote DROP COLUMN candidate_id;`); 7 | 8 | // 2. drop the index column from the vote table 9 | await db.query(`ALTER TABLE vote DROP COLUMN index;`); 10 | } 11 | 12 | async down(db) { 13 | // 1. add the candidate_id column to the vote table 14 | await db.query(`ALTER TABLE vote ADD COLUMN candidate_id int4;`); 15 | 16 | // 2. add the index column to the vote table 17 | await db.query(`ALTER TABLE vote ADD COLUMN index int4;`); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /db/migrations/1741083743969-Data.js: -------------------------------------------------------------------------------- 1 | module.exports = class Data1741083743969 { 2 | name = "Data1741083743969"; 3 | 4 | async up(db) { 5 | // 1. drop duplicate index 6 | await db.query(`DROP INDEX "public"."IDX_objekt_owner_received_at"`); 7 | 8 | // 2. create an index specifically for the cosmo-spin account 9 | await db.query( 10 | `CREATE INDEX "IDX_objekts_spin_initial" ON "objekt" ("received_at" DESC) WHERE owner = '0xd3d5f29881ad87bb10c1100e2c709c9596de345f'` 11 | ); 12 | } 13 | 14 | async down(db) { 15 | // recreate 1 16 | await db.query( 17 | `CREATE INDEX "IDX_objekt_owner_received_at" ON "objekt" ("owner", "received_at") ` 18 | ); 19 | 20 | // drop the cosmo-spin index 21 | await db.query(`DROP INDEX "public"."IDX_objekts_spin"`); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /src/model/generated/comoBalance.model.ts: -------------------------------------------------------------------------------- 1 | import { Entity, Column, PrimaryColumn, Index } from "typeorm"; 2 | import * as marshal from "./marshal"; 3 | 4 | @Entity() 5 | export class ComoBalance { 6 | constructor(props?: Partial) { 7 | Object.assign(this, props); 8 | } 9 | 10 | // for some reason subsquid tries to cast this to ::text, so uuid won't work 11 | @PrimaryColumn({ 12 | type: "varchar", 13 | length: 36, 14 | }) 15 | id!: string; 16 | 17 | @Index() 18 | @Column("numeric", { nullable: false }) 19 | tokenId!: number; 20 | 21 | @Index() 22 | @Column("text", { nullable: false }) 23 | owner!: string; 24 | 25 | @Index() 26 | @Column("numeric", { 27 | transformer: marshal.bigintTransformer, 28 | nullable: false, 29 | }) 30 | amount!: bigint; 31 | } 32 | -------------------------------------------------------------------------------- /db/migrations/1739771974876-Data.js: -------------------------------------------------------------------------------- 1 | module.exports = class Data1739771974876 { 2 | name = "Data1739771974876"; 3 | 4 | async up(db) { 5 | // for querying stats for the last 24 hours 6 | await db.query( 7 | `CREATE INDEX "IDX_objekt_minted_at" ON "objekt" ("minted_at") ` 8 | ); 9 | 10 | // improves performance when joining objekt and collection 11 | await db.query(`CREATE INDEX "IDX_collection_id" ON "collection" ("id") `); 12 | 13 | // improves leaderboard query performance 14 | await db.query( 15 | `CREATE INDEX "IDX_objekt_collection_owner" ON "objekt" ("collection_id", "owner") ` 16 | ); 17 | } 18 | 19 | async down(db) { 20 | await db.query(`DROP INDEX "public"."IDX_objekt_minted_at"`); 21 | await db.query(`DROP INDEX "public"."IDX_collection_id"`); 22 | await db.query(`DROP INDEX "public"."IDX_objekt_collection_owner"`); 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /src/http/drizzle-proxy/server.ts: -------------------------------------------------------------------------------- 1 | import { Hono } from "hono"; 2 | import { env } from "./env"; 3 | import { processQuery } from "./handlers/process-query"; 4 | import { processorStatus } from "./handlers/processor-status"; 5 | import { rescanMetadata } from "./handlers/rescan-metadata"; 6 | import { setBand } from "./handlers/set-band"; 7 | 8 | const app = new Hono(); 9 | 10 | // middleware 11 | app.use(async (c, next) => { 12 | const key = c.req.header("proxy-key"); 13 | if (key !== env.PROXY_KEY) { 14 | return c.json({ error: "Invalid key" }, 401); 15 | } 16 | 17 | await next(); 18 | }); 19 | 20 | app.post("/query", processQuery); 21 | app.get("/status", processorStatus); 22 | app.post("/rescan-metadata/:tokenId", rescanMetadata); 23 | app.post("/set-band", setBand); 24 | 25 | console.log(`[drizzle-proxy] Listening on port ${env.PROXY_HTTP_PORT}`); 26 | 27 | export default { 28 | port: env.PROXY_HTTP_PORT, 29 | fetch: app.fetch, 30 | }; 31 | -------------------------------------------------------------------------------- /src/model/generated/vote.model.ts: -------------------------------------------------------------------------------- 1 | import { Entity, Column, PrimaryColumn, Index } from "typeorm"; 2 | import * as marshal from "./marshal"; 3 | 4 | @Entity() 5 | export class Vote { 6 | constructor(props?: Partial) { 7 | Object.assign(this, props); 8 | } 9 | 10 | // for some reason subsquid tries to cast this to ::text, so uuid won't work 11 | @PrimaryColumn({ 12 | type: "varchar", 13 | length: 36, 14 | }) 15 | id!: string; 16 | 17 | @Index() 18 | @Column("text", { nullable: false }) 19 | from!: string; 20 | 21 | @Index() 22 | @Column("timestamp with time zone", { nullable: false }) 23 | createdAt!: Date; 24 | 25 | @Index() 26 | @Column("text", { nullable: false }) 27 | contract!: string; 28 | 29 | @Index() 30 | @Column("int4", { nullable: false }) 31 | pollId!: number; 32 | 33 | @Index() 34 | @Column("numeric", { 35 | transformer: marshal.bigintTransformer, 36 | nullable: false, 37 | }) 38 | amount!: bigint; 39 | } 40 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cosmo-db", 3 | "version": "1.0.0", 4 | "private": true, 5 | "license": "MIT", 6 | "scripts": { 7 | "build": "rm -rf lib/ && tsc", 8 | "typecheck": "tsc --pretty --noEmit" 9 | }, 10 | "dependencies": { 11 | "@subsquid/evm-abi": "^0.3.1", 12 | "@subsquid/evm-codec": "^0.3.0", 13 | "@subsquid/evm-processor": "^1.27.1", 14 | "@subsquid/graphql-server": "^4.9.0", 15 | "@subsquid/typeorm-migration": "^1.3.0", 16 | "@subsquid/typeorm-store": "^1.5.1", 17 | "@subsquid/util-internal": "^3.2.0", 18 | "dotenv": "^16.5.0", 19 | "ethers": "^6.13.5", 20 | "hono": "^4.7.7", 21 | "ofetch": "^1.4.1", 22 | "pg": "^8.14.1", 23 | "typeorm": "^0.3.22", 24 | "zod": "^3.24.3" 25 | }, 26 | "devDependencies": { 27 | "@subsquid/evm-typegen": "^4.3.0", 28 | "@subsquid/squid-gen-evm": "^2.0.0", 29 | "@subsquid/typeorm-codegen": "^2.0.2", 30 | "@types/node": "^22.14.1", 31 | "@types/pg": "^8.11.13", 32 | "typescript": "~5.8.3" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2025 Reflex 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /src/http/drizzle-proxy/handlers/processor-status.ts: -------------------------------------------------------------------------------- 1 | import { Context } from "hono"; 2 | import { env } from "../env"; 3 | import { writePool } from "../db"; 4 | 5 | export async function processorStatus(c: Context) { 6 | const client = await writePool.connect(); 7 | 8 | try { 9 | const result = await client.query( 10 | "select * from squid_processor.status" 11 | ); 12 | 13 | if (result.rows.length > 0) { 14 | const row = result.rows[0]; 15 | return c.json({ height: row.height }, 200, { 16 | "Cache-Control": `public, max-age=${env.PROXY_CACHE_MAX_AGE}, stale-while-revalidate=30`, 17 | }); 18 | } 19 | 20 | return c.json({ height: 0 }); 21 | } catch (e) { 22 | console.error("Database status query error:", e); 23 | return c.json({ error: "Failed to get processor status" }, 500); 24 | } finally { 25 | client.release(); // always release the connection back to the pool 26 | } 27 | } 28 | 29 | type Status = { 30 | id: number; 31 | height: number; 32 | hash: string; 33 | nonce: number; 34 | }; 35 | -------------------------------------------------------------------------------- /src/model/generated/objekt.model.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Entity, 3 | Column, 4 | PrimaryColumn, 5 | Index, 6 | OneToMany, 7 | ManyToOne, 8 | } from "typeorm"; 9 | import { Transfer } from "./transfer.model"; 10 | import { Collection } from "./collection.model"; 11 | 12 | @Entity() 13 | export class Objekt { 14 | constructor(props?: Partial) { 15 | Object.assign(this, props); 16 | } 17 | 18 | @PrimaryColumn() 19 | id!: string; 20 | 21 | @Index() 22 | @Column("text", { nullable: false }) 23 | owner!: string; 24 | 25 | @Column("timestamp with time zone", { nullable: false }) 26 | mintedAt!: Date; 27 | 28 | @Index() 29 | @Column("timestamp with time zone", { nullable: false }) 30 | receivedAt!: Date; 31 | 32 | @Index() 33 | @Column("int4", { nullable: false }) 34 | serial!: number; 35 | 36 | @Index() 37 | @Column("boolean", { nullable: false }) 38 | transferable!: boolean; 39 | 40 | @OneToMany(() => Transfer, (e) => e.objekt) 41 | transfers!: Transfer[]; 42 | 43 | @Index() 44 | @ManyToOne(() => Collection, { nullable: true }) 45 | collection!: Collection; 46 | } 47 | -------------------------------------------------------------------------------- /src/http/drizzle-proxy/db.ts: -------------------------------------------------------------------------------- 1 | import { Pool } from "pg"; 2 | import { env } from "./env"; 3 | 4 | // connection pool for read operations 5 | export const readPool = new Pool({ 6 | host: env.DB_HOST, 7 | user: env.DB_READ_USER, 8 | database: env.DB_NAME, 9 | password: env.DB_READ_PASS, 10 | port: env.DB_PORT, 11 | max: 25, 12 | idleTimeoutMillis: 30000, 13 | connectionTimeoutMillis: 5000, 14 | }); 15 | 16 | // separate pool for write operations (status endpoint) 17 | export const writePool = new Pool({ 18 | host: env.DB_HOST, 19 | user: env.DB_USER, 20 | database: env.DB_NAME, 21 | password: env.DB_PASS, 22 | port: env.DB_PORT, 23 | max: 5, 24 | idleTimeoutMillis: 30000, 25 | connectionTimeoutMillis: 5000, 26 | }); 27 | 28 | // graceful shutdown 29 | process.on("SIGINT", async () => { 30 | console.log("Shutting down database pools..."); 31 | await readPool.end(); 32 | await writePool.end(); 33 | process.exit(0); 34 | }); 35 | 36 | process.on("SIGTERM", async () => { 37 | console.log("Shutting down database pools..."); 38 | await readPool.end(); 39 | await writePool.end(); 40 | process.exit(0); 41 | }); 42 | -------------------------------------------------------------------------------- /src/model/generated/transfer.model.ts: -------------------------------------------------------------------------------- 1 | import { Entity, Column, PrimaryColumn, Index, ManyToOne } from "typeorm"; 2 | import { Objekt } from "./objekt.model"; 3 | import { Collection } from "./collection.model"; 4 | 5 | @Entity() 6 | export class Transfer { 7 | constructor(props?: Partial) { 8 | Object.assign(this, props); 9 | } 10 | 11 | // for some reason subsquid tries to cast this to ::text, so uuid won't work 12 | @PrimaryColumn({ 13 | type: "varchar", 14 | length: 36, 15 | }) 16 | id!: string; 17 | 18 | @Index() 19 | @Column("text", { nullable: false }) 20 | from!: string; 21 | 22 | @Index() 23 | @Column("text", { nullable: false }) 24 | to!: string; 25 | 26 | @Column("timestamp with time zone", { nullable: false }) 27 | timestamp!: Date; 28 | 29 | @Column("text", { nullable: false }) 30 | tokenId!: string; 31 | 32 | @Column("text", { nullable: false }) 33 | hash!: string; 34 | 35 | @Index() 36 | @ManyToOne(() => Objekt, { nullable: true }) 37 | objekt!: Objekt; 38 | 39 | @Index() 40 | @ManyToOne(() => Collection, { nullable: true }) 41 | collection!: Collection; 42 | } 43 | -------------------------------------------------------------------------------- /.gitpod.yaml: -------------------------------------------------------------------------------- 1 | # This configuration file was automatically generated by Gitpod. 2 | # Please adjust to your needs (see https://www.gitpod.io/docs/introduction/learn-gitpod/gitpod-yaml) 3 | # and commit this file to your remote git repository to share the goodness with others. 4 | 5 | # Learn more from ready-to-use templates: https://www.gitpod.io/docs/introduction/getting-started/quickstart 6 | github: 7 | prebuilds: 8 | # enable for the master/default branch (defaults to true) 9 | master: true 10 | # enable for all branches in this repo (defaults to false) 11 | branches: false 12 | # enable for pull requests coming from this repo (defaults to true) 13 | pullRequests: true 14 | # add a check to pull requests (defaults to true) 15 | addCheck: true 16 | # add a "Review in Gitpod" button as a comment to pull requests (defaults to false) 17 | addComment: false 18 | 19 | tasks: 20 | - init: | 21 | npm i 22 | npm i -g @subsquid/cli 23 | docker compose pull 24 | gp sync-done setup 25 | - name: Squid processor 26 | command: | 27 | gp open commands.json 28 | gp sync-await setup 29 | -------------------------------------------------------------------------------- /docker/processor.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:22-alpine AS node 2 | FROM node AS node-with-gyp 3 | RUN apk add g++ make python3 4 | RUN npm install -g pnpm 5 | 6 | FROM node-with-gyp AS builder 7 | WORKDIR /squid 8 | ADD package.json . 9 | ADD pnpm-lock.yaml . 10 | # remove if needed 11 | ADD db db 12 | # remove if needed 13 | ADD schema.graphql . 14 | RUN pnpm install --frozen-lockfile 15 | ADD tsconfig.json . 16 | ADD src src 17 | RUN pnpm run build 18 | 19 | FROM node-with-gyp AS deps 20 | WORKDIR /squid 21 | ADD package.json . 22 | ADD pnpm-lock.yaml . 23 | RUN pnpm install --frozen-lockfile --prod 24 | 25 | FROM node AS squid 26 | WORKDIR /squid 27 | COPY --from=deps /squid/package.json . 28 | COPY --from=deps /squid/pnpm-lock.yaml . 29 | COPY --from=deps /squid/node_modules node_modules 30 | COPY --from=builder /squid/lib lib 31 | # remove if no db folder 32 | COPY --from=builder /squid/db db 33 | # remove if no schema.graphql is in the root 34 | COPY --from=builder /squid/schema.graphql schema.graphql 35 | # remove if no commands.json is in the root 36 | ADD commands.json . 37 | RUN echo -e "loglevel=silent\\nupdate-notifier=false" > /squid/.npmrc 38 | RUN npm i -g @subsquid/commands && mv $(which squid-commands) /usr/local/bin/sqd 39 | ENV PROCESSOR_PROMETHEUS_PORT 3000 -------------------------------------------------------------------------------- /src/http/gravity-api/handlers/poll-votes.ts: -------------------------------------------------------------------------------- 1 | import { Client } from "pg"; 2 | import { Context } from "hono"; 3 | import { env } from "../env"; 4 | 5 | const client = new Client({ 6 | host: env.DB_HOST, 7 | user: env.DB_READ_USER, 8 | database: env.DB_NAME, 9 | password: env.DB_READ_PASS, 10 | port: Number(env.DB_PORT), 11 | }); 12 | 13 | // @ts-ignore - using bun 14 | await client.connect(); 15 | 16 | /** 17 | * Fetch votes for a given poll and aggregate per candidate. 18 | */ 19 | export async function pollVotes(c: Context) { 20 | const pollId = parseInt(c.req.param("pollId")); 21 | 22 | if (isNaN(pollId)) { 23 | return c.json({ results: [] }); 24 | } 25 | 26 | const result = await client.query({ 27 | text: `SELECT * FROM vote WHERE candidate_id IS NOT NULL AND poll_id = $1 ORDER BY created_at ASC`, 28 | values: [pollId], 29 | }); 30 | 31 | const mapped = result.rows.reduce((acc, v: Vote) => { 32 | if (!acc[v.candidate_id]) { 33 | acc[v.candidate_id] = { comoAmount: 0, voteAmount: 0 }; 34 | } 35 | acc[v.candidate_id].comoAmount += Number(v.amount) / 10 ** 18; 36 | acc[v.candidate_id].voteAmount += 1; 37 | return acc; 38 | }, {} as Record); 39 | 40 | return c.json({ results: mapped }); 41 | } 42 | 43 | type Vote = { 44 | candidate_id: number; 45 | amount: string; 46 | }; 47 | 48 | type VoteRecord = { 49 | comoAmount: number; 50 | voteAmount: number; 51 | }; 52 | -------------------------------------------------------------------------------- /src/http/drizzle-proxy/handlers/process-query.ts: -------------------------------------------------------------------------------- 1 | import { Context } from "hono"; 2 | import { readPool } from "../db"; 3 | 4 | export async function processQuery(c: Context) { 5 | const { sql, params, method } = await c.req.json(); 6 | 7 | // validate input 8 | if (!sql || !method) { 9 | return c.json({ error: "Missing required fields: sql, method" }, 422); 10 | } 11 | 12 | // prevent multiple queries 13 | if ( 14 | sql.includes(";") && 15 | sql 16 | .trim() 17 | .split(";") 18 | .filter((s: string) => s.trim()).length > 1 19 | ) { 20 | return c.json({ error: "Multiple queries not allowed" }, 422); 21 | } 22 | 23 | const client = await readPool.connect(); 24 | 25 | try { 26 | if (method === "all") { 27 | const result = await client.query({ 28 | text: sql, 29 | values: params, 30 | rowMode: "array", 31 | }); 32 | return c.json(result.rows); 33 | } 34 | 35 | if (method === "execute") { 36 | const result = await client.query({ 37 | text: sql, 38 | values: params, 39 | }); 40 | return c.json(result.rows); 41 | } 42 | 43 | return c.json({ error: "Unknown method value" }, 422); 44 | } catch (e) { 45 | console.error("Database query error:", e); 46 | return c.json({ error: "Database query failed" }, 500); 47 | } finally { 48 | client.release(); // always release the connection back to the pool 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /schema.graphql: -------------------------------------------------------------------------------- 1 | type Collection @entity { 2 | id: ID! @index # uuid 3 | contract: String! @index 4 | createdAt: DateTime! @index 5 | slug: String! @index @unique 6 | # objekt fields 7 | collectionId: String! 8 | season: String! @index 9 | member: String! @index 10 | artist: String! @index 11 | collectionNo: String! @index 12 | class: String! @index 13 | thumbnailImage: String! 14 | frontImage: String! 15 | backImage: String! 16 | backgroundColor: String! 17 | textColor: String! 18 | accentColor: String! 19 | comoAmount: Int! 20 | onOffline: String! @index 21 | bandImageUrl: String 22 | frontMedia: String 23 | transfers: [Transfer!] @derivedFrom(field: "collection") 24 | objekts: [Objekt!] @derivedFrom(field: "collection") 25 | } 26 | 27 | type Transfer @entity { 28 | id: ID! @index # uuid 29 | from: String! @index 30 | to: String! @index 31 | createdAt: DateTime! @index 32 | tokenId: String! 33 | hash: String! 34 | objekt: Objekt! 35 | collection: Collection! 36 | } 37 | 38 | type Objekt @entity { 39 | id: ID! @index # tokenId 40 | owner: String! @index 41 | mintedAt: DateTime! 42 | receivedAt: DateTime! @index 43 | serial: Int! @index 44 | transferable: Boolean! @index 45 | transfers: [Transfer!] @derivedFrom(field: "objekt") 46 | collection: Collection! 47 | } 48 | 49 | type ComoBalance @entity { 50 | id: ID! # uuid 51 | contract: String! @index 52 | owner: String! @index 53 | amount: Int! @index 54 | } 55 | 56 | type Vote @entity { 57 | id: ID! # uuid 58 | from: String! @index 59 | createdAt: DateTime! @index 60 | contract: String! @index 61 | pollId: Int! @index 62 | candidateId: Int @index 63 | index: Int! @index 64 | amount: Int! @index 65 | } 66 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | db: 3 | restart: always 4 | image: postgres:15 5 | environment: 6 | - POSTGRES_DB=${DB_NAME} 7 | - POSTGRES_USER=${DB_USER} 8 | - POSTGRES_PASSWORD=${DB_PASS} 9 | - DB_READ_USER 10 | healthcheck: 11 | test: "pg_isready -d ${DB_NAME} -U ${DB_USER}" 12 | interval: 30s 13 | timeout: 60s 14 | retries: 5 15 | start_period: 80s 16 | ports: 17 | - "${DB_PORT}:5432" 18 | volumes: 19 | - db:/var/lib/postgresql/data 20 | # command: ["postgres", "-c", "log_statement=all"] 21 | processor: 22 | build: 23 | context: . 24 | dockerfile: docker/processor.Dockerfile 25 | restart: unless-stopped 26 | environment: 27 | - FORCE_PRETTY_LOGGER 28 | - RPC_ENDPOINT 29 | - RPC_RATE_LIMIT 30 | - RPC_FINALITY 31 | - SQD_ENDPOINT 32 | - DB_HOST 33 | - DB_PORT=5432 34 | - DB_NAME 35 | - DB_USER 36 | - DB_PASS 37 | - DB_READ_USER 38 | - DB_READ_PASS 39 | - ENABLE_OBJEKTS 40 | - ENABLE_GRAVITY 41 | - COSMO_PARALLEL_COUNT 42 | command: ["sqd", "process:prod"] 43 | depends_on: 44 | - db 45 | drizzle-proxy: 46 | build: 47 | context: . 48 | dockerfile: docker/drizzle-proxy.Dockerfile 49 | restart: always 50 | environment: 51 | - DB_HOST 52 | - DB_PORT=5432 53 | - DB_NAME 54 | - DB_USER 55 | - DB_PASS 56 | - DB_READ_USER 57 | - DB_READ_PASS 58 | - PROXY_HTTP_PORT 59 | - PROXY_KEY 60 | - PROXY_CACHE_MAX_AGE 61 | ports: 62 | - "${PROXY_HTTP_PORT}:${PROXY_HTTP_PORT}" 63 | depends_on: 64 | - db 65 | volumes: 66 | db: 67 | driver: local 68 | -------------------------------------------------------------------------------- /db/migrations/1741780963552-Data.js: -------------------------------------------------------------------------------- 1 | module.exports = class Data1741780963552 { 2 | name = "Data1741780963552"; 3 | 4 | async up(db) { 5 | // 1. create an index for loading transfers 6 | await db.query( 7 | `CREATE INDEX idx_transfer_from_timestamp ON transfer("from", "timestamp" DESC);` 8 | ); 9 | 10 | // 2. create an index for loading transfers 11 | await db.query( 12 | `CREATE INDEX idx_transfer_to_timestamp ON transfer("to", "timestamp" DESC);` 13 | ); 14 | 15 | // 3. specifically add an initial page load index for @cosmo-spin 16 | await db.query( 17 | `CREATE INDEX idx_transfer_timestamp_cosmo_spin ON transfer("timestamp" DESC) WHERE "from" = '0xd3d5f29881ad87bb10c1100e2c709c9596de345f' OR "to" = '0xd3d5f29881ad87bb10c1100e2c709c9596de345f';` 18 | ); 19 | 20 | // 4. improve collection join performance 21 | await db.query( 22 | `CREATE INDEX idx_transfer_collection_id ON transfer(collection_id);` 23 | ); 24 | 25 | // 5. improve objekt join performance 26 | await db.query( 27 | `CREATE INDEX idx_transfer_objekt_id ON transfer(objekt_id);` 28 | ); 29 | } 30 | 31 | async down(db) { 32 | // 1. drop the index for loading transfers 33 | await db.query(`DROP INDEX "public"."idx_transfer_from_timestamp"`); 34 | 35 | // 2. drop the index for loading transfers 36 | await db.query(`DROP INDEX "public"."idx_transfer_to_timestamp"`); 37 | 38 | // 3. drop the index for loading transfers from @cosmo-spin 39 | await db.query(`DROP INDEX "public"."idx_transfer_timestamp_cosmo_spin"`); 40 | 41 | // 4. drop the index for improving collection join performance 42 | await db.query(`DROP INDEX "public"."idx_transfer_collection_id"`); 43 | 44 | // 5. drop the index for improving objekt join performance 45 | await db.query(`DROP INDEX "public"."idx_transfer_objekt_id"`); 46 | } 47 | }; 48 | -------------------------------------------------------------------------------- /db/migrations/1745716395199-Data.js: -------------------------------------------------------------------------------- 1 | module.exports = class Data1745716395199 { 2 | name = "Data1745716395199"; 3 | 4 | async up(db) { 5 | // 1. drop the contract column from como_balance 6 | await db.query( 7 | `ALTER TABLE "public"."como_balance" DROP COLUMN "contract";` 8 | ); 9 | 10 | // 2. add token_id column to como_balance 11 | await db.query( 12 | `ALTER TABLE "public"."como_balance" ADD COLUMN "token_id" numeric NOT NULL;` 13 | ); 14 | 15 | // 3. add an index on owner 16 | await db.query( 17 | `CREATE INDEX "IDX_como_balance_owner" ON "public"."como_balance" ("owner");` 18 | ); 19 | 20 | // 4. add an index on owner and token_id 21 | await db.query( 22 | `CREATE INDEX "IDX_como_balance_owner_token_id" ON "public"."como_balance" ("owner", "token_id");` 23 | ); 24 | 25 | // 5. add a unique constraint on owner and token_id 26 | await db.query( 27 | `ALTER TABLE "public"."como_balance" ADD CONSTRAINT "UQ_como_balance_owner_token_id" UNIQUE ("owner", "token_id");` 28 | ); 29 | } 30 | 31 | async down(db) { 32 | // 1. drop the unique constraint on owner and token_id 33 | await db.query( 34 | `ALTER TABLE "public"."como_balance" DROP CONSTRAINT "UQ_como_balance_owner_token_id";` 35 | ); 36 | 37 | // 2. drop the index on owner and token_id 38 | await db.query(`DROP INDEX "public"."IDX_como_balance_owner_token_id";`); 39 | 40 | // 3. drop the index on owner 41 | await db.query(`DROP INDEX "public"."IDX_como_balance_owner";`); 42 | 43 | // 4. drop the token_id column from como_balance 44 | await db.query( 45 | `ALTER TABLE "public"."como_balance" DROP COLUMN "token_id";` 46 | ); 47 | 48 | // 5. add the contract column to como_balance 49 | await db.query( 50 | `ALTER TABLE "public"."como_balance" ADD COLUMN "contract" text NOT NULL;` 51 | ); 52 | } 53 | }; 54 | -------------------------------------------------------------------------------- /src/http/drizzle-proxy/handlers/set-band.ts: -------------------------------------------------------------------------------- 1 | import { Context } from "hono"; 2 | import { writePool } from "../db"; 3 | import z from "zod"; 4 | 5 | const schema = z.object({ 6 | items: z 7 | .object({ 8 | slug: z.string().min(1), 9 | bandImageUrl: z.string().min(1), 10 | }) 11 | .array(), 12 | }); 13 | 14 | export async function setBand(c: Context) { 15 | const client = await writePool.connect(); 16 | 17 | try { 18 | const body = await c.req.json(); 19 | var { items } = schema.parse(body); 20 | } catch (e) { 21 | if (e instanceof z.ZodError) { 22 | return c.json( 23 | { error: e.errors.map((error) => error.message).join(", ") }, 24 | 422 25 | ); 26 | } 27 | 28 | return c.json({ error: "Invalid request body" }, 422); 29 | } 30 | 31 | // early return if no items to update 32 | if (items.length === 0) { 33 | return c.json({ message: "No items to update" }, 200); 34 | } 35 | 36 | try { 37 | // make placeholders for the query 38 | const updateValues = items 39 | .map((item, index) => `($${index * 2 + 1}, $${index * 2 + 2})`) 40 | .join(", "); 41 | 42 | // make params for the query (bandImageUrl, slug) for each item 43 | const params = items.flatMap((item) => [item.bandImageUrl, item.slug]); 44 | 45 | // update collection table 46 | await client.query( 47 | `UPDATE collection 48 | SET band_image_url = updates.band_image_url 49 | FROM (VALUES ${updateValues}) AS updates(band_image_url, slug) 50 | WHERE collection.slug = updates.slug`, 51 | params 52 | ); 53 | 54 | return c.json({ message: "Band image url(s) updated" }, 200); 55 | } catch (e) { 56 | console.error("Database error:", e); 57 | return c.json({ error: "Database error" }, 500); 58 | } finally { 59 | client.release(); // always release the connection back to the pool 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cosmo-db 2 | 3 | A [Subsquid](https://subsquid.io/) indexer for ARTMS and tripleS NFTs. 4 | 5 | **NOTE:** On April 18th 2025, COSMO was migrated from the Polygon blockchain over to Abstract. The last snapshot of the project using Polygon can be found on the [`checkpoint/polygon`](https://github.com/teamreflex/cosmo-db/tree/checkpoint/polygon) branch. `main` contains the latest version running on Abstract. 6 | 7 | ## Setup 8 | 9 | ```bash 10 | $ git clone git@github.com:teamreflex/cosmo-db.git 11 | $ cd cosmo-db 12 | $ pnpm i 13 | $ cp .env.example .env 14 | $ docker compose up -d 15 | ``` 16 | 17 | ## Service Configuration 18 | 19 | ### db 20 | 21 | Postgres 15 database server. 22 | 23 | - `DB_PORT`: Port to expose the server on. Internally it still uses the default port. 24 | - `DB_READ_USER`: Creates a user with this name, only with SELECT privileges. 25 | - `DB_READ_PASS`: Password for the read user. 26 | 27 | ### processor 28 | 29 | Subsquid processor that parses and stores data from the chain. 30 | 31 | - `SQD_ENDPOINT`: Subsquid archive endpoint. 32 | - `RPC_ENDPOINT`: RPC endpoint to use for the chain. 33 | - `RPC_RATE_LIMIT`: Rate limit for the RPC endpoint (req/s). 34 | - `RPC_FINALITY`: Finality confirmation for RPC ingestion. 35 | - `ENABLE_OBJEKTS`: Enable objekt processing. 36 | - `COSMO_PARALLEL_COUNT`: Number of objekts to fetch metadata for in parallel. 37 | 38 | ### drizzle-proxy 39 | 40 | HTTP server that exposes the database for use with [Drizzle ORM's HTTP proxy](https://orm.drizzle.team/docs/get-started-postgresql#http-proxy) support for serverless environments. 41 | 42 | - `PROXY_HTTP_PORT`: Port to expose the server on. 43 | - `PROXY_KEY`: Key to use for authentication. 44 | - `PROXY_CACHE_MAX_AGE`: Max age (in seconds) to cache the `/status` endpoint for. 45 | 46 | ## Tooling 47 | 48 | - [Subsquid](https://subsquid.io/) 49 | - [Bun](https://bun.sh/) 50 | - [Hono](https://hono.dev/) 51 | 52 | ## License 53 | 54 | Licensed under the [MIT license](https://github.com/teamreflex/cosmo-db/blob/main/LICENSE.md). 55 | 56 | ## Contact 57 | 58 | - **Email**: kyle at reflex.lol 59 | - **Discord**: kairunz 60 | - **Cosmo ID**: Kairu 61 | -------------------------------------------------------------------------------- /db/migrations/1721359520022-Data.js: -------------------------------------------------------------------------------- 1 | module.exports = class Data1721359520022 { 2 | name = "Data1721359520022"; 3 | 4 | async up(db) { 5 | const dbName = process.env.DB_NAME; 6 | const readUser = process.env.DB_READ_USER; 7 | const readPass = process.env.DB_READ_PASS; 8 | 9 | // create role 10 | await db.query( 11 | `CREATE ROLE "${readUser}" WITH LOGIN PASSWORD '${readPass}'` 12 | ); 13 | // give connect privileges 14 | await db.query(`GRANT CONNECT ON DATABASE "${dbName}" TO "${readUser}"`); 15 | // allow schema access 16 | await db.query(`GRANT USAGE ON SCHEMA public TO "${readUser}"`); 17 | // give select privileges to all tables 18 | await db.query( 19 | `GRANT SELECT ON ALL TABLES IN SCHEMA public TO "${readUser}"` 20 | ); 21 | 22 | // give select privileges to all sequences 23 | await db.query( 24 | `GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO "${readUser}"` 25 | ); 26 | 27 | // give select privileges to all future tables 28 | await db.query( 29 | `ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO "${readUser}"` 30 | ); 31 | 32 | // give select privileges to all future sequences 33 | await db.query( 34 | `ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON SEQUENCES TO "${readUser}"` 35 | ); 36 | 37 | // grant on processor schema 38 | // await db.query(`GRANT USAGE ON SCHEMA squid_processor TO "${readUser}"`); 39 | // await db.query( 40 | // `GRANT SELECT ON ALL TABLES IN SCHEMA squid_processor TO "${readUser}"` 41 | // ); 42 | // await db.query( 43 | // `GRANT SELECT ON ALL SEQUENCES IN SCHEMA squid_processor TO "${readUser}"` 44 | // ); 45 | // await db.query( 46 | // `ALTER DEFAULT PRIVILEGES IN SCHEMA squid_processor GRANT SELECT ON TABLES TO "${readUser}"` 47 | // ); 48 | // await db.query( 49 | // `ALTER DEFAULT PRIVILEGES IN SCHEMA squid_processor GRANT SELECT ON SEQUENCES TO "${readUser}"` 50 | // ); 51 | } 52 | 53 | async down(db) { 54 | const readUser = process.env.DB_READ_USER; 55 | await db.query(`DROP USER IF EXISTS "${readUser}"`); 56 | } 57 | }; 58 | -------------------------------------------------------------------------------- /src/model/generated/collection.model.ts: -------------------------------------------------------------------------------- 1 | import { Entity, Column, PrimaryColumn, Index, OneToMany } from "typeorm"; 2 | import { Transfer } from "./transfer.model"; 3 | import { Objekt } from "./objekt.model"; 4 | 5 | @Entity() 6 | export class Collection { 7 | constructor(props?: Partial) { 8 | Object.assign(this, props); 9 | } 10 | 11 | // for some reason subsquid tries to cast this to ::text, so uuid won't work 12 | @PrimaryColumn({ 13 | type: "varchar", 14 | length: 36, 15 | }) 16 | id!: string; 17 | 18 | @Index() 19 | @Column("text", { nullable: false }) 20 | contract!: string; 21 | 22 | @Index() 23 | @Column("timestamp with time zone", { nullable: false }) 24 | createdAt!: Date; 25 | 26 | @Index({ unique: true }) 27 | @Column("text", { nullable: false }) 28 | slug!: string; 29 | 30 | @Column("text", { nullable: false }) 31 | collectionId!: string; 32 | 33 | @Index() 34 | @Column("text", { nullable: false }) 35 | season!: string; 36 | 37 | @Index() 38 | @Column("text", { nullable: false }) 39 | member!: string; 40 | 41 | @Index() 42 | @Column("text", { nullable: false }) 43 | artist!: string; 44 | 45 | @Index() 46 | @Column("text", { nullable: false }) 47 | collectionNo!: string; 48 | 49 | @Index() 50 | @Column("text", { nullable: false }) 51 | class!: string; 52 | 53 | @Column("text", { nullable: false }) 54 | thumbnailImage!: string; 55 | 56 | @Column("text", { nullable: false }) 57 | frontImage!: string; 58 | 59 | @Column("text", { nullable: false }) 60 | backImage!: string; 61 | 62 | @Column("text", { nullable: false }) 63 | backgroundColor!: string; 64 | 65 | @Column("text", { nullable: false }) 66 | textColor!: string; 67 | 68 | @Column("text", { nullable: false }) 69 | accentColor!: string; 70 | 71 | @Column("int4", { nullable: false }) 72 | comoAmount!: number; 73 | 74 | @Index() 75 | @Column("text", { nullable: false }) 76 | onOffline!: string; 77 | 78 | @Column({ type: "varchar", length: 255, nullable: true }) 79 | bandImageUrl!: string | null; 80 | 81 | @Column({ type: "varchar", length: 255, nullable: true }) 82 | frontMedia!: string | null; 83 | 84 | @OneToMany(() => Transfer, (e) => e.collection) 85 | transfers!: Transfer[]; 86 | 87 | @OneToMany(() => Objekt, (e) => e.collection) 88 | objekts!: Objekt[]; 89 | } 90 | -------------------------------------------------------------------------------- /src/processor.ts: -------------------------------------------------------------------------------- 1 | import { 2 | BlockHeader, 3 | DataHandlerContext, 4 | EvmBatchProcessor, 5 | EvmBatchProcessorFields, 6 | Log as _Log, 7 | Transaction as _Transaction, 8 | } from "@subsquid/evm-processor"; 9 | import * as ABI_OBJEKT from "./abi/objekt"; 10 | import * as ABI_COMO from "./abi/como"; 11 | import * as ABI_GRAVITY from "./abi/gravity"; 12 | import { env } from "./env/processor"; 13 | import { Addresses, COSMO_START_BLOCK } from "./constants"; 14 | 15 | console.log( 16 | `[processor] Starting processor with objekts ${env.ENABLE_OBJEKTS} and gravity ${env.ENABLE_GRAVITY}` 17 | ); 18 | 19 | const processor = new EvmBatchProcessor() 20 | .setGateway(env.SQD_ENDPOINT) 21 | .setRpcEndpoint({ 22 | url: env.RPC_ENDPOINT, 23 | rateLimit: env.RPC_RATE_LIMIT, 24 | }) 25 | .setFields({ 26 | log: { 27 | topics: true, 28 | data: true, 29 | transactionHash: true, 30 | }, 31 | transaction: { 32 | input: true, 33 | sighash: true, 34 | }, 35 | }) 36 | .setFinalityConfirmation(env.RPC_FINALITY); 37 | 38 | processor 39 | // objekt transfers 40 | .addLog({ 41 | address: [Addresses.OBJEKT], 42 | topic0: [ABI_OBJEKT.events.Transfer.topic], 43 | range: { from: COSMO_START_BLOCK }, 44 | }) 45 | // objekt transferability updates 46 | .addTransaction({ 47 | to: [Addresses.OBJEKT], 48 | sighash: [ABI_OBJEKT.functions.batchUpdateObjektTransferability.sighash], 49 | range: { from: COSMO_START_BLOCK }, 50 | }) 51 | // single como transfers 52 | .addLog({ 53 | address: [Addresses.COMO], 54 | topic0: [ABI_COMO.events.TransferSingle.topic], 55 | range: { from: COSMO_START_BLOCK }, 56 | }) 57 | // batch como transfers 58 | .addLog({ 59 | address: [Addresses.COMO], 60 | topic0: [ABI_COMO.events.TransferBatch.topic], 61 | range: { from: COSMO_START_BLOCK }, 62 | }) 63 | // gravity votes 64 | .addLog({ 65 | address: [Addresses.GRAVITY], 66 | topic0: [ABI_GRAVITY.events.Voted.topic], 67 | range: { from: COSMO_START_BLOCK }, 68 | }); 69 | 70 | export { processor }; 71 | export type Fields = EvmBatchProcessorFields; 72 | export type Block = BlockHeader; 73 | export type Log = _Log; 74 | export type Transaction = _Transaction; 75 | export type ProcessorContext = DataHandlerContext; 76 | -------------------------------------------------------------------------------- /src/http/drizzle-proxy/handlers/rescan-metadata.ts: -------------------------------------------------------------------------------- 1 | import { Context } from "hono"; 2 | import { writePool } from "../db"; 3 | import { fetchMetadata } from "../../../cosmo"; 4 | 5 | export async function rescanMetadata(c: Context) { 6 | const client = await writePool.connect(); 7 | 8 | const tokenId = c.req.param("tokenId"); 9 | 10 | // input validation 11 | if (!tokenId || typeof tokenId !== "string" || tokenId.trim() === "") { 12 | return c.json({ error: "Invalid token ID" }, 400); 13 | } 14 | 15 | try { 16 | // start transaction 17 | await client.query("BEGIN"); 18 | 19 | // fetch existing objekt row 20 | const result = await client.query( 21 | "select id, serial, collection_id from objekt where id = $1", 22 | [tokenId] 23 | ); 24 | 25 | // token id doesn't exist 26 | if (result.rows.length === 0) { 27 | await client.query("ROLLBACK"); 28 | return c.json({ error: "Objekt not found" }, 404); 29 | } 30 | 31 | const objekt = result.rows[0]; 32 | 33 | try { 34 | // fetch metadata from cosmo 35 | const metadata = await fetchMetadata(objekt.id); 36 | 37 | // update objekt table 38 | await client.query( 39 | "update objekt set transferable = $1, serial = $2 where id = $3", 40 | [metadata.objekt.transferable, metadata.objekt.objektNo, objekt.id] 41 | ); 42 | 43 | // update collection table 44 | await client.query( 45 | "update collection set thumbnail_image = $1, front_image = $2, back_image = $3, background_color = $4, text_color = $5 where id = $6", 46 | [ 47 | metadata.objekt.thumbnailImage, 48 | metadata.objekt.frontImage, 49 | metadata.objekt.backImage, 50 | metadata.objekt.backgroundColor, 51 | metadata.objekt.textColor, 52 | objekt.collection_id, 53 | ] 54 | ); 55 | 56 | // commit transaction 57 | await client.query("COMMIT"); 58 | return c.json({ message: "Metadata updated" }, 200); 59 | } catch (e) { 60 | await client.query("ROLLBACK"); 61 | console.error("Failed to fetch metadata:", e); 62 | return c.json({ error: "Failed to fetch metadata" }, 500); 63 | } 64 | } catch (e) { 65 | await client.query("ROLLBACK"); 66 | console.error("Database error:", e); 67 | return c.json({ error: "Database error" }, 500); 68 | } finally { 69 | client.release(); // always release the connection back to the pool 70 | } 71 | } 72 | 73 | type ObjektRow = { 74 | id: string; 75 | serial: number; 76 | collection_id: string; 77 | }; 78 | -------------------------------------------------------------------------------- /commands.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://cdn.subsquid.io/schemas/commands.json", 3 | "commands": { 4 | "clean": { 5 | "description": "delete all build artifacts", 6 | "cmd": ["npx", "--yes", "rimraf", "lib"] 7 | }, 8 | "build": { 9 | "description": "Build the squid project", 10 | "deps": ["clean"], 11 | "cmd": ["tsc"] 12 | }, 13 | "up": { 14 | "description": "Start a PG database", 15 | "cmd": ["docker", "compose", "up", "-d"] 16 | }, 17 | "down": { 18 | "description": "Drop a PG database", 19 | "cmd": ["docker", "compose", "down"] 20 | }, 21 | "migration:apply": { 22 | "description": "Apply the DB migrations", 23 | "cmd": ["squid-typeorm-migration", "apply"] 24 | }, 25 | "migration:generate": { 26 | "description": "Generate a DB migration matching the TypeORM entities", 27 | "deps": ["build", "migration:clean"], 28 | "cmd": ["squid-typeorm-migration", "generate"] 29 | }, 30 | "migration:clean": { 31 | "description": "Clean the migrations folder", 32 | "cmd": ["npx", "--yes", "rimraf", "./db/migrations"] 33 | }, 34 | "migration": { 35 | "deps": ["build"], 36 | "cmd": ["squid-typeorm-migration", "generate"], 37 | "hidden": true 38 | }, 39 | "codegen": { 40 | "description": "Generate TypeORM entities from the schema file", 41 | "cmd": ["squid-typeorm-codegen"] 42 | }, 43 | "typegen": { 44 | "description": "Generate data access classes for an ABI file(s) in the ./abi folder", 45 | "cmd": [ 46 | "squid-evm-typegen", 47 | "./src/abi", 48 | { "glob": "./abi/*.json" }, 49 | "--multicall" 50 | ] 51 | }, 52 | "process": { 53 | "description": "Load .env and start the squid processor", 54 | "deps": ["build", "migration:apply"], 55 | "cmd": ["node", "--require=dotenv/config", "lib/main.js"] 56 | }, 57 | "process:prod": { 58 | "description": "Start the squid processor", 59 | "deps": ["migration:apply"], 60 | "cmd": ["node", "lib/main.js"], 61 | "hidden": true 62 | }, 63 | "serve": { 64 | "description": "Start the GraphQL API server", 65 | "cmd": ["squid-graphql-server"] 66 | }, 67 | "serve:prod": { 68 | "description": "Start the GraphQL API server with caching and limits", 69 | "cmd": [ 70 | "squid-graphql-server", 71 | "--dumb-cache", 72 | "in-memory", 73 | "--dumb-cache-ttl", 74 | "1000", 75 | "--dumb-cache-size", 76 | "100", 77 | "--dumb-cache-max-age", 78 | "1000" 79 | ] 80 | }, 81 | "check-updates": { 82 | "cmd": [ 83 | "npx", 84 | "--yes", 85 | "npm-check-updates", 86 | "--filter=/subsquid/", 87 | "--upgrade" 88 | ], 89 | "hidden": true 90 | }, 91 | "bump": { 92 | "description": "Bump @subsquid packages to the latest versions", 93 | "deps": ["check-updates"], 94 | "cmd": ["npm", "i", "-f"] 95 | }, 96 | "open": { 97 | "description": "Open a local browser window", 98 | "cmd": ["npx", "--yes", "opener"] 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/cosmo.ts: -------------------------------------------------------------------------------- 1 | import { ofetch } from "ofetch"; 2 | 3 | export async function fetchMetadata(tokenId: string) { 4 | return await fetchMetadataV1(tokenId); 5 | // try { 6 | // return await fetchMetadataV1(tokenId); 7 | // } catch (error) { 8 | // console.log(`[fetchMetadata] Error fetching v1 metadata: ${error}`); 9 | // const v3 = await fetchMetadataV3(tokenId); 10 | // return normalizeV3(v3, tokenId); 11 | // } 12 | } 13 | 14 | export type MetadataV1 = { 15 | name: string; 16 | description: string; 17 | image: string; 18 | background_color: string; 19 | objekt: { 20 | collectionId: string; 21 | season: string; 22 | member: string; 23 | collectionNo: string; 24 | class: string; 25 | artists: string[]; 26 | thumbnailImage: string; 27 | frontImage: string; 28 | backImage: string; 29 | accentColor: string; 30 | backgroundColor: string; 31 | textColor: string; 32 | comoAmount: number; 33 | tokenId: string; 34 | objektNo: number; 35 | tokenAddress: string; 36 | transferable: boolean; 37 | }; 38 | }; 39 | 40 | /** 41 | * Fetch objekt metadata from the v1 API. 42 | */ 43 | export async function fetchMetadataV1(tokenId: string) { 44 | return await ofetch( 45 | `https://api.cosmo.fans/objekt/v1/token/${tokenId}`, 46 | { 47 | retry: 6, 48 | retryDelay: 750, // 750ms backoff 49 | } 50 | ); 51 | } 52 | 53 | type MetadataV3 = { 54 | name: string; 55 | description: string; 56 | image: string; 57 | background_color: string; 58 | attributes: { 59 | trait_type: string; 60 | value: string; 61 | }[]; 62 | }; 63 | 64 | /** 65 | * Fetch objekt metadata from the v3 API. 66 | */ 67 | export async function fetchMetadataV3(tokenId: string) { 68 | return await ofetch( 69 | `https://api.cosmo.fans/bff/v3/objekts/nft-metadata/${tokenId}`, 70 | { 71 | retry: 2, 72 | retryDelay: 500, // 500ms backoff 73 | } 74 | ); 75 | } 76 | 77 | /** 78 | * Attempt to convert v3 metadata to v1 metadata. 79 | */ 80 | export function normalizeV3(metadata: MetadataV3, tokenId: string): MetadataV1 { 81 | const artist = getTrait(metadata, tokenId, "Artist"); 82 | const className = getTrait(metadata, tokenId, "Class"); 83 | const member = getTrait(metadata, tokenId, "Member"); 84 | const season = getTrait(metadata, tokenId, "Season"); 85 | const collection = getTrait(metadata, tokenId, "Collection"); 86 | 87 | const thumbnail = metadata.image.replace(/\/(4x|original)/, "/thumbnail"); 88 | const comoAmount = ["Double", "Premier"].includes(className) ? 2 : 1; 89 | 90 | return { 91 | name: metadata.name, 92 | description: metadata.description, 93 | image: metadata.image, 94 | background_color: metadata.background_color, 95 | objekt: { 96 | collectionId: `${season}-${member}-${collection}`, 97 | season: season, 98 | member: member, 99 | collectionNo: collection, 100 | class: className, 101 | artists: [artist.toLowerCase()], 102 | thumbnailImage: thumbnail, 103 | frontImage: metadata.image, 104 | backgroundColor: metadata.background_color, 105 | comoAmount: comoAmount, 106 | tokenId: tokenId, 107 | // not possible to get from v3 108 | backImage: "", 109 | accentColor: "", 110 | textColor: "#000000", 111 | objektNo: 0, 112 | tokenAddress: "", 113 | transferable: false, 114 | }, 115 | }; 116 | } 117 | 118 | /** 119 | * Get a trait from the metadata attributes array. 120 | */ 121 | function getTrait(metadata: MetadataV3, tokenId: string, trait: string) { 122 | const attr = metadata.attributes.find((attr, i, arr) => { 123 | // special case for unit objekts 124 | if ( 125 | trait === "Member" && 126 | arr.findIndex((a) => a.trait_type === "Class" && a.value === "Unit") > -1 127 | ) { 128 | return attr.value.includes(" x "); 129 | } 130 | 131 | return attr.trait_type === trait; 132 | }); 133 | if (!attr) { 134 | throw new Error( 135 | `[normalizeV3] Trait ${trait} not found for token ${tokenId}` 136 | ); 137 | } 138 | return attr.value; 139 | } 140 | -------------------------------------------------------------------------------- /src/model/generated/marshal.ts: -------------------------------------------------------------------------------- 1 | import assert from 'assert' 2 | 3 | 4 | export interface Marshal { 5 | fromJSON(value: unknown): T 6 | toJSON(value: T): S 7 | } 8 | 9 | 10 | export const string: Marshal = { 11 | fromJSON(value: unknown): string { 12 | assert(typeof value === 'string', 'invalid String') 13 | return value 14 | }, 15 | toJSON(value) { 16 | return value 17 | } 18 | } 19 | 20 | 21 | export const id = string 22 | 23 | 24 | export const int: Marshal = { 25 | fromJSON(value: unknown): number { 26 | assert(Number.isInteger(value), 'invalid Int') 27 | return value as number 28 | }, 29 | toJSON(value) { 30 | return value 31 | } 32 | } 33 | 34 | 35 | export const float: Marshal = { 36 | fromJSON(value: unknown): number { 37 | assert(typeof value === 'number', 'invalid Float') 38 | return value as number 39 | }, 40 | toJSON(value) { 41 | return value 42 | } 43 | } 44 | 45 | 46 | export const boolean: Marshal = { 47 | fromJSON(value: unknown): boolean { 48 | assert(typeof value === 'boolean', 'invalid Boolean') 49 | return value 50 | }, 51 | toJSON(value: boolean): boolean { 52 | return value 53 | } 54 | } 55 | 56 | 57 | export const bigint: Marshal = { 58 | fromJSON(value: unknown): bigint { 59 | assert(typeof value === 'string', 'invalid BigInt') 60 | return BigInt(value) 61 | }, 62 | toJSON(value: bigint): string { 63 | return value.toString() 64 | } 65 | } 66 | 67 | 68 | export const bigdecimal: Marshal = { 69 | fromJSON(value: unknown): bigint { 70 | assert(typeof value === 'string', 'invalid BigDecimal') 71 | return decimal.BigDecimal(value) 72 | }, 73 | toJSON(value: any): string { 74 | return value.toString() 75 | } 76 | } 77 | 78 | 79 | // credit - https://github.com/Urigo/graphql-scalars/blob/91b4ea8df891be8af7904cf84751930cc0c6613d/src/scalars/iso-date/validator.ts#L122 80 | const RFC_3339_REGEX = 81 | /^(\d{4}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60))(\.\d{1,})?([Z])$/ 82 | 83 | 84 | function isIsoDateTimeString(s: string): boolean { 85 | return RFC_3339_REGEX.test(s) 86 | } 87 | 88 | 89 | export const datetime: Marshal = { 90 | fromJSON(value: unknown): Date { 91 | assert(typeof value === 'string', 'invalid DateTime') 92 | assert(isIsoDateTimeString(value), 'invalid DateTime') 93 | return new Date(value) 94 | }, 95 | toJSON(value: Date): string { 96 | return value.toISOString() 97 | } 98 | } 99 | 100 | 101 | export const bytes: Marshal = { 102 | fromJSON(value: unknown): Buffer { 103 | assert(typeof value === 'string', 'invalid Bytes') 104 | assert(value.length % 2 === 0, 'invalid Bytes') 105 | assert(/^0x[0-9a-f]+$/i.test(value), 'invalid Bytes') 106 | return Buffer.from(value.slice(2), 'hex') 107 | }, 108 | toJSON(value: Uint8Array): string { 109 | if (Buffer.isBuffer(value)) { 110 | return '0x' + value.toString('hex') 111 | } else { 112 | return '0x' + Buffer.from(value.buffer, value.byteOffset, value.byteLength).toString('hex') 113 | } 114 | } 115 | } 116 | 117 | 118 | export function fromList(list: unknown, f: (val: unknown) => T): T[] { 119 | assert(Array.isArray(list)) 120 | return list.map((val) => f(val)) 121 | } 122 | 123 | 124 | export function nonNull(val: T | undefined | null): T { 125 | assert(val != null, 'non-nullable value is null') 126 | return val 127 | } 128 | 129 | 130 | export const bigintTransformer = { 131 | to(x?: bigint) { 132 | return x?.toString() 133 | }, 134 | from(s?: string): bigint | undefined { 135 | return s == null ? undefined : BigInt(s) 136 | } 137 | } 138 | 139 | 140 | export const floatTransformer = { 141 | to(x?: number) { 142 | return x?.toString() 143 | }, 144 | from(s?: string): number | undefined { 145 | return s == null ? undefined : Number(s) 146 | } 147 | } 148 | 149 | 150 | export const bigdecimalTransformer = { 151 | to(x?: any) { 152 | return x?.toString() 153 | }, 154 | from(s?: any): any | undefined { 155 | return s == null ? undefined : decimal.BigDecimal(s) 156 | } 157 | } 158 | 159 | 160 | export function enumFromJson(json: unknown, enumObject: E): E[keyof E] { 161 | assert(typeof json == 'string', 'invalid enum value') 162 | let val = (enumObject as any)[json] 163 | assert(typeof val == 'string', `invalid enum value`) 164 | return val as any 165 | } 166 | 167 | 168 | const decimal = { 169 | get BigDecimal(): any { 170 | throw new Error('Package `@subsquid/big-decimal` is not installed') 171 | } 172 | } 173 | 174 | 175 | try { 176 | Object.defineProperty(decimal, "BigDecimal", { 177 | value: require('@subsquid/big-decimal').BigDecimal 178 | }) 179 | } catch (e) {} 180 | -------------------------------------------------------------------------------- /src/parser.ts: -------------------------------------------------------------------------------- 1 | import { Transfer } from "./model"; 2 | import { Fields, Log, Transaction } from "./processor"; 3 | import { addr } from "./util"; 4 | import { BlockData } from "@subsquid/evm-processor"; 5 | import * as ABI_OBJEKT from "./abi/objekt"; 6 | import * as ABI_COMO from "./abi/como"; 7 | import * as ABI_GRAVITY from "./abi/gravity"; 8 | import { Addresses } from "./constants"; 9 | import { randomUUID } from "crypto"; 10 | 11 | const transferability = ABI_OBJEKT.functions.batchUpdateObjektTransferability; 12 | 13 | /** 14 | * Parse incoming blocks. 15 | */ 16 | export function parseBlocks(blocks: BlockData[]) { 17 | const logs = blocks.flatMap((block) => block.logs); 18 | const transactions = blocks.flatMap((block) => block.transactions); 19 | 20 | return { 21 | // objekt transfers 22 | transfers: logs 23 | .map(parseTransferEvent) 24 | .filter((e) => e !== undefined) 25 | .map((event) => { 26 | return new Transfer({ 27 | id: randomUUID(), 28 | from: event.from, 29 | to: event.to, 30 | timestamp: new Date(event.timestamp), 31 | tokenId: event.tokenId, 32 | hash: event.hash, 33 | }); 34 | }), 35 | 36 | // objekt transferability updates 37 | transferability: transactions 38 | .filter( 39 | (tx) => 40 | !!tx.to && 41 | Addresses.OBJEKT === addr(tx.to) && 42 | tx.sighash === transferability.sighash 43 | ) 44 | .flatMap(parseTransferabilityUpdate) 45 | .filter((e) => e !== undefined), 46 | 47 | // como balance updates 48 | comoBalanceUpdates: logs 49 | .filter((log) => addr(Addresses.COMO) === addr(log.address)) 50 | .flatMap(parseComoBalanceEvents), 51 | 52 | // vote creations 53 | votes: logs 54 | .filter((log) => addr(Addresses.GRAVITY) === addr(log.address)) 55 | .map(parseVote) 56 | .filter((e) => e !== undefined), 57 | }; 58 | } 59 | 60 | export type TransferEvent = { 61 | hash: string; 62 | contract: string; 63 | from: string; 64 | to: string; 65 | tokenId: string; 66 | timestamp: number; 67 | }; 68 | 69 | /** 70 | * Parse a log into a Transfer. 71 | */ 72 | export function parseTransferEvent(log: Log): TransferEvent | undefined { 73 | try { 74 | if (log.topics[0] === ABI_OBJEKT.events.Transfer.topic) { 75 | const event = ABI_OBJEKT.events.Transfer.decode(log); 76 | return { 77 | hash: log.transactionHash, 78 | from: addr(event.from), 79 | to: addr(event.to), 80 | contract: addr(log.address), 81 | tokenId: event.tokenId.toString(), 82 | timestamp: log.block.timestamp, 83 | }; 84 | } 85 | return undefined; 86 | } catch (err) { 87 | return undefined; 88 | } 89 | } 90 | 91 | export type TransferabilityUpdate = { 92 | tokenId: string; 93 | transferable: boolean; 94 | }; 95 | 96 | /** 97 | * Parse an event into an objekt update. 98 | */ 99 | export function parseTransferabilityUpdate( 100 | tx: Transaction 101 | ): TransferabilityUpdate[] { 102 | try { 103 | const { tokenIds, transferable } = transferability.decode(tx.input); 104 | 105 | return tokenIds.map((tokenId) => ({ 106 | tokenId: tokenId.toString(), 107 | transferable: transferable, 108 | })); 109 | } catch (err) { 110 | return []; 111 | } 112 | } 113 | 114 | export type ComoBalanceEvent = { 115 | hash: string; 116 | tokenId: number; 117 | from: string; 118 | to: string; 119 | value: bigint; 120 | timestamp: number; 121 | }; 122 | 123 | /** 124 | * Parse a log into ComoBalanceEvents. 125 | */ 126 | export function parseComoBalanceEvents(log: Log): ComoBalanceEvent[] { 127 | try { 128 | switch (log.topics[0]) { 129 | // handle single token transfers 130 | case ABI_COMO.events.TransferSingle.topic: { 131 | const event = ABI_COMO.events.TransferSingle.decode(log); 132 | return [ 133 | { 134 | hash: log.transactionHash, 135 | from: addr(event.from), 136 | to: addr(event.to), 137 | tokenId: Number(event.id), 138 | value: event.value, 139 | timestamp: log.block.timestamp, 140 | }, 141 | ]; 142 | } 143 | // handle batch token transfers 144 | case ABI_COMO.events.TransferBatch.topic: { 145 | const event = ABI_COMO.events.TransferBatch.decode(log); 146 | const events: ComoBalanceEvent[] = []; 147 | 148 | for (let i = 0; i < event.ids.length; i++) { 149 | events.push({ 150 | hash: log.transactionHash, 151 | from: addr(event.from), 152 | to: addr(event.to), 153 | tokenId: Number(event.ids[i]), 154 | value: event.values[i], 155 | timestamp: log.block.timestamp, 156 | }); 157 | } 158 | return events; 159 | } 160 | // ? 161 | default: 162 | return []; 163 | } 164 | } catch (err) { 165 | return []; 166 | } 167 | } 168 | 169 | export type VoteEvent = { 170 | id: string; 171 | from: string; 172 | timestamp: number; 173 | contract: string; 174 | pollId: number; 175 | tokenAmount: bigint; 176 | }; 177 | 178 | /** 179 | * Parse a log into a vote. 180 | */ 181 | export function parseVote(log: Log): VoteEvent | undefined { 182 | try { 183 | const event = ABI_GRAVITY.events.Voted.decode(log); 184 | 185 | return { 186 | id: log.id, 187 | from: addr(event.voter), 188 | timestamp: log.block.timestamp, 189 | contract: addr(log.address), 190 | pollId: Number(event.pollId), 191 | tokenAmount: event.tokenAmount, 192 | }; 193 | } catch (err) { 194 | return undefined; 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /db/migrations/1721359520021-Data.js: -------------------------------------------------------------------------------- 1 | module.exports = class Data1721359520021 { 2 | name = "Data1721359520021"; 3 | 4 | async up(db) { 5 | await db.query( 6 | `CREATE TABLE "objekt" ("id" varchar NOT NULL, "owner" text NOT NULL, "minted_at" TIMESTAMP WITH TIME ZONE NOT NULL, "received_at" TIMESTAMP WITH TIME ZONE NOT NULL, "serial" integer NOT NULL, "transferable" boolean NOT NULL, "collection_id" varchar(36), CONSTRAINT "PK_a50fda223abd7f6ae55f2cf629f" PRIMARY KEY ("id"))` 7 | ); 8 | await db.query( 9 | `CREATE INDEX "IDX_d2ddf18405b46538e169ab03e8" ON "objekt" ("owner") ` 10 | ); 11 | await db.query( 12 | `CREATE INDEX "IDX_3d4c25bad83bb3fdae75fc0692" ON "objekt" ("received_at") ` 13 | ); 14 | await db.query( 15 | `CREATE INDEX "IDX_19209bac5cab521e9327f74013" ON "objekt" ("serial") ` 16 | ); 17 | await db.query( 18 | `CREATE INDEX "IDX_f47af96878c586b3fbb6d9439c" ON "objekt" ("transferable") ` 19 | ); 20 | await db.query( 21 | `CREATE INDEX "IDX_cc0196669f13f5958a307824a2" ON "objekt" ("collection_id") ` 22 | ); 23 | await db.query( 24 | `CREATE TABLE "transfer" ("id" varchar(36) NOT NULL, "from" text NOT NULL, "to" text NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "token_id" text NOT NULL, "hash" text NOT NULL, "objekt_id" varchar, "collection_id" varchar(36), CONSTRAINT "PK_fd9ddbdd49a17afcbe014401295" PRIMARY KEY ("id"))` 25 | ); 26 | await db.query( 27 | `CREATE INDEX "IDX_be54ea276e0f665ffc38630fc0" ON "transfer" ("from") ` 28 | ); 29 | await db.query( 30 | `CREATE INDEX "IDX_4cbc37e8c3b47ded161f44c24f" ON "transfer" ("to") ` 31 | ); 32 | await db.query( 33 | `CREATE INDEX "IDX_98d4c0e33193fdd3edfc826c37" ON "transfer" ("objekt_id") ` 34 | ); 35 | await db.query( 36 | `CREATE INDEX "IDX_15a8d2966ae7e5e9b2ff47104f" ON "transfer" ("collection_id") ` 37 | ); 38 | await db.query( 39 | `CREATE TABLE "collection" ("id" varchar(36) NOT NULL, "contract" text NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL, "slug" text NOT NULL, "collection_id" text NOT NULL, "season" text NOT NULL, "member" text NOT NULL, "artist" text NOT NULL, "collection_no" text NOT NULL, "class" text NOT NULL, "thumbnail_image" text NOT NULL, "front_image" text NOT NULL, "back_image" text NOT NULL, "background_color" text NOT NULL, "text_color" text NOT NULL, "accent_color" text NOT NULL, "como_amount" integer NOT NULL, "on_offline" text NOT NULL, CONSTRAINT "PK_ad3f485bbc99d875491f44d7c85" PRIMARY KEY ("id"))` 40 | ); 41 | await db.query( 42 | `CREATE INDEX "IDX_e814aff6539600dfcc88af41fc" ON "collection" ("contract") ` 43 | ); 44 | await db.query( 45 | `CREATE INDEX "IDX_f2c977a66579d262693a8cdbcd" ON "collection" ("created_at") ` 46 | ); 47 | await db.query( 48 | `CREATE UNIQUE INDEX "IDX_75a6fd6eedd7fa7378de400b0a" ON "collection" ("slug") ` 49 | ); 50 | await db.query( 51 | `CREATE INDEX "IDX_81f585f60e03d2dc803d8a4945" ON "collection" ("season") ` 52 | ); 53 | await db.query( 54 | `CREATE INDEX "IDX_76242b6e82adf6f4ab4b388858" ON "collection" ("member") ` 55 | ); 56 | await db.query( 57 | `CREATE INDEX "IDX_6f89ec57ebbfd978e196751051" ON "collection" ("artist") ` 58 | ); 59 | await db.query( 60 | `CREATE INDEX "IDX_a8dbe2a49e54f73e2e7063dbb0" ON "collection" ("collection_no") ` 61 | ); 62 | await db.query( 63 | `CREATE INDEX "IDX_d01899107849250643b52f2324" ON "collection" ("class") ` 64 | ); 65 | await db.query( 66 | `CREATE INDEX "IDX_429351eac26f87942861266e48" ON "collection" ("on_offline") ` 67 | ); 68 | await db.query( 69 | `CREATE TABLE "como_balance" ("id" varchar(36) NOT NULL, "contract" text NOT NULL, "owner" text NOT NULL, "amount" numeric NOT NULL, CONSTRAINT "PK_965a782766b42e9c0cf627e9295" PRIMARY KEY ("id"))` 70 | ); 71 | await db.query( 72 | `CREATE INDEX "IDX_e955d648996ebf3ae54bfa4c40" ON "como_balance" ("contract") ` 73 | ); 74 | await db.query( 75 | `CREATE INDEX "IDX_625af50d78a8e51e688e96a331" ON "como_balance" ("owner") ` 76 | ); 77 | await db.query( 78 | `CREATE INDEX "IDX_d840d7bff36ad3e0d91ea8b680" ON "como_balance" ("amount") ` 79 | ); 80 | await db.query( 81 | `CREATE INDEX "IDX_0b05935e7f689d090d1a67a8cf" ON "como_balance" ("owner", "contract") ` 82 | ); 83 | await db.query( 84 | `CREATE TABLE "vote" ("id" varchar(36) NOT NULL, "from" text NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL, "contract" text NOT NULL, "poll_id" integer NOT NULL, "candidate_id" integer, "index" integer NOT NULL, "amount" numeric NOT NULL, CONSTRAINT "PK_2d5932d46afe39c8176f9d4be72" PRIMARY KEY ("id"))` 85 | ); 86 | await db.query( 87 | `CREATE INDEX "IDX_8ea4539f32b721cfed8cb4796c" ON "vote" ("from") ` 88 | ); 89 | await db.query( 90 | `CREATE INDEX "IDX_41065267c13533592a24836335" ON "vote" ("created_at") ` 91 | ); 92 | await db.query( 93 | `CREATE INDEX "IDX_4c098d306adb4722a962ec89ea" ON "vote" ("contract") ` 94 | ); 95 | await db.query( 96 | `CREATE INDEX "IDX_0d7459852150cf964af26adcf6" ON "vote" ("poll_id") ` 97 | ); 98 | await db.query( 99 | `CREATE INDEX "IDX_8056b2df70cd298ce447bc186f" ON "vote" ("candidate_id") ` 100 | ); 101 | await db.query( 102 | `CREATE INDEX "IDX_c630f7364ebb0c91d431679011" ON "vote" ("index") ` 103 | ); 104 | await db.query( 105 | `CREATE INDEX "IDX_701e95fc921b4ca38caa9a4a2c" ON "vote" ("amount") ` 106 | ); 107 | await db.query( 108 | `CREATE INDEX "IDX_b1bff3977e6bfbca8ea3747d50" ON "vote" ("contract", "poll_id", "index") ` 109 | ); 110 | await db.query( 111 | `ALTER TABLE "objekt" ADD CONSTRAINT "FK_cc0196669f13f5958a307824a2b" FOREIGN KEY ("collection_id") REFERENCES "collection"("id") ON DELETE NO ACTION ON UPDATE NO ACTION` 112 | ); 113 | await db.query( 114 | `ALTER TABLE "transfer" ADD CONSTRAINT "FK_98d4c0e33193fdd3edfc826c37f" FOREIGN KEY ("objekt_id") REFERENCES "objekt"("id") ON DELETE NO ACTION ON UPDATE NO ACTION` 115 | ); 116 | await db.query( 117 | `ALTER TABLE "transfer" ADD CONSTRAINT "FK_15a8d2966ae7e5e9b2ff47104f0" FOREIGN KEY ("collection_id") REFERENCES "collection"("id") ON DELETE NO ACTION ON UPDATE NO ACTION` 118 | ); 119 | } 120 | 121 | async down(db) { 122 | await db.query(`DROP TABLE "objekt"`); 123 | await db.query(`DROP INDEX "public"."IDX_d2ddf18405b46538e169ab03e8"`); 124 | await db.query(`DROP INDEX "public"."IDX_3d4c25bad83bb3fdae75fc0692"`); 125 | await db.query(`DROP INDEX "public"."IDX_19209bac5cab521e9327f74013"`); 126 | await db.query(`DROP INDEX "public"."IDX_f47af96878c586b3fbb6d9439c"`); 127 | await db.query(`DROP INDEX "public"."IDX_cc0196669f13f5958a307824a2"`); 128 | await db.query(`DROP TABLE "transfer"`); 129 | await db.query(`DROP INDEX "public"."IDX_be54ea276e0f665ffc38630fc0"`); 130 | await db.query(`DROP INDEX "public"."IDX_4cbc37e8c3b47ded161f44c24f"`); 131 | await db.query(`DROP INDEX "public"."IDX_98d4c0e33193fdd3edfc826c37"`); 132 | await db.query(`DROP INDEX "public"."IDX_15a8d2966ae7e5e9b2ff47104f"`); 133 | await db.query(`DROP TABLE "collection"`); 134 | await db.query(`DROP INDEX "public"."IDX_e814aff6539600dfcc88af41fc"`); 135 | await db.query(`DROP INDEX "public"."IDX_f2c977a66579d262693a8cdbcd"`); 136 | await db.query(`DROP INDEX "public"."IDX_75a6fd6eedd7fa7378de400b0a"`); 137 | await db.query(`DROP INDEX "public"."IDX_81f585f60e03d2dc803d8a4945"`); 138 | await db.query(`DROP INDEX "public"."IDX_76242b6e82adf6f4ab4b388858"`); 139 | await db.query(`DROP INDEX "public"."IDX_6f89ec57ebbfd978e196751051"`); 140 | await db.query(`DROP INDEX "public"."IDX_a8dbe2a49e54f73e2e7063dbb0"`); 141 | await db.query(`DROP INDEX "public"."IDX_d01899107849250643b52f2324"`); 142 | await db.query(`DROP INDEX "public"."IDX_429351eac26f87942861266e48"`); 143 | await db.query(`DROP TABLE "como_balance"`); 144 | await db.query(`DROP INDEX "public"."IDX_e955d648996ebf3ae54bfa4c40"`); 145 | await db.query(`DROP INDEX "public"."IDX_625af50d78a8e51e688e96a331"`); 146 | await db.query(`DROP INDEX "public"."IDX_d840d7bff36ad3e0d91ea8b680"`); 147 | await db.query(`DROP INDEX "public"."IDX_0b05935e7f689d090d1a67a8cf"`); 148 | await db.query(`DROP TABLE "vote"`); 149 | await db.query(`DROP INDEX "public"."IDX_8ea4539f32b721cfed8cb4796c"`); 150 | await db.query(`DROP INDEX "public"."IDX_41065267c13533592a24836335"`); 151 | await db.query(`DROP INDEX "public"."IDX_4c098d306adb4722a962ec89ea"`); 152 | await db.query(`DROP INDEX "public"."IDX_0d7459852150cf964af26adcf6"`); 153 | await db.query(`DROP INDEX "public"."IDX_8056b2df70cd298ce447bc186f"`); 154 | await db.query(`DROP INDEX "public"."IDX_c630f7364ebb0c91d431679011"`); 155 | await db.query(`DROP INDEX "public"."IDX_701e95fc921b4ca38caa9a4a2c"`); 156 | await db.query(`DROP INDEX "public"."IDX_b1bff3977e6bfbca8ea3747d50"`); 157 | await db.query( 158 | `ALTER TABLE "objekt" DROP CONSTRAINT "FK_cc0196669f13f5958a307824a2b"` 159 | ); 160 | await db.query( 161 | `ALTER TABLE "transfer" DROP CONSTRAINT "FK_98d4c0e33193fdd3edfc826c37f"` 162 | ); 163 | await db.query( 164 | `ALTER TABLE "transfer" DROP CONSTRAINT "FK_15a8d2966ae7e5e9b2ff47104f0"` 165 | ); 166 | } 167 | }; 168 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { processor, ProcessorContext } from "./processor"; 2 | import { 3 | ComoBalanceEvent, 4 | TransferabilityUpdate, 5 | VoteEvent, 6 | parseBlocks, 7 | } from "./parser"; 8 | import { MetadataV1, fetchMetadata } from "./cosmo"; 9 | import { Collection, ComoBalance, Objekt, Transfer, Vote } from "./model"; 10 | import { addr, chunk } from "./util"; 11 | import { TypeormDatabase, Store } from "@subsquid/typeorm-store"; 12 | import { randomUUID } from "crypto"; 13 | import { env } from "./env/processor"; 14 | import { Addresses } from "./constants"; 15 | 16 | const db = new TypeormDatabase({ supportHotBlocks: true }); 17 | 18 | processor.run(db, async (ctx) => { 19 | const { transfers, transferability, comoBalanceUpdates, votes } = parseBlocks( 20 | ctx.blocks 21 | ); 22 | 23 | if (env.ENABLE_OBJEKTS) { 24 | if (transfers.length > 0) { 25 | ctx.log.info(`Processing ${transfers.length} objekt transfers`); 26 | } 27 | 28 | // chunk everything into batches 29 | await chunk(transfers, env.COSMO_PARALLEL_COUNT, async (chunk) => { 30 | const transferBatch: Transfer[] = []; 31 | const collectionBatch = new Map(); 32 | const objektBatch = new Map(); 33 | 34 | const metadataBatch = await Promise.allSettled( 35 | chunk.map((e) => fetchMetadata(e.tokenId)) 36 | ); 37 | 38 | // iterate over each objekt metadata request 39 | for (let j = 0; j < metadataBatch.length; j++) { 40 | const request = metadataBatch[j]; 41 | const currentTransfer = chunk[j]; 42 | if (request.status === "rejected") { 43 | ctx.log.error( 44 | `Unable to fetch metadata for token ${currentTransfer.tokenId}` 45 | ); 46 | continue; 47 | } 48 | 49 | // handle collection 50 | const collection = await handleCollection( 51 | ctx, 52 | request.value, 53 | collectionBatch, 54 | currentTransfer 55 | ); 56 | collectionBatch.set(collection.slug, collection); 57 | 58 | // handle objekt 59 | const objekt = await handleObjekt( 60 | ctx, 61 | request.value, 62 | objektBatch, 63 | currentTransfer 64 | ); 65 | objekt.collection = collection; 66 | objektBatch.set(objekt.id, objekt); 67 | 68 | // handle transfer 69 | currentTransfer.objekt = objekt; 70 | currentTransfer.collection = collection; 71 | transferBatch.push(currentTransfer); 72 | } 73 | 74 | // upsert collections 75 | if (collectionBatch.size > 0) { 76 | await ctx.store.upsert(Array.from(collectionBatch.values())); 77 | } 78 | 79 | // upsert objekts 80 | if (objektBatch.size > 0) { 81 | await ctx.store.upsert(Array.from(objektBatch.values())); 82 | } 83 | 84 | // upsert transfers 85 | if (transferBatch.length > 0) { 86 | await ctx.store.upsert(transferBatch); 87 | } 88 | }); 89 | 90 | // process transferability updates separately from transfers 91 | if (transferability.length > 0) { 92 | ctx.log.info( 93 | `Handling ${transferability.length} transferability updates` 94 | ); 95 | await handleTransferabilityUpdates(ctx, transferability); 96 | } 97 | } 98 | 99 | if (env.ENABLE_GRAVITY) { 100 | const voteBatch: Vote[] = []; 101 | 102 | if (votes.length > 0) { 103 | ctx.log.info(`Processing ${votes.length} gravity votes`); 104 | } 105 | 106 | // handle vote creation 107 | for (let i = 0; i < votes.length; i++) { 108 | const vote = await handleVoteCreation(votes[i]); 109 | voteBatch.push(vote); 110 | } 111 | 112 | if (voteBatch.length > 0) { 113 | await ctx.store.upsert(voteBatch); 114 | } 115 | 116 | if (comoBalanceUpdates.length > 0) { 117 | ctx.log.info( 118 | `Processing ${comoBalanceUpdates.length} COMO balance updates` 119 | ); 120 | } 121 | 122 | // handle como balance updates 123 | await chunk(comoBalanceUpdates, 2000, async (chunk) => { 124 | const comoBalanceBatch = new Map(); 125 | for (let i = 0; i < chunk.length; i++) { 126 | const balances = await handleComoBalanceUpdate( 127 | ctx, 128 | comoBalanceBatch, 129 | chunk[i] 130 | ); 131 | 132 | balances.forEach((balance) => { 133 | comoBalanceBatch.set( 134 | balanceKey({ owner: balance.owner, tokenId: balance.tokenId }), 135 | balance 136 | ); 137 | }); 138 | } 139 | 140 | if (comoBalanceBatch.size > 0) { 141 | await ctx.store.upsert(Array.from(comoBalanceBatch.values())); 142 | } 143 | }); 144 | } 145 | }); 146 | 147 | /** 148 | * Create or update the collection row. 149 | */ 150 | async function handleCollection( 151 | ctx: ProcessorContext, 152 | metadata: MetadataV1, 153 | buffer: Map, 154 | transfer: Transfer 155 | ) { 156 | const slug = metadata.objekt.collectionId 157 | .toLowerCase() 158 | // replace diacritics 159 | .normalize("NFD") 160 | .replace(/[\u0300-\u036f]/g, "") 161 | // remove non-alphanumeric characters 162 | .replace(/[^\w\s-]/g, "") 163 | // replace spaces with hyphens 164 | .replace(/\s+/g, "-"); 165 | 166 | // fetch from db 167 | let collection = await ctx.store.get(Collection, { 168 | where: { 169 | slug: slug, 170 | }, 171 | }); 172 | 173 | // fetch out of buffer 174 | if (!collection) { 175 | collection = buffer.get(slug); 176 | } 177 | 178 | // create 179 | if (!collection) { 180 | collection = new Collection({ 181 | id: randomUUID(), 182 | contract: addr(metadata.objekt.tokenAddress), 183 | createdAt: new Date(transfer.timestamp), 184 | collectionId: metadata.objekt.collectionId, 185 | slug: slug, 186 | }); 187 | } 188 | 189 | // set and/or update metadata 190 | collection.season = metadata.objekt.season; 191 | collection.member = metadata.objekt.member; 192 | collection.artist = metadata.objekt.artists[0].toLowerCase(); 193 | collection.collectionNo = metadata.objekt.collectionNo; 194 | collection.class = metadata.objekt.class; 195 | collection.comoAmount = metadata.objekt.comoAmount; 196 | collection.onOffline = metadata.objekt.collectionNo.includes("Z") 197 | ? "online" 198 | : "offline"; 199 | collection.thumbnailImage = metadata.objekt.thumbnailImage; 200 | collection.frontImage = metadata.objekt.frontImage; 201 | collection.backImage = metadata.objekt.backImage; 202 | collection.backgroundColor = metadata.objekt.backgroundColor; 203 | collection.textColor = metadata.objekt.textColor; 204 | collection.accentColor = metadata.objekt.accentColor; 205 | 206 | return collection; 207 | } 208 | 209 | /** 210 | * Create or update the objekt row. 211 | */ 212 | async function handleObjekt( 213 | ctx: ProcessorContext, 214 | metadata: MetadataV1, 215 | buffer: Map, 216 | transfer: Transfer 217 | ) { 218 | // fetch out of buffer 219 | let objekt = buffer.get(transfer.tokenId); 220 | 221 | // fetch from db 222 | if (!objekt) { 223 | objekt = await ctx.store.get(Objekt, transfer.tokenId); 224 | } 225 | 226 | // if not new, update fields. skip transferable 227 | if (objekt) { 228 | objekt.receivedAt = new Date(transfer.timestamp); 229 | objekt.owner = addr(transfer.to); 230 | return objekt; 231 | } 232 | 233 | // otherwise create it 234 | if (!objekt) { 235 | objekt = new Objekt({ 236 | id: transfer.tokenId, 237 | mintedAt: new Date(transfer.timestamp), 238 | receivedAt: new Date(transfer.timestamp), 239 | owner: addr(transfer.to), 240 | serial: metadata.objekt.objektNo, 241 | transferable: metadata.objekt.transferable, 242 | }); 243 | } 244 | 245 | return objekt; 246 | } 247 | 248 | /** 249 | * Update a batch of transferability updates. 250 | */ 251 | async function handleTransferabilityUpdates( 252 | ctx: ProcessorContext, 253 | updates: TransferabilityUpdate[] 254 | ) { 255 | const batch = new Map(); 256 | for (const update of updates) { 257 | const objekt = await ctx.store.get(Objekt, update.tokenId); 258 | if (objekt) { 259 | objekt.transferable = update.transferable; 260 | batch.set(objekt.id, objekt); 261 | } else { 262 | ctx.log.error( 263 | `Unable to find objekt ${update.tokenId} for transferability update` 264 | ); 265 | } 266 | } 267 | if (batch.size > 0) { 268 | await ctx.store.upsert(Array.from(batch.values())); 269 | } 270 | } 271 | 272 | const EXCLUDE = Object.values(Addresses); 273 | 274 | /** 275 | * Update como balance. 276 | */ 277 | async function handleComoBalanceUpdate( 278 | ctx: ProcessorContext, 279 | buffer: Map, 280 | event: ComoBalanceEvent 281 | ) { 282 | const toUpdate: ComoBalance[] = []; 283 | 284 | if (EXCLUDE.includes(event.from) === false) { 285 | const from = await getBalance(ctx, buffer, event.from, event.tokenId); 286 | 287 | from.amount -= event.value; 288 | toUpdate.push(from); 289 | } 290 | 291 | if (EXCLUDE.includes(event.to) === false) { 292 | const to = await getBalance(ctx, buffer, event.to, event.tokenId); 293 | 294 | to.amount += event.value; 295 | toUpdate.push(to); 296 | } 297 | 298 | return toUpdate; 299 | } 300 | 301 | /** 302 | * For the sake of not being able to mess this up. 303 | */ 304 | function balanceKey({ owner, tokenId }: { owner: string; tokenId: number }) { 305 | return `${owner}-${tokenId}`; 306 | } 307 | 308 | /** 309 | * Fetch a como balance from the buffer, db or create a new one. 310 | */ 311 | async function getBalance( 312 | ctx: ProcessorContext, 313 | buffer: Map, 314 | owner: string, 315 | tokenId: number 316 | ) { 317 | let balance = buffer.get(balanceKey({ owner, tokenId })); 318 | 319 | // fetch from db 320 | if (!balance) { 321 | balance = await ctx.store.get(ComoBalance, { 322 | where: { owner, tokenId }, 323 | }); 324 | } 325 | 326 | // create 327 | if (!balance) { 328 | balance = new ComoBalance({ 329 | id: randomUUID(), 330 | tokenId: tokenId, 331 | owner: owner, 332 | amount: BigInt(0), 333 | }); 334 | } 335 | 336 | return balance; 337 | } 338 | 339 | /** 340 | * Create a new vote row. 341 | */ 342 | async function handleVoteCreation(event: VoteEvent) { 343 | return new Vote({ 344 | id: randomUUID(), 345 | from: event.from, 346 | createdAt: new Date(event.timestamp), 347 | contract: event.contract, 348 | pollId: event.pollId, 349 | amount: event.tokenAmount, 350 | }); 351 | } 352 | -------------------------------------------------------------------------------- /src/abi/como.ts: -------------------------------------------------------------------------------- 1 | import * as p from "@subsquid/evm-codec"; 2 | import { event, fun, viewFun, indexed, ContractBase } from "@subsquid/evm-abi"; 3 | import type { 4 | EventParams as EParams, 5 | FunctionArguments, 6 | FunctionReturn, 7 | } from "@subsquid/evm-abi"; 8 | 9 | export const events = { 10 | ApprovalForAll: event( 11 | "0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31", 12 | "ApprovalForAll(address,address,bool)", 13 | { 14 | account: indexed(p.address), 15 | operator: indexed(p.address), 16 | approved: p.bool, 17 | } 18 | ), 19 | Initialized: event( 20 | "0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2", 21 | "Initialized(uint64)", 22 | { version: p.uint64 } 23 | ), 24 | RoleAdminChanged: event( 25 | "0xbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff", 26 | "RoleAdminChanged(bytes32,bytes32,bytes32)", 27 | { 28 | role: indexed(p.bytes32), 29 | previousAdminRole: indexed(p.bytes32), 30 | newAdminRole: indexed(p.bytes32), 31 | } 32 | ), 33 | RoleGranted: event( 34 | "0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d", 35 | "RoleGranted(bytes32,address,address)", 36 | { 37 | role: indexed(p.bytes32), 38 | account: indexed(p.address), 39 | sender: indexed(p.address), 40 | } 41 | ), 42 | RoleRevoked: event( 43 | "0xf6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b", 44 | "RoleRevoked(bytes32,address,address)", 45 | { 46 | role: indexed(p.bytes32), 47 | account: indexed(p.address), 48 | sender: indexed(p.address), 49 | } 50 | ), 51 | TransferBatch: event( 52 | "0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb", 53 | "TransferBatch(address,address,address,uint256[],uint256[])", 54 | { 55 | operator: indexed(p.address), 56 | from: indexed(p.address), 57 | to: indexed(p.address), 58 | ids: p.array(p.uint256), 59 | values: p.array(p.uint256), 60 | } 61 | ), 62 | TransferSingle: event( 63 | "0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62", 64 | "TransferSingle(address,address,address,uint256,uint256)", 65 | { 66 | operator: indexed(p.address), 67 | from: indexed(p.address), 68 | to: indexed(p.address), 69 | id: p.uint256, 70 | value: p.uint256, 71 | } 72 | ), 73 | URI: event( 74 | "0x6bb7ff708619ba0610cba295a58592e0451dee2622938c8755667688daf3529b", 75 | "URI(string,uint256)", 76 | { value: p.string, id: indexed(p.uint256) } 77 | ), 78 | Upgraded: event( 79 | "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", 80 | "Upgraded(address)", 81 | { implementation: indexed(p.address) } 82 | ), 83 | }; 84 | 85 | export const functions = { 86 | DEFAULT_ADMIN_ROLE: viewFun( 87 | "0xa217fddf", 88 | "DEFAULT_ADMIN_ROLE()", 89 | {}, 90 | p.bytes32 91 | ), 92 | ERROR_ALREADY_WHITELISTED: viewFun( 93 | "0x4b691084", 94 | "ERROR_ALREADY_WHITELISTED()", 95 | {}, 96 | p.string 97 | ), 98 | ERROR_TO_ADDRESS_NOT_IN_WHITELIST: viewFun( 99 | "0xc120e7ca", 100 | "ERROR_TO_ADDRESS_NOT_IN_WHITELIST()", 101 | {}, 102 | p.string 103 | ), 104 | ERROR_WHITELIST_ALREADY_REMOVED: viewFun( 105 | "0xa5813f57", 106 | "ERROR_WHITELIST_ALREADY_REMOVED()", 107 | {}, 108 | p.string 109 | ), 110 | ERROR_WHITELIST_NOT_INITIALIZED: viewFun( 111 | "0xf1af1e75", 112 | "ERROR_WHITELIST_NOT_INITIALIZED()", 113 | {}, 114 | p.string 115 | ), 116 | MANAGER_ROLE: viewFun("0xec87621c", "MANAGER_ROLE()", {}, p.bytes32), 117 | MINTER_ROLE: viewFun("0xd5391393", "MINTER_ROLE()", {}, p.bytes32), 118 | TRANSFERER_ROLE: viewFun("0x0ade7dc1", "TRANSFERER_ROLE()", {}, p.bytes32), 119 | UPGRADE_INTERFACE_VERSION: viewFun( 120 | "0xad3cb1cc", 121 | "UPGRADE_INTERFACE_VERSION()", 122 | {}, 123 | p.string 124 | ), 125 | addWhitelist: fun("0x3e0b892a", "addWhitelist(uint256,address)", { 126 | id: p.uint256, 127 | addr: p.address, 128 | }), 129 | balanceOf: viewFun( 130 | "0x00fdd58e", 131 | "balanceOf(address,uint256)", 132 | { account: p.address, id: p.uint256 }, 133 | p.uint256 134 | ), 135 | balanceOfBatch: viewFun( 136 | "0x4e1273f4", 137 | "balanceOfBatch(address[],uint256[])", 138 | { accounts: p.array(p.address), ids: p.array(p.uint256) }, 139 | p.array(p.uint256) 140 | ), 141 | burn: fun("0xf5298aca", "burn(address,uint256,uint256)", { 142 | account: p.address, 143 | id: p.uint256, 144 | value: p.uint256, 145 | }), 146 | getRoleAdmin: viewFun( 147 | "0x248a9ca3", 148 | "getRoleAdmin(bytes32)", 149 | { role: p.bytes32 }, 150 | p.bytes32 151 | ), 152 | grantRole: fun("0x2f2ff15d", "grantRole(bytes32,address)", { 153 | role: p.bytes32, 154 | account: p.address, 155 | }), 156 | hasRole: viewFun( 157 | "0x91d14854", 158 | "hasRole(bytes32,address)", 159 | { role: p.bytes32, account: p.address }, 160 | p.bool 161 | ), 162 | initialize: fun("0xf62d1888", "initialize(string)", { uri: p.string }), 163 | isApprovedForAll: viewFun( 164 | "0xe985e9c5", 165 | "isApprovedForAll(address,address)", 166 | { account: p.address, operator: p.address }, 167 | p.bool 168 | ), 169 | mint: fun("0x731133e9", "mint(address,uint256,uint256,bytes)", { 170 | to: p.address, 171 | id: p.uint256, 172 | amount: p.uint256, 173 | data: p.bytes, 174 | }), 175 | mintBatch: fun("0x1f7fdffa", "mintBatch(address,uint256[],uint256[],bytes)", { 176 | to: p.address, 177 | ids: p.array(p.uint256), 178 | amounts: p.array(p.uint256), 179 | data: p.bytes, 180 | }), 181 | proxiableUUID: viewFun("0x52d1902d", "proxiableUUID()", {}, p.bytes32), 182 | removeWhitelist: fun("0x94008a6e", "removeWhitelist(uint256,address)", { 183 | id: p.uint256, 184 | addr: p.address, 185 | }), 186 | renounceRole: fun("0x36568abe", "renounceRole(bytes32,address)", { 187 | role: p.bytes32, 188 | callerConfirmation: p.address, 189 | }), 190 | revokeRole: fun("0xd547741f", "revokeRole(bytes32,address)", { 191 | role: p.bytes32, 192 | account: p.address, 193 | }), 194 | safeBatchTransferFrom: fun( 195 | "0x2eb2c2d6", 196 | "safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)", 197 | { 198 | from: p.address, 199 | to: p.address, 200 | ids: p.array(p.uint256), 201 | values: p.array(p.uint256), 202 | data: p.bytes, 203 | } 204 | ), 205 | safeTransferFrom: fun( 206 | "0xf242432a", 207 | "safeTransferFrom(address,address,uint256,uint256,bytes)", 208 | { 209 | from: p.address, 210 | to: p.address, 211 | id: p.uint256, 212 | value: p.uint256, 213 | data: p.bytes, 214 | } 215 | ), 216 | setApprovalForAll: fun("0xa22cb465", "setApprovalForAll(address,bool)", { 217 | operator: p.address, 218 | approved: p.bool, 219 | }), 220 | setURI: fun("0x02fe5305", "setURI(string)", { uri: p.string }), 221 | setWhitelistCheck: fun("0xfcbb42ef", "setWhitelistCheck(uint256,bool)", { 222 | id: p.uint256, 223 | isWhitelistCheckEnabled: p.bool, 224 | }), 225 | supportsInterface: viewFun( 226 | "0x01ffc9a7", 227 | "supportsInterface(bytes4)", 228 | { interfaceId: p.bytes4 }, 229 | p.bool 230 | ), 231 | upgradeToAndCall: fun("0x4f1ef286", "upgradeToAndCall(address,bytes)", { 232 | newImplementation: p.address, 233 | data: p.bytes, 234 | }), 235 | uri: viewFun("0x0e89341c", "uri(uint256)", { id: p.uint256 }, p.string), 236 | whitelists: viewFun( 237 | "0xfe4d5add", 238 | "whitelists(uint256)", 239 | { _0: p.uint256 }, 240 | p.bool 241 | ), 242 | }; 243 | 244 | export class Contract extends ContractBase { 245 | DEFAULT_ADMIN_ROLE() { 246 | return this.eth_call(functions.DEFAULT_ADMIN_ROLE, {}); 247 | } 248 | 249 | ERROR_ALREADY_WHITELISTED() { 250 | return this.eth_call(functions.ERROR_ALREADY_WHITELISTED, {}); 251 | } 252 | 253 | ERROR_TO_ADDRESS_NOT_IN_WHITELIST() { 254 | return this.eth_call(functions.ERROR_TO_ADDRESS_NOT_IN_WHITELIST, {}); 255 | } 256 | 257 | ERROR_WHITELIST_ALREADY_REMOVED() { 258 | return this.eth_call(functions.ERROR_WHITELIST_ALREADY_REMOVED, {}); 259 | } 260 | 261 | ERROR_WHITELIST_NOT_INITIALIZED() { 262 | return this.eth_call(functions.ERROR_WHITELIST_NOT_INITIALIZED, {}); 263 | } 264 | 265 | MANAGER_ROLE() { 266 | return this.eth_call(functions.MANAGER_ROLE, {}); 267 | } 268 | 269 | MINTER_ROLE() { 270 | return this.eth_call(functions.MINTER_ROLE, {}); 271 | } 272 | 273 | TRANSFERER_ROLE() { 274 | return this.eth_call(functions.TRANSFERER_ROLE, {}); 275 | } 276 | 277 | UPGRADE_INTERFACE_VERSION() { 278 | return this.eth_call(functions.UPGRADE_INTERFACE_VERSION, {}); 279 | } 280 | 281 | balanceOf(account: BalanceOfParams["account"], id: BalanceOfParams["id"]) { 282 | return this.eth_call(functions.balanceOf, { account, id }); 283 | } 284 | 285 | balanceOfBatch( 286 | accounts: BalanceOfBatchParams["accounts"], 287 | ids: BalanceOfBatchParams["ids"] 288 | ) { 289 | return this.eth_call(functions.balanceOfBatch, { accounts, ids }); 290 | } 291 | 292 | getRoleAdmin(role: GetRoleAdminParams["role"]) { 293 | return this.eth_call(functions.getRoleAdmin, { role }); 294 | } 295 | 296 | hasRole(role: HasRoleParams["role"], account: HasRoleParams["account"]) { 297 | return this.eth_call(functions.hasRole, { role, account }); 298 | } 299 | 300 | isApprovedForAll( 301 | account: IsApprovedForAllParams["account"], 302 | operator: IsApprovedForAllParams["operator"] 303 | ) { 304 | return this.eth_call(functions.isApprovedForAll, { account, operator }); 305 | } 306 | 307 | proxiableUUID() { 308 | return this.eth_call(functions.proxiableUUID, {}); 309 | } 310 | 311 | supportsInterface(interfaceId: SupportsInterfaceParams["interfaceId"]) { 312 | return this.eth_call(functions.supportsInterface, { interfaceId }); 313 | } 314 | 315 | uri(id: UriParams["id"]) { 316 | return this.eth_call(functions.uri, { id }); 317 | } 318 | 319 | whitelists(_0: WhitelistsParams["_0"]) { 320 | return this.eth_call(functions.whitelists, { _0 }); 321 | } 322 | } 323 | 324 | /// Event types 325 | export type ApprovalForAllEventArgs = EParams; 326 | export type InitializedEventArgs = EParams; 327 | export type RoleAdminChangedEventArgs = EParams; 328 | export type RoleGrantedEventArgs = EParams; 329 | export type RoleRevokedEventArgs = EParams; 330 | export type TransferBatchEventArgs = EParams; 331 | export type TransferSingleEventArgs = EParams; 332 | export type URIEventArgs = EParams; 333 | export type UpgradedEventArgs = EParams; 334 | 335 | /// Function types 336 | export type DEFAULT_ADMIN_ROLEParams = FunctionArguments< 337 | typeof functions.DEFAULT_ADMIN_ROLE 338 | >; 339 | export type DEFAULT_ADMIN_ROLEReturn = FunctionReturn< 340 | typeof functions.DEFAULT_ADMIN_ROLE 341 | >; 342 | 343 | export type ERROR_ALREADY_WHITELISTEDParams = FunctionArguments< 344 | typeof functions.ERROR_ALREADY_WHITELISTED 345 | >; 346 | export type ERROR_ALREADY_WHITELISTEDReturn = FunctionReturn< 347 | typeof functions.ERROR_ALREADY_WHITELISTED 348 | >; 349 | 350 | export type ERROR_TO_ADDRESS_NOT_IN_WHITELISTParams = FunctionArguments< 351 | typeof functions.ERROR_TO_ADDRESS_NOT_IN_WHITELIST 352 | >; 353 | export type ERROR_TO_ADDRESS_NOT_IN_WHITELISTReturn = FunctionReturn< 354 | typeof functions.ERROR_TO_ADDRESS_NOT_IN_WHITELIST 355 | >; 356 | 357 | export type ERROR_WHITELIST_ALREADY_REMOVEDParams = FunctionArguments< 358 | typeof functions.ERROR_WHITELIST_ALREADY_REMOVED 359 | >; 360 | export type ERROR_WHITELIST_ALREADY_REMOVEDReturn = FunctionReturn< 361 | typeof functions.ERROR_WHITELIST_ALREADY_REMOVED 362 | >; 363 | 364 | export type ERROR_WHITELIST_NOT_INITIALIZEDParams = FunctionArguments< 365 | typeof functions.ERROR_WHITELIST_NOT_INITIALIZED 366 | >; 367 | export type ERROR_WHITELIST_NOT_INITIALIZEDReturn = FunctionReturn< 368 | typeof functions.ERROR_WHITELIST_NOT_INITIALIZED 369 | >; 370 | 371 | export type MANAGER_ROLEParams = FunctionArguments< 372 | typeof functions.MANAGER_ROLE 373 | >; 374 | export type MANAGER_ROLEReturn = FunctionReturn; 375 | 376 | export type MINTER_ROLEParams = FunctionArguments; 377 | export type MINTER_ROLEReturn = FunctionReturn; 378 | 379 | export type TRANSFERER_ROLEParams = FunctionArguments< 380 | typeof functions.TRANSFERER_ROLE 381 | >; 382 | export type TRANSFERER_ROLEReturn = FunctionReturn< 383 | typeof functions.TRANSFERER_ROLE 384 | >; 385 | 386 | export type UPGRADE_INTERFACE_VERSIONParams = FunctionArguments< 387 | typeof functions.UPGRADE_INTERFACE_VERSION 388 | >; 389 | export type UPGRADE_INTERFACE_VERSIONReturn = FunctionReturn< 390 | typeof functions.UPGRADE_INTERFACE_VERSION 391 | >; 392 | 393 | export type AddWhitelistParams = FunctionArguments< 394 | typeof functions.addWhitelist 395 | >; 396 | export type AddWhitelistReturn = FunctionReturn; 397 | 398 | export type BalanceOfParams = FunctionArguments; 399 | export type BalanceOfReturn = FunctionReturn; 400 | 401 | export type BalanceOfBatchParams = FunctionArguments< 402 | typeof functions.balanceOfBatch 403 | >; 404 | export type BalanceOfBatchReturn = FunctionReturn< 405 | typeof functions.balanceOfBatch 406 | >; 407 | 408 | export type BurnParams = FunctionArguments; 409 | export type BurnReturn = FunctionReturn; 410 | 411 | export type GetRoleAdminParams = FunctionArguments< 412 | typeof functions.getRoleAdmin 413 | >; 414 | export type GetRoleAdminReturn = FunctionReturn; 415 | 416 | export type GrantRoleParams = FunctionArguments; 417 | export type GrantRoleReturn = FunctionReturn; 418 | 419 | export type HasRoleParams = FunctionArguments; 420 | export type HasRoleReturn = FunctionReturn; 421 | 422 | export type InitializeParams = FunctionArguments; 423 | export type InitializeReturn = FunctionReturn; 424 | 425 | export type IsApprovedForAllParams = FunctionArguments< 426 | typeof functions.isApprovedForAll 427 | >; 428 | export type IsApprovedForAllReturn = FunctionReturn< 429 | typeof functions.isApprovedForAll 430 | >; 431 | 432 | export type MintParams = FunctionArguments; 433 | export type MintReturn = FunctionReturn; 434 | 435 | export type MintBatchParams = FunctionArguments; 436 | export type MintBatchReturn = FunctionReturn; 437 | 438 | export type ProxiableUUIDParams = FunctionArguments< 439 | typeof functions.proxiableUUID 440 | >; 441 | export type ProxiableUUIDReturn = FunctionReturn< 442 | typeof functions.proxiableUUID 443 | >; 444 | 445 | export type RemoveWhitelistParams = FunctionArguments< 446 | typeof functions.removeWhitelist 447 | >; 448 | export type RemoveWhitelistReturn = FunctionReturn< 449 | typeof functions.removeWhitelist 450 | >; 451 | 452 | export type RenounceRoleParams = FunctionArguments< 453 | typeof functions.renounceRole 454 | >; 455 | export type RenounceRoleReturn = FunctionReturn; 456 | 457 | export type RevokeRoleParams = FunctionArguments; 458 | export type RevokeRoleReturn = FunctionReturn; 459 | 460 | export type SafeBatchTransferFromParams = FunctionArguments< 461 | typeof functions.safeBatchTransferFrom 462 | >; 463 | export type SafeBatchTransferFromReturn = FunctionReturn< 464 | typeof functions.safeBatchTransferFrom 465 | >; 466 | 467 | export type SafeTransferFromParams = FunctionArguments< 468 | typeof functions.safeTransferFrom 469 | >; 470 | export type SafeTransferFromReturn = FunctionReturn< 471 | typeof functions.safeTransferFrom 472 | >; 473 | 474 | export type SetApprovalForAllParams = FunctionArguments< 475 | typeof functions.setApprovalForAll 476 | >; 477 | export type SetApprovalForAllReturn = FunctionReturn< 478 | typeof functions.setApprovalForAll 479 | >; 480 | 481 | export type SetURIParams = FunctionArguments; 482 | export type SetURIReturn = FunctionReturn; 483 | 484 | export type SetWhitelistCheckParams = FunctionArguments< 485 | typeof functions.setWhitelistCheck 486 | >; 487 | export type SetWhitelistCheckReturn = FunctionReturn< 488 | typeof functions.setWhitelistCheck 489 | >; 490 | 491 | export type SupportsInterfaceParams = FunctionArguments< 492 | typeof functions.supportsInterface 493 | >; 494 | export type SupportsInterfaceReturn = FunctionReturn< 495 | typeof functions.supportsInterface 496 | >; 497 | 498 | export type UpgradeToAndCallParams = FunctionArguments< 499 | typeof functions.upgradeToAndCall 500 | >; 501 | export type UpgradeToAndCallReturn = FunctionReturn< 502 | typeof functions.upgradeToAndCall 503 | >; 504 | 505 | export type UriParams = FunctionArguments; 506 | export type UriReturn = FunctionReturn; 507 | 508 | export type WhitelistsParams = FunctionArguments; 509 | export type WhitelistsReturn = FunctionReturn; 510 | -------------------------------------------------------------------------------- /src/abi/objekt.ts: -------------------------------------------------------------------------------- 1 | import * as p from "@subsquid/evm-codec"; 2 | import { event, fun, viewFun, indexed, ContractBase } from "@subsquid/evm-abi"; 3 | import type { 4 | EventParams as EParams, 5 | FunctionArguments, 6 | FunctionReturn, 7 | } from "@subsquid/evm-abi"; 8 | 9 | export const events = { 10 | Approval: event( 11 | "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", 12 | "Approval(address,address,uint256)", 13 | { 14 | owner: indexed(p.address), 15 | approved: indexed(p.address), 16 | tokenId: indexed(p.uint256), 17 | } 18 | ), 19 | ApprovalForAll: event( 20 | "0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31", 21 | "ApprovalForAll(address,address,bool)", 22 | { 23 | owner: indexed(p.address), 24 | operator: indexed(p.address), 25 | approved: p.bool, 26 | } 27 | ), 28 | Initialized: event( 29 | "0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2", 30 | "Initialized(uint64)", 31 | { version: p.uint64 } 32 | ), 33 | RoleAdminChanged: event( 34 | "0xbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff", 35 | "RoleAdminChanged(bytes32,bytes32,bytes32)", 36 | { 37 | role: indexed(p.bytes32), 38 | previousAdminRole: indexed(p.bytes32), 39 | newAdminRole: indexed(p.bytes32), 40 | } 41 | ), 42 | RoleGranted: event( 43 | "0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d", 44 | "RoleGranted(bytes32,address,address)", 45 | { 46 | role: indexed(p.bytes32), 47 | account: indexed(p.address), 48 | sender: indexed(p.address), 49 | } 50 | ), 51 | RoleRevoked: event( 52 | "0xf6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b", 53 | "RoleRevoked(bytes32,address,address)", 54 | { 55 | role: indexed(p.bytes32), 56 | account: indexed(p.address), 57 | sender: indexed(p.address), 58 | } 59 | ), 60 | Transfer: event( 61 | "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", 62 | "Transfer(address,address,uint256)", 63 | { 64 | from: indexed(p.address), 65 | to: indexed(p.address), 66 | tokenId: indexed(p.uint256), 67 | } 68 | ), 69 | Upgraded: event( 70 | "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", 71 | "Upgraded(address)", 72 | { implementation: indexed(p.address) } 73 | ), 74 | }; 75 | 76 | export const functions = { 77 | DEFAULT_ADMIN_ROLE: viewFun( 78 | "0xa217fddf", 79 | "DEFAULT_ADMIN_ROLE()", 80 | {}, 81 | p.bytes32 82 | ), 83 | ERROR_BLACKLISTED_MSG_SENDER: viewFun( 84 | "0x1111d78f", 85 | "ERROR_BLACKLISTED_MSG_SENDER()", 86 | {}, 87 | p.string 88 | ), 89 | ERROR_CANNOT_TRANSFER_EXTERNALLY: viewFun( 90 | "0xf304e990", 91 | "ERROR_CANNOT_TRANSFER_EXTERNALLY()", 92 | {}, 93 | p.string 94 | ), 95 | ERROR_INVALID_TOKEN_ID_RANGE: viewFun( 96 | "0xeab6fab3", 97 | "ERROR_INVALID_TOKEN_ID_RANGE()", 98 | {}, 99 | p.string 100 | ), 101 | ERROR_NON_TRANSFERABLE_OBJEKT: viewFun( 102 | "0x8a2969c8", 103 | "ERROR_NON_TRANSFERABLE_OBJEKT()", 104 | {}, 105 | p.string 106 | ), 107 | ERROR_NOT_ADMIN: viewFun("0x944565e2", "ERROR_NOT_ADMIN()", {}, p.string), 108 | ERROR_NOT_AUTHORIZED_APPROVAL: viewFun( 109 | "0xd5a40857", 110 | "ERROR_NOT_AUTHORIZED_APPROVAL()", 111 | {}, 112 | p.string 113 | ), 114 | ERROR_NOT_MINTER: viewFun("0x459c035e", "ERROR_NOT_MINTER()", {}, p.string), 115 | ERROR_ONLY_OWNER: viewFun("0x41f1162d", "ERROR_ONLY_OWNER()", {}, p.string), 116 | ERROR_SENDER_NOT_ADMIN: viewFun( 117 | "0x1abd34e2", 118 | "ERROR_SENDER_NOT_ADMIN()", 119 | {}, 120 | p.string 121 | ), 122 | MINTER_ROLE: viewFun("0xd5391393", "MINTER_ROLE()", {}, p.bytes32), 123 | OPERATOR_ROLE: viewFun("0xf5b541a6", "OPERATOR_ROLE()", {}, p.bytes32), 124 | UPGRADE_INTERFACE_VERSION: viewFun( 125 | "0xad3cb1cc", 126 | "UPGRADE_INTERFACE_VERSION()", 127 | {}, 128 | p.string 129 | ), 130 | approvalWhitelists: viewFun( 131 | "0x419e583d", 132 | "approvalWhitelists(address)", 133 | { _0: p.address }, 134 | p.bool 135 | ), 136 | approve: fun("0x095ea7b3", "approve(address,uint256)", { 137 | to: p.address, 138 | tokenId: p.uint256, 139 | }), 140 | balanceOf: viewFun( 141 | "0x70a08231", 142 | "balanceOf(address)", 143 | { owner: p.address }, 144 | p.uint256 145 | ), 146 | batchUpdateObjektTransferability: fun( 147 | "0xbf62c2e8", 148 | "batchUpdateObjektTransferability(uint256[],bool)", 149 | { tokenIds: p.array(p.uint256), transferable: p.bool } 150 | ), 151 | blacklists: viewFun( 152 | "0x16c02129", 153 | "blacklists(address)", 154 | { _0: p.address }, 155 | p.bool 156 | ), 157 | burn: fun("0x42966c68", "burn(uint256)", { tokenId: p.uint256 }), 158 | getApproved: viewFun( 159 | "0x081812fc", 160 | "getApproved(uint256)", 161 | { tokenId: p.uint256 }, 162 | p.address 163 | ), 164 | getRoleAdmin: viewFun( 165 | "0x248a9ca3", 166 | "getRoleAdmin(bytes32)", 167 | { role: p.bytes32 }, 168 | p.bytes32 169 | ), 170 | grantRole: fun("0x2f2ff15d", "grantRole(bytes32,address)", { 171 | role: p.bytes32, 172 | account: p.address, 173 | }), 174 | hasRole: viewFun( 175 | "0x91d14854", 176 | "hasRole(bytes32,address)", 177 | { role: p.bytes32, account: p.address }, 178 | p.bool 179 | ), 180 | initialize: fun("0xf62d1888", "initialize(string)", { baseURI_: p.string }), 181 | isApprovedForAll: viewFun( 182 | "0xe985e9c5", 183 | "isApprovedForAll(address,address)", 184 | { owner: p.address, operator: p.address }, 185 | p.bool 186 | ), 187 | isObjektTransferable: viewFun( 188 | "0xb2c03a50", 189 | "isObjektTransferable(uint256)", 190 | { _0: p.uint256 }, 191 | p.bool 192 | ), 193 | mint: fun("0xd1a1beb4", "mint(address,uint256,bool)", { 194 | to: p.address, 195 | tokenId: p.uint256, 196 | transferable: p.bool, 197 | }), 198 | mintBatch: fun("0x71c87f29", "mintBatch(address,uint256,uint256,bool)", { 199 | to: p.address, 200 | startTokenId: p.uint256, 201 | endTokenId: p.uint256, 202 | transferable: p.bool, 203 | }), 204 | name: viewFun("0x06fdde03", "name()", {}, p.string), 205 | ownerOf: viewFun( 206 | "0x6352211e", 207 | "ownerOf(uint256)", 208 | { tokenId: p.uint256 }, 209 | p.address 210 | ), 211 | proxiableUUID: viewFun("0x52d1902d", "proxiableUUID()", {}, p.bytes32), 212 | renounceRole: fun("0x36568abe", "renounceRole(bytes32,address)", { 213 | role: p.bytes32, 214 | callerConfirmation: p.address, 215 | }), 216 | revokeRole: fun("0xd547741f", "revokeRole(bytes32,address)", { 217 | role: p.bytes32, 218 | account: p.address, 219 | }), 220 | "safeTransferFrom(address,address,uint256)": fun( 221 | "0x42842e0e", 222 | "safeTransferFrom(address,address,uint256)", 223 | { from: p.address, to: p.address, tokenId: p.uint256 } 224 | ), 225 | "safeTransferFrom(address,address,uint256,bytes)": fun( 226 | "0xb88d4fde", 227 | "safeTransferFrom(address,address,uint256,bytes)", 228 | { from: p.address, to: p.address, tokenId: p.uint256, data: p.bytes } 229 | ), 230 | setApprovalForAll: fun("0xa22cb465", "setApprovalForAll(address,bool)", { 231 | operator: p.address, 232 | approved: p.bool, 233 | }), 234 | setApprovalWhitelist: fun( 235 | "0xca0429e0", 236 | "setApprovalWhitelist(address,bool)", 237 | { addr: p.address, isWhitelisted: p.bool } 238 | ), 239 | setBaseURI: fun("0x55f804b3", "setBaseURI(string)", { 240 | _newBaseURI: p.string, 241 | }), 242 | setBlacklist: fun("0x153b0d1e", "setBlacklist(address,bool)", { 243 | addr: p.address, 244 | isBlacklisted: p.bool, 245 | }), 246 | supportsInterface: viewFun( 247 | "0x01ffc9a7", 248 | "supportsInterface(bytes4)", 249 | { interfaceId: p.bytes4 }, 250 | p.bool 251 | ), 252 | symbol: viewFun("0x95d89b41", "symbol()", {}, p.string), 253 | tokenByIndex: viewFun( 254 | "0x4f6ccce7", 255 | "tokenByIndex(uint256)", 256 | { index: p.uint256 }, 257 | p.uint256 258 | ), 259 | tokenOfOwnerByIndex: viewFun( 260 | "0x2f745c59", 261 | "tokenOfOwnerByIndex(address,uint256)", 262 | { owner: p.address, index: p.uint256 }, 263 | p.uint256 264 | ), 265 | tokenURI: viewFun( 266 | "0xc87b56dd", 267 | "tokenURI(uint256)", 268 | { tokenId: p.uint256 }, 269 | p.string 270 | ), 271 | totalSupply: viewFun("0x18160ddd", "totalSupply()", {}, p.uint256), 272 | transferFrom: fun("0x23b872dd", "transferFrom(address,address,uint256)", { 273 | from: p.address, 274 | to: p.address, 275 | tokenId: p.uint256, 276 | }), 277 | upgradeToAndCall: fun("0x4f1ef286", "upgradeToAndCall(address,bytes)", { 278 | newImplementation: p.address, 279 | data: p.bytes, 280 | }), 281 | }; 282 | 283 | export class Contract extends ContractBase { 284 | DEFAULT_ADMIN_ROLE() { 285 | return this.eth_call(functions.DEFAULT_ADMIN_ROLE, {}); 286 | } 287 | 288 | ERROR_BLACKLISTED_MSG_SENDER() { 289 | return this.eth_call(functions.ERROR_BLACKLISTED_MSG_SENDER, {}); 290 | } 291 | 292 | ERROR_CANNOT_TRANSFER_EXTERNALLY() { 293 | return this.eth_call(functions.ERROR_CANNOT_TRANSFER_EXTERNALLY, {}); 294 | } 295 | 296 | ERROR_INVALID_TOKEN_ID_RANGE() { 297 | return this.eth_call(functions.ERROR_INVALID_TOKEN_ID_RANGE, {}); 298 | } 299 | 300 | ERROR_NON_TRANSFERABLE_OBJEKT() { 301 | return this.eth_call(functions.ERROR_NON_TRANSFERABLE_OBJEKT, {}); 302 | } 303 | 304 | ERROR_NOT_ADMIN() { 305 | return this.eth_call(functions.ERROR_NOT_ADMIN, {}); 306 | } 307 | 308 | ERROR_NOT_AUTHORIZED_APPROVAL() { 309 | return this.eth_call(functions.ERROR_NOT_AUTHORIZED_APPROVAL, {}); 310 | } 311 | 312 | ERROR_NOT_MINTER() { 313 | return this.eth_call(functions.ERROR_NOT_MINTER, {}); 314 | } 315 | 316 | ERROR_ONLY_OWNER() { 317 | return this.eth_call(functions.ERROR_ONLY_OWNER, {}); 318 | } 319 | 320 | ERROR_SENDER_NOT_ADMIN() { 321 | return this.eth_call(functions.ERROR_SENDER_NOT_ADMIN, {}); 322 | } 323 | 324 | MINTER_ROLE() { 325 | return this.eth_call(functions.MINTER_ROLE, {}); 326 | } 327 | 328 | OPERATOR_ROLE() { 329 | return this.eth_call(functions.OPERATOR_ROLE, {}); 330 | } 331 | 332 | UPGRADE_INTERFACE_VERSION() { 333 | return this.eth_call(functions.UPGRADE_INTERFACE_VERSION, {}); 334 | } 335 | 336 | approvalWhitelists(_0: ApprovalWhitelistsParams["_0"]) { 337 | return this.eth_call(functions.approvalWhitelists, { _0 }); 338 | } 339 | 340 | balanceOf(owner: BalanceOfParams["owner"]) { 341 | return this.eth_call(functions.balanceOf, { owner }); 342 | } 343 | 344 | blacklists(_0: BlacklistsParams["_0"]) { 345 | return this.eth_call(functions.blacklists, { _0 }); 346 | } 347 | 348 | getApproved(tokenId: GetApprovedParams["tokenId"]) { 349 | return this.eth_call(functions.getApproved, { tokenId }); 350 | } 351 | 352 | getRoleAdmin(role: GetRoleAdminParams["role"]) { 353 | return this.eth_call(functions.getRoleAdmin, { role }); 354 | } 355 | 356 | hasRole(role: HasRoleParams["role"], account: HasRoleParams["account"]) { 357 | return this.eth_call(functions.hasRole, { role, account }); 358 | } 359 | 360 | isApprovedForAll( 361 | owner: IsApprovedForAllParams["owner"], 362 | operator: IsApprovedForAllParams["operator"] 363 | ) { 364 | return this.eth_call(functions.isApprovedForAll, { owner, operator }); 365 | } 366 | 367 | isObjektTransferable(_0: IsObjektTransferableParams["_0"]) { 368 | return this.eth_call(functions.isObjektTransferable, { _0 }); 369 | } 370 | 371 | name() { 372 | return this.eth_call(functions.name, {}); 373 | } 374 | 375 | ownerOf(tokenId: OwnerOfParams["tokenId"]) { 376 | return this.eth_call(functions.ownerOf, { tokenId }); 377 | } 378 | 379 | proxiableUUID() { 380 | return this.eth_call(functions.proxiableUUID, {}); 381 | } 382 | 383 | supportsInterface(interfaceId: SupportsInterfaceParams["interfaceId"]) { 384 | return this.eth_call(functions.supportsInterface, { interfaceId }); 385 | } 386 | 387 | symbol() { 388 | return this.eth_call(functions.symbol, {}); 389 | } 390 | 391 | tokenByIndex(index: TokenByIndexParams["index"]) { 392 | return this.eth_call(functions.tokenByIndex, { index }); 393 | } 394 | 395 | tokenOfOwnerByIndex( 396 | owner: TokenOfOwnerByIndexParams["owner"], 397 | index: TokenOfOwnerByIndexParams["index"] 398 | ) { 399 | return this.eth_call(functions.tokenOfOwnerByIndex, { owner, index }); 400 | } 401 | 402 | tokenURI(tokenId: TokenURIParams["tokenId"]) { 403 | return this.eth_call(functions.tokenURI, { tokenId }); 404 | } 405 | 406 | totalSupply() { 407 | return this.eth_call(functions.totalSupply, {}); 408 | } 409 | } 410 | 411 | /// Event types 412 | export type ApprovalEventArgs = EParams; 413 | export type ApprovalForAllEventArgs = EParams; 414 | export type InitializedEventArgs = EParams; 415 | export type RoleAdminChangedEventArgs = EParams; 416 | export type RoleGrantedEventArgs = EParams; 417 | export type RoleRevokedEventArgs = EParams; 418 | export type TransferEventArgs = EParams; 419 | export type UpgradedEventArgs = EParams; 420 | 421 | /// Function types 422 | export type DEFAULT_ADMIN_ROLEParams = FunctionArguments< 423 | typeof functions.DEFAULT_ADMIN_ROLE 424 | >; 425 | export type DEFAULT_ADMIN_ROLEReturn = FunctionReturn< 426 | typeof functions.DEFAULT_ADMIN_ROLE 427 | >; 428 | 429 | export type ERROR_BLACKLISTED_MSG_SENDERParams = FunctionArguments< 430 | typeof functions.ERROR_BLACKLISTED_MSG_SENDER 431 | >; 432 | export type ERROR_BLACKLISTED_MSG_SENDERReturn = FunctionReturn< 433 | typeof functions.ERROR_BLACKLISTED_MSG_SENDER 434 | >; 435 | 436 | export type ERROR_CANNOT_TRANSFER_EXTERNALLYParams = FunctionArguments< 437 | typeof functions.ERROR_CANNOT_TRANSFER_EXTERNALLY 438 | >; 439 | export type ERROR_CANNOT_TRANSFER_EXTERNALLYReturn = FunctionReturn< 440 | typeof functions.ERROR_CANNOT_TRANSFER_EXTERNALLY 441 | >; 442 | 443 | export type ERROR_INVALID_TOKEN_ID_RANGEParams = FunctionArguments< 444 | typeof functions.ERROR_INVALID_TOKEN_ID_RANGE 445 | >; 446 | export type ERROR_INVALID_TOKEN_ID_RANGEReturn = FunctionReturn< 447 | typeof functions.ERROR_INVALID_TOKEN_ID_RANGE 448 | >; 449 | 450 | export type ERROR_NON_TRANSFERABLE_OBJEKTParams = FunctionArguments< 451 | typeof functions.ERROR_NON_TRANSFERABLE_OBJEKT 452 | >; 453 | export type ERROR_NON_TRANSFERABLE_OBJEKTReturn = FunctionReturn< 454 | typeof functions.ERROR_NON_TRANSFERABLE_OBJEKT 455 | >; 456 | 457 | export type ERROR_NOT_ADMINParams = FunctionArguments< 458 | typeof functions.ERROR_NOT_ADMIN 459 | >; 460 | export type ERROR_NOT_ADMINReturn = FunctionReturn< 461 | typeof functions.ERROR_NOT_ADMIN 462 | >; 463 | 464 | export type ERROR_NOT_AUTHORIZED_APPROVALParams = FunctionArguments< 465 | typeof functions.ERROR_NOT_AUTHORIZED_APPROVAL 466 | >; 467 | export type ERROR_NOT_AUTHORIZED_APPROVALReturn = FunctionReturn< 468 | typeof functions.ERROR_NOT_AUTHORIZED_APPROVAL 469 | >; 470 | 471 | export type ERROR_NOT_MINTERParams = FunctionArguments< 472 | typeof functions.ERROR_NOT_MINTER 473 | >; 474 | export type ERROR_NOT_MINTERReturn = FunctionReturn< 475 | typeof functions.ERROR_NOT_MINTER 476 | >; 477 | 478 | export type ERROR_ONLY_OWNERParams = FunctionArguments< 479 | typeof functions.ERROR_ONLY_OWNER 480 | >; 481 | export type ERROR_ONLY_OWNERReturn = FunctionReturn< 482 | typeof functions.ERROR_ONLY_OWNER 483 | >; 484 | 485 | export type ERROR_SENDER_NOT_ADMINParams = FunctionArguments< 486 | typeof functions.ERROR_SENDER_NOT_ADMIN 487 | >; 488 | export type ERROR_SENDER_NOT_ADMINReturn = FunctionReturn< 489 | typeof functions.ERROR_SENDER_NOT_ADMIN 490 | >; 491 | 492 | export type MINTER_ROLEParams = FunctionArguments; 493 | export type MINTER_ROLEReturn = FunctionReturn; 494 | 495 | export type OPERATOR_ROLEParams = FunctionArguments< 496 | typeof functions.OPERATOR_ROLE 497 | >; 498 | export type OPERATOR_ROLEReturn = FunctionReturn< 499 | typeof functions.OPERATOR_ROLE 500 | >; 501 | 502 | export type UPGRADE_INTERFACE_VERSIONParams = FunctionArguments< 503 | typeof functions.UPGRADE_INTERFACE_VERSION 504 | >; 505 | export type UPGRADE_INTERFACE_VERSIONReturn = FunctionReturn< 506 | typeof functions.UPGRADE_INTERFACE_VERSION 507 | >; 508 | 509 | export type ApprovalWhitelistsParams = FunctionArguments< 510 | typeof functions.approvalWhitelists 511 | >; 512 | export type ApprovalWhitelistsReturn = FunctionReturn< 513 | typeof functions.approvalWhitelists 514 | >; 515 | 516 | export type ApproveParams = FunctionArguments; 517 | export type ApproveReturn = FunctionReturn; 518 | 519 | export type BalanceOfParams = FunctionArguments; 520 | export type BalanceOfReturn = FunctionReturn; 521 | 522 | export type BatchUpdateObjektTransferabilityParams = FunctionArguments< 523 | typeof functions.batchUpdateObjektTransferability 524 | >; 525 | export type BatchUpdateObjektTransferabilityReturn = FunctionReturn< 526 | typeof functions.batchUpdateObjektTransferability 527 | >; 528 | 529 | export type BlacklistsParams = FunctionArguments; 530 | export type BlacklistsReturn = FunctionReturn; 531 | 532 | export type BurnParams = FunctionArguments; 533 | export type BurnReturn = FunctionReturn; 534 | 535 | export type GetApprovedParams = FunctionArguments; 536 | export type GetApprovedReturn = FunctionReturn; 537 | 538 | export type GetRoleAdminParams = FunctionArguments< 539 | typeof functions.getRoleAdmin 540 | >; 541 | export type GetRoleAdminReturn = FunctionReturn; 542 | 543 | export type GrantRoleParams = FunctionArguments; 544 | export type GrantRoleReturn = FunctionReturn; 545 | 546 | export type HasRoleParams = FunctionArguments; 547 | export type HasRoleReturn = FunctionReturn; 548 | 549 | export type InitializeParams = FunctionArguments; 550 | export type InitializeReturn = FunctionReturn; 551 | 552 | export type IsApprovedForAllParams = FunctionArguments< 553 | typeof functions.isApprovedForAll 554 | >; 555 | export type IsApprovedForAllReturn = FunctionReturn< 556 | typeof functions.isApprovedForAll 557 | >; 558 | 559 | export type IsObjektTransferableParams = FunctionArguments< 560 | typeof functions.isObjektTransferable 561 | >; 562 | export type IsObjektTransferableReturn = FunctionReturn< 563 | typeof functions.isObjektTransferable 564 | >; 565 | 566 | export type MintParams = FunctionArguments; 567 | export type MintReturn = FunctionReturn; 568 | 569 | export type MintBatchParams = FunctionArguments; 570 | export type MintBatchReturn = FunctionReturn; 571 | 572 | export type NameParams = FunctionArguments; 573 | export type NameReturn = FunctionReturn; 574 | 575 | export type OwnerOfParams = FunctionArguments; 576 | export type OwnerOfReturn = FunctionReturn; 577 | 578 | export type ProxiableUUIDParams = FunctionArguments< 579 | typeof functions.proxiableUUID 580 | >; 581 | export type ProxiableUUIDReturn = FunctionReturn< 582 | typeof functions.proxiableUUID 583 | >; 584 | 585 | export type RenounceRoleParams = FunctionArguments< 586 | typeof functions.renounceRole 587 | >; 588 | export type RenounceRoleReturn = FunctionReturn; 589 | 590 | export type RevokeRoleParams = FunctionArguments; 591 | export type RevokeRoleReturn = FunctionReturn; 592 | 593 | export type SafeTransferFromParams_0 = FunctionArguments< 594 | (typeof functions)["safeTransferFrom(address,address,uint256)"] 595 | >; 596 | export type SafeTransferFromReturn_0 = FunctionReturn< 597 | (typeof functions)["safeTransferFrom(address,address,uint256)"] 598 | >; 599 | 600 | export type SafeTransferFromParams_1 = FunctionArguments< 601 | (typeof functions)["safeTransferFrom(address,address,uint256,bytes)"] 602 | >; 603 | export type SafeTransferFromReturn_1 = FunctionReturn< 604 | (typeof functions)["safeTransferFrom(address,address,uint256,bytes)"] 605 | >; 606 | 607 | export type SetApprovalForAllParams = FunctionArguments< 608 | typeof functions.setApprovalForAll 609 | >; 610 | export type SetApprovalForAllReturn = FunctionReturn< 611 | typeof functions.setApprovalForAll 612 | >; 613 | 614 | export type SetApprovalWhitelistParams = FunctionArguments< 615 | typeof functions.setApprovalWhitelist 616 | >; 617 | export type SetApprovalWhitelistReturn = FunctionReturn< 618 | typeof functions.setApprovalWhitelist 619 | >; 620 | 621 | export type SetBaseURIParams = FunctionArguments; 622 | export type SetBaseURIReturn = FunctionReturn; 623 | 624 | export type SetBlacklistParams = FunctionArguments< 625 | typeof functions.setBlacklist 626 | >; 627 | export type SetBlacklistReturn = FunctionReturn; 628 | 629 | export type SupportsInterfaceParams = FunctionArguments< 630 | typeof functions.supportsInterface 631 | >; 632 | export type SupportsInterfaceReturn = FunctionReturn< 633 | typeof functions.supportsInterface 634 | >; 635 | 636 | export type SymbolParams = FunctionArguments; 637 | export type SymbolReturn = FunctionReturn; 638 | 639 | export type TokenByIndexParams = FunctionArguments< 640 | typeof functions.tokenByIndex 641 | >; 642 | export type TokenByIndexReturn = FunctionReturn; 643 | 644 | export type TokenOfOwnerByIndexParams = FunctionArguments< 645 | typeof functions.tokenOfOwnerByIndex 646 | >; 647 | export type TokenOfOwnerByIndexReturn = FunctionReturn< 648 | typeof functions.tokenOfOwnerByIndex 649 | >; 650 | 651 | export type TokenURIParams = FunctionArguments; 652 | export type TokenURIReturn = FunctionReturn; 653 | 654 | export type TotalSupplyParams = FunctionArguments; 655 | export type TotalSupplyReturn = FunctionReturn; 656 | 657 | export type TransferFromParams = FunctionArguments< 658 | typeof functions.transferFrom 659 | >; 660 | export type TransferFromReturn = FunctionReturn; 661 | 662 | export type UpgradeToAndCallParams = FunctionArguments< 663 | typeof functions.upgradeToAndCall 664 | >; 665 | export type UpgradeToAndCallReturn = FunctionReturn< 666 | typeof functions.upgradeToAndCall 667 | >; 668 | -------------------------------------------------------------------------------- /src/abi/gravity.ts: -------------------------------------------------------------------------------- 1 | import * as p from "@subsquid/evm-codec"; 2 | import { event, fun, viewFun, indexed, ContractBase } from "@subsquid/evm-abi"; 3 | import type { 4 | EventParams as EParams, 5 | FunctionArguments, 6 | FunctionReturn, 7 | } from "@subsquid/evm-abi"; 8 | 9 | export const events = { 10 | EIP712DomainChanged: event( 11 | "0x0a6387c9ea3628b88a633bb4f3b151770f70085117a15f9bf3787cda53f13d31", 12 | "EIP712DomainChanged()", 13 | {} 14 | ), 15 | Finalized: event( 16 | "0x069cb9cf3066619f9a83be465993a22843ea7baba731ac97ac208899985dc007", 17 | "Finalized(uint256,uint256,uint256)", 18 | { 19 | tokenId: indexed(p.uint256), 20 | pollId: indexed(p.uint256), 21 | burned: p.uint256, 22 | } 23 | ), 24 | Initialized: event( 25 | "0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2", 26 | "Initialized(uint64)", 27 | { version: p.uint64 } 28 | ), 29 | PollCreated: event( 30 | "0x79fb617314647013123901234cea859ab2df200423a4048a35d9cf59437a9b64", 31 | "PollCreated(uint256,uint256)", 32 | { tokenId: p.uint256, pollId: p.uint256 } 33 | ), 34 | Revealed: event( 35 | "0xa02957988806bd9d30dcd2845b047aa4f45bede519fbd15ebafe428505b0900f", 36 | "Revealed(uint256,uint256,uint256,uint256)", 37 | { 38 | tokenId: indexed(p.uint256), 39 | pollId: indexed(p.uint256), 40 | revealedVotes: p.uint256, 41 | remainingVotes: p.uint256, 42 | } 43 | ), 44 | RoleAdminChanged: event( 45 | "0xbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff", 46 | "RoleAdminChanged(bytes32,bytes32,bytes32)", 47 | { 48 | role: indexed(p.bytes32), 49 | previousAdminRole: indexed(p.bytes32), 50 | newAdminRole: indexed(p.bytes32), 51 | } 52 | ), 53 | RoleGranted: event( 54 | "0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d", 55 | "RoleGranted(bytes32,address,address)", 56 | { 57 | role: indexed(p.bytes32), 58 | account: indexed(p.address), 59 | sender: indexed(p.address), 60 | } 61 | ), 62 | RoleRevoked: event( 63 | "0xf6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b", 64 | "RoleRevoked(bytes32,address,address)", 65 | { 66 | role: indexed(p.bytes32), 67 | account: indexed(p.address), 68 | sender: indexed(p.address), 69 | } 70 | ), 71 | Upgraded: event( 72 | "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", 73 | "Upgraded(address)", 74 | { implementation: indexed(p.address) } 75 | ), 76 | Voted: event( 77 | "0x0f066129e5902e103e22209fff5d12a79f07dc9ef7c78fe10ff64c741a20c8ec", 78 | "Voted(uint256,uint256,address,uint256,bytes32)", 79 | { 80 | tokenId: indexed(p.uint256), 81 | pollId: indexed(p.uint256), 82 | voter: p.address, 83 | tokenAmount: p.uint256, 84 | hash: p.bytes32, 85 | } 86 | ), 87 | }; 88 | 89 | export const functions = { 90 | DEFAULT_ADMIN_ROLE: viewFun( 91 | "0xa217fddf", 92 | "DEFAULT_ADMIN_ROLE()", 93 | {}, 94 | p.bytes32 95 | ), 96 | ERROR_ALL_VOTES_REVEALED: viewFun( 97 | "0x46d04869", 98 | "ERROR_ALL_VOTES_REVEALED()", 99 | {}, 100 | p.string 101 | ), 102 | ERROR_EMPTY_CALLDATA: viewFun( 103 | "0xfdaff161", 104 | "ERROR_EMPTY_CALLDATA()", 105 | {}, 106 | p.string 107 | ), 108 | ERROR_INSUFFICIENT_TOKEN_AMOUNT: viewFun( 109 | "0x61871ac4", 110 | "ERROR_INSUFFICIENT_TOKEN_AMOUNT()", 111 | {}, 112 | p.string 113 | ), 114 | ERROR_INVALID_CANDIDATES_LENGTH: viewFun( 115 | "0x31a869a5", 116 | "ERROR_INVALID_CANDIDATES_LENGTH()", 117 | {}, 118 | p.string 119 | ), 120 | ERROR_INVALID_DATA_LENGTH: viewFun( 121 | "0x5ee958b2", 122 | "ERROR_INVALID_DATA_LENGTH()", 123 | {}, 124 | p.string 125 | ), 126 | ERROR_INVALID_DUE: viewFun("0x074a485c", "ERROR_INVALID_DUE()", {}, p.string), 127 | ERROR_INVALID_FROM_ADDRESS: viewFun( 128 | "0xd807d5fd", 129 | "ERROR_INVALID_FROM_ADDRESS()", 130 | {}, 131 | p.string 132 | ), 133 | ERROR_INVALID_MINIMUM_TOKEN: viewFun( 134 | "0xf8f9a91a", 135 | "ERROR_INVALID_MINIMUM_TOKEN()", 136 | {}, 137 | p.string 138 | ), 139 | ERROR_INVALID_OFFSET: viewFun( 140 | "0xe8a39056", 141 | "ERROR_INVALID_OFFSET()", 142 | {}, 143 | p.string 144 | ), 145 | ERROR_INVALID_REVEAL_DATA: viewFun( 146 | "0x63d32a89", 147 | "ERROR_INVALID_REVEAL_DATA()", 148 | {}, 149 | p.string 150 | ), 151 | ERROR_INVALID_START_AT: viewFun( 152 | "0xf055304e", 153 | "ERROR_INVALID_START_AT()", 154 | {}, 155 | p.string 156 | ), 157 | ERROR_INVALID_TOKEN_UNIT: viewFun( 158 | "0xd66a493e", 159 | "ERROR_INVALID_TOKEN_UNIT()", 160 | {}, 161 | p.string 162 | ), 163 | ERROR_INVALID_VOTE_SIGNER_ADDRESS: viewFun( 164 | "0x23cc8ac8", 165 | "ERROR_INVALID_VOTE_SIGNER_ADDRESS()", 166 | {}, 167 | p.string 168 | ), 169 | ERROR_INVALID_VOTE_UNIT: viewFun( 170 | "0x7c69a472", 171 | "ERROR_INVALID_VOTE_UNIT()", 172 | {}, 173 | p.string 174 | ), 175 | ERROR_NOT_ALL_VOTES_REVEALED: viewFun( 176 | "0xb762c542", 177 | "ERROR_NOT_ALL_VOTES_REVEALED()", 178 | {}, 179 | p.string 180 | ), 181 | ERROR_NOT_IN_PROGRESS: viewFun( 182 | "0x22b207ff", 183 | "ERROR_NOT_IN_PROGRESS()", 184 | {}, 185 | p.string 186 | ), 187 | ERROR_POLL_ALREADY_EXISTS: viewFun( 188 | "0x7803e191", 189 | "ERROR_POLL_ALREADY_EXISTS()", 190 | {}, 191 | p.string 192 | ), 193 | ERROR_POLL_ALREADY_FINALIZED: viewFun( 194 | "0xdcf470c7", 195 | "ERROR_POLL_ALREADY_FINALIZED()", 196 | {}, 197 | p.string 198 | ), 199 | ERROR_POLL_NOT_EXISTS: viewFun( 200 | "0xcb219983", 201 | "ERROR_POLL_NOT_EXISTS()", 202 | {}, 203 | p.string 204 | ), 205 | ERROR_VOTE_HASH_CANNOT_BE_REPLAYED: viewFun( 206 | "0xe303a43e", 207 | "ERROR_VOTE_HASH_CANNOT_BE_REPLAYED()", 208 | {}, 209 | p.string 210 | ), 211 | ERROR_VOTE_SIGNER_INVALID_SIGNATURE: viewFun( 212 | "0x80bba5c2", 213 | "ERROR_VOTE_SIGNER_INVALID_SIGNATURE()", 214 | {}, 215 | p.string 216 | ), 217 | OPERATOR_ROLE: viewFun("0xf5b541a6", "OPERATOR_ROLE()", {}, p.bytes32), 218 | UPGRADE_INTERFACE_VERSION: viewFun( 219 | "0xad3cb1cc", 220 | "UPGRADE_INTERFACE_VERSION()", 221 | {}, 222 | p.string 223 | ), 224 | candidates: viewFun( 225 | "0x7de14242", 226 | "candidates(uint256,uint256)", 227 | { tokenId: p.uint256, pollId: p.uint256 }, 228 | p.array(p.string) 229 | ), 230 | createPoll: fun( 231 | "0x9612e586", 232 | "createPoll(uint256,uint256,string,string[],uint256,uint256,uint256,uint256)", 233 | { 234 | tokenId_: p.uint256, 235 | pollId_: p.uint256, 236 | title_: p.string, 237 | candidates_: p.array(p.string), 238 | startAt_: p.uint256, 239 | due_: p.uint256, 240 | minimumToken_: p.uint256, 241 | voteUnit_: p.uint256, 242 | } 243 | ), 244 | eip712Domain: viewFun( 245 | "0x84b0196e", 246 | "eip712Domain()", 247 | {}, 248 | { 249 | fields: p.bytes1, 250 | name: p.string, 251 | version: p.string, 252 | chainId: p.uint256, 253 | verifyingContract: p.address, 254 | salt: p.bytes32, 255 | extensions: p.array(p.uint256), 256 | } 257 | ), 258 | finalize: fun("0xb6013cef", "finalize(uint256,uint256)", { 259 | tokenId: p.uint256, 260 | pollId: p.uint256, 261 | }), 262 | getRoleAdmin: viewFun( 263 | "0x248a9ca3", 264 | "getRoleAdmin(bytes32)", 265 | { role: p.bytes32 }, 266 | p.bytes32 267 | ), 268 | grantRole: fun("0x2f2ff15d", "grantRole(bytes32,address)", { 269 | role: p.bytes32, 270 | account: p.address, 271 | }), 272 | hasRole: viewFun( 273 | "0x91d14854", 274 | "hasRole(bytes32,address)", 275 | { role: p.bytes32, account: p.address }, 276 | p.bool 277 | ), 278 | initialize: fun("0x485cc955", "initialize(address,address)", { 279 | tokenAddress: p.address, 280 | voteSignerAddress: p.address, 281 | }), 282 | onERC1155BatchReceived: fun( 283 | "0xbc197c81", 284 | "onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)", 285 | { 286 | operator: p.address, 287 | from: p.address, 288 | tokenIds: p.array(p.uint256), 289 | values: p.array(p.uint256), 290 | data: p.bytes, 291 | }, 292 | p.bytes4 293 | ), 294 | onERC1155Received: fun( 295 | "0xf23a6e61", 296 | "onERC1155Received(address,address,uint256,uint256,bytes)", 297 | { 298 | operator: p.address, 299 | from: p.address, 300 | tokenId: p.uint256, 301 | amount: p.uint256, 302 | data: p.bytes, 303 | }, 304 | p.bytes4 305 | ), 306 | proxiableUUID: viewFun("0x52d1902d", "proxiableUUID()", {}, p.bytes32), 307 | remainingVotesCount: viewFun( 308 | "0xe25afda5", 309 | "remainingVotesCount(uint256,uint256)", 310 | { tokenId: p.uint256, pollId: p.uint256 }, 311 | p.uint256 312 | ), 313 | renounceRole: fun("0x36568abe", "renounceRole(bytes32,address)", { 314 | role: p.bytes32, 315 | callerConfirmation: p.address, 316 | }), 317 | reveal: fun( 318 | "0x63cf5547", 319 | "reveal(uint256,uint256,(uint256,bytes32)[],uint256)", 320 | { 321 | tokenId: p.uint256, 322 | pollId: p.uint256, 323 | data: p.array(p.struct({ votedCandidateId: p.uint256, salt: p.bytes32 })), 324 | offset: p.uint256, 325 | } 326 | ), 327 | revokeRole: fun("0xd547741f", "revokeRole(bytes32,address)", { 328 | role: p.bytes32, 329 | account: p.address, 330 | }), 331 | setVoteSignerAddress: fun("0xf7729a2c", "setVoteSignerAddress(address)", { 332 | addr: p.address, 333 | }), 334 | supportsInterface: viewFun( 335 | "0x01ffc9a7", 336 | "supportsInterface(bytes4)", 337 | { interfaceId: p.bytes4 }, 338 | p.bool 339 | ), 340 | totalVotesCount: viewFun( 341 | "0xf65f9a7d", 342 | "totalVotesCount(uint256,uint256)", 343 | { tokenId: p.uint256, pollId: p.uint256 }, 344 | p.uint256 345 | ), 346 | upgradeToAndCall: fun("0x4f1ef286", "upgradeToAndCall(address,bytes)", { 347 | newImplementation: p.address, 348 | data: p.bytes, 349 | }), 350 | voteSignerAddress: viewFun( 351 | "0x525e70f6", 352 | "voteSignerAddress()", 353 | {}, 354 | p.address 355 | ), 356 | votesPerCandidates: viewFun( 357 | "0x28833187", 358 | "votesPerCandidates(uint256,uint256)", 359 | { tokenId: p.uint256, pollId: p.uint256 }, 360 | p.array(p.uint256) 361 | ), 362 | }; 363 | 364 | export class Contract extends ContractBase { 365 | DEFAULT_ADMIN_ROLE() { 366 | return this.eth_call(functions.DEFAULT_ADMIN_ROLE, {}); 367 | } 368 | 369 | ERROR_ALL_VOTES_REVEALED() { 370 | return this.eth_call(functions.ERROR_ALL_VOTES_REVEALED, {}); 371 | } 372 | 373 | ERROR_EMPTY_CALLDATA() { 374 | return this.eth_call(functions.ERROR_EMPTY_CALLDATA, {}); 375 | } 376 | 377 | ERROR_INSUFFICIENT_TOKEN_AMOUNT() { 378 | return this.eth_call(functions.ERROR_INSUFFICIENT_TOKEN_AMOUNT, {}); 379 | } 380 | 381 | ERROR_INVALID_CANDIDATES_LENGTH() { 382 | return this.eth_call(functions.ERROR_INVALID_CANDIDATES_LENGTH, {}); 383 | } 384 | 385 | ERROR_INVALID_DATA_LENGTH() { 386 | return this.eth_call(functions.ERROR_INVALID_DATA_LENGTH, {}); 387 | } 388 | 389 | ERROR_INVALID_DUE() { 390 | return this.eth_call(functions.ERROR_INVALID_DUE, {}); 391 | } 392 | 393 | ERROR_INVALID_FROM_ADDRESS() { 394 | return this.eth_call(functions.ERROR_INVALID_FROM_ADDRESS, {}); 395 | } 396 | 397 | ERROR_INVALID_MINIMUM_TOKEN() { 398 | return this.eth_call(functions.ERROR_INVALID_MINIMUM_TOKEN, {}); 399 | } 400 | 401 | ERROR_INVALID_OFFSET() { 402 | return this.eth_call(functions.ERROR_INVALID_OFFSET, {}); 403 | } 404 | 405 | ERROR_INVALID_REVEAL_DATA() { 406 | return this.eth_call(functions.ERROR_INVALID_REVEAL_DATA, {}); 407 | } 408 | 409 | ERROR_INVALID_START_AT() { 410 | return this.eth_call(functions.ERROR_INVALID_START_AT, {}); 411 | } 412 | 413 | ERROR_INVALID_TOKEN_UNIT() { 414 | return this.eth_call(functions.ERROR_INVALID_TOKEN_UNIT, {}); 415 | } 416 | 417 | ERROR_INVALID_VOTE_SIGNER_ADDRESS() { 418 | return this.eth_call(functions.ERROR_INVALID_VOTE_SIGNER_ADDRESS, {}); 419 | } 420 | 421 | ERROR_INVALID_VOTE_UNIT() { 422 | return this.eth_call(functions.ERROR_INVALID_VOTE_UNIT, {}); 423 | } 424 | 425 | ERROR_NOT_ALL_VOTES_REVEALED() { 426 | return this.eth_call(functions.ERROR_NOT_ALL_VOTES_REVEALED, {}); 427 | } 428 | 429 | ERROR_NOT_IN_PROGRESS() { 430 | return this.eth_call(functions.ERROR_NOT_IN_PROGRESS, {}); 431 | } 432 | 433 | ERROR_POLL_ALREADY_EXISTS() { 434 | return this.eth_call(functions.ERROR_POLL_ALREADY_EXISTS, {}); 435 | } 436 | 437 | ERROR_POLL_ALREADY_FINALIZED() { 438 | return this.eth_call(functions.ERROR_POLL_ALREADY_FINALIZED, {}); 439 | } 440 | 441 | ERROR_POLL_NOT_EXISTS() { 442 | return this.eth_call(functions.ERROR_POLL_NOT_EXISTS, {}); 443 | } 444 | 445 | ERROR_VOTE_HASH_CANNOT_BE_REPLAYED() { 446 | return this.eth_call(functions.ERROR_VOTE_HASH_CANNOT_BE_REPLAYED, {}); 447 | } 448 | 449 | ERROR_VOTE_SIGNER_INVALID_SIGNATURE() { 450 | return this.eth_call(functions.ERROR_VOTE_SIGNER_INVALID_SIGNATURE, {}); 451 | } 452 | 453 | OPERATOR_ROLE() { 454 | return this.eth_call(functions.OPERATOR_ROLE, {}); 455 | } 456 | 457 | UPGRADE_INTERFACE_VERSION() { 458 | return this.eth_call(functions.UPGRADE_INTERFACE_VERSION, {}); 459 | } 460 | 461 | candidates( 462 | tokenId: CandidatesParams["tokenId"], 463 | pollId: CandidatesParams["pollId"] 464 | ) { 465 | return this.eth_call(functions.candidates, { tokenId, pollId }); 466 | } 467 | 468 | eip712Domain() { 469 | return this.eth_call(functions.eip712Domain, {}); 470 | } 471 | 472 | getRoleAdmin(role: GetRoleAdminParams["role"]) { 473 | return this.eth_call(functions.getRoleAdmin, { role }); 474 | } 475 | 476 | hasRole(role: HasRoleParams["role"], account: HasRoleParams["account"]) { 477 | return this.eth_call(functions.hasRole, { role, account }); 478 | } 479 | 480 | proxiableUUID() { 481 | return this.eth_call(functions.proxiableUUID, {}); 482 | } 483 | 484 | remainingVotesCount( 485 | tokenId: RemainingVotesCountParams["tokenId"], 486 | pollId: RemainingVotesCountParams["pollId"] 487 | ) { 488 | return this.eth_call(functions.remainingVotesCount, { tokenId, pollId }); 489 | } 490 | 491 | supportsInterface(interfaceId: SupportsInterfaceParams["interfaceId"]) { 492 | return this.eth_call(functions.supportsInterface, { interfaceId }); 493 | } 494 | 495 | totalVotesCount( 496 | tokenId: TotalVotesCountParams["tokenId"], 497 | pollId: TotalVotesCountParams["pollId"] 498 | ) { 499 | return this.eth_call(functions.totalVotesCount, { tokenId, pollId }); 500 | } 501 | 502 | voteSignerAddress() { 503 | return this.eth_call(functions.voteSignerAddress, {}); 504 | } 505 | 506 | votesPerCandidates( 507 | tokenId: VotesPerCandidatesParams["tokenId"], 508 | pollId: VotesPerCandidatesParams["pollId"] 509 | ) { 510 | return this.eth_call(functions.votesPerCandidates, { tokenId, pollId }); 511 | } 512 | } 513 | 514 | /// Event types 515 | export type EIP712DomainChangedEventArgs = EParams< 516 | typeof events.EIP712DomainChanged 517 | >; 518 | export type FinalizedEventArgs = EParams; 519 | export type InitializedEventArgs = EParams; 520 | export type PollCreatedEventArgs = EParams; 521 | export type RevealedEventArgs = EParams; 522 | export type RoleAdminChangedEventArgs = EParams; 523 | export type RoleGrantedEventArgs = EParams; 524 | export type RoleRevokedEventArgs = EParams; 525 | export type UpgradedEventArgs = EParams; 526 | export type VotedEventArgs = EParams; 527 | 528 | /// Function types 529 | export type DEFAULT_ADMIN_ROLEParams = FunctionArguments< 530 | typeof functions.DEFAULT_ADMIN_ROLE 531 | >; 532 | export type DEFAULT_ADMIN_ROLEReturn = FunctionReturn< 533 | typeof functions.DEFAULT_ADMIN_ROLE 534 | >; 535 | 536 | export type ERROR_ALL_VOTES_REVEALEDParams = FunctionArguments< 537 | typeof functions.ERROR_ALL_VOTES_REVEALED 538 | >; 539 | export type ERROR_ALL_VOTES_REVEALEDReturn = FunctionReturn< 540 | typeof functions.ERROR_ALL_VOTES_REVEALED 541 | >; 542 | 543 | export type ERROR_EMPTY_CALLDATAParams = FunctionArguments< 544 | typeof functions.ERROR_EMPTY_CALLDATA 545 | >; 546 | export type ERROR_EMPTY_CALLDATAReturn = FunctionReturn< 547 | typeof functions.ERROR_EMPTY_CALLDATA 548 | >; 549 | 550 | export type ERROR_INSUFFICIENT_TOKEN_AMOUNTParams = FunctionArguments< 551 | typeof functions.ERROR_INSUFFICIENT_TOKEN_AMOUNT 552 | >; 553 | export type ERROR_INSUFFICIENT_TOKEN_AMOUNTReturn = FunctionReturn< 554 | typeof functions.ERROR_INSUFFICIENT_TOKEN_AMOUNT 555 | >; 556 | 557 | export type ERROR_INVALID_CANDIDATES_LENGTHParams = FunctionArguments< 558 | typeof functions.ERROR_INVALID_CANDIDATES_LENGTH 559 | >; 560 | export type ERROR_INVALID_CANDIDATES_LENGTHReturn = FunctionReturn< 561 | typeof functions.ERROR_INVALID_CANDIDATES_LENGTH 562 | >; 563 | 564 | export type ERROR_INVALID_DATA_LENGTHParams = FunctionArguments< 565 | typeof functions.ERROR_INVALID_DATA_LENGTH 566 | >; 567 | export type ERROR_INVALID_DATA_LENGTHReturn = FunctionReturn< 568 | typeof functions.ERROR_INVALID_DATA_LENGTH 569 | >; 570 | 571 | export type ERROR_INVALID_DUEParams = FunctionArguments< 572 | typeof functions.ERROR_INVALID_DUE 573 | >; 574 | export type ERROR_INVALID_DUEReturn = FunctionReturn< 575 | typeof functions.ERROR_INVALID_DUE 576 | >; 577 | 578 | export type ERROR_INVALID_FROM_ADDRESSParams = FunctionArguments< 579 | typeof functions.ERROR_INVALID_FROM_ADDRESS 580 | >; 581 | export type ERROR_INVALID_FROM_ADDRESSReturn = FunctionReturn< 582 | typeof functions.ERROR_INVALID_FROM_ADDRESS 583 | >; 584 | 585 | export type ERROR_INVALID_MINIMUM_TOKENParams = FunctionArguments< 586 | typeof functions.ERROR_INVALID_MINIMUM_TOKEN 587 | >; 588 | export type ERROR_INVALID_MINIMUM_TOKENReturn = FunctionReturn< 589 | typeof functions.ERROR_INVALID_MINIMUM_TOKEN 590 | >; 591 | 592 | export type ERROR_INVALID_OFFSETParams = FunctionArguments< 593 | typeof functions.ERROR_INVALID_OFFSET 594 | >; 595 | export type ERROR_INVALID_OFFSETReturn = FunctionReturn< 596 | typeof functions.ERROR_INVALID_OFFSET 597 | >; 598 | 599 | export type ERROR_INVALID_REVEAL_DATAParams = FunctionArguments< 600 | typeof functions.ERROR_INVALID_REVEAL_DATA 601 | >; 602 | export type ERROR_INVALID_REVEAL_DATAReturn = FunctionReturn< 603 | typeof functions.ERROR_INVALID_REVEAL_DATA 604 | >; 605 | 606 | export type ERROR_INVALID_START_ATParams = FunctionArguments< 607 | typeof functions.ERROR_INVALID_START_AT 608 | >; 609 | export type ERROR_INVALID_START_ATReturn = FunctionReturn< 610 | typeof functions.ERROR_INVALID_START_AT 611 | >; 612 | 613 | export type ERROR_INVALID_TOKEN_UNITParams = FunctionArguments< 614 | typeof functions.ERROR_INVALID_TOKEN_UNIT 615 | >; 616 | export type ERROR_INVALID_TOKEN_UNITReturn = FunctionReturn< 617 | typeof functions.ERROR_INVALID_TOKEN_UNIT 618 | >; 619 | 620 | export type ERROR_INVALID_VOTE_SIGNER_ADDRESSParams = FunctionArguments< 621 | typeof functions.ERROR_INVALID_VOTE_SIGNER_ADDRESS 622 | >; 623 | export type ERROR_INVALID_VOTE_SIGNER_ADDRESSReturn = FunctionReturn< 624 | typeof functions.ERROR_INVALID_VOTE_SIGNER_ADDRESS 625 | >; 626 | 627 | export type ERROR_INVALID_VOTE_UNITParams = FunctionArguments< 628 | typeof functions.ERROR_INVALID_VOTE_UNIT 629 | >; 630 | export type ERROR_INVALID_VOTE_UNITReturn = FunctionReturn< 631 | typeof functions.ERROR_INVALID_VOTE_UNIT 632 | >; 633 | 634 | export type ERROR_NOT_ALL_VOTES_REVEALEDParams = FunctionArguments< 635 | typeof functions.ERROR_NOT_ALL_VOTES_REVEALED 636 | >; 637 | export type ERROR_NOT_ALL_VOTES_REVEALEDReturn = FunctionReturn< 638 | typeof functions.ERROR_NOT_ALL_VOTES_REVEALED 639 | >; 640 | 641 | export type ERROR_NOT_IN_PROGRESSParams = FunctionArguments< 642 | typeof functions.ERROR_NOT_IN_PROGRESS 643 | >; 644 | export type ERROR_NOT_IN_PROGRESSReturn = FunctionReturn< 645 | typeof functions.ERROR_NOT_IN_PROGRESS 646 | >; 647 | 648 | export type ERROR_POLL_ALREADY_EXISTSParams = FunctionArguments< 649 | typeof functions.ERROR_POLL_ALREADY_EXISTS 650 | >; 651 | export type ERROR_POLL_ALREADY_EXISTSReturn = FunctionReturn< 652 | typeof functions.ERROR_POLL_ALREADY_EXISTS 653 | >; 654 | 655 | export type ERROR_POLL_ALREADY_FINALIZEDParams = FunctionArguments< 656 | typeof functions.ERROR_POLL_ALREADY_FINALIZED 657 | >; 658 | export type ERROR_POLL_ALREADY_FINALIZEDReturn = FunctionReturn< 659 | typeof functions.ERROR_POLL_ALREADY_FINALIZED 660 | >; 661 | 662 | export type ERROR_POLL_NOT_EXISTSParams = FunctionArguments< 663 | typeof functions.ERROR_POLL_NOT_EXISTS 664 | >; 665 | export type ERROR_POLL_NOT_EXISTSReturn = FunctionReturn< 666 | typeof functions.ERROR_POLL_NOT_EXISTS 667 | >; 668 | 669 | export type ERROR_VOTE_HASH_CANNOT_BE_REPLAYEDParams = FunctionArguments< 670 | typeof functions.ERROR_VOTE_HASH_CANNOT_BE_REPLAYED 671 | >; 672 | export type ERROR_VOTE_HASH_CANNOT_BE_REPLAYEDReturn = FunctionReturn< 673 | typeof functions.ERROR_VOTE_HASH_CANNOT_BE_REPLAYED 674 | >; 675 | 676 | export type ERROR_VOTE_SIGNER_INVALID_SIGNATUREParams = FunctionArguments< 677 | typeof functions.ERROR_VOTE_SIGNER_INVALID_SIGNATURE 678 | >; 679 | export type ERROR_VOTE_SIGNER_INVALID_SIGNATUREReturn = FunctionReturn< 680 | typeof functions.ERROR_VOTE_SIGNER_INVALID_SIGNATURE 681 | >; 682 | 683 | export type OPERATOR_ROLEParams = FunctionArguments< 684 | typeof functions.OPERATOR_ROLE 685 | >; 686 | export type OPERATOR_ROLEReturn = FunctionReturn< 687 | typeof functions.OPERATOR_ROLE 688 | >; 689 | 690 | export type UPGRADE_INTERFACE_VERSIONParams = FunctionArguments< 691 | typeof functions.UPGRADE_INTERFACE_VERSION 692 | >; 693 | export type UPGRADE_INTERFACE_VERSIONReturn = FunctionReturn< 694 | typeof functions.UPGRADE_INTERFACE_VERSION 695 | >; 696 | 697 | export type CandidatesParams = FunctionArguments; 698 | export type CandidatesReturn = FunctionReturn; 699 | 700 | export type CreatePollParams = FunctionArguments; 701 | export type CreatePollReturn = FunctionReturn; 702 | 703 | export type Eip712DomainParams = FunctionArguments< 704 | typeof functions.eip712Domain 705 | >; 706 | export type Eip712DomainReturn = FunctionReturn; 707 | 708 | export type FinalizeParams = FunctionArguments; 709 | export type FinalizeReturn = FunctionReturn; 710 | 711 | export type GetRoleAdminParams = FunctionArguments< 712 | typeof functions.getRoleAdmin 713 | >; 714 | export type GetRoleAdminReturn = FunctionReturn; 715 | 716 | export type GrantRoleParams = FunctionArguments; 717 | export type GrantRoleReturn = FunctionReturn; 718 | 719 | export type HasRoleParams = FunctionArguments; 720 | export type HasRoleReturn = FunctionReturn; 721 | 722 | export type InitializeParams = FunctionArguments; 723 | export type InitializeReturn = FunctionReturn; 724 | 725 | export type OnERC1155BatchReceivedParams = FunctionArguments< 726 | typeof functions.onERC1155BatchReceived 727 | >; 728 | export type OnERC1155BatchReceivedReturn = FunctionReturn< 729 | typeof functions.onERC1155BatchReceived 730 | >; 731 | 732 | export type OnERC1155ReceivedParams = FunctionArguments< 733 | typeof functions.onERC1155Received 734 | >; 735 | export type OnERC1155ReceivedReturn = FunctionReturn< 736 | typeof functions.onERC1155Received 737 | >; 738 | 739 | export type ProxiableUUIDParams = FunctionArguments< 740 | typeof functions.proxiableUUID 741 | >; 742 | export type ProxiableUUIDReturn = FunctionReturn< 743 | typeof functions.proxiableUUID 744 | >; 745 | 746 | export type RemainingVotesCountParams = FunctionArguments< 747 | typeof functions.remainingVotesCount 748 | >; 749 | export type RemainingVotesCountReturn = FunctionReturn< 750 | typeof functions.remainingVotesCount 751 | >; 752 | 753 | export type RenounceRoleParams = FunctionArguments< 754 | typeof functions.renounceRole 755 | >; 756 | export type RenounceRoleReturn = FunctionReturn; 757 | 758 | export type RevealParams = FunctionArguments; 759 | export type RevealReturn = FunctionReturn; 760 | 761 | export type RevokeRoleParams = FunctionArguments; 762 | export type RevokeRoleReturn = FunctionReturn; 763 | 764 | export type SetVoteSignerAddressParams = FunctionArguments< 765 | typeof functions.setVoteSignerAddress 766 | >; 767 | export type SetVoteSignerAddressReturn = FunctionReturn< 768 | typeof functions.setVoteSignerAddress 769 | >; 770 | 771 | export type SupportsInterfaceParams = FunctionArguments< 772 | typeof functions.supportsInterface 773 | >; 774 | export type SupportsInterfaceReturn = FunctionReturn< 775 | typeof functions.supportsInterface 776 | >; 777 | 778 | export type TotalVotesCountParams = FunctionArguments< 779 | typeof functions.totalVotesCount 780 | >; 781 | export type TotalVotesCountReturn = FunctionReturn< 782 | typeof functions.totalVotesCount 783 | >; 784 | 785 | export type UpgradeToAndCallParams = FunctionArguments< 786 | typeof functions.upgradeToAndCall 787 | >; 788 | export type UpgradeToAndCallReturn = FunctionReturn< 789 | typeof functions.upgradeToAndCall 790 | >; 791 | 792 | export type VoteSignerAddressParams = FunctionArguments< 793 | typeof functions.voteSignerAddress 794 | >; 795 | export type VoteSignerAddressReturn = FunctionReturn< 796 | typeof functions.voteSignerAddress 797 | >; 798 | 799 | export type VotesPerCandidatesParams = FunctionArguments< 800 | typeof functions.votesPerCandidates 801 | >; 802 | export type VotesPerCandidatesReturn = FunctionReturn< 803 | typeof functions.votesPerCandidates 804 | >; 805 | --------------------------------------------------------------------------------