diff --git a/commands.json b/commands.json index cb3a6a93..efc15905 100644 --- a/commands.json +++ b/commands.json @@ -103,8 +103,7 @@ "process:oeth": { "description": "Load .env and start the squid processor", "deps": [ - "build", - "migration:apply" + "build" ], "cmd": [ "node", @@ -115,8 +114,7 @@ "process:ousd": { "description": "Load .env and start the squid processor", "deps": [ - "build", - "migration:apply" + "build" ], "cmd": [ "node", @@ -127,8 +125,7 @@ "process:ogv": { "description": "Load .env and start the squid processor", "deps": [ - "build", - "migration:apply" + "build" ], "cmd": [ "node", @@ -139,8 +136,7 @@ "process:other": { "description": "Load .env and start the squid processor", "deps": [ - "build", - "migration:apply" + "build" ], "cmd": [ "node", diff --git a/db/migrations/1701276030021-Data.js b/db/migrations/1701729003798-Data.js similarity index 91% rename from db/migrations/1701276030021-Data.js rename to db/migrations/1701729003798-Data.js index e290900d..921f625b 100644 --- a/db/migrations/1701276030021-Data.js +++ b/db/migrations/1701729003798-Data.js @@ -1,13 +1,10 @@ -module.exports = class Data1701276030021 { - name = 'Data1701276030021' +module.exports = class Data1701729003798 { + name = 'Data1701729003798' async up(db) { await db.query(`CREATE TABLE "exchange_rate" ("id" character varying NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "block_number" integer NOT NULL, "pair" text NOT NULL, "base" text NOT NULL, "quote" text NOT NULL, "rate" numeric NOT NULL, CONSTRAINT "PK_5c5d27d2b900ef6cdeef0398472" PRIMARY KEY ("id"))`) await db.query(`CREATE INDEX "IDX_9e23a3f1bf3634820c873a0fe8" ON "exchange_rate" ("timestamp") `) await db.query(`CREATE INDEX "IDX_c61a93768eed9e58ce399bbe01" ON "exchange_rate" ("block_number") `) - await db.query(`CREATE TABLE "balance" ("id" character varying NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "block_number" integer NOT NULL, "token" text NOT NULL, "address" text NOT NULL, "balance" numeric NOT NULL, CONSTRAINT "PK_079dddd31a81672e8143a649ca0" PRIMARY KEY ("id"))`) - await db.query(`CREATE INDEX "IDX_a956b410c329b8eca7898c3c51" ON "balance" ("timestamp") `) - await db.query(`CREATE INDEX "IDX_6b451b59c9f6a6fdd685f530b2" ON "balance" ("block_number") `) await db.query(`CREATE TABLE "strategy_balance" ("id" character varying NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "block_number" integer NOT NULL, "strategy" text NOT NULL, "asset" text NOT NULL, "balance" numeric NOT NULL, CONSTRAINT "PK_ca6f93229d1392e9546d01dae4f" PRIMARY KEY ("id"))`) await db.query(`CREATE INDEX "IDX_0113bf0b63183bea0d22cd0d08" ON "strategy_balance" ("timestamp") `) await db.query(`CREATE INDEX "IDX_a88065dcd92011698bbe7df7b1" ON "strategy_balance" ("block_number") `) @@ -17,9 +14,23 @@ module.exports = class Data1701276030021 { await db.query(`CREATE TABLE "strategy_daily_yield" ("id" character varying NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "block_number" integer NOT NULL, "strategy" text NOT NULL, "asset" text NOT NULL, "balance" numeric NOT NULL, "balance_weight" numeric NOT NULL, "earnings" numeric NOT NULL, "earnings_change" numeric NOT NULL, "apr" numeric NOT NULL, "apy" numeric NOT NULL, CONSTRAINT "PK_b0dd2686bc95bb032ff532b3a0e" PRIMARY KEY ("id"))`) await db.query(`CREATE INDEX "IDX_0ba1974747f1906e0c102cd2cd" ON "strategy_daily_yield" ("timestamp") `) await db.query(`CREATE INDEX "IDX_df364fb6e82d1feeed2a5dfffa" ON "strategy_daily_yield" ("block_number") `) + await db.query(`CREATE TABLE "erc20" ("id" character varying NOT NULL, "address" text NOT NULL, "name" text NOT NULL, "decimals" integer NOT NULL, "symbol" text NOT NULL, CONSTRAINT "PK_8d43ce15401ba044c55a72a8ceb" PRIMARY KEY ("id"))`) + await db.query(`CREATE TABLE "erc20_holder" ("id" character varying NOT NULL, "address" text NOT NULL, "account" text NOT NULL, CONSTRAINT "PK_3adce7edbac4bcb03c662c3a1ac" PRIMARY KEY ("id"))`) + await db.query(`CREATE TABLE "erc20_state" ("id" character varying NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "block_number" integer NOT NULL, "address" text NOT NULL, "total_supply" numeric NOT NULL, "holder_count" integer NOT NULL, CONSTRAINT "PK_eac1124b07bbdedafd4fff2f7b7" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_c3d08eb2dafe4b5b188924d835" ON "erc20_state" ("timestamp") `) + await db.query(`CREATE INDEX "IDX_29e8edc6ba8cc37c0c16ff0baf" ON "erc20_state" ("block_number") `) + await db.query(`CREATE TABLE "erc20_balance" ("id" character varying NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "block_number" integer NOT NULL, "address" text NOT NULL, "account" text NOT NULL, "balance" numeric NOT NULL, CONSTRAINT "PK_069b6549e7a9938cc89f32063a6" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_c9fbe21a3411d93ea586af2a4c" ON "erc20_balance" ("timestamp") `) + await db.query(`CREATE INDEX "IDX_d1f50dc39003331b76fad8a640" ON "erc20_balance" ("block_number") `) await db.query(`CREATE TABLE "curve_pool_balance" ("id" character varying NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "block_number" integer NOT NULL, "address" text NOT NULL, "balance0" numeric NOT NULL, "balance1" numeric NOT NULL, "balance2" numeric NOT NULL, CONSTRAINT "PK_40412750bb910ca560aa084dd88" PRIMARY KEY ("id"))`) await db.query(`CREATE INDEX "IDX_ffb0d0f86f03faacef7cb3e092" ON "curve_pool_balance" ("timestamp") `) await db.query(`CREATE INDEX "IDX_db5522c865eb8ed76fa7aeb4a8" ON "curve_pool_balance" ("block_number") `) + await db.query(`CREATE TABLE "balancer_pool_balance" ("id" character varying NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "block_number" integer NOT NULL, "address" text NOT NULL, "balance0" numeric NOT NULL, "balance1" numeric NOT NULL, "balance2" numeric NOT NULL, "balance3" numeric NOT NULL, CONSTRAINT "PK_441c7de293266414608c8e9de4c" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_642115282f1c3026edbc4bc2a7" ON "balancer_pool_balance" ("timestamp") `) + await db.query(`CREATE INDEX "IDX_eecdc2d6132041e3806d77c7b4" ON "balancer_pool_balance" ("block_number") `) + await db.query(`CREATE TABLE "balancer_pool_rate" ("id" character varying NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "block_number" integer NOT NULL, "address" text NOT NULL, "rate0" numeric NOT NULL, "rate1" numeric NOT NULL, "rate2" numeric NOT NULL, "rate3" numeric NOT NULL, CONSTRAINT "PK_3ca9825db8d15c7850b715d9121" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_b1656f8d048f72b38bb637ea24" ON "balancer_pool_rate" ("timestamp") `) + await db.query(`CREATE INDEX "IDX_c55345ef048232dfe36e60e4c5" ON "balancer_pool_rate" ("block_number") `) await db.query(`CREATE TABLE "oeth" ("id" character varying NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "block_number" integer NOT NULL, "total_supply" numeric NOT NULL, "rebasing_supply" numeric NOT NULL, "non_rebasing_supply" numeric NOT NULL, CONSTRAINT "PK_de1d885501070dbd1ab6f8577ba" PRIMARY KEY ("id"))`) await db.query(`CREATE INDEX "IDX_5b81a67229bac2d68e0dc92cc4" ON "oeth" ("timestamp") `) await db.query(`CREATE INDEX "IDX_408e5f79f83093aa5cf2b0ea32" ON "oeth" ("block_number") `) @@ -171,9 +182,6 @@ module.exports = class Data1701276030021 { await db.query(`DROP TABLE "exchange_rate"`) await db.query(`DROP INDEX "public"."IDX_9e23a3f1bf3634820c873a0fe8"`) await db.query(`DROP INDEX "public"."IDX_c61a93768eed9e58ce399bbe01"`) - await db.query(`DROP TABLE "balance"`) - await db.query(`DROP INDEX "public"."IDX_a956b410c329b8eca7898c3c51"`) - await db.query(`DROP INDEX "public"."IDX_6b451b59c9f6a6fdd685f530b2"`) await db.query(`DROP TABLE "strategy_balance"`) await db.query(`DROP INDEX "public"."IDX_0113bf0b63183bea0d22cd0d08"`) await db.query(`DROP INDEX "public"."IDX_a88065dcd92011698bbe7df7b1"`) @@ -183,9 +191,23 @@ module.exports = class Data1701276030021 { await db.query(`DROP TABLE "strategy_daily_yield"`) await db.query(`DROP INDEX "public"."IDX_0ba1974747f1906e0c102cd2cd"`) await db.query(`DROP INDEX "public"."IDX_df364fb6e82d1feeed2a5dfffa"`) + await db.query(`DROP TABLE "erc20"`) + await db.query(`DROP TABLE "erc20_holder"`) + await db.query(`DROP TABLE "erc20_state"`) + await db.query(`DROP INDEX "public"."IDX_c3d08eb2dafe4b5b188924d835"`) + await db.query(`DROP INDEX "public"."IDX_29e8edc6ba8cc37c0c16ff0baf"`) + await db.query(`DROP TABLE "erc20_balance"`) + await db.query(`DROP INDEX "public"."IDX_c9fbe21a3411d93ea586af2a4c"`) + await db.query(`DROP INDEX "public"."IDX_d1f50dc39003331b76fad8a640"`) await db.query(`DROP TABLE "curve_pool_balance"`) await db.query(`DROP INDEX "public"."IDX_ffb0d0f86f03faacef7cb3e092"`) await db.query(`DROP INDEX "public"."IDX_db5522c865eb8ed76fa7aeb4a8"`) + await db.query(`DROP TABLE "balancer_pool_balance"`) + await db.query(`DROP INDEX "public"."IDX_642115282f1c3026edbc4bc2a7"`) + await db.query(`DROP INDEX "public"."IDX_eecdc2d6132041e3806d77c7b4"`) + await db.query(`DROP TABLE "balancer_pool_rate"`) + await db.query(`DROP INDEX "public"."IDX_b1656f8d048f72b38bb637ea24"`) + await db.query(`DROP INDEX "public"."IDX_c55345ef048232dfe36e60e4c5"`) await db.query(`DROP TABLE "oeth"`) await db.query(`DROP INDEX "public"."IDX_5b81a67229bac2d68e0dc92cc4"`) await db.query(`DROP INDEX "public"."IDX_408e5f79f83093aa5cf2b0ea32"`) diff --git a/graphql.config.yml b/graphql.config.yml index 612bcc5d..5b40586e 100644 --- a/graphql.config.yml +++ b/graphql.config.yml @@ -20,10 +20,5 @@ projects: include: - schema-base.graphql - types.graphql - curve: - schema: schema-curve.graphql - include: - - schema-base.graphql - - types.graphql include: - types.graphql diff --git a/package-lock.json b/package-lock.json index abaa0144..e8595519 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,6 +31,7 @@ "@types/js-yaml": "^4.0.8", "@types/node": "^18.16.17", "@types/uuid": "^9.0.2", + "npm-run-all": "^4.1.5", "prettier": "^3.0.3", "typescript": "~5.1.3" } @@ -2209,6 +2210,27 @@ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "license": "MIT" }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", + "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-array-buffer": "^3.0.2", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/async-retry": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", @@ -2349,13 +2371,13 @@ } }, "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "license": "MIT", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2652,6 +2674,31 @@ "node": ">= 0.10" } }, + "node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/cross-spawn/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, "node_modules/cssfilter": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz", @@ -2862,6 +2909,68 @@ "node": ">= 0.8" } }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.22.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", + "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "arraybuffer.prototype.slice": "^1.0.2", + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.5", + "es-set-tostringtag": "^2.0.1", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.2", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.12", + "is-weakref": "^1.0.2", + "object-inspect": "^1.13.1", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "safe-array-concat": "^1.0.1", + "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.8", + "string.prototype.trimend": "^1.0.7", + "string.prototype.trimstart": "^1.0.7", + "typed-array-buffer": "^1.0.0", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/es-get-iterator": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", @@ -2881,6 +2990,37 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-set-tostringtag": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", + "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.2", + "has-tostringtag": "^1.0.0", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/es5-ext": { "version": "0.10.62", "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", @@ -3190,10 +3330,30 @@ "license": "ISC" }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "license": "MIT" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/functions-have-names": { "version": "1.2.3", @@ -3213,15 +3373,30 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", - "license": "MIT", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", + "function-bind": "^1.1.2", "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3256,6 +3431,21 @@ "node": ">=4" } }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -3267,6 +3457,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, "node_modules/graphql": { "version": "15.8.0", "resolved": "https://registry.npmjs.org/graphql/-/graphql-15.8.0.tgz", @@ -3438,6 +3634,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/highlight.js": { "version": "10.7.3", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", @@ -3447,6 +3654,12 @@ "node": "*" } }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -3613,6 +3826,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, "node_modules/is-bigint": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", @@ -3650,6 +3869,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-date-object": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", @@ -3681,6 +3912,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-number-object": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", @@ -3784,6 +4027,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-weakset": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", @@ -3801,6 +4056,12 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, "node_modules/isomorphic-ws": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", @@ -3858,6 +4119,12 @@ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "license": "MIT" }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, "node_modules/keyv": { "version": "4.5.3", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", @@ -3873,6 +4140,21 @@ "integrity": "sha512-eeHcvGafEYCaKB4fo2uBINfG7j7PcGwBHUaTVfbwl/6KcjCgIKNlIOsSXVRp9BH10NQwmvvk+nQ1e/Yp4BGB7w==", "peer": true }, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -3939,6 +4221,15 @@ "node": ">= 0.6" } }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, "node_modules/merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -4045,6 +4336,12 @@ "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, "node_modules/node-abort-controller": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", @@ -4100,6 +4397,101 @@ "node-gyp-build-test": "build-test.js" } }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm-run-all/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/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==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/npm-run-all/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/npm-run-all/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/npm-run-all/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -4110,10 +4502,9 @@ } }, "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "license": "MIT", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -4185,6 +4576,19 @@ "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==", "license": "MIT" }, + "node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/parse5": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", @@ -4223,12 +4627,39 @@ "node": ">=0.10.0" } }, + "node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, "node_modules/path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", "license": "MIT" }, + "node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/pg": { "version": "8.11.3", "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz", @@ -4320,6 +4751,27 @@ "split2": "^4.1.0" } }, + "node_modules/pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "dev": true, + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/postgres-array": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", @@ -4438,6 +4890,20 @@ "node": ">= 0.8" } }, + "node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "dev": true, + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/redis-errors": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", @@ -4496,6 +4962,23 @@ "node": ">=0.10.0" } }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/retry": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", @@ -4505,6 +4988,24 @@ "node": ">= 4" } }, + "node_modules/safe-array-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", + "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -4525,6 +5026,20 @@ ], "license": "MIT" }, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -4601,6 +5116,20 @@ "node": ">= 0.8.0" } }, + "node_modules/set-function-length": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", + "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "dependencies": { + "define-data-property": "^1.1.1", + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/set-function-name": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", @@ -4633,6 +5162,36 @@ "sha.js": "bin.js" } }, + "node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -4657,6 +5216,38 @@ "node": ">=0.10.0" } }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.16", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz", + "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==", + "dev": true + }, "node_modules/split2": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", @@ -4715,6 +5306,68 @@ "node": ">=8" } }, + "node_modules/string.prototype.padend": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.5.tgz", + "integrity": "sha512-DOB27b/2UTTD+4myKUFh+/fXWcu/UDyASIXfg+7VzoCNNGOfWvoyU/x5pvVHr++ztyt/oSYI1BcWBBG/hmlNjA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", + "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", + "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", + "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -4727,6 +5380,15 @@ "node": ">=8" } }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -4741,6 +5403,18 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/sync-fetch": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/sync-fetch/-/sync-fetch-0.5.2.tgz", @@ -4897,6 +5571,71 @@ "node": ">= 0.6" } }, + "node_modules/typed-array-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", + "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", + "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", + "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", @@ -5076,6 +5815,21 @@ "node": ">=14.17" } }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -5120,6 +5874,16 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, "node_modules/validator": { "version": "13.11.0", "resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz", @@ -5272,6 +6036,18 @@ "webidl-conversions": "^3.0.0" } }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, "node_modules/which-boxed-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", @@ -5302,12 +6078,12 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", - "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", + "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", "dependencies": { "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "call-bind": "^1.0.4", "for-each": "^0.3.3", "gopd": "^1.0.1", "has-tostringtag": "^1.0.0" diff --git a/package.json b/package.json index 31ee67b9..204ad95a 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,13 @@ "migration:generate": "sqd down && sqd up && sqd migration:generate", "build": "rm -rf lib && tsc", "prettier-check": "prettier --check **/*.ts **/*.json", - "prettier-fix": "prettier --write **/*.ts **/*.json" + "prettier-fix": "prettier --write **/*.ts **/*.json", + "setup": "sqd down && sqd up && sleep 2 && sqd migration:apply", + "process:oeth": "sqd process:oeth", + "process:ousd": "sqd process:ousd", + "process:ogv": "sqd process:ogv", + "process:other": "sqd process:other", + "process": "npm-run-all --parallel process:oeth process:ousd process:ogv process:other" }, "dependencies": { "@subsquid/archive-registry": "^3.3.0", @@ -34,6 +40,7 @@ "@types/js-yaml": "^4.0.8", "@types/node": "^18.16.17", "@types/uuid": "^9.0.2", + "npm-run-all": "^4.1.5", "prettier": "^3.0.3", "typescript": "~5.1.3" } diff --git a/schema-base.graphql b/schema-base.graphql index e50e83bd..4c97d3e7 100644 --- a/schema-base.graphql +++ b/schema-base.graphql @@ -16,18 +16,6 @@ type ExchangeRate @entity { rate: BigInt! } -type Balance @entity { - """ - Format: 'token:address:blockNumber' - """ - id: ID! - timestamp: DateTime! @index - blockNumber: Int! @index - token: String! - address: String! - balance: BigInt! -} - enum RebasingOption { OptIn OptOut @@ -82,4 +70,80 @@ type StrategyDailyYield @entity { earningsChange: BigInt! apr: Float! apy: Float! -} \ No newline at end of file +} + +type ERC20 @entity { + """ + Format: 'address' + """ + id: ID! + address: String! + name: String! + decimals: Int! + symbol: String! +} + +type ERC20Holder @entity { + """ + Format: 'address:account' + """ + id: ID! + address: String! + account: String! +} + +type ERC20State @entity { + """ + Format: 'address:blockNumber' + """ + id: ID! + timestamp: DateTime! @index + blockNumber: Int! @index + address: String! + totalSupply: BigInt! + holderCount: Int! +} + +type ERC20Balance @entity { + """ + Format: 'address:account:blockNumber' + """ + id: ID! + timestamp: DateTime! @index + blockNumber: Int! @index + address: String! + account: String! + balance: BigInt! +} + +type CurvePoolBalance @entity { + id: ID! + timestamp: DateTime! @index + blockNumber: Int! @index + address: String! + balance0: BigInt! + balance1: BigInt! + balance2: BigInt! +} + +type BalancerPoolBalance @entity { + id: ID! + timestamp: DateTime! @index + blockNumber: Int! @index + address: String! + balance0: BigInt! + balance1: BigInt! + balance2: BigInt! + balance3: BigInt! +} + +type BalancerPoolRate @entity { + id: ID! + timestamp: DateTime! @index + blockNumber: Int! @index + address: String! + rate0: BigInt! + rate1: BigInt! + rate2: BigInt! + rate3: BigInt! +} diff --git a/schema-curve.graphql b/schema-curve.graphql deleted file mode 100644 index a1736ba1..00000000 --- a/schema-curve.graphql +++ /dev/null @@ -1,9 +0,0 @@ -type CurvePoolBalance @entity { - id: ID! - timestamp: DateTime! @index - blockNumber: Int! @index - address: String! - balance0: BigInt! - balance1: BigInt! - balance2: BigInt! -} diff --git a/schema.graphql b/schema.graphql index 38f82b95..e07bbb1a 100644 --- a/schema.graphql +++ b/schema.graphql @@ -18,18 +18,6 @@ type ExchangeRate @entity { rate: BigInt! } -type Balance @entity { - """ - Format: 'token:address:blockNumber' - """ - id: ID! - timestamp: DateTime! @index - blockNumber: Int! @index - token: String! - address: String! - balance: BigInt! -} - enum RebasingOption { OptIn OptOut @@ -84,7 +72,53 @@ type StrategyDailyYield @entity { earningsChange: BigInt! apr: Float! apy: Float! -}type CurvePoolBalance @entity { +} + +type ERC20 @entity { + """ + Format: 'address' + """ + id: ID! + address: String! + name: String! + decimals: Int! + symbol: String! +} + +type ERC20Holder @entity { + """ + Format: 'address:account' + """ + id: ID! + address: String! + account: String! +} + +type ERC20State @entity { + """ + Format: 'address:blockNumber' + """ + id: ID! + timestamp: DateTime! @index + blockNumber: Int! @index + address: String! + totalSupply: BigInt! + holderCount: Int! +} + +type ERC20Balance @entity { + """ + Format: 'address:account:blockNumber' + """ + id: ID! + timestamp: DateTime! @index + blockNumber: Int! @index + address: String! + account: String! + balance: BigInt! +} + +type CurvePoolBalance @entity { id: ID! timestamp: DateTime! @index blockNumber: Int! @index @@ -93,6 +127,28 @@ type StrategyDailyYield @entity { balance1: BigInt! balance2: BigInt! } + +type BalancerPoolBalance @entity { + id: ID! + timestamp: DateTime! @index + blockNumber: Int! @index + address: String! + balance0: BigInt! + balance1: BigInt! + balance2: BigInt! + balance3: BigInt! +} + +type BalancerPoolRate @entity { + id: ID! + timestamp: DateTime! @index + blockNumber: Int! @index + address: String! + rate0: BigInt! + rate1: BigInt! + rate2: BigInt! + rate3: BigInt! +} """ The OETH entity tracks the change in total supply of OETH over time. """ diff --git a/src/main-other.ts b/src/main-other.ts index 64398ed1..16a0bff0 100644 --- a/src/main-other.ts +++ b/src/main-other.ts @@ -1,11 +1,13 @@ import { run } from './processor' import * as exchangeRates from './shared/post-processors/exchange-rates' import * as aaveCompound from './shared/processors/aave-compound' +import * as balancer from './shared/processors/balancer' import * as curve from './shared/processors/curve' +import { erc20s } from './shared/processors/erc20s' export const processor = { stateSchema: 'other-processor', - processors: [aaveCompound, curve], + processors: [aaveCompound, balancer, curve, ...erc20s], postProcessors: [exchangeRates], validators: [], } diff --git a/src/model/generated/balancerPoolBalance.model.ts b/src/model/generated/balancerPoolBalance.model.ts new file mode 100644 index 00000000..ac458e8b --- /dev/null +++ b/src/model/generated/balancerPoolBalance.model.ts @@ -0,0 +1,35 @@ +import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, Index as Index_} from "typeorm" +import * as marshal from "./marshal" + +@Entity_() +export class BalancerPoolBalance { + constructor(props?: Partial) { + Object.assign(this, props) + } + + @PrimaryColumn_() + id!: string + + @Index_() + @Column_("timestamp with time zone", {nullable: false}) + timestamp!: Date + + @Index_() + @Column_("int4", {nullable: false}) + blockNumber!: number + + @Column_("text", {nullable: false}) + address!: string + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + balance0!: bigint + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + balance1!: bigint + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + balance2!: bigint + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + balance3!: bigint +} diff --git a/src/model/generated/balancerPoolRate.model.ts b/src/model/generated/balancerPoolRate.model.ts new file mode 100644 index 00000000..a8a42097 --- /dev/null +++ b/src/model/generated/balancerPoolRate.model.ts @@ -0,0 +1,35 @@ +import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, Index as Index_} from "typeorm" +import * as marshal from "./marshal" + +@Entity_() +export class BalancerPoolRate { + constructor(props?: Partial) { + Object.assign(this, props) + } + + @PrimaryColumn_() + id!: string + + @Index_() + @Column_("timestamp with time zone", {nullable: false}) + timestamp!: Date + + @Index_() + @Column_("int4", {nullable: false}) + blockNumber!: number + + @Column_("text", {nullable: false}) + address!: string + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + rate0!: bigint + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + rate1!: bigint + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + rate2!: bigint + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + rate3!: bigint +} diff --git a/src/model/generated/erc20.model.ts b/src/model/generated/erc20.model.ts new file mode 100644 index 00000000..35530660 --- /dev/null +++ b/src/model/generated/erc20.model.ts @@ -0,0 +1,26 @@ +import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_} from "typeorm" + +@Entity_() +export class ERC20 { + constructor(props?: Partial) { + Object.assign(this, props) + } + + /** + * Format: 'address' + */ + @PrimaryColumn_() + id!: string + + @Column_("text", {nullable: false}) + address!: string + + @Column_("text", {nullable: false}) + name!: string + + @Column_("int4", {nullable: false}) + decimals!: number + + @Column_("text", {nullable: false}) + symbol!: string +} diff --git a/src/model/generated/balance.model.ts b/src/model/generated/erc20Balance.model.ts similarity index 81% rename from src/model/generated/balance.model.ts rename to src/model/generated/erc20Balance.model.ts index 4d5fc326..f78a6524 100644 --- a/src/model/generated/balance.model.ts +++ b/src/model/generated/erc20Balance.model.ts @@ -2,13 +2,13 @@ import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, I import * as marshal from "./marshal" @Entity_() -export class Balance { - constructor(props?: Partial) { +export class ERC20Balance { + constructor(props?: Partial) { Object.assign(this, props) } /** - * Format: 'token:address:blockNumber' + * Format: 'address:account:blockNumber' */ @PrimaryColumn_() id!: string @@ -22,10 +22,10 @@ export class Balance { blockNumber!: number @Column_("text", {nullable: false}) - token!: string + address!: string @Column_("text", {nullable: false}) - address!: string + account!: string @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) balance!: bigint diff --git a/src/model/generated/erc20Holder.model.ts b/src/model/generated/erc20Holder.model.ts new file mode 100644 index 00000000..c39d5612 --- /dev/null +++ b/src/model/generated/erc20Holder.model.ts @@ -0,0 +1,20 @@ +import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_} from "typeorm" + +@Entity_() +export class ERC20Holder { + constructor(props?: Partial) { + Object.assign(this, props) + } + + /** + * Format: 'address:account' + */ + @PrimaryColumn_() + id!: string + + @Column_("text", {nullable: false}) + address!: string + + @Column_("text", {nullable: false}) + account!: string +} diff --git a/src/model/generated/erc20State.model.ts b/src/model/generated/erc20State.model.ts new file mode 100644 index 00000000..3d2dd72d --- /dev/null +++ b/src/model/generated/erc20State.model.ts @@ -0,0 +1,32 @@ +import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, Index as Index_} from "typeorm" +import * as marshal from "./marshal" + +@Entity_() +export class ERC20State { + constructor(props?: Partial) { + Object.assign(this, props) + } + + /** + * Format: 'address:blockNumber' + */ + @PrimaryColumn_() + id!: string + + @Index_() + @Column_("timestamp with time zone", {nullable: false}) + timestamp!: Date + + @Index_() + @Column_("int4", {nullable: false}) + blockNumber!: number + + @Column_("text", {nullable: false}) + address!: string + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + totalSupply!: bigint + + @Column_("int4", {nullable: false}) + holderCount!: number +} diff --git a/src/model/generated/index.ts b/src/model/generated/index.ts index 53235a21..737b66fa 100644 --- a/src/model/generated/index.ts +++ b/src/model/generated/index.ts @@ -1,9 +1,14 @@ export * from "./exchangeRate.model" -export * from "./balance.model" export * from "./strategyBalance.model" export * from "./strategyYield.model" export * from "./strategyDailyYield.model" +export * from "./erc20.model" +export * from "./erc20Holder.model" +export * from "./erc20State.model" +export * from "./erc20Balance.model" export * from "./curvePoolBalance.model" +export * from "./balancerPoolBalance.model" +export * from "./balancerPoolRate.model" export * from "./oeth.model" export * from "./oethAddress.model" export * from "./_rebasingOption" diff --git a/src/oeth/processors/strategies/strategies.ts b/src/oeth/processors/strategies/strategies.ts index 65c5bb93..f9141058 100644 --- a/src/oeth/processors/strategies/strategies.ts +++ b/src/oeth/processors/strategies/strategies.ts @@ -71,11 +71,24 @@ export const oethStrategies: readonly IStrategyData[] = [ oTokenAddress: OETH_ADDRESS, kind: 'BalancerMetaStablePool', base: { address: currencies.ETH, decimals: 18 }, - assets: [WETH_ADDRESS, RETH_ADDRESS].map((address) => ({ - address, - decimals: 18, - })), - earnings: { rewardTokenCollected: true, passiveByDepositWithdrawal: true }, + assets: [ + { + address: WETH_ADDRESS, + decimals: 18, + }, + { + address: RETH_ADDRESS, + decimals: 18, + convertTo: { + address: ETH_ADDRESS, + decimals: 18, + }, + }, + ], + earnings: { + rewardTokenCollected: true, + passiveByDepositWithdrawal: true, + }, balancerPoolInfo: { poolId: '0x1e19cf2d73a72ef1332c882f20534b6519be0276000200000000000000000112', diff --git a/src/ousd/validators/validate-ousd/validate-ousd.ts b/src/ousd/validators/validate-ousd/validate-ousd.ts index 227c4efc..df84071a 100644 --- a/src/ousd/validators/validate-ousd/validate-ousd.ts +++ b/src/ousd/validators/validate-ousd/validate-ousd.ts @@ -7,7 +7,7 @@ import { Block, Context } from '../../../processor' import { env } from '../../../utils/env' import { jsonify } from '../../../utils/jsonify' -export const name = 'validate-oeth' +export const name = 'validate-ousd' let firstBlock = true diff --git a/src/processor.ts b/src/processor.ts index 2589c18c..c1eea534 100644 --- a/src/processor.ts +++ b/src/processor.ts @@ -5,6 +5,7 @@ import { EvmBatchProcessorFields, } from '@subsquid/evm-processor' import { Store, TypeormDatabase } from '@subsquid/typeorm-store' +import assert from 'assert' import dayjs from 'dayjs' import duration from 'dayjs/plugin/duration' import utc from 'dayjs/plugin/utc' @@ -83,6 +84,11 @@ export const run = ({ postProcessors?: Pick[] validators?: Pick[] }) => { + assert( + !processors.find((p) => p.from === undefined), + 'All processors must have a `from` defined', + ) + const processor = createSquidProcessor() processor.setBlockRange({ diff --git a/src/shared/post-processors/exchange-rates/exchange-rates.ts b/src/shared/post-processors/exchange-rates/exchange-rates.ts index 95a357f3..0e1dd08b 100644 --- a/src/shared/post-processors/exchange-rates/exchange-rates.ts +++ b/src/shared/post-processors/exchange-rates/exchange-rates.ts @@ -87,9 +87,9 @@ export const ensureExchangeRatesAverages = async ( const pair = `${base}_${quote}` const rate = rates.reduce((sum, r) => sum + r.rate, 0n) / BigInt(rates.length) - ctx.log.info( - `Created average exchange rate of ${rate} using ${rates.length} rates`, - ) + // ctx.log.info( + // `Created average exchange rate of ${rate} using ${rates.length} rates`, + // ) return new ExchangeRate({ base: rates[0].base, quote: rates[0].quote, diff --git a/src/shared/post-processors/exchange-rates/price-routing.ts b/src/shared/post-processors/exchange-rates/price-routing.ts index 60ccce07..a68da771 100644 --- a/src/shared/post-processors/exchange-rates/price-routing.ts +++ b/src/shared/post-processors/exchange-rates/price-routing.ts @@ -1,6 +1,9 @@ +import { memoize } from 'lodash' + +import * as balancerRateProvider from '../../../abi/balancer-rate-provider' import * as chainlinkFeedRegistry from '../../../abi/chainlink-feed-registry' -import * as eacAggregatorProxy from '../../../abi/eac-aggregator-proxy' import * as frxEthFraxOracle from '../../../abi/frx-eth-frax-oracle' +import * as balancerMetaStablePoolAbi from '../../../abi/meta-stable-pool' import * as oethOracleRouter from '../../../abi/oeth-oracle-router' import * as stakedFraxEth from '../../../abi/sfrx-eth' import { Context } from '../../../processor' @@ -36,15 +39,21 @@ export const getPrice = async ( return getChainlinkPrice(ctx, height, base, quote) } -const rETHRegistryAddress = '0x536218f9E9Eb48863970252233c8F271f554C2d0' -export const getRETHPrice = (ctx: Context, height: number) => { - if (height < 16700133) return undefined - const registry = new eacAggregatorProxy.Contract( +export const getRETHPrice = async (ctx: Context, height: number) => { + if (height < 13846138) return undefined + // Balancer rETH Stable Pool Rate Provider + const rateProvider = await getBalancePoolRateProviders( ctx, { height }, - rETHRegistryAddress, + '0x1e19cf2d73a72ef1332c882f20534b6519be0276', ) - return registry.latestAnswer() + // Balancer Vault `getPoolTokens` https://etherscan.io/address/0xba12222222228d8ba445958a75a0704d566bf2c8#readContract#F10 + const provider = new balancerRateProvider.Contract( + ctx, + { height }, + rateProvider[0], // rETH Rate + ) + return await provider.getRate() } const registryAddress = '0x47fb2585d2c56fe188d0e6ec628a38b74fceeedf' @@ -106,3 +115,11 @@ export const getFrxEthPrice = (ctx: Context, height: number) => { ) return frxEth.latestRoundData().then((lrd) => lrd.answer) } + +export const getBalancePoolRateProviders = memoize( + async (ctx: Context, block: { height: number }, address: string) => { + const pool = new balancerMetaStablePoolAbi.Contract(ctx, block, address) + return await pool.getRateProviders() + }, + (_ctx, _block, address) => address.toLowerCase(), +) diff --git a/src/shared/processor-templates/balancer/balancer.ts b/src/shared/processor-templates/balancer/balancer.ts new file mode 100644 index 00000000..5fe2448c --- /dev/null +++ b/src/shared/processor-templates/balancer/balancer.ts @@ -0,0 +1,94 @@ +import { EvmBatchProcessor } from '@subsquid/evm-processor' + +import * as balancerRateProvider from '../../../abi/balancer-rate-provider' +import * as balancerVaultAbi from '../../../abi/balancer-vault' +import * as balancerMetaStablePoolAbi from '../../../abi/meta-stable-pool' +import { BalancerPoolBalance, BalancerPoolRate } from '../../../model' +import { Context } from '../../../processor' +import { ADDRESS_ZERO, BALANCER_VAULT } from '../../../utils/addresses' +import { blockFrequencyUpdater } from '../../../utils/blockFrequencyUpdater' + +const eth1 = BigInt('1000000000000000000') + +interface ProcessResult { + balancerPoolBalances: BalancerPoolBalance[] + balancerPoolRates: BalancerPoolRate[] +} + +export const createBalancerSetup = ( + from: number, + processor: EvmBatchProcessor, +) => { + processor.includeAllBlocks({ from }) +} + +export const createBalancerProcessor = ( + poolAddress: string, + poolId: string, + poolType: 'MetaStable' | 'ComposableStable' | 'Weighted', + from: number, +) => { + const update = blockFrequencyUpdater({ from }) + return async (ctx: Context) => { + const result: ProcessResult = { + balancerPoolBalances: [], + balancerPoolRates: [], + } + await update(ctx, async (ctx, block) => { + const balancerVault = new balancerVaultAbi.Contract( + ctx, + block.header, + BALANCER_VAULT, + ) + const [tokens, balances] = await balancerVault.getPoolTokens(poolId) + const balance = new BalancerPoolBalance({ + id: `${poolAddress}-${block.header.height}`, + blockNumber: block.header.height, + timestamp: new Date(block.header.timestamp), + address: poolAddress, + balance0: balances[0], + balance1: balances[1], + balance2: balances.length > 2 ? balances[2] : 0n, + balance3: balances.length > 3 ? balances[3] : 0n, + }) + result.balancerPoolBalances.push(balance) + + if (poolType === 'MetaStable') { + const balancerPool = new balancerMetaStablePoolAbi.Contract( + ctx, + block.header, + poolAddress, + ) + const rateProviders = await balancerPool.getRateProviders() + const rates: bigint[] = [] + for (let i = 0; i < tokens.length; i++) { + // ctx.log.info(`${rateProviders[i]}`) + if (rateProviders[i] === ADDRESS_ZERO) { + rates.push(eth1) + } else { + const provider = new balancerRateProvider.Contract( + ctx, + block.header, + rateProviders[i], + ) + const rate = await provider.getRate() + rates.push(rate) + } + } + const rate = new BalancerPoolRate({ + id: `${poolAddress}-${block.header.height}`, + blockNumber: block.header.height, + timestamp: new Date(block.header.timestamp), + address: poolAddress, + rate0: rates[0], + rate1: rates[1], + rate2: rates[2] ?? 0n, + rate3: rates[3] ?? 0n, + }) + result.balancerPoolRates.push(rate) + } + }) + await ctx.store.insert(result.balancerPoolBalances) + await ctx.store.insert(result.balancerPoolRates) + } +} diff --git a/src/shared/processor-templates/balancer/index.ts b/src/shared/processor-templates/balancer/index.ts new file mode 100644 index 00000000..9431c4f7 --- /dev/null +++ b/src/shared/processor-templates/balancer/index.ts @@ -0,0 +1 @@ +export * from './balancer' diff --git a/src/shared/processor-templates/erc20-balance/erc20-balance.ts b/src/shared/processor-templates/erc20-balance/erc20-balance.ts deleted file mode 100644 index 050e4544..00000000 --- a/src/shared/processor-templates/erc20-balance/erc20-balance.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { EvmBatchProcessor } from '@subsquid/evm-processor' - -import * as erc20 from '../../../abi/erc20' -import { Balance } from '../../../model' -import { Context } from '../../../processor' -import { blockFrequencyUpdater } from '../../../utils/blockFrequencyUpdater' - -interface ProcessResult { - balances: Balance[] -} - -export const createER20BalanceSetup = - (from: number) => (processor: EvmBatchProcessor) => { - processor.includeAllBlocks({ from }) - } - -export const createERC20BalanceProcessor = ({ - from, - address, - token, -}: { - from: number - address: string - token: string -}) => { - const update = blockFrequencyUpdater({ from }) - return async (ctx: Context) => { - const result: ProcessResult = { - balances: [], - } - await update(ctx, async (ctx, block) => { - const contract = new erc20.Contract(ctx, block.header, token) - const curve = new Balance({ - id: `${token}:${address}:${block.header.height}`, - blockNumber: block.header.height, - timestamp: new Date(block.header.timestamp), - token, - address, - balance: await contract.balanceOf(address), - }) - result.balances.push(curve) - }) - await ctx.store.insert(result.balances) - } -} diff --git a/src/shared/processor-templates/erc20-balance/index.ts b/src/shared/processor-templates/erc20-balance/index.ts deleted file mode 100644 index 98d9605e..00000000 --- a/src/shared/processor-templates/erc20-balance/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './erc20-balance' diff --git a/src/shared/processor-templates/erc20/erc20.ts b/src/shared/processor-templates/erc20/erc20.ts new file mode 100644 index 00000000..2088ef00 --- /dev/null +++ b/src/shared/processor-templates/erc20/erc20.ts @@ -0,0 +1,215 @@ +import { EvmBatchProcessor } from '@subsquid/evm-processor' + +import * as abi from '../../../abi/erc20' +import { ERC20, ERC20Balance, ERC20Holder, ERC20State } from '../../../model' +import { Context } from '../../../processor' +import { ADDRESS_ZERO } from '../../../utils/addresses' +import { blockFrequencyTracker } from '../../../utils/blockFrequencyUpdater' +import { LogFilter, logFilter } from '../../../utils/logFilter' +import { multicall } from '../../../utils/multicall' + +const duplicateTracker = new Set() + +export const createERC20Tracker = ({ + from, + address, + accountFilter = undefined, + rebaseFilters = [], + intervalTracking = false, +}: { + from: number + address: string + accountFilter?: string[] + rebaseFilters?: LogFilter[] + intervalTracking?: boolean // To be used *with* `accountFilter`. +}) => { + address = address.toLowerCase() + accountFilter = accountFilter?.map((a) => a.toLowerCase()) + if (duplicateTracker.has(address)) { + throw new Error('An ERC20 tracker was already created for: ' + address) + } + duplicateTracker.add(address) + const accountFilterSet = accountFilter + ? new Set(accountFilter.map((account) => account.toLowerCase())) + : undefined + + const intervalTracker = intervalTracking + ? blockFrequencyTracker({ from }) + : undefined + + let erc20: ERC20 | undefined + // Keep an in-memory record of what our current holders are. + // TODO: Consider doing this differently? + // Eventually memory may become a constraint. + let holders: Set + const transferLogFilters = [ + logFilter({ + address: [address.toLowerCase()], + topic0: [abi.events.Transfer.topic], + topic1: accountFilter, + range: { from }, + }), + logFilter({ + address: [address.toLowerCase()], + topic0: [abi.events.Transfer.topic], + topic2: accountFilter, + range: { from }, + }), + ] + const initialize = async (ctx: Context) => { + if (erc20) return + const block = ctx.blocks.find((b) => b.header.height >= from) + if (!block) return + erc20 = await ctx.store.findOne(ERC20, { where: { address } }) + try { + if (!erc20) { + const contract = new abi.Contract(ctx, block.header, address) + const [name, symbol, decimals] = await Promise.all([ + contract.name(), + contract.symbol(), + contract.decimals(), + ]) + erc20 = new ERC20({ + id: address, + address, + name, + symbol, + decimals, + }) + await ctx.store.insert(erc20) + } + } catch (err) { + ctx.log.error({ height: block.header.height, err }) + } + } + return { + from, + setup(processor: EvmBatchProcessor) { + if (intervalTracker) { + processor.includeAllBlocks({ from }) + } else { + transferLogFilters.forEach((filter) => processor.addLog(filter.value)) + } + rebaseFilters.forEach((filter) => processor.addLog(filter.value)) + }, + async process(ctx: Context) { + await initialize(ctx) + if (!holders) { + const holdersArray = await ctx.store.findBy(ERC20Holder, { address }) + holders = new Set() + for (const holder of holdersArray) { + holders.add(holder.account) + } + } + const result = { + states: new Map(), + balances: new Map(), + newHolders: new Map(), + removedHolders: new Set(), + } + for (const block of ctx.blocks) { + if (block.header.height < from) continue + const contract = new abi.Contract(ctx, block.header, address) + const updateState = async () => { + const id = `${block.header.height}:${address}` + const totalSupply = await contract.totalSupply() + const state = new ERC20State({ + id, + address, + timestamp: new Date(block.header.timestamp), + blockNumber: block.header.height, + totalSupply, + holderCount: holders.size, + }) + result.states.set(id, state) + } + const updateBalances = async ( + accounts: string[], + doStateUpdate = false, + ) => { + if (accountFilterSet) { + accounts = accounts.filter((a) => accountFilterSet.has(a)) + } + if (accounts.length > 0) { + const balances = await multicall( + ctx, + block.header, + abi.functions.balanceOf, + address, + accounts.map((account) => [account]), + ) + accounts.forEach((account, i) => { + if (account === ADDRESS_ZERO) return + const id = `${block.header.height}:${address}:${account}` + const balance = new ERC20Balance({ + id, + timestamp: new Date(block.header.timestamp), + blockNumber: block.header.height, + address, + account: account.toLowerCase(), + balance: balances[i], + }) + result.balances.set(id, balance) + if (balance.balance === 0n) { + doStateUpdate = true + holders.delete(account) + result.newHolders.delete(`${address}:${account}`) + result.removedHolders.add(`${address}:${account}`) + } else if (!holders.has(account)) { + const newHolder = new ERC20Holder({ + id: `${address}:${account}`, + address, + account, + }) + if (!holders.has(account)) { + doStateUpdate = true + holders.add(account) + } + result.newHolders.set(`${address}:${account}`, newHolder) + result.removedHolders.delete(`${address}:${account}`) + } + }) + } + if (doStateUpdate) { + await updateState() + } + } + const accounts = new Set() + let haveRebase = false + if (intervalTracker && intervalTracker(ctx, block) && accountFilter) { + await updateBalances(accountFilter) + } + + for (const log of block.logs) { + const isTransferLog = transferLogFilters.find((l) => l.matches(log)) + if (isTransferLog) { + const transfer = abi.events.Transfer.decode(log) + accounts.add(transfer.from) + accounts.add(transfer.to) + } + const isRebaseLog = rebaseFilters.find((l) => l.matches(log)) + if (isRebaseLog) { + haveRebase = true + const holders = await ctx.store.find(ERC20Holder, { + where: { address }, + }) + for (const holder of holders) { + accounts.add(holder.account) + } + } + } + await updateBalances([...accounts], haveRebase) + } + await Promise.all([ + ctx.store.upsert([...result.newHolders.values()]), + ctx.store.insert([...result.states.values()]), + ctx.store.insert([...result.balances.values()]), + ctx.store.remove( + [...result.removedHolders.values()].map( + (id) => new ERC20Holder({ id }), + ), + ), + ]) + }, + } +} diff --git a/src/shared/processor-templates/erc20/index.ts b/src/shared/processor-templates/erc20/index.ts new file mode 100644 index 00000000..33559911 --- /dev/null +++ b/src/shared/processor-templates/erc20/index.ts @@ -0,0 +1 @@ +export * from './erc20' diff --git a/src/shared/processor-templates/otoken/otoken.ts b/src/shared/processor-templates/otoken/otoken.ts index f8e32806..d4c91169 100644 --- a/src/shared/processor-templates/otoken/otoken.ts +++ b/src/shared/processor-templates/otoken/otoken.ts @@ -355,10 +355,10 @@ export const createOTokenProcessor = (params: { const data = otoken.events.TotalSupplyUpdatedHighres.decode(log) // OToken Object - const oethObject = await getLatestOTokenObject(ctx, result, block) - oethObject.totalSupply = data.totalSupply - oethObject.rebasingSupply = - oethObject.totalSupply - oethObject.nonRebasingSupply + const otokenObject = await getLatestOTokenObject(ctx, result, block) + otokenObject.totalSupply = data.totalSupply + otokenObject.rebasingSupply = + otokenObject.totalSupply - otokenObject.nonRebasingSupply if (!result.lastYieldDistributionEvent) { throw new Error('lastYieldDistributionEvent is not set') diff --git a/src/shared/processor-templates/strategy/strategy-balancer.ts b/src/shared/processor-templates/strategy/strategy-balancer.ts index 0cdcc34c..2d0b49b7 100644 --- a/src/shared/processor-templates/strategy/strategy-balancer.ts +++ b/src/shared/processor-templates/strategy/strategy-balancer.ts @@ -14,6 +14,7 @@ import { WETH_ADDRESS, } from '../../../utils/addresses' import { blockFrequencyUpdater } from '../../../utils/blockFrequencyUpdater' +import { getBalancePoolRateProviders } from '../../post-processors/exchange-rates/price-routing' import { IStrategyData } from './index' import { processStrategyEarnings, @@ -58,7 +59,27 @@ export const process = async (ctx: Context, strategyData: IStrategyData) => { data.push(...results) }) await ctx.store.insert(data) - await processStrategyEarnings(ctx, strategyData, getBalancerStrategyHoldings) + await processStrategyEarnings(ctx, strategyData, getStrategyETHBalance) +} + +export const getStrategyETHBalance = async ( + ctx: Context, + block: { height: number }, + strategyData: IStrategyData, +) => { + const { address } = strategyData + const strategy = new balancerMetaStablePoolStrategyAbi.Contract( + ctx, + block, + address, + ) + return [ + { + address, + asset: WETH_ADDRESS, + balance: await strategy['checkBalance()'](), + }, + ] } export const getBalancerStrategyHoldings = async ( @@ -69,7 +90,7 @@ export const getBalancerStrategyHoldings = async ( const { address, balancerPoolInfo } = strategyData const { poolAddress, poolId } = balancerPoolInfo! - const rateProviders = await _getBalancePoolRateProviders( + const rateProviders = await getBalancePoolRateProviders( ctx, block, poolAddress, @@ -127,12 +148,3 @@ export const getBalancerStrategyHoldings = async ( return { address, asset: asset.toLowerCase(), balance } }) } - -const _getBalancePoolRateProviders = memoize( - async (ctx: Context, block: { height: number }, address: string) => { - const pool = new balancerMetaStablePoolAbi.Contract(ctx, block, address) - const rateProviders = await pool.getRateProviders() - return rateProviders - }, - (_ctx, _block, address) => address.toLowerCase(), -) diff --git a/src/shared/processor-templates/strategy/strategy-curve-amo.ts b/src/shared/processor-templates/strategy/strategy-curve-amo.ts index 9cfb5dad..f450c6e1 100644 --- a/src/shared/processor-templates/strategy/strategy-curve-amo.ts +++ b/src/shared/processor-templates/strategy/strategy-curve-amo.ts @@ -1,16 +1,11 @@ import { EvmBatchProcessor } from '@subsquid/evm-processor' -import { pad } from 'viem' import * as curvePool from '../../../abi/curve-lp-token' import * as erc20 from '../../../abi/erc20' import * as abstractStrategyAbi from '../../../abi/initializable-abstract-strategy' import { StrategyBalance } from '../../../model' import { Block, Context } from '../../../processor' -import { - ETH_ADDRESS, - OETH_ADDRESS, - WETH_ADDRESS, -} from '../../../utils/addresses' +import { ETH_ADDRESS, WETH_ADDRESS } from '../../../utils/addresses' import { blockFrequencyUpdater } from '../../../utils/blockFrequencyUpdater' import { IStrategyData } from './index' import { @@ -124,7 +119,7 @@ export const getConvexEthMetaStrategyBalances = async ( pTokenAddresses.add(pTokenAddr) const pToken = new erc20.Contract(ctx, block, pTokenAddr) const pTokenBalance = await pToken.balanceOf(address) - ctx.log.info({ height: block.height, pTokenAddr, pTokenBalance }) + // ctx.log.info({ height: block.height, pTokenAddr, pTokenBalance }) unstakedBalance += pTokenBalance } } diff --git a/src/shared/processor-templates/strategy/strategy-earnings.ts b/src/shared/processor-templates/strategy/strategy-earnings.ts index c5b17673..aac0efee 100644 --- a/src/shared/processor-templates/strategy/strategy-earnings.ts +++ b/src/shared/processor-templates/strategy/strategy-earnings.ts @@ -1,14 +1,11 @@ import { EvmBatchProcessor } from '@subsquid/evm-processor' import dayjs from 'dayjs' import { LessThan } from 'typeorm' -import { formatEther, pad, parseEther, parseUnits } from 'viem' +import { formatEther, pad } from 'viem' -import * as aaveLendingPool from '../../../abi/aave-lending-pool' -import * as aToken from '../../../abi/aave-token' import * as baseRewardPool from '../../../abi/base-reward-pool' import * as erc20 from '../../../abi/erc20' import * as abstractStrategyAbi from '../../../abi/initializable-abstract-strategy' -import * as otoken from '../../../abi/otoken' import { OETH, StrategyYield } from '../../../model' import { Block, Context } from '../../../processor' import { @@ -25,7 +22,7 @@ import { import { blockFrequencyTracker } from '../../../utils/blockFrequencyUpdater' import { logFilter } from '../../../utils/logFilter' import { convertDecimals, lastExcept } from '../../../utils/utils' -import { ensureExchangeRatesAverages } from '../../post-processors/exchange-rates' +import { ensureExchangeRates } from '../../post-processors/exchange-rates' import { Currency, convertRate, @@ -219,18 +216,6 @@ export const processStrategyEarnings = async ( balances, ) } - const balanceTrackingUpdateBalancerMetaStablePool = async () => { - // ctx.log.info(`balanceTrackingUpdateBalancerMetaStablePool`) - didUpdate = true - const balances = await getBalances() - await processDepositWithdrawal( - ctx, - strategyData, - block, - strategyYields, - balances, - ) - } if ( strategyData.balanceUpdateTraceFilters && @@ -290,7 +275,7 @@ export const processStrategyEarnings = async ( baseRewardPoolTopics.has(log.topics[0]) && log.topics[1] === pad(strategyData.address as `0x${string}`) ) { - await balanceTrackingUpdateBalancerMetaStablePool() + await balanceTrackingUpdate() } else if ( strategyData.balanceUpdateLogFilters?.find((f) => f.matches(log)) ) { @@ -300,13 +285,13 @@ export const processStrategyEarnings = async ( strategyData.earnings?.passiveByDepositWithdrawal && depositWithdrawalTopics.has(log.topics[0]) ) { - ctx.log.info({ - type: - log.topics[0] === abstractStrategyAbi.events.Deposit.topic - ? 'Deposit' - : 'Withdrawal', - transactionHash: log.transactionHash, - }) + // ctx.log.info({ + // type: + // log.topics[0] === abstractStrategyAbi.events.Deposit.topic + // ? 'Deposit' + // : 'Withdrawal', + // transactionHash: log.transactionHash, + // }) await balanceTrackingUpdate() } else if ( log.address === strategyData.address && @@ -411,13 +396,7 @@ const processDepositWithdrawal = async ( const desiredRates = strategyData.assets .filter((a) => a.convertTo) .map((a) => [a.convertTo!.address, a.address]) as [Currency, Currency][] - const rates = await ensureExchangeRatesAverages( - ctx, - block, - dayjs.utc(block.header.timestamp).subtract(21, 'days').toDate(), - new Date(block.header.timestamp), - desiredRates, - ) + const rates = await ensureExchangeRates(ctx, block, desiredRates) const previousBalance = assets.reduce((sum, a, index) => { const asset = strategyData.assets[index] const compareBalance = asset.convertTo @@ -465,17 +444,17 @@ const processDepositWithdrawal = async ( earningsChange, earnings: (latest?.earnings ?? 0n) + earningsChange, }) - ctx.log.info( - `${block.header.height} Setting balance: ${formatEther( - balance, - )}, last balance: ${formatEther( - latest?.balance ?? 0n, - )}, previous block balance: ${formatEther( - previousBalance, - )}, perceived earnings: ${formatEther( - earningsChange, - )}, total earnings: ${formatEther(current.earnings)}`, - ) + // ctx.log.info( + // `${block.header.height} Setting balance: ${formatEther( + // balance, + // )}, last balance: ${formatEther( + // latest?.balance ?? 0n, + // )}, previous block balance: ${formatEther( + // previousBalance, + // )}, perceived earnings: ${formatEther( + // earningsChange, + // )}, total earnings: ${formatEther(current.earnings)}`, + // ) if (earningsChange < 0) { ctx.log.warn('WARNING: earnings change is negative') diff --git a/src/shared/processors/aave-compound.ts b/src/shared/processors/aave-compound.ts index b5456810..d4322dfe 100644 --- a/src/shared/processors/aave-compound.ts +++ b/src/shared/processors/aave-compound.ts @@ -1,32 +1,36 @@ +import { EvmBatchProcessor } from '@subsquid/evm-processor' + import { Context } from '../../processor' -import { - createER20BalanceSetup, - createERC20BalanceProcessor, -} from '../processor-templates/erc20-balance' +import { createERC20Tracker } from '../processor-templates/erc20' -const tracks: Parameters[0][] = [ +const tracks: Parameters[0][] = [ { from: 11362821, - address: '0x3Ed3B47Dd13EC9a98b44e6204A523E766B225811', // aUSDT - token: '0xdac17f958d2ee523a2206206994597c13d831ec7', // USDT + address: '0xdac17f958d2ee523a2206206994597c13d831ec7', // USDT + accountFilter: ['0x3Ed3B47Dd13EC9a98b44e6204A523E766B225811'], // aUSDT + intervalTracking: true, }, { from: 11367200, - address: '0xBcca60bB61934080951369a648Fb03DF4F96263C', // aUSDC - token: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', // USDC + address: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', // USDC + accountFilter: ['0xBcca60bB61934080951369a648Fb03DF4F96263C'], // aUSDC + intervalTracking: true, }, { from: 11367184, - address: '0x028171bca77440897b824ca71d1c56cac55b68a3', // aDAI - token: '0x6b175474e89094c44da98b954eedeac495271d0f', // DAI + address: '0x6b175474e89094c44da98b954eedeac495271d0f', // DAI + accountFilter: ['0x028171bca77440897b824ca71d1c56cac55b68a3'], // aDAI + intervalTracking: true, }, ] export const from = Math.min(...tracks.map((t) => t.from)) -export const setup = createER20BalanceSetup(from) +const processors = tracks.map(createERC20Tracker) -const processors = tracks.map(createERC20BalanceProcessor) +export const setup = (processor: EvmBatchProcessor) => { + processors.forEach((p) => p.setup(processor)) +} export const process = async (ctx: Context) => { - await Promise.all(processors.map((p) => p(ctx))) + await Promise.all(processors.map((p) => p.process(ctx))) } diff --git a/src/shared/processors/balancer/balancer.ts b/src/shared/processors/balancer/balancer.ts new file mode 100644 index 00000000..aac65ba7 --- /dev/null +++ b/src/shared/processors/balancer/balancer.ts @@ -0,0 +1,70 @@ +import { EvmBatchProcessor } from '@subsquid/evm-processor' + +import { Context } from '../../../processor' +import { + createBalancerProcessor, + createBalancerSetup, +} from '../../processor-templates/balancer' + +const ousdResetFrom = 11585978 +const oethDeployFrom = 16933090 + +const pools = [ + { + name: 'Balancer rETH Stable Pool', + poolSymbol: 'B-rETH-STABLE', + poolType: 'MetaStable', + poolAddress: '0x1e19cf2d73a72ef1332c882f20534b6519be0276', + poolId: + '0x1e19cf2d73a72ef1332c882f20534b6519be0276000200000000000000000112', + from: Math.max(13850000, oethDeployFrom), + }, + // { + // name: 'wstETH-rETH-sfrxETH-BPT', + // poolSymbol: 'wstETH-rETH-sfrxETH-BPT', + // poolType: 'ComposableStable', + // poolAddress: '0x42ed016f826165c2e5976fe5bc3df540c5ad0af7', + // poolId: + // '0x42ed016f826165c2e5976fe5bc3df540c5ad0af700000000000000000000058b', + // from: Math.max(17680000, oethDeployFrom), + // }, + // { + // name: 'Balancer wstETH-WETH Stable Pool', + // poolSymbol: 'wstETH-WETH-BPT', + // poolType: 'ComposableStable', + // poolAddress: '0x93d199263632a4ef4bb438f1feb99e57b4b5f0bd', + // poolId: + // '0x93d199263632a4ef4bb438f1feb99e57b4b5f0bd0000000000000000000005c2', + // from: Math.max(17920000, oethDeployFrom), + // }, + // { + // name: 'Balancer 80 BAL 20 WETH', + // poolSymbol: 'B-80BAL-20WETH', + // poolType: 'Weighted', + // poolAddress: '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56', + // poolId: + // '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014', + // from: Math.max(12370000, oethDeployFrom), + // }, +] as const + +export const from = Math.min(...pools.map((p) => p.from)) + +export const setup = (processor: EvmBatchProcessor) => { + for (const pool of pools) { + createBalancerSetup(pool.from, processor) + } +} + +const processors = pools.map((pool) => + createBalancerProcessor( + pool.poolAddress.toLowerCase(), + pool.poolId.toLowerCase(), + pool.poolType, + pool.from, + ), +) + +export const process = async (ctx: Context) => { + await Promise.all(processors.map((p) => p(ctx))) +} diff --git a/src/shared/processors/balancer/index.ts b/src/shared/processors/balancer/index.ts new file mode 100644 index 00000000..9431c4f7 --- /dev/null +++ b/src/shared/processors/balancer/index.ts @@ -0,0 +1 @@ +export * from './balancer' diff --git a/src/shared/processors/erc20s.ts b/src/shared/processors/erc20s.ts new file mode 100644 index 00000000..65729bfc --- /dev/null +++ b/src/shared/processors/erc20s.ts @@ -0,0 +1,19 @@ +import * as otoken from '../../abi/otoken' +import { OETH_ADDRESS } from '../../utils/addresses' +import { logFilter } from '../../utils/logFilter' +import { createERC20Tracker } from '../processor-templates/erc20' + +export const erc20s = [ + createERC20Tracker({ + from: 16935276, + address: OETH_ADDRESS, + rebaseFilters: [ + logFilter({ + address: [OETH_ADDRESS], + topic0: [otoken.events.TotalSupplyUpdatedHighres.topic], + transaction: true, + range: { from: 16935276 }, + }), + ], + }), +] diff --git a/src/shared/validators/validate-shared.ts b/src/shared/validators/validate-shared.ts new file mode 100644 index 00000000..809503af --- /dev/null +++ b/src/shared/validators/validate-shared.ts @@ -0,0 +1,103 @@ +import { Entity, EntityClass } from '@subsquid/typeorm-store' +import assert from 'assert' +import { sortBy } from 'lodash' + +import { ERC20Balance } from '../../model' +import { Block, Context } from '../../processor' +import { env } from '../../utils/env' +import { jsonify } from '../../utils/jsonify' + +export const name = 'validate-shared' + +let firstBlock = true + +export const process = async (ctx: Context) => { + if (env.BLOCK_FROM) return + for (const block of ctx.blocks) { + await validateExpectations( + ctx, + block, + ERC20Balance, + expectations.erc20Balances, + ) + firstBlock = false + } +} + +const validateExpectations = async < + T extends Entity & { + timestamp: string + blockNumber: number + }, +>( + ctx: Context, + block: Block, + Class: EntityClass, + expectations?: T[], +) => { + if (!expectations) return + if (firstBlock) { + while (expectations[0]?.blockNumber < block.header.height) { + const entity = expectations.shift()! + await validateExpectation(ctx, Class, entity) + } + } + assert( + !expectations.length || expectations[0]?.blockNumber >= block.header.height, + 'Something is missing', + ) + while (expectations[0]?.blockNumber === block.header.height) { + const entity = expectations.shift()! + await validateExpectation(ctx, Class, entity) + } +} + +const validateExpectation = async < + T extends Entity & { + timestamp: string + blockNumber: number + }, +>( + ctx: Context, + Class: EntityClass, + expectation: T, +) => { + const actual = await ctx.store.findOne(Class, { + where: { id: expectation.id }, + }) + assert( + actual, + `Expected entity does not exist: Entity=${Class.name} id=${expectation.id}`, + ) + expectation.timestamp = new Date(expectation.timestamp).toJSON() + // We decide to only care about float decimal accuracy to the 8th. + assert.deepEqual( + JSON.parse( + jsonify(actual, (_key, value) => + typeof value === 'number' ? Number(value.toFixed(8)) : value, + ), + ), + JSON.parse( + jsonify(expectation, (_key, value) => + typeof value === 'number' ? Number(value.toFixed(8)) : value, + ), + ), + ) + ctx.log.info(`Validated entity: Entity=${Class.name} id=${expectation.id}`) +} + +const e = (arr: any[]) => { + return sortBy(arr, (v) => v.blockNumber) +} + +const expectations = { + erc20Balances: e([ + { + id: '11424000:0x6b175474e89094c44da98b954eedeac495271d0f:0x028171bca77440897b824ca71d1c56cac55b68a3', + blockNumber: 11424000, + address: '0x6b175474e89094c44da98b954eedeac495271d0f', + account: '0x028171bca77440897b824ca71d1c56cac55b68a3', + balance: '345330738036343186917749', + }, + ]), +} as const diff --git a/src/utils/multicall.ts b/src/utils/multicall.ts new file mode 100644 index 00000000..eaa6b5c0 --- /dev/null +++ b/src/utils/multicall.ts @@ -0,0 +1,30 @@ +import { toHex } from 'viem' + +import { Func } from '../abi/abi.support' +import { Multicall } from '../abi/multicall' +import { Block, Context } from '../processor' + +const MULTICALL_CONTRACT = '0x5ba1e12693dc8f9c48aad8770482f4739beed696' +const from = 12336033 + +export const multicall = async ( + ctx: Context, + header: Block['header'], + func: Func, + address: string, + calls: Args[], +) => { + if (header.height >= from) { + const multicall = new Multicall(ctx, header, MULTICALL_CONTRACT) + return multicall.aggregate(func, address, calls) + } + const batchCalls = calls.map((fnParams) => ({ + method: 'eth_call', + params: [ + { to: address, data: func.encode(fnParams) }, + toHex(header.height), + ], + })) + const results = await ctx._chain.client.batchCall(batchCalls) + return results.map((r) => func.decodeResult(r)) +}