diff --git a/.env b/.env.template similarity index 67% rename from .env rename to .env.template index 34800ec..b40d2ad 100644 --- a/.env +++ b/.env.template @@ -1,12 +1,14 @@ +DB_HOST=squid-db DB_NAME=squid -DB_PORT=23798 +DB_PORT=5432 GQL_PORT=4350 +GQL_URL="https://graphql.mydomain.com" # JSON-RPC node endpoints (wss or https) # Use private endpoints in production! # Set urls via secrets if deploying to Cloud: https://docs.subsquid.io/deploy-squid/env-variables/ # OR use the RPC proxy service: https://docs.subsquid.io/deploy-squid/rpc-proxy/ -RPC_ENDPOINT=https://eth-sepolia.g.alchemy.com/v2/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +RPC_ENDPOINT=https://eth-sepolia.g.alchemy.com/v2/XXXXXXXXXXXXXXXXXXXXXXXXXX # Uncommenting this line enables the debug mode # More info at https://docs.subsquid.io/basics/logging/ @@ -15,4 +17,5 @@ RPC_ENDPOINT=https://eth-sepolia.g.alchemy.com/v2/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX EAS_CONTRACT_ADDRESS=0xC2679fBD37d54388Ce493F1DB75320D236e1815e SCHEMA_CONTRACT_ADDRESS=0x0a7E2Ff54e76B8E6659aedc9103FB21c038050D0 -PROJECT_VERIFY_ATTESTATION_SCHEMA=0x9e55855a361f8ff089592b238c6341c880a9228521d87be3fd8fa8922d88c484 +PROJECT_VERIFY_ATTESTATION_SCHEMA=0x3D5854AF182F27966DEA837C446A051B3509224DDC03150E55097B362D111B1B +PROJECT_GIVBACK_ELIGIBLE_ATTESTATION_SCHEMA=0x0000000000000000000000000000000000000000000000000000000000000000 diff --git a/.github/workflows/pipeline-develop.yml b/.github/workflows/pipeline-develop.yml new file mode 100644 index 0000000..c706ce0 --- /dev/null +++ b/.github/workflows/pipeline-develop.yml @@ -0,0 +1,28 @@ +name: auto-deploy-develop +on: + push: + branches: + - develop +jobs: + + build: + name: Build + runs-on: ubuntu-latest + steps: + - name: Production Auto-Deploy + uses: appleboy/ssh-action@v1.0.3 + with: + host: ${{ secrets.STAGING_HOST }} + username: ${{ secrets.STAGING_USERNAME }} + key: ${{ secrets.STAGING_PRIVATE_KEY }} + port: 22 + script: | + cd $HOME/DeVouch-BE + git checkout develop + git checkout -- . + git pull + docker compose stop devouch squid-db + docker compose rm -v -f devouch squid-db + docker rmi -f $(docker images | grep 'devouch' | awk '{print $3}') + docker volume rm $(docker volume ls | grep 'db-data' | awk '{print $2}') + docker compose up -d --build diff --git a/.github/workflows/start-devouch.yml b/.github/workflows/start-devouch.yml new file mode 100644 index 0000000..762a6fa --- /dev/null +++ b/.github/workflows/start-devouch.yml @@ -0,0 +1,21 @@ +name: start-devouch + +on: + workflow_dispatch: + +jobs: + + start: + name: Start Devouch + runs-on: ubuntu-latest + steps: + - name: Start Devouch + uses: appleboy/ssh-action@v1.0.3 + with: + host: ${{ secrets.STAGING_HOST }} + username: ${{ secrets.STAGING_USERNAME }} + key: ${{ secrets.STAGING_PRIVATE_KEY }} + port: 22 + script: | + cd $HOME/DeVouch-BE + docker compose up -d --build diff --git a/.github/workflows/stop-devouch.yml b/.github/workflows/stop-devouch.yml new file mode 100644 index 0000000..bd89580 --- /dev/null +++ b/.github/workflows/stop-devouch.yml @@ -0,0 +1,24 @@ +name: stop-devouch + +on: + workflow_dispatch: + +jobs: + + stop: + name: Stop Devouch + runs-on: ubuntu-latest + steps: + - name: Stop Devouch + uses: appleboy/ssh-action@v1.0.3 + with: + host: ${{ secrets.STAGING_HOST }} + username: ${{ secrets.STAGING_USERNAME }} + key: ${{ secrets.STAGING_PRIVATE_KEY }} + port: 22 + script: | + cd $HOME/DeVouch-BE + docker compose stop devouch squid-db + docker compose rm -f -v devouch squid-db + docker rmi -f $(docker images | grep 'devouch' | awk '{print $3}') + docker volume rm $(docker volume ls | grep 'db-data' | awk '{print $2}') diff --git a/.gitignore b/.gitignore index 84ab48a..976a9a7 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,4 @@ # IDE files /.idea /.vscode -.env +/.env diff --git a/Caddyfile b/Caddyfile new file mode 100644 index 0000000..803bbe8 --- /dev/null +++ b/Caddyfile @@ -0,0 +1,29 @@ +# Global Options +{ + log global { + output file /usr/src/app/global.log + format json + level error + } + servers { + metrics + } + admin 0.0.0.0:2020 +} + +# ADMIN ROUTE #### +:2019 { + route { + @allowed { + path /* + remote_ip {$IP_WHITELIST} + } + reverse_proxy @allowed 127.0.0.1:2020 + respond 403 + } +} + +# HTTP site block +{$GQL_URL} { + reverse_proxy devouch:4350 +} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..47f0882 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,29 @@ +# Use the official Node.js 20 image as a parent image +FROM node:20-alpine + +# Set the working directory in the Docker container +WORKDIR /usr/src/app + +# Install Python and build-essential (for C++ bindings) +RUN apk add --no-cache python3 py3-pip make g++ + +# Copy the package.json and package-lock.json for npm install +COPY package*.json ./ + +# Install any global dependencies +RUN npm install -g @subsquid/cli + +# Install project dependencies +RUN npm ci + +# Copy the rest of the application +COPY . . + +# Build the project using the Squid CLI +RUN sqd build + +# Expose the port the app runs on +EXPOSE 4350 + +# Command to run the application +CMD ["sqd", "run", "."] diff --git a/add-organisation.js b/add-organisation.js index 219901c..26bd405 100644 --- a/add-organisation.js +++ b/add-organisation.js @@ -1,11 +1,11 @@ const createOrganisationAddMigration = require("./db/create-organisation-add-migration").default; -const ORGANISATION_NAME = "Trace"; // Replace with your own organisation name +const ORGANISATION_NAME = "Giveth Verification Team"; // Replace with your own organisation name const SCHEMA_ID = - "0x2e22df9a11e06c306ed8f64ca45ceae02efcf8a443371395a78371bc4fb6f722"; // Replace with your own schema id -const AUTHORIZED_ATTESTOR = "0xF23eA0b5F14afcbe532A1df273F7B233EBe41C78"; -const COLOR = "#FF0000"; + "0xf63f2a7159ee674aa6fce42196a8bb0605eafcf20c19e91a7eafba8d39fa0404"; // Replace with your own schema id +const AUTHORIZED_ATTESTOR = "0x826976d7C600d45FB8287CA1d7c76FC8eb732030"; +const COLOR = "#7f64cb"; function main() { createOrganisationAddMigration( diff --git a/db/create-organisation-add-migration.js b/db/create-organisation-add-migration.js index 8b74853..a9fee93 100644 --- a/db/create-organisation-add-migration.js +++ b/db/create-organisation-add-migration.js @@ -1,11 +1,15 @@ const fs = require("fs"); + +// To make all add organisation migration run after the initial migration +const ADD_ORG_MIGRATION_OFFSET = 10000000000; // More than 317 years! + exports.default = function createOrganisationAddMigration( organisationName, schemaId, authorizedAttestor, color = null ) { - const timestamp = new Date().getTime(); + const timestamp = new Date().getTime() + ADD_ORG_MIGRATION_OFFSET; const fileName = `${timestamp}-Add${organisationName}.js`; const className = `Add${organisationName.replace(/ /g, "")}${timestamp}`; diff --git a/db/migrations/1714572602788-AddTrace.js b/db/migrations/1714572602788-AddTrace.js deleted file mode 100644 index bbcd33e..0000000 --- a/db/migrations/1714572602788-AddTrace.js +++ /dev/null @@ -1,25 +0,0 @@ - -module.exports = class AddTrace1714572602788 { - name = "AddTrace1714572602788"; - - async up(db) { - // add organisation with name "Trace" and schema id "0x2e22df9a11e06c306ed8f64ca45ceae02efcf8a443371395a78371bc4fb6f722" - await db.query( - `INSERT INTO "organisation" ("id", "name", "issuer", "color") - VALUES ( - '0x2e22df9a11e06c306ed8f64ca45ceae02efcf8a443371395a78371bc4fb6f722', - 'Trace', - '0xf23ea0b5f14afcbe532a1df273f7b233ebe41c78', - '#ff0000' - )` - ); - } - - async down(db) { - // remove organisation with name "Trace" and schema id "0x2e22df9a11e06c306ed8f64ca45ceae02efcf8a443371395a78371bc4fb6f722" - await db.query( - `DELETE FROM "organisation" WHERE "id" = '0x2e22df9a11e06c306ed8f64ca45ceae02efcf8a443371395a78371bc4fb6f722'` - ); - } -}; - \ No newline at end of file diff --git a/db/migrations/1714572602760-Data.js b/db/migrations/1715693415593-Data.js similarity index 55% rename from db/migrations/1714572602760-Data.js rename to db/migrations/1715693415593-Data.js index 33bae17..64383ef 100644 --- a/db/migrations/1714572602760-Data.js +++ b/db/migrations/1715693415593-Data.js @@ -1,17 +1,24 @@ -module.exports = class Data1714572602760 { - name = 'Data1714572602760' +module.exports = class Data1715693415593 { + name = 'Data1715693415593' async up(db) { await db.query(`CREATE TABLE "attestor" ("id" character varying NOT NULL, CONSTRAINT "PK_2ba0dae296b9deebeb9ecbbf508" PRIMARY KEY ("id"))`) + await db.query(`CREATE TABLE "project" ("id" character varying NOT NULL, "source" text NOT NULL, "project_id" text NOT NULL, "title" text, "description" text, "total_vouches" integer NOT NULL, "total_flags" integer NOT NULL, "total_attests" integer NOT NULL, "last_updated_timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, CONSTRAINT "PK_4d68b1358bb5b766d3e78f32f57" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_399e8555e92ea7fd5f129fe178" ON "project" ("source") `) + await db.query(`CREATE INDEX "IDX_1a480c5734c5aacb9cef7b1499" ON "project" ("project_id") `) + await db.query(`CREATE TABLE "organisation_project" ("id" character varying NOT NULL, "vouch" boolean NOT NULL, "count" integer NOT NULL, "organisation_id" character varying, "project_id" character varying, CONSTRAINT "PK_4ee2279a4757fecde9a56f003f2" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_202ff9497fc7d9d0c3e7a74b17" ON "organisation_project" ("organisation_id") `) + await db.query(`CREATE INDEX "IDX_356298298d0613568b73c63a1f" ON "organisation_project" ("project_id") `) await db.query(`CREATE TABLE "organisation" ("id" character varying NOT NULL, "name" text NOT NULL, "issuer" text NOT NULL, "color" text, CONSTRAINT "PK_c725ae234ef1b74cce43d2d00c1" PRIMARY KEY ("id"))`) await db.query(`CREATE UNIQUE INDEX "IDX_d9428f9c8e3052d6617e3aab0e" ON "organisation" ("name") `) - await db.query(`CREATE TABLE "attestor_organisation" ("id" character varying NOT NULL, "uid" text NOT NULL, "attest_timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "revoked" boolean NOT NULL, "attestor_id" character varying, "organisation_id" character varying, CONSTRAINT "PK_ac02a8a577635d60275796a9d03" PRIMARY KEY ("id"))`) + await db.query(`CREATE TABLE "attestor_organisation" ("id" character varying NOT NULL, "attest_timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "revoked" boolean NOT NULL, "attestor_id" character varying, "organisation_id" character varying, CONSTRAINT "PK_ac02a8a577635d60275796a9d03" PRIMARY KEY ("id"))`) await db.query(`CREATE INDEX "IDX_22cd09c4533533cebedb5487f4" ON "attestor_organisation" ("attestor_id") `) await db.query(`CREATE INDEX "IDX_b0d947390c1e10152bb1387fa2" ON "attestor_organisation" ("organisation_id") `) - await db.query(`CREATE TABLE "project" ("id" character varying NOT NULL, "source" text NOT NULL, "project_id" text NOT NULL, "title" text, "description" text, "total_vouches" integer NOT NULL, "total_flags" integer NOT NULL, "last_updated_timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, CONSTRAINT "PK_4d68b1358bb5b766d3e78f32f57" PRIMARY KEY ("id"))`) - await db.query(`CREATE TABLE "project_attestation" ("id" character varying NOT NULL, "recipient" text NOT NULL, "vouch_or_flag" boolean NOT NULL, "tx_hash" text NOT NULL, "revoked" boolean NOT NULL, "attest_timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "comment" text, "attestor_organisation_id" character varying, "project_id" character varying, CONSTRAINT "PK_b54887e7eb9193e705303c2b0a0" PRIMARY KEY ("id"))`) + await db.query(`CREATE TABLE "project_attestation" ("id" character varying NOT NULL, "recipient" text NOT NULL, "vouch" boolean NOT NULL, "tx_hash" text NOT NULL, "revoked" boolean NOT NULL, "attest_timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "comment" text, "attestor_organisation_id" character varying, "project_id" character varying, CONSTRAINT "PK_b54887e7eb9193e705303c2b0a0" PRIMARY KEY ("id"))`) await db.query(`CREATE INDEX "IDX_d482a5af31e29569b8b42d9252" ON "project_attestation" ("attestor_organisation_id") `) await db.query(`CREATE INDEX "IDX_1082147528db937cb5b50fb2a0" ON "project_attestation" ("project_id") `) + await db.query(`ALTER TABLE "organisation_project" ADD CONSTRAINT "FK_202ff9497fc7d9d0c3e7a74b17f" FOREIGN KEY ("organisation_id") REFERENCES "organisation"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) + await db.query(`ALTER TABLE "organisation_project" ADD CONSTRAINT "FK_356298298d0613568b73c63a1fc" FOREIGN KEY ("project_id") REFERENCES "project"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) await db.query(`ALTER TABLE "attestor_organisation" ADD CONSTRAINT "FK_22cd09c4533533cebedb5487f44" FOREIGN KEY ("attestor_id") REFERENCES "attestor"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) await db.query(`ALTER TABLE "attestor_organisation" ADD CONSTRAINT "FK_b0d947390c1e10152bb1387fa23" FOREIGN KEY ("organisation_id") REFERENCES "organisation"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) await db.query(`ALTER TABLE "project_attestation" ADD CONSTRAINT "FK_d482a5af31e29569b8b42d92525" FOREIGN KEY ("attestor_organisation_id") REFERENCES "attestor_organisation"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) @@ -20,15 +27,22 @@ module.exports = class Data1714572602760 { async down(db) { await db.query(`DROP TABLE "attestor"`) + await db.query(`DROP TABLE "project"`) + await db.query(`DROP INDEX "public"."IDX_399e8555e92ea7fd5f129fe178"`) + await db.query(`DROP INDEX "public"."IDX_1a480c5734c5aacb9cef7b1499"`) + await db.query(`DROP TABLE "organisation_project"`) + await db.query(`DROP INDEX "public"."IDX_202ff9497fc7d9d0c3e7a74b17"`) + await db.query(`DROP INDEX "public"."IDX_356298298d0613568b73c63a1f"`) await db.query(`DROP TABLE "organisation"`) await db.query(`DROP INDEX "public"."IDX_d9428f9c8e3052d6617e3aab0e"`) await db.query(`DROP TABLE "attestor_organisation"`) await db.query(`DROP INDEX "public"."IDX_22cd09c4533533cebedb5487f4"`) await db.query(`DROP INDEX "public"."IDX_b0d947390c1e10152bb1387fa2"`) - await db.query(`DROP TABLE "project"`) await db.query(`DROP TABLE "project_attestation"`) await db.query(`DROP INDEX "public"."IDX_d482a5af31e29569b8b42d9252"`) await db.query(`DROP INDEX "public"."IDX_1082147528db937cb5b50fb2a0"`) + await db.query(`ALTER TABLE "organisation_project" DROP CONSTRAINT "FK_202ff9497fc7d9d0c3e7a74b17f"`) + await db.query(`ALTER TABLE "organisation_project" DROP CONSTRAINT "FK_356298298d0613568b73c63a1fc"`) await db.query(`ALTER TABLE "attestor_organisation" DROP CONSTRAINT "FK_22cd09c4533533cebedb5487f44"`) await db.query(`ALTER TABLE "attestor_organisation" DROP CONSTRAINT "FK_b0d947390c1e10152bb1387fa23"`) await db.query(`ALTER TABLE "project_attestation" DROP CONSTRAINT "FK_d482a5af31e29569b8b42d92525"`) diff --git a/db/migrations/1725254645149-AddTrace.js b/db/migrations/1725254645149-AddTrace.js new file mode 100644 index 0000000..a81539b --- /dev/null +++ b/db/migrations/1725254645149-AddTrace.js @@ -0,0 +1,23 @@ +module.exports = class AddTrace1725254645149 { + name = "AddTrace1725254645149"; + + async up(db) { + // add organisation with name "Trace" and schema id "0x2e22df9a11e06c306ed8f64ca45ceae02efcf8a443371395a78371bc4fb6f722" + await db.query( + `INSERT INTO "organisation" ("id", "name", "issuer", "color") + VALUES ( + '0x2e22df9a11e06c306ed8f64ca45ceae02efcf8a443371395a78371bc4fb6f722', + 'Trace', + '0xf23ea0b5f14afcbe532a1df273f7b233ebe41c78', + '#ff0000' + )` + ); + } + + async down(db) { + // remove organisation with name "Trace" and schema id "0x2e22df9a11e06c306ed8f64ca45ceae02efcf8a443371395a78371bc4fb6f722" + await db.query( + `DELETE FROM "organisation" WHERE "id" = '0x2e22df9a11e06c306ed8f64ca45ceae02efcf8a443371395a78371bc4fb6f722'` + ); + } +}; diff --git a/db/migrations/1725344741698-AddGiveth Verification Team.js b/db/migrations/1725344741698-AddGiveth Verification Team.js new file mode 100644 index 0000000..7e1148b --- /dev/null +++ b/db/migrations/1725344741698-AddGiveth Verification Team.js @@ -0,0 +1,23 @@ +module.exports = class AddGivethVerificationTeam1725344741698 { + name = "AddGivethVerificationTeam1725344741698"; + + async up(db) { + // add organisation with name "Giveth Verification Team" and schema id "0xf63f2a7159ee674aa6fce42196a8bb0605eafcf20c19e91a7eafba8d39fa0404" + await db.query( + `INSERT INTO "organisation" ("id", "name", "issuer", "color") + VALUES ( + '0xf63f2a7159ee674aa6fce42196a8bb0605eafcf20c19e91a7eafba8d39fa0404', + 'Giveth Verification Team', + '0x826976d7c600d45fb8287ca1d7c76fc8eb732030', + '#7f64cb' + )` + ); + } + + async down(db) { + // remove organisation with name "Giveth Verification Team" and schema id "0xf63f2a7159ee674aa6fce42196a8bb0605eafcf20c19e91a7eafba8d39fa0404" + await db.query( + `DELETE FROM "organisation" WHERE "id" = '0xf63f2a7159ee674aa6fce42196a8bb0605eafcf20c19e91a7eafba8d39fa0404'` + ); + } +}; diff --git a/docker-compose-test.yml b/docker-compose-potgres-test.yml similarity index 100% rename from docker-compose-test.yml rename to docker-compose-potgres-test.yml diff --git a/docker-compose-potgres.yml b/docker-compose-potgres.yml new file mode 100644 index 0000000..8e4a6dc --- /dev/null +++ b/docker-compose-potgres.yml @@ -0,0 +1,12 @@ +version: "3" + +services: + db: + image: postgres:15 + environment: + POSTGRES_DB: squid + POSTGRES_PASSWORD: postgres + ports: + - "${DB_PORT}:5432" + # command: ["postgres", "-c", "log_statement=all"] + shm_size: 1gb diff --git a/docker-compose.yml b/docker-compose.yml index 8e4a6dc..64db4e4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,8 +1,12 @@ version: "3" services: - db: + squid-db: image: postgres:15 + container_name: squid-db + restart: always + networks: + - devouch-be environment: POSTGRES_DB: squid POSTGRES_PASSWORD: postgres @@ -10,3 +14,49 @@ services: - "${DB_PORT}:5432" # command: ["postgres", "-c", "log_statement=all"] shm_size: 1gb + volumes: + - db-data:/var/lib/postgresql/data + + devouch: + build: + context: . + dockerfile: Dockerfile + container_name: devouch-be + restart: always + networks: + - devouch-be + env_file: + - .env + ports: + - "${GQL_PORT}:4350" + depends_on: + - squid-db + + caddy: + image: caddy:2.7.5 + container_name: caddy + restart: always + networks: + - devouch-be + ports: + - 80:80 + - 443:443 + - 2019:2019 + - 2020:2020 + env_file: + - .env + environment: + GQL_URL: ${GQL_URL:-} + volumes: + - caddy_data:/data + - caddy_config:/config + - ./Caddyfile:/etc/caddy/Caddyfile + +volumes: + caddy_data: + caddy_config: + db-data: + +networks: + devouch-be: + driver: bridge diff --git a/jest.config.js b/jest.config.js index 7c43702..c8eb546 100644 --- a/jest.config.js +++ b/jest.config.js @@ -3,8 +3,11 @@ module.exports = { // tests pat preset: "ts-jest", testEnvironment: "node", - setupFiles: ["./src/test/bootstrap.ts"], - testMatch: ["/src/test/*.test.ts"], + // setupFiles: ["./src/test/bootstrap.ts"], + testMatch: [ + "/src/test/*.test.ts", + "/src/controllers/utils/*.test.ts", + ], forceExit: true, detectOpenHandles: true, testTimeout: 30000, diff --git a/package-lock.json b/package-lock.json index 6330478..ed3f44b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,16 +7,17 @@ "name": "squid-evm-template", "dependencies": { "@ethereum-attestation-service/eas-sdk": "^1.5.0", - "@subsquid/archive-registry": "^3.3.0", - "@subsquid/evm-processor": "^1.14.1", - "@subsquid/graphql-server": "^4.5.0", + "@subsquid/archive-registry": "^3.3.2", + "@subsquid/evm-processor": "^1.18.1", + "@subsquid/graphql-server": "^4.6.0", "@subsquid/typeorm-migration": "^1.3.0", - "@subsquid/typeorm-store": "^1.2.6", + "@subsquid/typeorm-store": "^1.4.0", "dotenv": "^16.4.4", - "ethers": "^6.11.1", - "pg": "^8.11.3", + "ethers": "^6.12.1", + "pg": "^8.11.5", + "type-graphql": "^1.2.0-rc.1", "typeorm": "^0.3.20", - "zod": "^3.23.4" + "zod": "^3.23.8" }, "devDependencies": { "@dotenvx/dotenvx": "^0.35.1", @@ -3548,9 +3549,10 @@ "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==" }, "node_modules/@subsquid/archive-registry": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@subsquid/archive-registry/-/archive-registry-3.3.0.tgz", - "integrity": "sha512-moXnGNOSmKMHyuDvUiOHbpL7IePSo4XZOW9OnzESIND64eGxlKusIsCi8Nth1DgzbE/UTTFeNJvYKu0v7SOIqw==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@subsquid/archive-registry/-/archive-registry-3.3.2.tgz", + "integrity": "sha512-8Zt+Kr8z/mapouRsr90EbfMH2t5KaaSOg7oQYmIqF8ndUeEwa2b23Bb8xNaoo3uvVL2UUM9oxxuxmCf9ixZC7Q==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "dependencies": { "@subsquid/util-internal": "^1.0.0", "commander": "^10.0.0", @@ -3562,27 +3564,27 @@ } }, "node_modules/@subsquid/evm-processor": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/@subsquid/evm-processor/-/evm-processor-1.17.1.tgz", - "integrity": "sha512-WQrcrfiDTi745n9dk7g0z1woUUuzWUASSfcPNHcFgVmR5X0L1Kak5Zhlb+g7GS9hg0kh94YS/Mlm82YROQYfKg==", + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/@subsquid/evm-processor/-/evm-processor-1.18.1.tgz", + "integrity": "sha512-3BzG9PrzbYgUUopEXo0RAwvO0Kec1RX2ekq68r3w2S3+WUtGvZwFgOSTzGrryofHg4Sqcu+91UmdBC1+xdM+xw==", "dependencies": { "@subsquid/http-client": "^1.4.0", "@subsquid/logger": "^1.3.3", - "@subsquid/rpc-client": "^4.7.0", - "@subsquid/util-internal": "^3.1.0", + "@subsquid/rpc-client": "^4.9.0", + "@subsquid/util-internal": "^3.2.0", "@subsquid/util-internal-archive-client": "^0.1.2", "@subsquid/util-internal-hex": "^1.2.2", "@subsquid/util-internal-ingest-tools": "^1.1.2", - "@subsquid/util-internal-processor-tools": "^4.1.0", + "@subsquid/util-internal-processor-tools": "^4.1.1", "@subsquid/util-internal-range": "^0.3.0", - "@subsquid/util-internal-validation": "^0.4.0", + "@subsquid/util-internal-validation": "^0.5.0", "@subsquid/util-timeout": "^2.3.2" } }, "node_modules/@subsquid/evm-processor/node_modules/@subsquid/util-internal": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@subsquid/util-internal/-/util-internal-3.1.0.tgz", - "integrity": "sha512-m1lIiy7Tc2+QR5Jcx9eGsVsB4ASR/bA5Z9gnB+qUy1BzYuz5FEiJOYCQm6J5Bt+VnYDYYyANEUMq4Cl3J5wuSg==" + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@subsquid/util-internal/-/util-internal-3.2.0.tgz", + "integrity": "sha512-foNCjOmZaP8MKMa9sNe2GXTjFSDM9UqA0I0C0/ZvCxM1lCmG3mxZb70f8Wyi7TePXC/eV8eARbIqFyz0GjQmzA==" }, "node_modules/@subsquid/evm-typegen": { "version": "3.3.0", @@ -3634,9 +3636,9 @@ "integrity": "sha512-C89mus6IXnNi0xMQrZqUFBZwLj8tbuq9lye8Gq/lHmmERAUpi6UsWEyLdJLx2mneZzF3JtY8eNiiZ16jmjtvfw==" }, "node_modules/@subsquid/graphql-server": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@subsquid/graphql-server/-/graphql-server-4.5.1.tgz", - "integrity": "sha512-PMC5Kc9ptjhrBIBRvvhkHiSRUEGJqv6Bc838VnvLtF5kMrGjSI+pPUiA5t+rhdfxywX1j+ZEdBpaRypAGo6Otw==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@subsquid/graphql-server/-/graphql-server-4.6.0.tgz", + "integrity": "sha512-urQr5yLgYaPKSkCDQQ7ZwmpnYDOFfUSIKGxA7qk5LFoJlIJ+ycO4rkzb3ln29LrhSw25D3E/ZfrDlS+PFnV4ZA==", "dependencies": { "@apollo/utils.keyvadapter": "~1.1.2", "@apollo/utils.keyvaluecache": "~1.0.2", @@ -3645,10 +3647,10 @@ "@graphql-tools/utils": "^10.0.11", "@keyv/redis": "~2.5.8", "@subsquid/logger": "^1.3.3", - "@subsquid/openreader": "^4.5.1", - "@subsquid/typeorm-config": "^4.1.0", - "@subsquid/util-internal": "^3.1.0", - "@subsquid/util-internal-commander": "^1.3.2", + "@subsquid/openreader": "^4.6.0", + "@subsquid/typeorm-config": "^4.1.1", + "@subsquid/util-internal": "^3.2.0", + "@subsquid/util-internal-commander": "^1.4.0", "@subsquid/util-internal-http-server": "^2.0.0", "@subsquid/util-internal-ts-node": "^0.0.0", "apollo-server-core": "^3.13.0", @@ -3688,14 +3690,14 @@ } }, "node_modules/@subsquid/graphql-server/node_modules/@subsquid/util-internal": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@subsquid/util-internal/-/util-internal-3.1.0.tgz", - "integrity": "sha512-m1lIiy7Tc2+QR5Jcx9eGsVsB4ASR/bA5Z9gnB+qUy1BzYuz5FEiJOYCQm6J5Bt+VnYDYYyANEUMq4Cl3J5wuSg==" + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@subsquid/util-internal/-/util-internal-3.2.0.tgz", + "integrity": "sha512-foNCjOmZaP8MKMa9sNe2GXTjFSDM9UqA0I0C0/ZvCxM1lCmG3mxZb70f8Wyi7TePXC/eV8eARbIqFyz0GjQmzA==" }, "node_modules/@subsquid/graphql-server/node_modules/@subsquid/util-internal-commander": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@subsquid/util-internal-commander/-/util-internal-commander-1.3.2.tgz", - "integrity": "sha512-9/1vI1dmGQMp5wjN6hb94VCnSosT+caob33tAesFaIdqLzqQlDtlTSRq1TFFossAgtsEJFi7GiQ8i31L/gaxSQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@subsquid/util-internal-commander/-/util-internal-commander-1.4.0.tgz", + "integrity": "sha512-I+IztlLVow9z2S5lK/ON4aBRYXKtAKXl/rVPUn1Ue5vq+5JgEFbWEKJgnwXkd0qKnKeoYeaRFlcyQVfxirxzJw==", "peerDependencies": { "commander": "^11.1.0" } @@ -3734,18 +3736,18 @@ } }, "node_modules/@subsquid/openreader": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@subsquid/openreader/-/openreader-4.5.1.tgz", - "integrity": "sha512-N4aFZ0AzYiFCzocqbW971M8LOIKo9E8d4lt5He4x8Jcm19hwYYlNy8wwnOv1USCxFO/uCPZm/RTkARlyjOL10A==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@subsquid/openreader/-/openreader-4.6.0.tgz", + "integrity": "sha512-/05vSoKP3UHxU6GJYfKcfSIi4bWfzbr9x/H2wW5XxE4uc8gFeaVZHCmQMTaO9941oK//9rk9BQTH5Slhwuj7gw==", "dependencies": { "@graphql-tools/merge": "^9.0.1", "@subsquid/graphiql-console": "^0.3.0", "@subsquid/logger": "^1.3.3", - "@subsquid/util-internal": "^3.1.0", - "@subsquid/util-internal-commander": "^1.3.2", + "@subsquid/util-internal": "^3.2.0", + "@subsquid/util-internal-commander": "^1.4.0", "@subsquid/util-internal-hex": "^1.2.2", "@subsquid/util-internal-http-server": "^2.0.0", - "@subsquid/util-naming": "^1.2.2", + "@subsquid/util-naming": "^1.3.0", "apollo-server-core": "^3.13.0", "apollo-server-express": "^3.13.0", "commander": "^11.1.0", @@ -3770,14 +3772,14 @@ } }, "node_modules/@subsquid/openreader/node_modules/@subsquid/util-internal": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@subsquid/util-internal/-/util-internal-3.1.0.tgz", - "integrity": "sha512-m1lIiy7Tc2+QR5Jcx9eGsVsB4ASR/bA5Z9gnB+qUy1BzYuz5FEiJOYCQm6J5Bt+VnYDYYyANEUMq4Cl3J5wuSg==" + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@subsquid/util-internal/-/util-internal-3.2.0.tgz", + "integrity": "sha512-foNCjOmZaP8MKMa9sNe2GXTjFSDM9UqA0I0C0/ZvCxM1lCmG3mxZb70f8Wyi7TePXC/eV8eARbIqFyz0GjQmzA==" }, "node_modules/@subsquid/openreader/node_modules/@subsquid/util-internal-commander": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@subsquid/util-internal-commander/-/util-internal-commander-1.3.2.tgz", - "integrity": "sha512-9/1vI1dmGQMp5wjN6hb94VCnSosT+caob33tAesFaIdqLzqQlDtlTSRq1TFFossAgtsEJFi7GiQ8i31L/gaxSQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@subsquid/util-internal-commander/-/util-internal-commander-1.4.0.tgz", + "integrity": "sha512-I+IztlLVow9z2S5lK/ON4aBRYXKtAKXl/rVPUn1Ue5vq+5JgEFbWEKJgnwXkd0qKnKeoYeaRFlcyQVfxirxzJw==", "peerDependencies": { "commander": "^11.1.0" } @@ -3791,13 +3793,13 @@ } }, "node_modules/@subsquid/rpc-client": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@subsquid/rpc-client/-/rpc-client-4.7.0.tgz", - "integrity": "sha512-RPVLg4X+C/4QiowGMT2gdJfqshmGFgg0RzPsoRLkTgVojqnHIF4Kv7ytSvKidQLmR75aHGn8BUZ3raLwAD+YLA==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@subsquid/rpc-client/-/rpc-client-4.9.0.tgz", + "integrity": "sha512-lpb6qRMMlaacXOFPRhv4CZ7g4w7pKIR7ZEbMjyFexLOdv9MkcYzuGD5XT5REGaBA6mfQMaLa33K5lqAb+tJKBQ==", "dependencies": { "@subsquid/http-client": "^1.4.0", "@subsquid/logger": "^1.3.3", - "@subsquid/util-internal": "^3.1.0", + "@subsquid/util-internal": "^3.2.0", "@subsquid/util-internal-binary-heap": "^1.0.0", "@subsquid/util-internal-counters": "^1.3.2", "@subsquid/util-internal-json-fix-unsafe-integers": "^0.0.0", @@ -3805,9 +3807,9 @@ } }, "node_modules/@subsquid/rpc-client/node_modules/@subsquid/util-internal": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@subsquid/util-internal/-/util-internal-3.1.0.tgz", - "integrity": "sha512-m1lIiy7Tc2+QR5Jcx9eGsVsB4ASR/bA5Z9gnB+qUy1BzYuz5FEiJOYCQm6J5Bt+VnYDYYyANEUMq4Cl3J5wuSg==" + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@subsquid/util-internal/-/util-internal-3.2.0.tgz", + "integrity": "sha512-foNCjOmZaP8MKMa9sNe2GXTjFSDM9UqA0I0C0/ZvCxM1lCmG3mxZb70f8Wyi7TePXC/eV8eARbIqFyz0GjQmzA==" }, "node_modules/@subsquid/typeorm-codegen": { "version": "1.3.3", @@ -3841,13 +3843,13 @@ } }, "node_modules/@subsquid/typeorm-config": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@subsquid/typeorm-config/-/typeorm-config-4.1.0.tgz", - "integrity": "sha512-onZsHQIypCTRguOeec1Gmm+6EPlTg63QNE7Y74nrbBb4sabZtrK3K+EQJId4KIdk/kxA2Wi/g5H/VDNZNFSe9A==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@subsquid/typeorm-config/-/typeorm-config-4.1.1.tgz", + "integrity": "sha512-3T2L2jmFIRYxWHL/w4rMuaSiHLhDywQWPKtfD3TaSohjXR+VdDG5XimDMmSwM4dzQTBToGpnfUEkzH3v1+EnCg==", "dependencies": { - "@subsquid/logger": "^1.3.2", + "@subsquid/logger": "^1.3.3", "@subsquid/util-internal-ts-node": "^0.0.0", - "@subsquid/util-naming": "^1.2.2" + "@subsquid/util-naming": "^1.3.0" }, "peerDependencies": { "typeorm": "^0.3.17" @@ -3895,9 +3897,9 @@ } }, "node_modules/@subsquid/typeorm-store": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@subsquid/typeorm-store/-/typeorm-store-1.3.0.tgz", - "integrity": "sha512-32jwKA2fEYuGpyKEisKOlrz6JTd0hXTXOLC9S4IIsOnhkY70Ua69QDe0CCTgKTrvMNOCwk3gPR+q8RwrlEMupQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@subsquid/typeorm-store/-/typeorm-store-1.4.0.tgz", + "integrity": "sha512-Ff96ySH3btbGC8QgFkMEtuylz8lrBeAcbBSy/XF9pQLDdpun01yxekJwmKKPswQxYqSQgwEt38Nmp2jvC0BysQ==", "dependencies": { "@subsquid/typeorm-config": "^4.1.0", "@subsquid/util-internal": "^3.1.0" @@ -4005,22 +4007,23 @@ "integrity": "sha512-mtbN15IgXtV4yo98RQla+O3DhFwB28o3JTBrFuBc/i/qzxyZNbKoVdq/uczomGdXrHxGkWhTDe/istIQe9gn6w==" }, "node_modules/@subsquid/util-internal-processor-tools": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@subsquid/util-internal-processor-tools/-/util-internal-processor-tools-4.1.0.tgz", - "integrity": "sha512-7yOsPTB+sTSntNa79NVHo+lHFFs/QtJttut0IYp89gXNEOhAZdJ2MrwawHVTW+DEZ+UILMOO356fos42On9yGw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@subsquid/util-internal-processor-tools/-/util-internal-processor-tools-4.1.1.tgz", + "integrity": "sha512-zzisejusRteAvwjqFDLlFapH9b86E8GdfNswuNWSjASE+VWadJ/PLfrlXFnsbAo4SxPKtqXWEewoK8cjzVjaZA==", "dependencies": { "@subsquid/logger": "^1.3.3", - "@subsquid/util-internal": "^3.1.0", + "@subsquid/util-internal": "^3.2.0", "@subsquid/util-internal-counters": "^1.3.2", "@subsquid/util-internal-prometheus-server": "^1.3.0", "@subsquid/util-internal-range": "^0.3.0", + "@subsquid/util-internal-squid-id": "^0.0.0", "prom-client": "^14.2.0" } }, "node_modules/@subsquid/util-internal-processor-tools/node_modules/@subsquid/util-internal": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@subsquid/util-internal/-/util-internal-3.1.0.tgz", - "integrity": "sha512-m1lIiy7Tc2+QR5Jcx9eGsVsB4ASR/bA5Z9gnB+qUy1BzYuz5FEiJOYCQm6J5Bt+VnYDYYyANEUMq4Cl3J5wuSg==" + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@subsquid/util-internal/-/util-internal-3.2.0.tgz", + "integrity": "sha512-foNCjOmZaP8MKMa9sNe2GXTjFSDM9UqA0I0C0/ZvCxM1lCmG3mxZb70f8Wyi7TePXC/eV8eARbIqFyz0GjQmzA==" }, "node_modules/@subsquid/util-internal-prometheus-server": { "version": "1.3.0", @@ -4047,15 +4050,20 @@ "resolved": "https://registry.npmjs.org/@subsquid/util-internal/-/util-internal-3.1.0.tgz", "integrity": "sha512-m1lIiy7Tc2+QR5Jcx9eGsVsB4ASR/bA5Z9gnB+qUy1BzYuz5FEiJOYCQm6J5Bt+VnYDYYyANEUMq4Cl3J5wuSg==" }, + "node_modules/@subsquid/util-internal-squid-id": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@subsquid/util-internal-squid-id/-/util-internal-squid-id-0.0.0.tgz", + "integrity": "sha512-LyVZIGUbC87r+3VFBRiNOEycxvpkOEEjt5enY02iGl6MneLwq3m17D44xAkwfFj/U+t7GA76eeHIoI2ZkiQKog==" + }, "node_modules/@subsquid/util-internal-ts-node": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/@subsquid/util-internal-ts-node/-/util-internal-ts-node-0.0.0.tgz", "integrity": "sha512-VBnrKrkNcqbT3hMLrjpEPuwMAihFhW9oUmK53bccBCCXrUiATNUblQD2S4IWd9/UBO5Q33ohpbE9sAodDq2DXw==" }, "node_modules/@subsquid/util-internal-validation": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@subsquid/util-internal-validation/-/util-internal-validation-0.4.0.tgz", - "integrity": "sha512-YrwzbMd2Ez4ufCHDWc/AZpNqZ8LR/YCJ+1Aox5HF46INvkFVun5g5YI3HjXReMwvwrpT4Gsy+JvDTYvaHtTUlQ==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@subsquid/util-internal-validation/-/util-internal-validation-0.5.0.tgz", + "integrity": "sha512-mGiwOzc/Fq651CbFe7aEpERXBr+BkqAz8cDpqTVAsve6ghvB5tvwlAv7i1MWnyt/g7OqX1LhYMhh7NFipyMeyA==", "peerDependencies": { "@subsquid/logger": "^1.3.3" }, @@ -4066,9 +4074,9 @@ } }, "node_modules/@subsquid/util-naming": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@subsquid/util-naming/-/util-naming-1.2.2.tgz", - "integrity": "sha512-NveXOiAbWiWkWd3Iv2jEwSAKvQHOG/HfIsPmmNab8TPX/XgJ6J5Ngx6lHEiqs746m4sOhZ2yipxKAEDgrERaxA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@subsquid/util-naming/-/util-naming-1.3.0.tgz", + "integrity": "sha512-PfYg1uFHwb7e6egbkzIbQTWf7DVlZIQr2gHy4VE35ZNiA15R9wkJLo/Mym6OkwLQyjJwhhq7pCFhkz6tm19m+A==", "dependencies": { "camelcase": "^6.3.0", "inflected": "^2.1.0" @@ -4203,6 +4211,15 @@ "@types/range-parser": "*" } }, + "node_modules/@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", @@ -4266,6 +4283,11 @@ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.4.tgz", "integrity": "sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw==" }, + "node_modules/@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==" + }, "node_modules/@types/mute-stream": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.4.tgz", @@ -4323,6 +4345,11 @@ "@types/node": "*" } }, + "node_modules/@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==" + }, "node_modules/@types/serve-static": { "version": "1.15.5", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", @@ -4345,6 +4372,12 @@ "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", "dev": true }, + "node_modules/@types/validator": { + "version": "13.11.10", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.10.tgz", + "integrity": "sha512-e2PNXoXLr6Z+dbfx5zSh9TRlXJrELycxiaXznp4S5+D2M3b9bqJEitNHA5923jhnB2zzFiZHa2f0SI1HoIahpg==", + "peer": true + }, "node_modules/@types/wrap-ansi": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz", @@ -5319,14 +5352,6 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, - "node_modules/buffer-writer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", - "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", - "engines": { - "node": ">=4" - } - }, "node_modules/buffer-xor": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", @@ -5504,6 +5529,17 @@ "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", "dev": true }, + "node_modules/class-validator": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.1.tgz", + "integrity": "sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ==", + "peer": true, + "dependencies": { + "@types/validator": "^13.11.8", + "libphonenumber-js": "^1.10.53", + "validator": "^13.9.0" + } + }, "node_modules/classic-level": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/classic-level/-/classic-level-1.4.1.tgz", @@ -6004,12 +6040,15 @@ "dev": true }, "node_modules/d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", + "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", "dependencies": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" + "es5-ext": "^0.10.64", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.12" } }, "node_modules/data-uri-to-buffer": { @@ -6436,13 +6475,14 @@ } }, "node_modules/es5-ext": { - "version": "0.10.62", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", - "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", "hasInstallScript": true, "dependencies": { "es6-iterator": "^2.0.3", "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", "next-tick": "^1.1.0" }, "engines": { @@ -6460,12 +6500,15 @@ } }, "node_modules/es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", + "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", "dependencies": { - "d": "^1.0.1", - "ext": "^1.1.2" + "d": "^1.0.2", + "ext": "^1.7.0" + }, + "engines": { + "node": ">=0.12" } }, "node_modules/escalade": { @@ -6492,6 +6535,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -6599,9 +6656,9 @@ } }, "node_modules/ethers": { - "version": "6.11.1", - "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.11.1.tgz", - "integrity": "sha512-mxTAE6wqJQAbp5QAe/+o+rXOID7Nw91OZXvgpjDa1r4fAbq2Nu314oEZSbjoRLacuCzs7kUC3clEvkCQowffGg==", + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.12.1.tgz", + "integrity": "sha512-j6wcVoZf06nqEcBbDWkKg8Fp895SS96dSnTCjiXT+8vt2o02raTn4Lo9ERUuIVU5bAjoPYeA+7ytQFexFmLuVw==", "funding": [ { "type": "individual", @@ -6668,6 +6725,15 @@ "npm": ">=3" } }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, "node_modules/evp_bytestokey": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", @@ -6817,11 +6883,6 @@ "type": "^2.7.2" } }, - "node_modules/ext/node_modules/type": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", - "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -7230,6 +7291,28 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/graphql-query-complexity": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/graphql-query-complexity/-/graphql-query-complexity-0.7.2.tgz", + "integrity": "sha512-+VgmrfxGEjHI3zuojWOR8bsz7Ycz/BZjNjxnlUieTz5DsB92WoIrYCSZdWG7UWZ3rfcA1Gb2Nf+wB80GsaZWuQ==", + "dependencies": { + "lodash.get": "^4.4.2" + }, + "peerDependencies": { + "graphql": "^0.13.0 || ^14.0.0 || ^15.0.0" + } + }, + "node_modules/graphql-subscriptions": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/graphql-subscriptions/-/graphql-subscriptions-1.2.1.tgz", + "integrity": "sha512-95yD/tKi24q8xYa7Q9rhQN16AYj5wPbrb8tmHGM3WRc9EBmWrG/0kkMl+tQG8wcEuE9ibR4zyOM31p5Sdr2v4g==", + "dependencies": { + "iterall": "^1.3.0" + }, + "peerDependencies": { + "graphql": "^0.10.5 || ^0.11.3 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0" + } + }, "node_modules/graphql-tag": { "version": "2.12.6", "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz", @@ -8323,6 +8406,11 @@ "node": ">=8" } }, + "node_modules/iterall": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.3.0.tgz", + "integrity": "sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==" + }, "node_modules/jackspeak": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", @@ -9236,6 +9324,12 @@ "node": ">=6" } }, + "node_modules/libphonenumber-js": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.11.1.tgz", + "integrity": "sha512-Wze1LPwcnzvcKGcRHFGFECTaLzxOtujwpf924difr5zniyYv1C2PiW0419qDR7m8lKDxsImu5mwxFuXhXpjmvw==", + "peer": true + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -9264,6 +9358,11 @@ "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" + }, "node_modules/lodash.isarguments": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", @@ -10183,11 +10282,6 @@ "node": ">=4" } }, - "node_modules/packet-reader": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", - "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" - }, "node_modules/pako": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", @@ -10310,15 +10404,13 @@ } }, "node_modules/pg": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz", - "integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==", - "dependencies": { - "buffer-writer": "2.0.0", - "packet-reader": "1.0.0", - "pg-connection-string": "^2.6.2", - "pg-pool": "^3.6.1", - "pg-protocol": "^1.6.0", + "version": "8.11.5", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.5.tgz", + "integrity": "sha512-jqgNHSKL5cbDjFlHyYsCXmQDrfIX/3RsNwYqpd4N0Kt8niLuNoRNH+aazv6cOd43gPh9Y4DjQCtb+X0MH0Hvnw==", + "dependencies": { + "pg-connection-string": "^2.6.4", + "pg-pool": "^3.6.2", + "pg-protocol": "^1.6.1", "pg-types": "^2.1.0", "pgpass": "1.x" }, @@ -10344,9 +10436,9 @@ "optional": true }, "node_modules/pg-connection-string": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", - "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.4.tgz", + "integrity": "sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==" }, "node_modules/pg-int8": { "version": "1.0.1", @@ -10357,17 +10449,17 @@ } }, "node_modules/pg-pool": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz", - "integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.2.tgz", + "integrity": "sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==", "peerDependencies": { "pg": ">=8.0" } }, "node_modules/pg-protocol": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", - "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==" + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.1.tgz", + "integrity": "sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==" }, "node_modules/pg-types": { "version": "2.2.0", @@ -11919,9 +12011,9 @@ "integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==" }, "node_modules/type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" }, "node_modules/type-detect": { "version": "4.0.8", @@ -11943,6 +12035,72 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/type-graphql": { + "version": "1.2.0-rc.1", + "resolved": "https://registry.npmjs.org/type-graphql/-/type-graphql-1.2.0-rc.1.tgz", + "integrity": "sha512-W1p51DN+n/zX4ilunMC6/FcyGlx/ND3hreQ0ARDhfhyR9oGtfKzQNnkHhk8uXlYm2zzyTEd1LkRHJr8bbnRlIA==", + "hasInstallScript": true, + "dependencies": { + "@types/glob": "^7.1.3", + "@types/node": "*", + "@types/semver": "^7.3.4", + "glob": "^7.1.6", + "graphql-query-complexity": "^0.7.2", + "graphql-subscriptions": "^1.2.0", + "semver": "^7.3.4", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">= 10.13" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typegraphql" + }, + "peerDependencies": { + "class-validator": ">=0.12.0", + "graphql": "^15.5.0" + } + }, + "node_modules/type-graphql/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/type-graphql/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/type-graphql/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -12230,6 +12388,15 @@ "node": ">=10.12.0" } }, + "node_modules/validator": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", + "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==", + "peer": true, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/value-or-promise": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.12.tgz", @@ -12676,9 +12843,9 @@ } }, "node_modules/zod": { - "version": "3.23.4", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.4.tgz", - "integrity": "sha512-/AtWOKbBgjzEYYQRNfoGKHObgfAZag6qUJX1VbHo2PRBgS+wfWagEY2mizjfyAPcGesrJOcx/wcl0L9WnVrHFw==", + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/package.json b/package.json index e0b0b0f..c6ae277 100644 --- a/package.json +++ b/package.json @@ -4,21 +4,25 @@ "scripts": { "build": "rm -rf lib && tsc", "test:run-migration": "dotenvx run --env-file=.env.test -- squid-typeorm-migration apply", - "test:run-fresh-db": "docker-compose -f docker-compose-test.yml down -v; docker-compose --env-file .env.test -f docker-compose-test.yml up -d", - "test": "npm run test:run-fresh-db; jest" + "test:run-fresh-db": "docker-compose -f docker-compose-potgres-test.yml down -v; docker-compose --env-file .env.test -f docker-compose-potgres-test.yml up -d", + "test": "npm run test:run-fresh-db; dotenvx run --env-file=.env.test -- jest --runInBand", + "clear:generate:migration:run:locally": "sqd codegen ; docker compose -f docker-compose-potgres.yml down -v;docker compose -f docker-compose-potgres.yml up -d;sqd migration:generate; sqd run", + "clear:run:locally": "sqd build ; docker compose -f docker-compose-potgres.yml down -v;docker compose -f docker-compose-potgres.yml up -d; sqd run", + "run:locally": "docker compose -f docker-compose-potgres.yml up -d; sqd build; sqd run" }, "dependencies": { "@ethereum-attestation-service/eas-sdk": "^1.5.0", - "@subsquid/archive-registry": "^3.3.0", - "@subsquid/evm-processor": "^1.14.1", - "@subsquid/graphql-server": "^4.5.0", + "@subsquid/archive-registry": "^3.3.2", + "@subsquid/evm-processor": "^1.18.1", + "@subsquid/graphql-server": "^4.6.0", "@subsquid/typeorm-migration": "^1.3.0", - "@subsquid/typeorm-store": "^1.2.6", + "@subsquid/typeorm-store": "^1.4.0", "dotenv": "^16.4.4", - "ethers": "^6.11.1", - "pg": "^8.11.3", + "ethers": "^6.12.1", + "pg": "^8.11.5", + "type-graphql": "^1.2.0-rc.1", "typeorm": "^0.3.20", - "zod": "^3.23.4" + "zod": "^3.23.8" }, "devDependencies": { "@dotenvx/dotenvx": "^0.35.1", diff --git a/schema.graphql b/schema.graphql index ec9da3c..e8861ea 100644 --- a/schema.graphql +++ b/schema.graphql @@ -2,7 +2,7 @@ type ProjectAttestation @entity { "UID of the attestation" id: ID! recipient: String! - vouchOrFlag: Boolean! + vouch: Boolean! txHash: String! revoked: Boolean! attestorOrganisation: AttestorOrganisation! @@ -12,9 +12,8 @@ type ProjectAttestation @entity { } type AttestorOrganisation @entity { - id: ID! "UID of the attestation made the relationship" - uid: String! + id: ID! attestor: Attestor! organisation: Organisation! "Timestamp at which the relationship was created" @@ -40,15 +39,16 @@ type Organisation @entity { color: String "Organization Attestors" attestors: [AttestorOrganisation!]! @derivedFrom(field: "organisation") + attestedProjects: [OrganisationProject!]! @derivedFrom(field: "organisation") } type Project @entity { "Project Source and Project ID separated by a hyphen" id: ID! "Source of the project" - source: String! + source: String! @index "Project ID. Unique within the source" - projectId: String! + projectId: String! @index "Title of the project" title: String "Description of the project" @@ -57,8 +57,18 @@ type Project @entity { totalVouches: Int! "Total attests with value False" totalFlags: Int! - # givbackEligibleTrue: Int! - # givbackEligibleFalse: Int! + "Total attests" + totalAttests: Int! lastUpdatedTimestamp: DateTime! attests: [ProjectAttestation!]! @derivedFrom(field: "project") + attestedOrganisations: [OrganisationProject!]! @derivedFrom(field: "project") +} + +type OrganisationProject @entity { + "-" + id: ID! + organisation: Organisation! + project: Project! + vouch: Boolean! + count: Int! } diff --git a/src/constants.ts b/src/constants.ts index 6f20f72..577c2c5 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -10,3 +10,5 @@ export const SCHEMA_CONTRACT_ADDRESS = assertNotNull( export const PROJECT_VERIFY_SCHEMA = assertNotNull( process.env.PROJECT_VERIFY_ATTESTATION_SCHEMA ).toLocaleLowerCase(); + +export const START_BLOCK = parseInt(process.env.START_BLOCK || "5815457"); diff --git a/src/controllers/authorizeAttestation.ts b/src/controllers/authorizeAttestation.ts index 4fbc2f2..0938ee9 100644 --- a/src/controllers/authorizeAttestation.ts +++ b/src/controllers/authorizeAttestation.ts @@ -2,7 +2,7 @@ import { DataHandlerContext, Log } from "@subsquid/evm-processor"; import { Store } from "@subsquid/typeorm-store"; import { Attestor, AttestorOrganisation, Organisation } from "../model"; import * as EASContract from "../abi/EAS"; -import { getAttestationData } from "./utils/easHelper"; +import { getAttestor } from "./utils/modelHelper"; export const handleAuthorize = async ( ctx: DataHandlerContext, @@ -24,8 +24,6 @@ export const handleAuthorize = async ( ctx.log.debug(`Processing authorize attestation with uid: ${uid}`); - const decodedData = await getAttestationData(ctx, log.block, uid, schemaUid); - const accountAddress = recipient.toLocaleLowerCase(); if (!accountAddress) { @@ -33,16 +31,14 @@ export const handleAuthorize = async ( return; } - const attestor = new Attestor({ id: accountAddress }); - await ctx.store.upsert([attestor]); + const attestor = await getAttestor(ctx, accountAddress); - const key = `${accountAddress}-${organisation.id}`; + const key = uid.toLocaleLowerCase(); let attestorOrganisation: AttestorOrganisation = new AttestorOrganisation({ id: key, attestor, organisation, - uid, revoked: false, attestTimestamp: new Date(log.block.timestamp), }); @@ -50,6 +46,27 @@ export const handleAuthorize = async ( ctx.store.upsert(attestorOrganisation); ctx.log.debug( - `Attestor ${accountAddress} authorized for organisation ${organisation.name}: ${attestorOrganisation}` + `Attestor ${accountAddress} authorized for organisation ${ + organisation.name + }: ${JSON.stringify(attestorOrganisation)}` ); }; + +export const handleAuthorizeRevoke = async ( + ctx: DataHandlerContext, + uid: string +) => { + const attestation = await ctx.store.findOne(AttestorOrganisation, { + where: { + id: uid.toLocaleLowerCase(), + }, + }); + + if (!attestation) { + return; + } + + attestation.revoked = true; + await ctx.store.upsert(attestation); + ctx.log.debug(`Revoked authorize attestation ${attestation}`); +}; diff --git a/src/controllers/projectVerificationAttestation.ts b/src/controllers/projectVerificationAttestation.ts index 4f9a98e..0134b0a 100644 --- a/src/controllers/projectVerificationAttestation.ts +++ b/src/controllers/projectVerificationAttestation.ts @@ -1,16 +1,16 @@ import { DataHandlerContext, Log } from "@subsquid/evm-processor"; import { Store } from "@subsquid/typeorm-store"; import * as EASContract from "../abi/EAS"; -import { getAttestationData } from "./utils/easHelper"; -import { ProjectAttestation } from "../model"; +import { + getAttestationData, + removeDuplicateProjectAttestations, +} from "./utils/easHelper"; +import { AttestorOrganisation, ProjectAttestation } from "../model"; import { getProject, updateProjectAttestationCounts, } from "./utils/modelHelper"; -import { - checkProjectAttestation, - parseAttestationData, -} from "./utils/projectVerificationHelper"; +import { parseAttestationData } from "./utils/projectVerificationHelper"; export const handleProjectAttestation = async ( ctx: DataHandlerContext, @@ -23,7 +23,36 @@ export const handleProjectAttestation = async ( recipient, } = EASContract.events.Attested.decode(log); - const decodedData = await getAttestationData(ctx, log.block, uid, schemaUid); + const { decodedData, refUID } = await getAttestationData( + ctx, + log.block, + uid, + schemaUid + ); + + const attestorOrganisation = await ctx.store.get(AttestorOrganisation, { + where: { + id: refUID.toLowerCase(), + }, + relations: { + organisation: true, + attestor: true, + }, + }); + + if (!attestorOrganisation) { + ctx.log.debug( + `Attestor ${issuer} is not part of any organisation with ref UI ${refUID}` + ); + return; + } + + if (attestorOrganisation.revoked) { + ctx.log.debug( + `Attestor ${issuer} authorization attestation ${refUID} is revoked from organisation ${attestorOrganisation.organisation.name}` + ); + return; + } const { success, data: projectVerificationAttestation } = parseAttestationData(decodedData); @@ -33,60 +62,45 @@ export const handleProjectAttestation = async ( uid: ${uid} schemaUid: ${schemaUid} issuer: ${issuer} - decodedData: ${decodedData} + decodedData: ${JSON.stringify(decodedData)} + projectVerificationAttestation: ${projectVerificationAttestation} `); - return; + throw new Error("Error parsing project verification attestation"); } - for (const attestorGroup of projectVerificationAttestation.attestorGroup) { - ctx.log.debug(`Processing project attestation with uid: ${uid}`); - // Check if the attestor is part of the organisation - const attestorOrganisation = await checkProjectAttestation( - ctx, - attestorGroup, - issuer - ); + ctx.log.debug(`Processing project attestation with uid: ${uid}`); - if (!attestorOrganisation) { - ctx.log.debug( - `Attestor ${issuer} is not part of the organisation ${attestorGroup} in project verification attestation - skipped` - ); - break; - } - const project = await getProject( - ctx, - projectVerificationAttestation.projectSource, - projectVerificationAttestation.projectId - ); + const project = await getProject( + ctx, + projectVerificationAttestation.projectSource, + projectVerificationAttestation.projectId + ); + + // Delete the previous attestation + await removeDuplicateProjectAttestations( + ctx, + project, + attestorOrganisation.attestor, + attestorOrganisation.organisation + ); - // Delete the previous attestation - const oldAttestation = await ctx.store.findOneBy(ProjectAttestation, { - project, - attestorOrganisation, - }); - if (oldAttestation) { - await ctx.store.remove(oldAttestation); - } + const { vouch, comment } = projectVerificationAttestation; + const projectAttestation = new ProjectAttestation({ + id: uid, + vouch, + txHash: log.getTransaction().hash, + project, + attestorOrganisation, + comment, + attestTimestamp: new Date(log.block.timestamp), + revoked: false, + recipient, + }); - const { vouchOrFlag, comment } = projectVerificationAttestation; + await ctx.store.upsert(projectAttestation); + ctx.log.debug(`Upserted project attestation ${projectAttestation}`); - const projectAttestation = new ProjectAttestation({ - id: uid, - vouchOrFlag, - txHash: log.getTransaction().hash, - project, - attestorOrganisation, - comment: comment, - attestTimestamp: new Date(log.block.timestamp), - revoked: false, - recipient, - }); - - await ctx.store.upsert(projectAttestation); - ctx.log.debug(`Upserted project attestation ${projectAttestation}`); - - await updateProjectAttestationCounts(ctx, project); - } + await updateProjectAttestationCounts(ctx, project); }; export const handleProjectAttestationRevoke = async ( @@ -110,7 +124,7 @@ export const handleProjectAttestationRevoke = async ( attestation.revoked = true; await ctx.store.upsert(attestation); - ctx.log.debug(`Revoked project attestation ${attestation}`); + ctx.log.debug(`Revoked project attestation ${JSON.stringify(attestation)}`); await updateProjectAttestationCounts(ctx, attestation.project); }; diff --git a/src/controllers/utils/databaseHelper.ts b/src/controllers/utils/databaseHelper.ts new file mode 100644 index 0000000..a54a93d --- /dev/null +++ b/src/controllers/utils/databaseHelper.ts @@ -0,0 +1,12 @@ +import { DataHandlerContext } from "@subsquid/evm-processor"; +import { Store } from "@subsquid/typeorm-store"; +import { assert } from "console"; +import { EntityManager } from "typeorm/entity-manager/EntityManager"; + +export const getEntityManger = ( + ctx: DataHandlerContext +): EntityManager => { + const em = (ctx.store as unknown as { em: () => EntityManager }).em(); + assert(em, "EntityManager not found in store"); + return em; +}; diff --git a/src/controllers/utils/easHelper.ts b/src/controllers/utils/easHelper.ts index f579004..1cd5cd6 100644 --- a/src/controllers/utils/easHelper.ts +++ b/src/controllers/utils/easHelper.ts @@ -8,6 +8,13 @@ import * as EASContract from "../../abi/EAS"; import * as SchemaContract from "../../abi/Schema"; import { EAS_CONTRACT_ADDRESS, SCHEMA_CONTRACT_ADDRESS } from "../../constants"; import { Block } from "../../abi/abi.support"; +import { + Attestor, + AttestorOrganisation, + Organisation, + Project, + ProjectAttestation, +} from "../../model"; export const getEasSchemaEncoder = async ( ctx: DataHandlerContext, @@ -29,15 +36,48 @@ export const getAttestationData = async ( block: Block, attestationUid: string, schemaUid: string -): Promise => { +): Promise<{ + decodedData: SchemaDecodedItem[]; + refUID: string; +}> => { const easContract = new EASContract.Contract( ctx, block, EAS_CONTRACT_ADDRESS ); - const { data } = await easContract.getAttestation(attestationUid); + const { data, refUID } = await easContract.getAttestation(attestationUid); const schemaEncoder = await getEasSchemaEncoder(ctx, block, schemaUid); - return schemaEncoder.decodeData(data); + const decodedData = schemaEncoder.decodeData(data); + return { + decodedData, + refUID, + }; +}; + +export const removeDuplicateProjectAttestations = async ( + ctx: DataHandlerContext, + project: Project, + attestor: Attestor, + organisation: Organisation +) => { + const attestorOrganisations = await ctx.store.findBy(AttestorOrganisation, { + attestor, + organisation, + }); + + for (const attestorOrganisation of attestorOrganisations) { + const projectAttestation = await ctx.store.findOneBy(ProjectAttestation, { + project, + attestorOrganisation, + }); + + if (projectAttestation) { + ctx.log.debug( + `Removing duplicate project attestation ${projectAttestation.id}` + ); + await ctx.store.remove(projectAttestation); + } + } }; diff --git a/src/controllers/utils/easTypes.ts b/src/controllers/utils/easTypes.ts deleted file mode 100644 index 5b9ef6b..0000000 --- a/src/controllers/utils/easTypes.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { z } from "zod"; - -export const ProjectVerificationAttestation = z.object({ - vouchOrFlag: z.boolean(), - projectSource: z.string(), - projectId: z.string(), - attestorGroup: z.array(z.string()), - comment: z.string().optional(), -}); - -export type ProjectVerificationAttestation = z.infer< - typeof ProjectVerificationAttestation ->; diff --git a/src/controllers/utils/modelHelper.ts b/src/controllers/utils/modelHelper.ts index 99c75a4..8f6d403 100644 --- a/src/controllers/utils/modelHelper.ts +++ b/src/controllers/utils/modelHelper.ts @@ -1,32 +1,54 @@ import { DataHandlerContext } from "@subsquid/evm-processor"; import { Store } from "@subsquid/typeorm-store"; -import { Project, ProjectAttestation } from "../../model"; +import { + Attestor, + Organisation, + OrganisationProject, + Project, +} from "../../model"; +import { getEntityManger } from "./databaseHelper"; +import { ProjectStats } from "./types"; + +export const upsertOrganisatoinProject = async ( + ctx: DataHandlerContext, + project: Project, + organisationId: string, + vouch: boolean, + count: number +): Promise => { + const organisation = await ctx.store.get(Organisation, organisationId); + const key = `${project.id}-${organisationId}-${vouch ? "vouch" : "flag"}`; + const organisationProject = new OrganisationProject({ + id: key, + project, + organisation, + vouch, + count, + }); + ctx.store.upsert(organisationProject); +}; export const updateProjectAttestationCounts = async ( ctx: DataHandlerContext, project: Project ): Promise => { - const [vouchCount, flagCount] = await Promise.all([ - ctx.store.count(ProjectAttestation, { - where: { - project, - vouchOrFlag: true, - revoked: false, - }, - }), - ctx.store.count(ProjectAttestation, { - where: { - project, - vouchOrFlag: false, - revoked: false, - }, - }), - ]); - - project.totalVouches = vouchCount; - project.totalFlags = flagCount; + const em = getEntityManger(ctx); + const projectStats = await getProjectStats(ctx, project); + project.totalVouches = projectStats.pr_total_vouches; + project.totalFlags = projectStats.pr_total_flags; + project.totalAttests = projectStats.pr_total_attestations; await ctx.store.upsert(project); + + await em.getRepository(OrganisationProject).delete({ project }); + + for (const [orgId, count] of projectStats.org_flags) { + await upsertOrganisatoinProject(ctx, project, orgId, false, +count); + } + + for (const [orgId, count] of projectStats.org_vouches) { + await upsertOrganisatoinProject(ctx, project, orgId, true, +count); + } }; export const getProject = async ( @@ -46,6 +68,7 @@ export const getProject = async ( projectId, totalVouches: 0, totalFlags: 0, + totalAttests: 0, lastUpdatedTimestamp: new Date(), }) ); @@ -54,3 +77,109 @@ export const getProject = async ( return project as Project; }; + +export const getAttestor = async ( + ctx: DataHandlerContext, + address: string +): Promise => { + let attestor = await ctx.store.get(Attestor, address); + if (!attestor) { + await ctx.store.upsert(new Attestor({ id: address })); + attestor = await ctx.store.get(Attestor, address); + } + + return attestor as Attestor; +}; + +export const getProjectStats = async ( + ctx: DataHandlerContext, + project: Project +): Promise => { + const em = getEntityManger(ctx); + + const result = await em.query( + ` + WITH + ORG_ATTESTATIONS AS ( + SELECT + PR_AT.PROJECT_ID, + OG.id as org_id, + PR_AT.VOUCH + FROM + PROJECT_ATTESTATION AS PR_AT + INNER JOIN ATTESTOR_ORGANISATION AS AT_OG ON PR_AT.ATTESTOR_ORGANISATION_ID = AT_OG.ID + INNER JOIN ORGANISATION AS OG ON AT_OG.ORGANISATION_ID = OG.ID + WHERE + PR_AT.REVOKED = FALSE + AND AT_OG.REVOKED = FALSE + AND PR_AT.PROJECT_ID = $1 + ), + PR_ORG AS ( + SELECT + PROJECT_ID, + ARRAY_AGG(DISTINCT ORG_ATTESTATIONS.org_id) AS UNIQ_ORGS + FROM + ORG_ATTESTATIONS + WHERE + PROJECT_ID = $1 + GROUP BY + PROJECT_ID + ), + PR_ORG_V AS ( + SELECT + ORG_ATTESTATIONS.PROJECT_ID, + ORG_ATTESTATIONS.org_id, + ORG_ATTESTATIONS.VOUCH, + COUNT(*) + FROM + ORG_ATTESTATIONS + GROUP BY + ORG_ATTESTATIONS.PROJECT_ID, + ORG_ATTESTATIONS.org_id, + ORG_ATTESTATIONS.VOUCH + ), + ORG_FLAG_AGG AS ( + SELECT + PR_ORG_V.PROJECT_ID, + ARRAY_AGG(ROW (PR_ORG_V.org_id, PR_ORG_V.COUNT)) AS ORG_FLAGS, + SUM(PR_ORG_V.COUNT) AS PR_TOTAL_FLAGS + FROM + PR_ORG_V + WHERE + PR_ORG_V.VOUCH = FALSE + GROUP BY + PR_ORG_V.PROJECT_ID + ), + ORG_ATTESTATIONS_AGG AS ( + SELECT + PR_ORG_V.PROJECT_ID, + ARRAY_AGG(ROW (PR_ORG_V.org_id, PR_ORG_V.COUNT)) AS ORG_VOUCHES, + SUM(PR_ORG_V.COUNT) AS PR_TOTAL_VOUCHES + FROM + PR_ORG_V + WHERE + PR_ORG_V.VOUCH = TRUE + GROUP BY + PR_ORG_V.PROJECT_ID + ) + SELECT + ID, + PR_TOTAL_FLAGS, + PR_TOTAL_VOUCHES, + PR_TOTAL_FLAGS + PR_TOTAL_VOUCHES AS PR_TOTAL_ATTESTATIONS, + ORG_FLAGS, + ORG_VOUCHES, + UNIQ_ORGS + FROM + PROJECT + LEFT JOIN PR_ORG ON PR_ORG.PROJECT_ID = PROJECT.ID + LEFT JOIN ORG_FLAG_AGG ON ORG_FLAG_AGG.PROJECT_ID = PROJECT.ID + LEFT JOIN ORG_ATTESTATIONS_AGG ON ORG_ATTESTATIONS_AGG.PROJECT_ID = PROJECT.ID + WHERE + PROJECT.ID = $1 + `, + [project.id] + ); + + return ProjectStats.parse(result[0]); +}; diff --git a/src/controllers/utils/projectVerificationHelper.ts b/src/controllers/utils/projectVerificationHelper.ts index c9a0211..727cbbe 100644 --- a/src/controllers/utils/projectVerificationHelper.ts +++ b/src/controllers/utils/projectVerificationHelper.ts @@ -1,6 +1,6 @@ import { DataHandlerContext, Log } from "@subsquid/evm-processor"; import { Store } from "@subsquid/typeorm-store"; -import { ProjectVerificationAttestation } from "./easTypes"; +import { ProjectVerificationAttestation } from "./types"; import { SchemaDecodedItem } from "@ethereum-attestation-service/eas-sdk"; import { SafeParseReturnType } from "zod"; import { Attestor, AttestorOrganisation, Organisation } from "../../model"; @@ -44,17 +44,16 @@ export const checkProjectAttestation = async ( export const parseAttestationData = ( decodedData: SchemaDecodedItem[] ): SafeParseReturnType => { - let vouchOrFlag: boolean; + let vouch: boolean; let projectSource: string; let projectId: string; - let attestorGroup: string[]; let comment: string; for (const item of decodedData) { const value = item.value.value; switch (item.name) { - case "vouchOrFlag": - vouchOrFlag = value as boolean; + case "vouch": + vouch = value as boolean; break; case "projectSource": projectSource = value as string; @@ -62,9 +61,6 @@ export const parseAttestationData = ( case "projectId": projectId = value as string; break; - case "attestorGroup": - attestorGroup = Object.values(value).map((v) => v.toString()); - break; case "comment": comment = value as string; break; @@ -73,14 +69,12 @@ export const parseAttestationData = ( const projectVerificationAttestation: ProjectVerificationAttestation = { // @ts-ignore - vouchOrFlag, + vouch, // @ts-ignore projectSource, // @ts-ignore projectId, // @ts-ignore - attestorGroup, - // @ts-ignore comment, }; diff --git a/src/controllers/utils/types.test.ts b/src/controllers/utils/types.test.ts new file mode 100644 index 0000000..24b8c28 --- /dev/null +++ b/src/controllers/utils/types.test.ts @@ -0,0 +1,20 @@ +import { orgCountTuplesTypes } from "./types"; + +describe.only("parse database query", () => { + it("should parse project stats query", () => { + const raw = `{"(0x2e22df9a11e06c306ed8f64ca45ceae02efcf8a443371395a78371bc4fb6f722,1)","(0xf63f2a7159ee674aa6fce42196a8bb0605eafcf20c19e91a7eafba8d39fa0404,1)"}`; + + const result = orgCountTuplesTypes.parse(raw); + + expect(result).toEqual([ + [ + "0x2e22df9a11e06c306ed8f64ca45ceae02efcf8a443371395a78371bc4fb6f722", + "1", + ], + [ + "0xf63f2a7159ee674aa6fce42196a8bb0605eafcf20c19e91a7eafba8d39fa0404", + "1", + ], + ]); + }); +}); diff --git a/src/controllers/utils/types.ts b/src/controllers/utils/types.ts new file mode 100644 index 0000000..b297666 --- /dev/null +++ b/src/controllers/utils/types.ts @@ -0,0 +1,55 @@ +import { z } from "zod"; + +export const ProjectVerificationAttestation = z.object({ + vouch: z.boolean(), + projectSource: z.string(), + projectId: z.string(), + comment: z.string().optional(), +}); + +export type ProjectVerificationAttestation = z.infer< + typeof ProjectVerificationAttestation +>; + +const nullableIntType = z + .string() + .nullable() + .transform((val) => parseInt(val || "0")); + +export const orgCountTuplesTypes = z + .string() + .optional() + .nullable() + .transform((val) => { + if (!val) return []; + return ( + val + // Split the string using a regex that targets commas only if they are outside the parentheses + .slice(1, -1) + // Split the string using a regex that targets commas only if they are outside the parentheses + .split(/(?<=\)\"),(?=\"\()/) + // Remove leading and trailing quotes + .map((part) => + part + // Remove leading and trailing quotes + .trim() + .replace(/^"/, "") + .replace(/"$/, "") + // Remove '(' from begining and ')' from the end + .slice(1, -1) + .split(",") + ) + ); + }); + +export const ProjectStats = z.object({ + id: z.string(), + pr_total_flags: nullableIntType, + pr_total_vouches: nullableIntType, + pr_total_attestations: nullableIntType, + org_flags: orgCountTuplesTypes, + org_vouches: orgCountTuplesTypes, + uniq_orgs: z.array(z.string()), +}); + +export type ProjectStats = z.infer; diff --git a/src/mappings/revoke.ts b/src/mappings/revoke.ts index 8af11dd..89bd452 100644 --- a/src/mappings/revoke.ts +++ b/src/mappings/revoke.ts @@ -3,6 +3,7 @@ import { Store } from "@subsquid/typeorm-store"; import * as EASContract from "../abi/EAS"; import { PROJECT_VERIFY_SCHEMA } from "../constants"; import { handleProjectAttestationRevoke } from "../controllers/projectVerificationAttestation"; +import { handleAuthorizeRevoke } from "../controllers/authorizeAttestation"; export async function processRevokeLog( ctx: DataHandlerContext, @@ -16,5 +17,6 @@ export async function processRevokeLog( break; default: + await handleAuthorizeRevoke(ctx, uid); } } diff --git a/src/model/generated/attestorOrganisation.model.ts b/src/model/generated/attestorOrganisation.model.ts index 0e00412..cfbcf13 100644 --- a/src/model/generated/attestorOrganisation.model.ts +++ b/src/model/generated/attestorOrganisation.model.ts @@ -8,14 +8,11 @@ export class AttestorOrganisation { Object.assign(this, props) } - @PrimaryColumn_() - id!: string - /** * UID of the attestation made the relationship */ - @Column_("text", {nullable: false}) - uid!: string + @PrimaryColumn_() + id!: string @Index_() @ManyToOne_(() => Attestor, {nullable: true}) diff --git a/src/model/generated/index.ts b/src/model/generated/index.ts index 1c13f2c..cbcf289 100644 --- a/src/model/generated/index.ts +++ b/src/model/generated/index.ts @@ -3,3 +3,4 @@ export * from "./attestorOrganisation.model" export * from "./attestor.model" export * from "./organisation.model" export * from "./project.model" +export * from "./organisationProject.model" diff --git a/src/model/generated/organisation.model.ts b/src/model/generated/organisation.model.ts index 64e1b0b..b2db066 100644 --- a/src/model/generated/organisation.model.ts +++ b/src/model/generated/organisation.model.ts @@ -1,5 +1,6 @@ import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, Index as Index_, OneToMany as OneToMany_} from "typeorm" import {AttestorOrganisation} from "./attestorOrganisation.model" +import {OrganisationProject} from "./organisationProject.model" @Entity_() export class Organisation { @@ -37,4 +38,7 @@ export class Organisation { */ @OneToMany_(() => AttestorOrganisation, e => e.organisation) attestors!: AttestorOrganisation[] + + @OneToMany_(() => OrganisationProject, e => e.organisation) + attestedProjects!: OrganisationProject[] } diff --git a/src/model/generated/organisationProject.model.ts b/src/model/generated/organisationProject.model.ts new file mode 100644 index 0000000..75be74c --- /dev/null +++ b/src/model/generated/organisationProject.model.ts @@ -0,0 +1,30 @@ +import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, ManyToOne as ManyToOne_, Index as Index_} from "typeorm" +import {Organisation} from "./organisation.model" +import {Project} from "./project.model" + +@Entity_() +export class OrganisationProject { + constructor(props?: Partial) { + Object.assign(this, props) + } + + /** + * - + */ + @PrimaryColumn_() + id!: string + + @Index_() + @ManyToOne_(() => Organisation, {nullable: true}) + organisation!: Organisation + + @Index_() + @ManyToOne_(() => Project, {nullable: true}) + project!: Project + + @Column_("bool", {nullable: false}) + vouch!: boolean + + @Column_("int4", {nullable: false}) + count!: number +} diff --git a/src/model/generated/project.model.ts b/src/model/generated/project.model.ts index c98d760..37ffca4 100644 --- a/src/model/generated/project.model.ts +++ b/src/model/generated/project.model.ts @@ -1,5 +1,6 @@ -import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, OneToMany as OneToMany_} from "typeorm" +import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, Index as Index_, OneToMany as OneToMany_} from "typeorm" import {ProjectAttestation} from "./projectAttestation.model" +import {OrganisationProject} from "./organisationProject.model" @Entity_() export class Project { @@ -16,12 +17,14 @@ export class Project { /** * Source of the project */ + @Index_() @Column_("text", {nullable: false}) source!: string /** * Project ID. Unique within the source */ + @Index_() @Column_("text", {nullable: false}) projectId!: string @@ -49,9 +52,18 @@ export class Project { @Column_("int4", {nullable: false}) totalFlags!: number + /** + * Total attests + */ + @Column_("int4", {nullable: false}) + totalAttests!: number + @Column_("timestamp with time zone", {nullable: false}) lastUpdatedTimestamp!: Date @OneToMany_(() => ProjectAttestation, e => e.project) attests!: ProjectAttestation[] + + @OneToMany_(() => OrganisationProject, e => e.project) + attestedOrganisations!: OrganisationProject[] } diff --git a/src/model/generated/projectAttestation.model.ts b/src/model/generated/projectAttestation.model.ts index 2b97f7b..4750dd1 100644 --- a/src/model/generated/projectAttestation.model.ts +++ b/src/model/generated/projectAttestation.model.ts @@ -18,7 +18,7 @@ export class ProjectAttestation { recipient!: string @Column_("bool", {nullable: false}) - vouchOrFlag!: boolean + vouch!: boolean @Column_("text", {nullable: false}) txHash!: string diff --git a/src/processor.ts b/src/processor.ts index db1fb33..ce29bbc 100644 --- a/src/processor.ts +++ b/src/processor.ts @@ -10,7 +10,7 @@ import { } from "@subsquid/evm-processor"; import * as EASContract from "./abi/EAS"; -import { EAS_CONTRACT_ADDRESS } from "./constants"; +import { EAS_CONTRACT_ADDRESS, START_BLOCK } from "./constants"; export const processor = new EvmBatchProcessor() // Lookup archive by the network name in Subsquid registry @@ -35,7 +35,7 @@ export const processor = new EvmBatchProcessor() }, }) .setBlockRange({ - from: 5815457, + from: START_BLOCK, }) .addLog({ address: [EAS_CONTRACT_ADDRESS], diff --git a/src/test/store.test.ts b/src/test/store.test.ts index c0645ac..1c1d2aa 100644 --- a/src/test/store.test.ts +++ b/src/test/store.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test, afterAll } from "@jest/globals"; -import { closeConnection, getCtx } from "./utils"; +import { closeConnection, getTestCtx } from "./utils"; import { Organisation } from "../model"; describe.skip("simple storage", () => { @@ -8,7 +8,7 @@ describe.skip("simple storage", () => { }); test("sample authorized attest", async () => { - const ctx = await getCtx(); + const ctx = await getTestCtx(); const organization = new Organisation({ id: "schemaUid", name: "name", diff --git a/src/test/utils.ts b/src/test/utils.ts index cf98373..96cce4b 100644 --- a/src/test/utils.ts +++ b/src/test/utils.ts @@ -1,6 +1,6 @@ import { Store, TypeormDatabase } from "@subsquid/typeorm-store"; import { createOrmConfig } from "@subsquid/typeorm-config"; -import { DataSource, EntityManager } from "typeorm"; +import { DataSource, DataSourceOptions, EntityManager } from "typeorm"; import { DataHandlerContext } from "@subsquid/evm-processor"; // import dotenv from "dotenv"; @@ -11,7 +11,7 @@ import { DataHandlerContext } from "@subsquid/evm-processor"; let connection: DataSource | undefined; -export async function getEntityManager(): Promise { +export async function getTestEntityManager(): Promise { if (!connection) { let cfg = createOrmConfig({ projectDir: __dirname + "/../.." }); (cfg.entities as string[]).push(__dirname + "/../model/generated/*.ts"); @@ -32,13 +32,13 @@ export async function closeConnection() { } } -export async function getStore(): Promise { - const em = await getEntityManager(); +export async function getTestStore(): Promise { + const em = await getTestEntityManager(); return new Store(() => em); } -export async function getCtx(): Promise> { - const store = await getStore(); +export async function getTestCtx(): Promise> { + const store = await getTestStore(); return { _chain: undefined, log: undefined,