diff --git a/indexers/staking-squid/db/migrations/1722613051042-Data.js b/indexers/staking-squid/db/migrations/1722724863452-Data.js similarity index 74% rename from indexers/staking-squid/db/migrations/1722613051042-Data.js rename to indexers/staking-squid/db/migrations/1722724863452-Data.js index 3be75732c..d56b294f3 100644 --- a/indexers/staking-squid/db/migrations/1722613051042-Data.js +++ b/indexers/staking-squid/db/migrations/1722724863452-Data.js @@ -1,25 +1,39 @@ -module.exports = class Data1722613051042 { - name = 'Data1722613051042' +module.exports = class Data1722724863452 { + name = 'Data1722724863452' async up(db) { - await db.query(`CREATE TABLE "domain" ("id" character varying NOT NULL, "sort_id" integer NOT NULL, "completed_epoch" integer NOT NULL, "last_domain_block_number" integer NOT NULL, "total_deposits" numeric NOT NULL, "total_tax_collected" numeric NOT NULL, "total_rewards_collected" numeric NOT NULL, "current_total_stake" numeric NOT NULL, "current_storage_fee_deposit" numeric NOT NULL, "created_at" integer, "updated_at" integer, CONSTRAINT "PK_27e3ec3ea0ae02c8c5bceab3ba9" PRIMARY KEY ("id"))`) + await db.query(`CREATE TABLE "domain" ("id" character varying NOT NULL, "sort_id" integer NOT NULL, "account_id" text NOT NULL, "name" text NOT NULL, "runtime_id" integer NOT NULL, "runtime" character varying(6) NOT NULL, "runtime_info" text, "completed_epoch" integer NOT NULL, "last_domain_block_number" integer NOT NULL, "total_deposits" numeric NOT NULL, "total_tax_collected" numeric NOT NULL, "total_rewards_collected" numeric NOT NULL, "total_transfers_in" numeric NOT NULL, "transfers_in_count" integer NOT NULL, "total_transfers_out" numeric NOT NULL, "transfers_out_count" integer NOT NULL, "total_rejected_transfers_claimed" numeric NOT NULL, "rejected_transfers_claimed_count" integer NOT NULL, "total_transfers_rejected" numeric NOT NULL, "transfers_rejected_count" integer NOT NULL, "total_volume" numeric NOT NULL, "total_consensus_storage_fee" numeric NOT NULL, "total_domain_execution_fee" numeric NOT NULL, "total_burned_balance" numeric NOT NULL, "current_total_stake" numeric NOT NULL, "current_storage_fee_deposit" numeric NOT NULL, "bundle_count" integer NOT NULL, "last_bundle_at" integer NOT NULL, "created_at" integer, "updated_at" integer, CONSTRAINT "PK_27e3ec3ea0ae02c8c5bceab3ba9" PRIMARY KEY ("id"))`) await db.query(`CREATE INDEX "IDX_c1e90b3654ffe0a5544e502edb" ON "domain" ("sort_id") `) + await db.query(`CREATE INDEX "IDX_c117722589b2b0f927aa16e25f" ON "domain" ("account_id") `) + await db.query(`CREATE INDEX "IDX_26a07113f90df161f919c7d5a6" ON "domain" ("name") `) + await db.query(`CREATE INDEX "IDX_36bfbe927612b8c0712c25f5a0" ON "domain" ("runtime_id") `) + await db.query(`CREATE INDEX "IDX_9f96c312a490b7a2a3d017721e" ON "domain" ("runtime") `) await db.query(`CREATE INDEX "IDX_80867983eb3f6e204acfea4214" ON "domain" ("completed_epoch") `) await db.query(`CREATE INDEX "IDX_e3a1b94f40001682587d9b0a3d" ON "domain" ("last_domain_block_number") `) + await db.query(`CREATE INDEX "IDX_6da91e378c5e17e2d467b592b0" ON "domain" ("total_volume") `) await db.query(`CREATE INDEX "IDX_35b49bec8ab1e3864de09a60d6" ON "domain" ("created_at") `) await db.query(`CREATE INDEX "IDX_c3ac554ab7a2ed97d9a0af4083" ON "domain" ("updated_at") `) await db.query(`CREATE TABLE "account" ("id" character varying NOT NULL, "total_deposits" numeric NOT NULL, "total_tax_collected" numeric NOT NULL, "created_at" integer, "updated_at" integer, CONSTRAINT "PK_54115ee388cdb6d86bb4bf5b2ea" PRIMARY KEY ("id"))`) await db.query(`CREATE INDEX "IDX_2740156ea8742b8df1ad9d9774" ON "account" ("created_at") `) await db.query(`CREATE INDEX "IDX_8bed31488e09ed64770378600b" ON "account" ("updated_at") `) - await db.query(`CREATE TABLE "operator" ("id" character varying NOT NULL, "sort_id" integer NOT NULL, "account_id" text NOT NULL, "domain_id" text NOT NULL, "signing_key" text NOT NULL, "minimum_nominator_stake" numeric NOT NULL, "nomination_tax" integer NOT NULL, "current_total_stake" numeric NOT NULL, "current_storage_fee_deposit" numeric NOT NULL, "current_epoch_rewards" numeric NOT NULL, "current_total_shares" numeric NOT NULL, "total_deposits" numeric NOT NULL, "total_tax_collected" numeric NOT NULL, "total_rewards_collected" numeric NOT NULL, "raw_status" text, "status" character varying(15) NOT NULL, "bundle_count" integer NOT NULL, "last_bundle_at" integer NOT NULL, "created_at" integer, "updated_at" integer, CONSTRAINT "PK_8b950e1572745d9f69be7748ae8" PRIMARY KEY ("id"))`) + await db.query(`CREATE TABLE "operator" ("id" character varying NOT NULL, "sort_id" integer NOT NULL, "account_id" text NOT NULL, "domain_id" text NOT NULL, "signing_key" text NOT NULL, "minimum_nominator_stake" numeric NOT NULL, "nomination_tax" integer NOT NULL, "current_total_stake" numeric NOT NULL, "current_storage_fee_deposit" numeric NOT NULL, "current_epoch_rewards" numeric NOT NULL, "current_total_shares" numeric NOT NULL, "total_deposits" numeric NOT NULL, "total_tax_collected" numeric NOT NULL, "total_rewards_collected" numeric NOT NULL, "total_transfers_in" numeric NOT NULL, "transfers_in_count" integer NOT NULL, "total_transfers_out" numeric NOT NULL, "transfers_out_count" integer NOT NULL, "total_rejected_transfers_claimed" numeric NOT NULL, "rejected_transfers_claimed_count" integer NOT NULL, "total_transfers_rejected" numeric NOT NULL, "transfers_rejected_count" integer NOT NULL, "total_volume" numeric NOT NULL, "total_consensus_storage_fee" numeric NOT NULL, "total_domain_execution_fee" numeric NOT NULL, "total_burned_balance" numeric NOT NULL, "raw_status" text, "status" character varying(15) NOT NULL, "active_epoch_count" integer NOT NULL, "bundle_count" integer NOT NULL, "last_bundle_at" integer NOT NULL, "created_at" integer, "updated_at" integer, CONSTRAINT "PK_8b950e1572745d9f69be7748ae8" PRIMARY KEY ("id"))`) await db.query(`CREATE INDEX "IDX_ed3a1bcef6df98998f07a571a7" ON "operator" ("sort_id") `) await db.query(`CREATE INDEX "IDX_91b197ab29ad85b5e616289ea0" ON "operator" ("account_id") `) await db.query(`CREATE INDEX "IDX_1c800426a1f738c1b202ff839f" ON "operator" ("domain_id") `) await db.query(`CREATE INDEX "IDX_51b6c3609906ff3cd25e39e1b2" ON "operator" ("signing_key") `) + await db.query(`CREATE INDEX "IDX_df2e0bb74daa727ab46b3292b0" ON "operator" ("total_volume") `) await db.query(`CREATE INDEX "IDX_c7fd0bf382a9832cf1db87827c" ON "operator" ("status") `) await db.query(`CREATE INDEX "IDX_d6260ed02d20cf8231ebb742d6" ON "operator" ("created_at") `) await db.query(`CREATE INDEX "IDX_d6d18ca05472785030a7a3963b" ON "operator" ("updated_at") `) - await db.query(`CREATE TABLE "nominator" ("id" character varying NOT NULL, "account_id" text NOT NULL, "domain_id" text NOT NULL, "operator_id" text NOT NULL, "shares" numeric NOT NULL, "total_deposits" numeric NOT NULL, "status" character varying(12) NOT NULL, "created_at" integer, "updated_at" integer, CONSTRAINT "PK_7489b7a79b066f2660eab25f60b" PRIMARY KEY ("id"))`) + await db.query(`CREATE TABLE "bundle" ("id" character varying NOT NULL, "account_id" text NOT NULL, "domain_id" text NOT NULL, "operator_id" text NOT NULL, "domain_block_number" integer NOT NULL, "consensus_block_number" integer NOT NULL, "consensus_block_hash" text NOT NULL, "total_transfers_in" numeric NOT NULL, "transfers_in_count" integer NOT NULL, "total_transfers_out" numeric NOT NULL, "transfers_out_count" integer NOT NULL, "total_rejected_transfers_claimed" numeric NOT NULL, "rejected_transfers_claimed_count" integer NOT NULL, "total_transfers_rejected" numeric NOT NULL, "transfers_rejected_count" integer NOT NULL, "total_volume" numeric NOT NULL, "consensus_storage_fee" numeric NOT NULL, "domain_execution_fee" numeric NOT NULL, "burned_balance" numeric NOT NULL, CONSTRAINT "PK_637e3f87e837d6532109c198dea" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_ff3a1311a556605695ae534097" ON "bundle" ("account_id") `) + await db.query(`CREATE INDEX "IDX_82b7b684e1b50da245f72d6020" ON "bundle" ("domain_id") `) + await db.query(`CREATE INDEX "IDX_74f4813b760e4ab18e662e2394" ON "bundle" ("operator_id") `) + await db.query(`CREATE INDEX "IDX_b5c951f317cc0451d9d3c21aaa" ON "bundle" ("domain_block_number") `) + await db.query(`CREATE INDEX "IDX_5c848822e75fbe863d43231468" ON "bundle" ("consensus_block_number") `) + await db.query(`CREATE INDEX "IDX_33b73ddabaa5e322cff6ab15b1" ON "bundle" ("consensus_block_hash") `) + await db.query(`CREATE INDEX "IDX_e49c7ec4d50ca7652f97f57172" ON "bundle" ("total_volume") `) + await db.query(`CREATE TABLE "nominator" ("id" character varying NOT NULL, "account_id" text NOT NULL, "domain_id" text NOT NULL, "operator_id" text NOT NULL, "shares" numeric NOT NULL, "total_deposits" numeric NOT NULL, "status" character varying(15) NOT NULL, "created_at" integer, "updated_at" integer, CONSTRAINT "PK_7489b7a79b066f2660eab25f60b" PRIMARY KEY ("id"))`) await db.query(`CREATE INDEX "IDX_917636e6d1130ea9506eaeafef" ON "nominator" ("account_id") `) await db.query(`CREATE INDEX "IDX_b017cafe03fa79059bd8164c4e" ON "nominator" ("domain_id") `) await db.query(`CREATE INDEX "IDX_14374f281ccb6e72c55dab3c20" ON "nominator" ("operator_id") `) @@ -75,8 +89,13 @@ module.exports = class Data1722613051042 { async down(db) { await db.query(`DROP TABLE "domain"`) await db.query(`DROP INDEX "public"."IDX_c1e90b3654ffe0a5544e502edb"`) + await db.query(`DROP INDEX "public"."IDX_c117722589b2b0f927aa16e25f"`) + await db.query(`DROP INDEX "public"."IDX_26a07113f90df161f919c7d5a6"`) + await db.query(`DROP INDEX "public"."IDX_36bfbe927612b8c0712c25f5a0"`) + await db.query(`DROP INDEX "public"."IDX_9f96c312a490b7a2a3d017721e"`) await db.query(`DROP INDEX "public"."IDX_80867983eb3f6e204acfea4214"`) await db.query(`DROP INDEX "public"."IDX_e3a1b94f40001682587d9b0a3d"`) + await db.query(`DROP INDEX "public"."IDX_6da91e378c5e17e2d467b592b0"`) await db.query(`DROP INDEX "public"."IDX_35b49bec8ab1e3864de09a60d6"`) await db.query(`DROP INDEX "public"."IDX_c3ac554ab7a2ed97d9a0af4083"`) await db.query(`DROP TABLE "account"`) @@ -87,9 +106,18 @@ module.exports = class Data1722613051042 { await db.query(`DROP INDEX "public"."IDX_91b197ab29ad85b5e616289ea0"`) await db.query(`DROP INDEX "public"."IDX_1c800426a1f738c1b202ff839f"`) await db.query(`DROP INDEX "public"."IDX_51b6c3609906ff3cd25e39e1b2"`) + await db.query(`DROP INDEX "public"."IDX_df2e0bb74daa727ab46b3292b0"`) await db.query(`DROP INDEX "public"."IDX_c7fd0bf382a9832cf1db87827c"`) await db.query(`DROP INDEX "public"."IDX_d6260ed02d20cf8231ebb742d6"`) await db.query(`DROP INDEX "public"."IDX_d6d18ca05472785030a7a3963b"`) + await db.query(`DROP TABLE "bundle"`) + await db.query(`DROP INDEX "public"."IDX_ff3a1311a556605695ae534097"`) + await db.query(`DROP INDEX "public"."IDX_82b7b684e1b50da245f72d6020"`) + await db.query(`DROP INDEX "public"."IDX_74f4813b760e4ab18e662e2394"`) + await db.query(`DROP INDEX "public"."IDX_b5c951f317cc0451d9d3c21aaa"`) + await db.query(`DROP INDEX "public"."IDX_5c848822e75fbe863d43231468"`) + await db.query(`DROP INDEX "public"."IDX_33b73ddabaa5e322cff6ab15b1"`) + await db.query(`DROP INDEX "public"."IDX_e49c7ec4d50ca7652f97f57172"`) await db.query(`DROP TABLE "nominator"`) await db.query(`DROP INDEX "public"."IDX_917636e6d1130ea9506eaeafef"`) await db.query(`DROP INDEX "public"."IDX_b017cafe03fa79059bd8164c4e"`) diff --git a/indexers/staking-squid/hasura/hasura_metadata.json b/indexers/staking-squid/hasura/hasura_metadata.json new file mode 100644 index 000000000..3f27e53a6 --- /dev/null +++ b/indexers/staking-squid/hasura/hasura_metadata.json @@ -0,0 +1,1030 @@ +{ + "resource_version": 16, + "metadata": { + "version": 3, + "sources": [ + { + "name": "default", + "kind": "postgres", + "tables": [ + { + "table": { + "name": "account", + "schema": "public" + }, + "array_relationships": [ + { + "name": "bundles", + "using": { + "manual_configuration": { + "column_mapping": { + "id": "account_id" + }, + "insertion_order": null, + "remote_table": { + "name": "bundle", + "schema": "public" + } + } + } + }, + { + "name": "deposits", + "using": { + "manual_configuration": { + "column_mapping": { + "id": "account_id" + }, + "insertion_order": null, + "remote_table": { + "name": "deposit", + "schema": "public" + } + } + } + }, + { + "name": "domain", + "using": { + "manual_configuration": { + "column_mapping": { + "id": "account_id" + }, + "insertion_order": null, + "remote_table": { + "name": "domain", + "schema": "public" + } + } + } + }, + { + "name": "nominators", + "using": { + "manual_configuration": { + "column_mapping": { + "id": "account_id" + }, + "insertion_order": null, + "remote_table": { + "name": "nominator", + "schema": "public" + } + } + } + }, + { + "name": "operators", + "using": { + "manual_configuration": { + "column_mapping": { + "id": "account_id" + }, + "insertion_order": null, + "remote_table": { + "name": "operator", + "schema": "public" + } + } + } + }, + { + "name": "withdrawals", + "using": { + "manual_configuration": { + "column_mapping": { + "id": "account_id" + }, + "insertion_order": null, + "remote_table": { + "name": "withdrawal", + "schema": "public" + } + } + } + } + ], + "select_permissions": [ + { + "role": "user", + "permission": { + "columns": [ + "id", + "created_at", + "updated_at", + "total_deposits", + "total_tax_collected" + ], + "filter": {}, + "allow_aggregations": true + }, + "comment": "" + } + ] + }, + { + "table": { + "name": "bundle", + "schema": "public" + }, + "object_relationships": [ + { + "name": "account", + "using": { + "manual_configuration": { + "column_mapping": { + "account_id": "id" + }, + "insertion_order": null, + "remote_table": { + "name": "account", + "schema": "public" + } + } + } + }, + { + "name": "domain", + "using": { + "manual_configuration": { + "column_mapping": { + "domain_id": "id" + }, + "insertion_order": null, + "remote_table": { + "name": "domain", + "schema": "public" + } + } + } + }, + { + "name": "operator", + "using": { + "manual_configuration": { + "column_mapping": { + "operator_id": "id" + }, + "insertion_order": null, + "remote_table": { + "name": "operator", + "schema": "public" + } + } + } + } + ], + "select_permissions": [ + { + "role": "user", + "permission": { + "columns": [ + "id", + "consensus_block_number", + "domain_block_number", + "rejected_transfers_claimed_count", + "transfers_in_count", + "transfers_out_count", + "transfers_rejected_count", + "burned_balance", + "consensus_storage_fee", + "domain_execution_fee", + "total_rejected_transfers_claimed", + "total_transfers_in", + "total_transfers_out", + "total_transfers_rejected", + "total_volume", + "account_id", + "consensus_block_hash", + "domain_id", + "operator_id" + ], + "filter": {} + }, + "comment": "" + } + ] + }, + { + "table": { + "name": "deposit", + "schema": "public" + }, + "object_relationships": [ + { + "name": "account", + "using": { + "manual_configuration": { + "column_mapping": { + "account_id": "id" + }, + "insertion_order": null, + "remote_table": { + "name": "account", + "schema": "public" + } + } + } + }, + { + "name": "domain", + "using": { + "manual_configuration": { + "column_mapping": { + "domain_id": "id" + }, + "insertion_order": null, + "remote_table": { + "name": "domain", + "schema": "public" + } + } + } + }, + { + "name": "nominator", + "using": { + "manual_configuration": { + "column_mapping": { + "nominator_id": "id" + }, + "insertion_order": null, + "remote_table": { + "name": "nominator", + "schema": "public" + } + } + } + }, + { + "name": "operator", + "using": { + "manual_configuration": { + "column_mapping": { + "operator_id": "id" + }, + "insertion_order": null, + "remote_table": { + "name": "operator", + "schema": "public" + } + } + } + } + ], + "select_permissions": [ + { + "role": "user", + "permission": { + "columns": [ + "id", + "status", + "block_number", + "amount", + "storage_fee_deposit", + "account_id", + "domain_id", + "extrinsic_hash", + "nominator_id", + "operator_id", + "timestamp" + ], + "filter": {}, + "allow_aggregations": true + }, + "comment": "" + } + ] + }, + { + "table": { + "name": "domain", + "schema": "public" + }, + "object_relationships": [ + { + "name": "account", + "using": { + "manual_configuration": { + "column_mapping": { + "account_id": "id" + }, + "insertion_order": null, + "remote_table": { + "name": "account", + "schema": "public" + } + } + } + } + ], + "array_relationships": [ + { + "name": "bundles", + "using": { + "manual_configuration": { + "column_mapping": { + "id": "domain_id" + }, + "insertion_order": null, + "remote_table": { + "name": "bundle", + "schema": "public" + } + } + } + }, + { + "name": "deposits", + "using": { + "manual_configuration": { + "column_mapping": { + "id": "domain_id" + }, + "insertion_order": null, + "remote_table": { + "name": "deposit", + "schema": "public" + } + } + } + }, + { + "name": "nominators", + "using": { + "manual_configuration": { + "column_mapping": { + "id": "domain_id" + }, + "insertion_order": null, + "remote_table": { + "name": "nominator", + "schema": "public" + } + } + } + }, + { + "name": "operators", + "using": { + "manual_configuration": { + "column_mapping": { + "id": "domain_id" + }, + "insertion_order": null, + "remote_table": { + "name": "operator", + "schema": "public" + } + } + } + }, + { + "name": "rewards", + "using": { + "manual_configuration": { + "column_mapping": { + "id": "domain_id" + }, + "insertion_order": null, + "remote_table": { + "name": "operator_reward_event", + "schema": "public" + } + } + } + }, + { + "name": "withdrawals", + "using": { + "manual_configuration": { + "column_mapping": { + "id": "domain_id" + }, + "insertion_order": null, + "remote_table": { + "name": "withdrawal", + "schema": "public" + } + } + } + } + ], + "select_permissions": [ + { + "role": "user", + "permission": { + "columns": [ + "id", + "runtime", + "bundle_count", + "completed_epoch", + "created_at", + "last_bundle_at", + "last_domain_block_number", + "rejected_transfers_claimed_count", + "runtime_id", + "sort_id", + "transfers_in_count", + "transfers_out_count", + "transfers_rejected_count", + "updated_at", + "current_storage_fee_deposit", + "current_total_stake", + "total_burned_balance", + "total_consensus_storage_fee", + "total_deposits", + "total_domain_execution_fee", + "total_rejected_transfers_claimed", + "total_rewards_collected", + "total_tax_collected", + "total_transfers_in", + "total_transfers_out", + "total_transfers_rejected", + "total_volume", + "account_id", + "name", + "runtime_info" + ], + "filter": {}, + "allow_aggregations": true + }, + "comment": "" + } + ] + }, + { + "table": { + "name": "nominator", + "schema": "public" + }, + "object_relationships": [ + { + "name": "account", + "using": { + "manual_configuration": { + "column_mapping": { + "account_id": "id" + }, + "insertion_order": null, + "remote_table": { + "name": "account", + "schema": "public" + } + } + } + }, + { + "name": "domain", + "using": { + "manual_configuration": { + "column_mapping": { + "domain_id": "id" + }, + "insertion_order": null, + "remote_table": { + "name": "domain", + "schema": "public" + } + } + } + }, + { + "name": "operator", + "using": { + "manual_configuration": { + "column_mapping": { + "operator_id": "id" + }, + "insertion_order": null, + "remote_table": { + "name": "operator", + "schema": "public" + } + } + } + } + ], + "array_relationships": [ + { + "name": "deposits", + "using": { + "manual_configuration": { + "column_mapping": { + "id": "nominator_id" + }, + "insertion_order": null, + "remote_table": { + "name": "deposit", + "schema": "public" + } + } + } + }, + { + "name": "withdrawals", + "using": { + "manual_configuration": { + "column_mapping": { + "id": "nominator_id" + }, + "insertion_order": null, + "remote_table": { + "name": "withdrawal", + "schema": "public" + } + } + } + } + ], + "select_permissions": [ + { + "role": "user", + "permission": { + "columns": [ + "id", + "status", + "created_at", + "updated_at", + "shares", + "total_deposits", + "account_id", + "domain_id", + "operator_id" + ], + "filter": {}, + "allow_aggregations": true + }, + "comment": "" + } + ] + }, + { + "table": { + "name": "operator", + "schema": "public" + }, + "object_relationships": [ + { + "name": "account", + "using": { + "manual_configuration": { + "column_mapping": { + "account_id": "id" + }, + "insertion_order": null, + "remote_table": { + "name": "account", + "schema": "public" + } + } + } + }, + { + "name": "domain", + "using": { + "manual_configuration": { + "column_mapping": { + "domain_id": "id" + }, + "insertion_order": null, + "remote_table": { + "name": "domain", + "schema": "public" + } + } + } + } + ], + "array_relationships": [ + { + "name": "bundles", + "using": { + "manual_configuration": { + "column_mapping": { + "id": "operator_id" + }, + "insertion_order": null, + "remote_table": { + "name": "bundle", + "schema": "public" + } + } + } + }, + { + "name": "deposits", + "using": { + "manual_configuration": { + "column_mapping": { + "id": "operator_id" + }, + "insertion_order": null, + "remote_table": { + "name": "deposit", + "schema": "public" + } + } + } + }, + { + "name": "nominators", + "using": { + "manual_configuration": { + "column_mapping": { + "id": "operator_id" + }, + "insertion_order": null, + "remote_table": { + "name": "nominator", + "schema": "public" + } + } + } + }, + { + "name": "rewards", + "using": { + "manual_configuration": { + "column_mapping": { + "id": "operator_id" + }, + "insertion_order": null, + "remote_table": { + "name": "operator_reward_event", + "schema": "public" + } + } + } + }, + { + "name": "withdrawals", + "using": { + "manual_configuration": { + "column_mapping": { + "id": "operator_id" + }, + "insertion_order": null, + "remote_table": { + "name": "withdrawal", + "schema": "public" + } + } + } + } + ], + "select_permissions": [ + { + "role": "user", + "permission": { + "columns": [ + "id", + "status", + "active_epoch_count", + "bundle_count", + "created_at", + "last_bundle_at", + "nomination_tax", + "rejected_transfers_claimed_count", + "sort_id", + "transfers_in_count", + "transfers_out_count", + "transfers_rejected_count", + "updated_at", + "current_epoch_rewards", + "current_storage_fee_deposit", + "current_total_shares", + "current_total_stake", + "minimum_nominator_stake", + "total_burned_balance", + "total_consensus_storage_fee", + "total_deposits", + "total_domain_execution_fee", + "total_rejected_transfers_claimed", + "total_rewards_collected", + "total_tax_collected", + "total_transfers_in", + "total_transfers_out", + "total_transfers_rejected", + "total_volume", + "account_id", + "domain_id", + "raw_status", + "signing_key" + ], + "filter": {}, + "allow_aggregations": true + }, + "comment": "" + } + ] + }, + { + "table": { + "name": "operator_reward_event", + "schema": "public" + }, + "object_relationships": [ + { + "name": "domain", + "using": { + "manual_configuration": { + "column_mapping": { + "domain_id": "id" + }, + "insertion_order": null, + "remote_table": { + "name": "domain", + "schema": "public" + } + } + } + }, + { + "name": "operator", + "using": { + "manual_configuration": { + "column_mapping": { + "operator_id": "id" + }, + "insertion_order": null, + "remote_table": { + "name": "operator", + "schema": "public" + } + } + } + } + ], + "select_permissions": [ + { + "role": "user", + "permission": { + "columns": [ + "id", + "block_number", + "amount", + "domain_id", + "extrinsic_hash", + "operator_id", + "timestamp" + ], + "filter": {} + }, + "comment": "" + } + ] + }, + { + "table": { + "name": "stats", + "schema": "public" + }, + "select_permissions": [ + { + "role": "user", + "permission": { + "columns": [ + "id", + "active_operators_count", + "block_number", + "deposits_count", + "domains_count", + "nominators_count", + "operators_count", + "slashed_operators_count", + "withdrawals_count", + "all_time_high_staked", + "total_deposits", + "total_fees", + "total_staked", + "total_withdrawals", + "timestamp" + ], + "filter": {} + }, + "comment": "" + } + ] + }, + { + "table": { + "name": "stats_per_domain", + "schema": "public" + }, + "object_relationships": [ + { + "name": "domain", + "using": { + "manual_configuration": { + "column_mapping": { + "domain_id": "id" + }, + "insertion_order": null, + "remote_table": { + "name": "domain", + "schema": "public" + } + } + } + } + ], + "select_permissions": [ + { + "role": "user", + "permission": { + "columns": [ + "id", + "active_operators_count", + "block_number", + "deposits_count", + "nominators_count", + "operators_count", + "slashed_operators_count", + "withdrawals_count", + "all_time_high_staked", + "total_deposits", + "total_fees", + "total_staked", + "total_withdrawals", + "domain_id", + "timestamp" + ], + "filter": {} + }, + "comment": "" + } + ] + }, + { + "table": { + "name": "stats_per_operator", + "schema": "public" + }, + "object_relationships": [ + { + "name": "domain", + "using": { + "manual_configuration": { + "column_mapping": { + "domain_id": "id" + }, + "insertion_order": null, + "remote_table": { + "name": "domain", + "schema": "public" + } + } + } + }, + { + "name": "operator", + "using": { + "manual_configuration": { + "column_mapping": { + "operator_id": "id" + }, + "insertion_order": null, + "remote_table": { + "name": "operator", + "schema": "public" + } + } + } + } + ], + "select_permissions": [ + { + "role": "user", + "permission": { + "columns": [ + "id", + "block_number", + "deposits_count", + "nominators_count", + "withdrawals_count", + "all_time_high_staked", + "total_deposits", + "total_fees", + "total_staked", + "total_withdrawals", + "domain_id", + "operator_id", + "timestamp" + ], + "filter": {} + }, + "comment": "" + } + ] + }, + { + "table": { + "name": "withdrawal", + "schema": "public" + }, + "object_relationships": [ + { + "name": "account", + "using": { + "manual_configuration": { + "column_mapping": { + "account_id": "id" + }, + "insertion_order": null, + "remote_table": { + "name": "account", + "schema": "public" + } + } + } + }, + { + "name": "domain", + "using": { + "manual_configuration": { + "column_mapping": { + "domain_id": "id" + }, + "insertion_order": null, + "remote_table": { + "name": "domain", + "schema": "public" + } + } + } + }, + { + "name": "nominator", + "using": { + "manual_configuration": { + "column_mapping": { + "nominator_id": "id" + }, + "insertion_order": null, + "remote_table": { + "name": "nominator", + "schema": "public" + } + } + } + }, + { + "name": "operator", + "using": { + "manual_configuration": { + "column_mapping": { + "operator_id": "id" + }, + "insertion_order": null, + "remote_table": { + "name": "operator", + "schema": "public" + } + } + } + } + ], + "select_permissions": [ + { + "role": "user", + "permission": { + "columns": [ + "id", + "status", + "block_number", + "shares", + "account_id", + "domain_id", + "extrinsic_hash", + "nominator_id", + "operator_id", + "timestamp" + ], + "filter": {}, + "allow_aggregations": true + }, + "comment": "" + } + ] + } + ], + "configuration": { + "connection_info": { + "database_url": { + "from_env": "HASURA_GRAPHQL_DATABASE_URL" + }, + "isolation_level": "read-committed", + "pool_settings": { + "connection_lifetime": 600, + "idle_timeout": 180, + "max_connections": 50, + "retries": 1 + }, + "use_prepared_statements": true + } + } + } + ] + } +} diff --git a/indexers/staking-squid/schema.graphql b/indexers/staking-squid/schema.graphql index 3600d8cc8..b0063b7aa 100644 --- a/indexers/staking-squid/schema.graphql +++ b/indexers/staking-squid/schema.graphql @@ -1,13 +1,37 @@ +enum DomainRuntime { + EVM + AutoId +} + type Domain @entity { id: ID! @index sortId: Int! @index + accountId: String! @index + name: String! @index + runtimeId: Int! @index + runtime: DomainRuntime! @index + runtimeInfo: String completedEpoch: Int! @index lastDomainBlockNumber: Int! @index totalDeposits: BigInt! totalTaxCollected: BigInt! totalRewardsCollected: BigInt! + totalTransfersIn: BigInt! + transfersInCount: Int! + totalTransfersOut: BigInt! + transfersOutCount: Int! + totalRejectedTransfersClaimed: BigInt! + rejectedTransfersClaimedCount: Int! + totalTransfersRejected: BigInt! + transfersRejectedCount: Int! + totalVolume: BigInt! @index + totalConsensusStorageFee: BigInt! + totalDomainExecutionFee: BigInt! + totalBurnedBalance: BigInt! currentTotalStake: BigInt! currentStorageFeeDeposit: BigInt! + bundleCount: Int! + lastBundleAt: Int! createdAt: Int @index updatedAt: Int @index } @@ -43,18 +67,54 @@ type Operator @entity { totalDeposits: BigInt! totalTaxCollected: BigInt! totalRewardsCollected: BigInt! + totalTransfersIn: BigInt! + transfersInCount: Int! + totalTransfersOut: BigInt! + transfersOutCount: Int! + totalRejectedTransfersClaimed: BigInt! + rejectedTransfersClaimedCount: Int! + totalTransfersRejected: BigInt! + transfersRejectedCount: Int! + totalVolume: BigInt! @index + totalConsensusStorageFee: BigInt! + totalDomainExecutionFee: BigInt! + totalBurnedBalance: BigInt! rawStatus: String status: OperatorStatus! @index + activeEpochCount: Int! bundleCount: Int! lastBundleAt: Int! createdAt: Int @index updatedAt: Int @index } +type Bundle @entity { + id: ID! @index + accountId: String! @index + domainId: String! @index + operatorId: String! @index + domainBlockNumber: Int! @index + consensusBlockNumber: Int! @index + consensusBlockHash: String! @index + totalTransfersIn: BigInt! + transfersInCount: Int! + totalTransfersOut: BigInt! + transfersOutCount: Int! + totalRejectedTransfersClaimed: BigInt! + rejectedTransfersClaimedCount: Int! + totalTransfersRejected: BigInt! + transfersRejectedCount: Int! + totalVolume: BigInt! @index + consensusStorageFee: BigInt! + domainExecutionFee: BigInt! + burnedBalance: BigInt! +} + enum NominatorStatus { PENDING STAKING DEREGISTERED + READY_TO_UNLOCK SLASHED } diff --git a/indexers/staking-squid/squid.yaml b/indexers/staking-squid/squid.yaml index 05d778221..823bc4a9f 100644 --- a/indexers/staking-squid/squid.yaml +++ b/indexers/staking-squid/squid.yaml @@ -1,6 +1,6 @@ manifestVersion: subsquid.io/v0.1 name: staking-squid -version: 2 +version: 8 description: Autonomys - Astral - Staking Indexer build: deploy: @@ -28,6 +28,6 @@ scale: addons: postgres: storage: 100G - profile: medium + profile: large processor: profile: medium diff --git a/indexers/staking-squid/src/events/bundle.ts b/indexers/staking-squid/src/events/bundle.ts index 1b9877c00..8f93fb379 100644 --- a/indexers/staking-squid/src/events/bundle.ts +++ b/indexers/staking-squid/src/events/bundle.ts @@ -1,5 +1,11 @@ import type { CtxBlock, CtxEvent, CtxExtrinsic } from "../processor"; -import { getOrCreateDomain, getOrCreateOperator } from "../storage"; +import { + createBundle, + getOrCreateAccount, + getOrCreateDomain, + getOrCreateOperator, +} from "../storage"; +import { ExecutionReceipt, SealedBundleHeader, Transfers } from "../types/v1"; import { getBlockNumber } from "../utils"; import { Cache } from "../utils/cache"; @@ -9,20 +15,105 @@ export function processBundleStoredEvent( extrinsic: CtxExtrinsic, event: CtxEvent ) { - const domainId = Number(event.args.domainId); - const operatorId = Number(event.args.bundleAuthor); - const lastDomainBlockNumber = Number( - extrinsic.call?.args.opaqueBundle.sealedHeader.header.receipt - .domainBlockNumber - ); - const domain = getOrCreateDomain(cache, block, domainId); + const { domainId, bundleAuthor } = event.args; + const domainIdNum = Number(domainId); + const operatorId = Number(bundleAuthor); + + const sealedHeader = extrinsic.call?.args.opaqueBundle + .sealedHeader as SealedBundleHeader; + const domain = getOrCreateDomain(cache, block, domainIdNum); const operator = getOrCreateOperator(cache, block, operatorId); + const account = getOrCreateAccount(cache, block, operator.accountId); + + const receipt = sealedHeader.header.receipt as ExecutionReceipt; + const { transfers } = receipt; + + const totalTransfersIn = transfers.transfersIn.reduce( + (acc, [, amount]) => acc + BigInt(amount), + BigInt(0) + ); + const transfersInCount = transfers.transfersIn.length; + + const totalTransfersOut = transfers.transfersOut.reduce( + (acc, [, amount]) => acc + BigInt(amount), + BigInt(0) + ); + const transfersOutCount = transfers.transfersOut.length; + + const totalRejectedTransfersClaimed = + transfers.rejectedTransfersClaimed.reduce( + (acc, [, amount]) => acc + BigInt(amount), + BigInt(0) + ); + const rejectedTransfersClaimedCount = + transfers.rejectedTransfersClaimed.length; + + const totalTransfersRejected = transfers.transfersRejected.reduce( + (acc, [, amount]) => acc + BigInt(amount), + BigInt(0) + ); + const transfersRejectedCount = transfers.transfersRejected.length; + + const totalVolume = totalTransfersIn + totalTransfersOut; + + const { + domainBlockNumber, + consensusBlockNumber, + consensusBlockHash, + blockFees, + } = receipt; + + const bundle = createBundle(account.id, domain.id, operator.id, { + domainBlockNumber: Number(domainBlockNumber), + consensusBlockNumber: Number(consensusBlockNumber), + consensusBlockHash, + totalTransfersIn, + transfersInCount, + totalTransfersOut, + transfersOutCount, + totalRejectedTransfersClaimed, + rejectedTransfersClaimedCount, + totalTransfersRejected, + transfersRejectedCount, + totalVolume, + consensusStorageFee: BigInt(blockFees.consensusStorageFee), + domainExecutionFee: BigInt(blockFees.domainExecutionFee), + burnedBalance: BigInt(blockFees.burnedBalance), + }); + + cache.bundles.set(bundle.id, bundle); - domain.lastDomainBlockNumber = lastDomainBlockNumber; + domain.lastDomainBlockNumber = Number(domainBlockNumber); + domain.totalTransfersIn += totalTransfersIn; + domain.transfersInCount += transfersInCount; + domain.totalTransfersOut += totalTransfersOut; + domain.transfersOutCount += transfersOutCount; + domain.totalRejectedTransfersClaimed += totalRejectedTransfersClaimed; + domain.rejectedTransfersClaimedCount += rejectedTransfersClaimedCount; + domain.totalTransfersRejected += totalTransfersRejected; + domain.transfersRejectedCount += transfersRejectedCount; + domain.totalVolume += totalVolume; + domain.totalConsensusStorageFee += BigInt(blockFees.consensusStorageFee); + domain.totalDomainExecutionFee += BigInt(blockFees.domainExecutionFee); + domain.totalBurnedBalance += BigInt(blockFees.burnedBalance); + domain.bundleCount++; + domain.lastBundleAt = getBlockNumber(block); domain.updatedAt = getBlockNumber(block); cache.domains.set(domain.id, domain); + operator.totalTransfersIn += totalTransfersIn; + operator.transfersInCount += transfersInCount; + operator.totalTransfersOut += totalTransfersOut; + operator.transfersOutCount += transfersOutCount; + operator.totalRejectedTransfersClaimed += totalRejectedTransfersClaimed; + operator.rejectedTransfersClaimedCount += rejectedTransfersClaimedCount; + operator.totalTransfersRejected += totalTransfersRejected; + operator.transfersRejectedCount += transfersRejectedCount; + operator.totalConsensusStorageFee += BigInt(blockFees.consensusStorageFee); + operator.totalDomainExecutionFee += BigInt(blockFees.domainExecutionFee); + operator.totalBurnedBalance += BigInt(blockFees.burnedBalance); + operator.totalVolume += totalVolume; operator.bundleCount++; operator.lastBundleAt = getBlockNumber(block); operator.updatedAt = getBlockNumber(block); diff --git a/indexers/staking-squid/src/events/domain.ts b/indexers/staking-squid/src/events/domain.ts index f0dcfd4fc..878fb8022 100644 --- a/indexers/staking-squid/src/events/domain.ts +++ b/indexers/staking-squid/src/events/domain.ts @@ -7,9 +7,11 @@ export function processDomainInstantiatedEvent( block: CtxBlock, event: CtxEvent ) { - const domainId = Number(event.args.domainId || 0); + const { domainId, completedEpochIndex } = event.args; + const completedEpoch = Number(completedEpochIndex ?? 0); + const domain = getOrCreateDomain(cache, block, domainId, { - completedEpoch: Number(event.args.completedEpochIndex || 0), + completedEpoch, }); cache.domains.set(domain.id, domain); diff --git a/indexers/staking-squid/src/events/epoch.ts b/indexers/staking-squid/src/events/epoch.ts index 0e2866ec5..e8102dccb 100644 --- a/indexers/staking-squid/src/events/epoch.ts +++ b/indexers/staking-squid/src/events/epoch.ts @@ -1,11 +1,18 @@ import { operators as getOperators } from "@autonomys/auto-consensus"; import type { ApiPromise } from "@autonomys/auto-utils"; -import { DepositStatus, NominatorStatus, OperatorStatus } from "../model"; +import { + DepositStatus, + DomainRuntime, + NominatorStatus, + OperatorStatus, + WithdrawalStatus, +} from "../model"; import type { CtxBlock, CtxEvent, CtxExtrinsic } from "../processor"; import { createStats, createStatsPerDomain, createStatsPerOperator, + getOrCreateAccount, getOrCreateDomain, getOrCreateOperator, } from "../storage"; @@ -19,51 +26,124 @@ export async function processEpochTransitionEvent( extrinsic: CtxExtrinsic, event: CtxEvent ) { - const domainId = Number(event.args.domainId); + const { domainId, completedEpochIndex } = event.args; const domain = getOrCreateDomain(cache, block, domainId); - const completedEpoch = Number(event.args.completedEpochIndex); + const completedEpoch = Number(completedEpochIndex); + const currentBlockNumber = getBlockNumber(block); const apiAt = await api.at(block.header.hash); + const [domainRegistry, operatorsAll] = await Promise.all([ + apiAt.query.domains.domainRegistry.entries(), + getOperators(apiAt), + ]); + + const parsedDomains = domainRegistry.map((domain) => ({ + domainId: (domain[0].toHuman() as string[])[0], + ...(domain[1].toJSON() as Omit< + { + domainId: string; + ownerAccountId: string; + domainConfig: { + domainName: string; + runtimeId: number; + operatorAllowList: { + operators: string[]; + }; + }; + domainRuntimeInfo: object; + }, + "domainId" + >), + })); - const operatorsAll = await getOperators(apiAt); const allOperators = operatorsAll.filter( (o) => o.operatorDetails.currentDomainId === BigInt(domainId) ); - for (const operator of allOperators) { - const op = getOrCreateOperator( - cache, - block, - parseInt(operator.operatorId.toString()) - ); + for (const pDomain of parsedDomains) { + const accountId = pDomain.ownerAccountId; + const _domain = getOrCreateDomain(cache, block, Number(pDomain.domainId)); + + cache.accounts.set(accountId, getOrCreateAccount(cache, block, accountId)); - const rawStatus = JSON.stringify(operator.operatorDetails.status); - op.currentEpochRewards = operator.operatorDetails.currentEpochRewards; - op.currentTotalShares = operator.operatorDetails.currentTotalShares; - op.currentTotalStake = operator.operatorDetails.currentTotalStake; - op.currentStorageFeeDeposit = - operator.operatorDetails.totalStorageFeeDeposit; + _domain.accountId = accountId; + _domain.name = pDomain.domainConfig.domainName; + _domain.runtimeId = pDomain.domainConfig.runtimeId; + + const stringifiedRuntime = JSON.stringify(pDomain.domainRuntimeInfo); + _domain.runtime = stringifiedRuntime.includes("AutoId") + ? DomainRuntime.AutoId + : DomainRuntime.EVM; + _domain.runtimeInfo = stringifiedRuntime; + _domain.updatedAt = currentBlockNumber; + + cache.domains.set(_domain.id, _domain); + } + + for (const operator of allOperators) { + const op = getOrCreateOperator(cache, block, Number(operator.operatorId)); + + const { + currentEpochRewards, + currentTotalShares, + currentTotalStake, + totalStorageFeeDeposit, + status, + } = operator.operatorDetails; + + const rawStatus = JSON.stringify(status); + op.currentEpochRewards = currentEpochRewards; + op.currentTotalShares = currentTotalShares; + op.currentTotalStake = currentTotalStake; + op.currentStorageFeeDeposit = totalStorageFeeDeposit; op.rawStatus = rawStatus; - op.updatedAt = getBlockNumber(block); + op.updatedAt = currentBlockNumber; cache.operators.set(op.id, op); try { - const rawStatusKey = Object.keys(rawStatus); - if (rawStatusKey[0] === "deregistered") { - const unlockBlock = ( - rawStatus as unknown as { - deregistered: { unlockAtConfirmedDomainBlockNumber: number }; - } - ).deregistered.unlockAtConfirmedDomainBlockNumber; - - if (unlockBlock <= domain.lastDomainBlockNumber) { - op.status = OperatorStatus.READY_TO_UNLOCK; - cache.operators.set(op.id, op); - } + const _status = JSON.parse(rawStatus) as { + registered?: null; + deregistered?: { + domainEpoch: [number, number]; + unlockAtConfirmedDomainBlockNumber: number; + }; + }; + + if ( + _status.deregistered && + _status.deregistered.unlockAtConfirmedDomainBlockNumber <= + domain.lastDomainBlockNumber + ) { + op.status = OperatorStatus.READY_TO_UNLOCK; + cache.operators.set(op.id, op); + + Array.from(cache.nominators.values()) + .filter( + (n) => + n.status === NominatorStatus.STAKING && n.operatorId === op.id + ) + .forEach((n) => { + n.status = NominatorStatus.READY_TO_UNLOCK; + n.updatedAt = currentBlockNumber; + cache.nominators.set(n.id, n); + }); + + Array.from(cache.withdrawals.values()) + .filter( + (w) => + w.status === WithdrawalStatus.PENDING && w.domainId === domain.id + ) + .forEach((w) => { + w.status = WithdrawalStatus.READY; + cache.withdrawals.set(w.id, w); + }); } } catch (e) { - console.error("Error in processEpochTransitionEvent", e); + console.error( + "Error parsing operator status in processEpochTransitionEvent", + e + ); } } @@ -77,28 +157,45 @@ export async function processEpochTransitionEvent( ); domain.completedEpoch = completedEpoch; - domain.updatedAt = getBlockNumber(block); + domain.updatedAt = currentBlockNumber; cache.domains.set(domain.id, domain); - // Switch Pending to Active Array.from(cache.operators.values()) - .filter((o) => o.status === OperatorStatus.PENDING) - .map((o) => { + .filter( + (o) => o.status === OperatorStatus.REGISTERED && o.domainId === domain.id + ) + .forEach((o) => { + o.activeEpochCount++; + o.updatedAt = currentBlockNumber; + cache.operators.set(o.id, o); + }); + + Array.from(cache.operators.values()) + .filter( + (o) => o.status === OperatorStatus.PENDING && o.domainId === domain.id + ) + .forEach((o) => { o.status = OperatorStatus.REGISTERED; - o.updatedAt = getBlockNumber(block); + o.updatedAt = currentBlockNumber; cache.operators.set(o.id, o); }); + Array.from(cache.nominators.values()) - .filter((n) => n.status === NominatorStatus.PENDING) - .map((n) => { + .filter( + (n) => n.status === NominatorStatus.PENDING && n.domainId === domain.id + ) + .forEach((n) => { n.status = NominatorStatus.STAKING; - n.updatedAt = getBlockNumber(block); + n.updatedAt = currentBlockNumber; cache.nominators.set(n.id, n); }); + Array.from(cache.deposits.values()) - .filter((d) => d.status === DepositStatus.PENDING) - .map((d) => { + .filter( + (d) => d.status === DepositStatus.PENDING && d.domainId === domain.id + ) + .forEach((d) => { d.status = DepositStatus.DEPOSITED; cache.deposits.set(d.id, d); }); @@ -107,23 +204,21 @@ export async function processEpochTransitionEvent( const stats = createStats(cache, block); cache.stats.set(stats.id, stats); - const domains = Array.from(cache.domains.values()); - for (const domain of domains) { - const statsPerDomain = createStatsPerDomain(cache, block, domain); - cache.statsPerDomain.set(statsPerDomain.id, statsPerDomain); + const statsPerDomain = createStatsPerDomain(cache, block, domain); + cache.statsPerDomain.set(statsPerDomain.id, statsPerDomain); - const operators = Array.from(cache.operators.values()).filter( - (o) => o.domainId === domain.id + const operators = Array.from(cache.operators.values()).filter( + (o) => o.domainId === domain.id && o.status === OperatorStatus.REGISTERED + ); + + for (const operator of operators) { + const statsPerOperator = createStatsPerOperator( + cache, + block, + domain, + operator ); - for (const operator of operators) { - const statsPerOperator = createStatsPerOperator( - cache, - block, - domain, - operator - ); - cache.statsPerOperator.set(statsPerOperator.id, statsPerOperator); - } + cache.statsPerOperator.set(statsPerOperator.id, statsPerOperator); } return cache; diff --git a/indexers/staking-squid/src/events/index.ts b/indexers/staking-squid/src/events/index.ts index 3ab496c75..c316e90d0 100644 --- a/indexers/staking-squid/src/events/index.ts +++ b/indexers/staking-squid/src/events/index.ts @@ -32,7 +32,6 @@ async function processEvent( extrinsic: CtxExtrinsic, event: CtxEvent ) { - console.log("event", event.name); switch (event.name) { // new domain case events.domains.domainInstantiated.name: diff --git a/indexers/staking-squid/src/events/operator.ts b/indexers/staking-squid/src/events/operator.ts index 3e7c02868..a21290391 100644 --- a/indexers/staking-squid/src/events/operator.ts +++ b/indexers/staking-squid/src/events/operator.ts @@ -1,4 +1,4 @@ -import { DepositStatus, NominatorStatus, OperatorStatus } from "../model"; +import { NominatorStatus, OperatorStatus } from "../model"; import type { CtxBlock, CtxEvent, CtxExtrinsic } from "../processor"; import { createDeposit, @@ -18,36 +18,33 @@ export function processOperatorNominatedEvent( extrinsic: CtxExtrinsic, event: CtxEvent ) { + const { operatorId } = event.args; + const operatorIdNum = Number(operatorId); const address = getCallSigner(extrinsic.call); const blockNumber = getBlockNumber(block); - const operatorId = Number(event.args.operatorId); const storageFeeDepositedEvent = extrinsic.events.find( (e) => e.name === events.domains.storageFeeDeposited.name ); - const amount = extrinsic.call - ? BigInt(extrinsic.call.args.amount) - : BigInt(0); - const storageFeeDeposit = storageFeeDepositedEvent - ? BigInt(storageFeeDepositedEvent.args.amount) - : BigInt(0); + const amount = BigInt(extrinsic.call?.args.amount ?? 0); + const storageFeeDeposit = BigInt(storageFeeDepositedEvent?.args.amount ?? 0); const account = getOrCreateAccount(cache, block, address); - cache.accounts.set(account.id, account); - - const operator = getOrCreateOperator(cache, block, operatorId, {}); - cache.operators.set(operator.id, operator); - + const operator = getOrCreateOperator(cache, block, operatorIdNum, {}); const domain = getOrCreateDomain(cache, block, operator.domainId); - cache.domains.set(domain.id, domain); - const nominator = getOrCreateNominator(cache, block, extrinsic, operatorId, { - domainId: domain.id, - accountId: account.id, - operatorId: operator.id, - }); - cache.nominators.set(nominator.id, nominator); + const nominator = getOrCreateNominator( + cache, + block, + extrinsic, + operatorIdNum, + { + domainId: domain.id, + accountId: account.id, + operatorId: operator.id, + } + ); const deposit = createDeposit(block, extrinsic, { domainId: domain.id, @@ -57,28 +54,25 @@ export function processOperatorNominatedEvent( amount, storageFeeDeposit, }); - cache.deposits.set(deposit.id, deposit); operator.totalDeposits += amount; operator.updatedAt = blockNumber; - cache.operators.set(operator.id, operator); nominator.totalDeposits += amount; nominator.updatedAt = blockNumber; - cache.nominators.set(nominator.id, nominator); domain.totalDeposits += amount; domain.updatedAt = blockNumber; - cache.domains.set(domain.id, domain); account.totalDeposits += amount; account.updatedAt = blockNumber; - cache.accounts.set(account.id, account); + cache.deposits.set(deposit.id, deposit); + return cache; } @@ -88,21 +82,20 @@ export function processOperatorSlashedEvent( extrinsic: CtxExtrinsic, event: CtxEvent ) { - const operatorId = Number(event.args.operatorId); + const { operatorId } = event.args; const operator = getOrCreateOperator(cache, block, operatorId); operator.currentTotalStake = BigInt(0); operator.currentStorageFeeDeposit = BigInt(0); operator.status = OperatorStatus.SLASHED; operator.updatedAt = getBlockNumber(block); - cache.operators.set(operator.id, operator); Array.from(cache.nominators.values()) .filter((n) => n.operatorId === operator.id) - .map((n) => { + .forEach((n) => { n.status = NominatorStatus.SLASHED; - n.updatedAt = getBlockNumber(block); + n.updatedAt = operator.updatedAt; cache.nominators.set(n.id, n); }); @@ -115,33 +108,24 @@ export function processOperatorTaxCollectedEvent( extrinsic: CtxExtrinsic, event: CtxEvent ) { + const { operatorId, tax } = event.args; const blockNumber = getBlockNumber(block); - const operatorId = Number(event.args.operatorId); - const tax = BigInt(event.args.tax); + const taxAmount = BigInt(tax); const operator = getOrCreateOperator(cache, block, operatorId); - cache.operators.set(operator.id, operator); - const account = getOrCreateAccount(cache, block, operator.accountId); - cache.accounts.set(account.id, account); - const domain = getOrCreateDomain(cache, block, operator.domainId); - cache.domains.set(domain.id, domain); - operator.totalTaxCollected += tax; + operator.totalTaxCollected += taxAmount; operator.updatedAt = blockNumber; - cache.operators.set(operator.id, operator); - account.totalTaxCollected += tax; + account.totalTaxCollected += taxAmount; account.updatedAt = blockNumber; - cache.accounts.set(account.id, account); - domain.totalTaxCollected += tax; - + domain.totalTaxCollected += taxAmount; domain.updatedAt = blockNumber; - cache.domains.set(domain.id, domain); return cache; @@ -153,8 +137,8 @@ export function processOperatorRewardedEvent( extrinsic: CtxExtrinsic, event: CtxEvent ) { - const operatorId = Number(event.args.operatorId); - const amount = BigInt(event.args.reward); + const { operatorId, reward } = event.args; + const amount = BigInt(reward); const operator = getOrCreateOperator(cache, block, operatorId); const domain = getOrCreateDomain(cache, block, operator.domainId); diff --git a/indexers/staking-squid/src/events/withdraw.ts b/indexers/staking-squid/src/events/withdraw.ts index 523369708..7e284e433 100644 --- a/indexers/staking-squid/src/events/withdraw.ts +++ b/indexers/staking-squid/src/events/withdraw.ts @@ -15,17 +15,15 @@ export function processWithdrewStakeEvent( extrinsic: CtxExtrinsic, event: CtxEvent ) { + const { operatorId } = event.args; + const { shares = 0 } = extrinsic.call?.args ?? {}; const address = getCallSigner(extrinsic.call); - const operatorId = Number(event.args.operatorId); - - const shares = extrinsic.call - ? BigInt(extrinsic.call.args.shares) - : BigInt(0); + const sharesBigInt = BigInt(shares); const account = getOrCreateAccount(cache, block, address); cache.accounts.set(account.id, account); - const operator = getOrCreateOperator(cache, block, operatorId, {}); + const operator = getOrCreateOperator(cache, block, Number(operatorId), {}); cache.operators.set(operator.id, operator); const domain = getOrCreateDomain(cache, block, operator.domainId); @@ -35,7 +33,7 @@ export function processWithdrewStakeEvent( domainId: domain.id, accountId: account.id, operatorId: operator.id, - shares, + shares: sharesBigInt, }); cache.nominators.set(nominator.id, nominator); @@ -44,7 +42,7 @@ export function processWithdrewStakeEvent( accountId: account.id, operatorId: operator.id, nominatorId: nominator.id, - shares, + shares: sharesBigInt, }); cache.withdrawals.set(withdrawal.id, withdrawal); diff --git a/indexers/staking-squid/src/extrinsics/index.ts b/indexers/staking-squid/src/extrinsics/index.ts index 2d57c92f3..838015f69 100644 --- a/indexers/staking-squid/src/extrinsics/index.ts +++ b/indexers/staking-squid/src/extrinsics/index.ts @@ -23,7 +23,6 @@ export async function processExtrinsic( block: CtxBlock, extrinsic: CtxExtrinsic ) { - console.log("extrinsic", extrinsic.call?.name); switch (extrinsic.call?.name) { case calls.domains.registerOperator.name: return processRegisterOperator(cache, block, extrinsic); diff --git a/indexers/staking-squid/src/extrinsics/operator.ts b/indexers/staking-squid/src/extrinsics/operator.ts index d7585cdb0..8c4e45e37 100644 --- a/indexers/staking-squid/src/extrinsics/operator.ts +++ b/indexers/staking-squid/src/extrinsics/operator.ts @@ -17,72 +17,60 @@ export function processRegisterOperator( block: CtxBlock, extrinsic: CtxExtrinsic ) { - const address = getCallSigner(extrinsic.call); - const domainId = Number(extrinsic.call?.args.domainId); + const { call, events: extrinsicEvents } = extrinsic; + const { domainId, amount, config } = call?.args ?? {}; + const { signingKey, minimumNominatorStake, nominationTax } = config ?? {}; + const address = getCallSigner(call); + const domainIdNum = Number(domainId ?? 0); - const operatorRegisteredEvent = extrinsic.events.find( + const operatorRegisteredEvent = extrinsicEvents.find( (e) => e.name === events.domains.operatorRegistered.name ); if (!operatorRegisteredEvent) return cache; - const storageFeeDepositedEvent = extrinsic.events.find( + const storageFeeDepositedEvent = extrinsicEvents.find( (e) => e.name === events.domains.storageFeeDeposited.name ); - const operatorId = operatorRegisteredEvent - ? Number(operatorRegisteredEvent.args.operatorId) - : 0; - const amount = extrinsic.call - ? BigInt(extrinsic.call.args.amount) - : BigInt(0); - const storageFeeDeposit = storageFeeDepositedEvent - ? BigInt(storageFeeDepositedEvent.args.amount) - : BigInt(0); - - const domain = getOrCreateDomain(cache, block, domainId); - cache.domains.set(domain.id, domain); + const operatorId = Number(operatorRegisteredEvent.args.operatorId ?? 0); + const amountBigInt = BigInt(amount ?? 0); + const storageFeeDeposit = BigInt(storageFeeDepositedEvent?.args.amount ?? 0); + const domain = getOrCreateDomain(cache, block, domainIdNum); const account = getOrCreateAccount(cache, block, address); - cache.accounts.set(account.id, account); - if (operatorRegisteredEvent) { - const operator = createOperator(block, operatorId, { - domainId: domain.id, - accountId: account.id, - signingKey: extrinsic.call?.args.config.signingKey, - minimumNominatorStake: BigInt( - extrinsic.call?.args.config.minimumNominatorStake - ), - nominationTax: extrinsic.call?.args.config.nominationTax, - totalDeposits: amount, - }); - cache.operators.set(operator.id, operator); - - const nominator = createNominator(block, extrinsic, operatorId, { - domainId: domain.id, - accountId: account.id, - operatorId: operator.id, - }); - cache.nominators.set(nominator.id, nominator); - - const deposit = createDeposit(block, extrinsic, { - domainId: domain.id, - accountId: account.id, - operatorId: operator.id, - nominatorId: nominator.id, - amount, - storageFeeDeposit, - }); - cache.deposits.set(deposit.id, deposit); - - domain.totalDeposits += amount; - - cache.domains.set(domain.id, domain); - - account.totalDeposits += amount; - - cache.accounts.set(account.id, account); - } + const operator = createOperator(block, operatorId, { + domainId: domain.id, + accountId: account.id, + signingKey: signingKey ?? "", + minimumNominatorStake: BigInt(minimumNominatorStake ?? 0), + nominationTax: nominationTax ?? 0, + totalDeposits: amountBigInt, + }); + cache.operators.set(operator.id, operator); + + const nominator = createNominator(block, extrinsic, operatorId, { + domainId: domain.id, + accountId: account.id, + operatorId: operator.id, + }); + cache.nominators.set(nominator.id, nominator); + + const deposit = createDeposit(block, extrinsic, { + domainId: domain.id, + accountId: account.id, + operatorId: operator.id, + nominatorId: nominator.id, + amount: amountBigInt, + storageFeeDeposit, + }); + cache.deposits.set(deposit.id, deposit); + + domain.totalDeposits += amountBigInt; + cache.domains.set(domain.id, domain); + + account.totalDeposits += amountBigInt; + cache.accounts.set(account.id, account); return cache; } @@ -92,21 +80,20 @@ export function processDeregisterOperator( block: CtxBlock, extrinsic: CtxExtrinsic ) { - const operatorId = Number(extrinsic.call?.args.operatorId); + const { call, events: extrinsicEvents } = extrinsic; + const operatorId = Number(call?.args.operatorId ?? 0); const operator = getOrCreateOperator(cache, block, operatorId); - const operatorDeregisteredEvent = extrinsic.events.find( + const operatorDeregisteredEvent = extrinsicEvents.find( (e) => e.name === events.domains.operatorDeregistered.name ); if (!operatorDeregisteredEvent) return cache; - if (operatorDeregisteredEvent) { - operator.currentTotalStake = BigInt(0); - operator.currentStorageFeeDeposit = BigInt(0); - operator.status = OperatorStatus.DEREGISTERED; + operator.currentTotalStake = BigInt(0); + operator.currentStorageFeeDeposit = BigInt(0); + operator.status = OperatorStatus.DEREGISTERED; - cache.operators.set(operator.id, operator); - } + cache.operators.set(operator.id, operator); return cache; } diff --git a/indexers/staking-squid/src/model/generated/index.ts b/indexers/staking-squid/src/model/generated/index.ts index aa6a49f6f..420c06e18 100644 --- a/indexers/staking-squid/src/model/generated/index.ts +++ b/indexers/staking-squid/src/model/generated/index.ts @@ -1,7 +1,9 @@ export * from "./domain.model" +export * from "./_domainRuntime" export * from "./account.model" export * from "./operator.model" export * from "./_operatorStatus" +export * from "./bundle.model" export * from "./nominator.model" export * from "./_nominatorStatus" export * from "./deposit.model" diff --git a/indexers/staking-squid/src/storage/account.ts b/indexers/staking-squid/src/storage/account.ts index 4d106b156..eee12349b 100644 --- a/indexers/staking-squid/src/storage/account.ts +++ b/indexers/staking-squid/src/storage/account.ts @@ -6,16 +6,18 @@ import { Cache } from "../utils/cache"; export const createAccount = ( block: CtxBlock, address: string, - props: Partial -): Account => - new Account({ + props: Partial = {} +): Account => { + const blockNumber = getBlockNumber(block); + return new Account({ id: address, - totalDeposits: BigInt(0), - totalTaxCollected: BigInt(0), + totalDeposits: props.totalDeposits ?? BigInt(0), + totalTaxCollected: props.totalTaxCollected ?? BigInt(0), ...props, - createdAt: getBlockNumber(block), - updatedAt: getBlockNumber(block), + createdAt: blockNumber, + updatedAt: blockNumber, }); +}; export const getOrCreateAccount = ( cache: Cache, diff --git a/indexers/staking-squid/src/storage/bundle.ts b/indexers/staking-squid/src/storage/bundle.ts new file mode 100644 index 000000000..0fc8edcca --- /dev/null +++ b/indexers/staking-squid/src/storage/bundle.ts @@ -0,0 +1,31 @@ +import { randomUUID } from "crypto"; +import { Bundle } from "../model"; + +export const createBundle = ( + accountId: string, + domainId: string, + operatorId: string, + props?: Partial +): Bundle => + new Bundle({ + id: randomUUID(), + accountId, + domainId, + operatorId, + domainBlockNumber: 0, + consensusBlockNumber: 0, + consensusBlockHash: "", + totalTransfersIn: BigInt(0), + transfersInCount: 0, + totalTransfersOut: BigInt(0), + transfersOutCount: 0, + totalRejectedTransfersClaimed: BigInt(0), + rejectedTransfersClaimedCount: 0, + totalTransfersRejected: BigInt(0), + transfersRejectedCount: 0, + totalVolume: BigInt(0), + consensusStorageFee: BigInt(0), + domainExecutionFee: BigInt(0), + burnedBalance: BigInt(0), + ...props, + }); diff --git a/indexers/staking-squid/src/storage/domain.ts b/indexers/staking-squid/src/storage/domain.ts index 62a148d3d..1348cfe83 100644 --- a/indexers/staking-squid/src/storage/domain.ts +++ b/indexers/staking-squid/src/storage/domain.ts @@ -1,4 +1,4 @@ -import { Domain } from "../model"; +import { Domain, DomainRuntime } from "../model"; import type { CtxBlock } from "../processor"; import { domainUID, getBlockNumber } from "../utils"; import { Cache } from "../utils/cache"; @@ -11,13 +11,32 @@ export const createDomain = ( new Domain({ id: typeof domainId === "string" ? domainId : domainUID(domainId), sortId: typeof domainId === "string" ? parseInt(domainId) : domainId, + accountId: "0x", + name: "", + runtimeId: 0, + runtime: DomainRuntime.EVM, + runtimeInfo: JSON.stringify({}), completedEpoch: 0, lastDomainBlockNumber: 0, totalDeposits: BigInt(0), totalTaxCollected: BigInt(0), totalRewardsCollected: BigInt(0), + totalTransfersIn: BigInt(0), + transfersInCount: 0, + totalTransfersOut: BigInt(0), + transfersOutCount: 0, + totalRejectedTransfersClaimed: BigInt(0), + rejectedTransfersClaimedCount: 0, + totalTransfersRejected: BigInt(0), + transfersRejectedCount: 0, + totalVolume: BigInt(0), + totalConsensusStorageFee: BigInt(0), + totalDomainExecutionFee: BigInt(0), + totalBurnedBalance: BigInt(0), currentTotalStake: BigInt(0), currentStorageFeeDeposit: BigInt(0), + bundleCount: 0, + lastBundleAt: 0, ...props, createdAt: getBlockNumber(block), updatedAt: getBlockNumber(block), diff --git a/indexers/staking-squid/src/storage/index.ts b/indexers/staking-squid/src/storage/index.ts index a174753a8..03ae334d2 100644 --- a/indexers/staking-squid/src/storage/index.ts +++ b/indexers/staking-squid/src/storage/index.ts @@ -1,4 +1,5 @@ export * from "./account"; +export * from "./bundle"; export * from "./deposit"; export * from "./domain"; export * from "./nominator"; diff --git a/indexers/staking-squid/src/storage/operator.ts b/indexers/staking-squid/src/storage/operator.ts index aaec301e2..ce6ca677b 100644 --- a/indexers/staking-squid/src/storage/operator.ts +++ b/indexers/staking-squid/src/storage/operator.ts @@ -22,8 +22,21 @@ export const createOperator = ( totalDeposits: BigInt(0), totalTaxCollected: BigInt(0), totalRewardsCollected: BigInt(0), + totalTransfersIn: BigInt(0), + transfersInCount: 0, + totalTransfersOut: BigInt(0), + transfersOutCount: 0, + totalRejectedTransfersClaimed: BigInt(0), + rejectedTransfersClaimedCount: 0, + totalTransfersRejected: BigInt(0), + transfersRejectedCount: 0, + totalVolume: BigInt(0), + totalConsensusStorageFee: BigInt(0), + totalDomainExecutionFee: BigInt(0), + totalBurnedBalance: BigInt(0), rawStatus: JSON.stringify({}), status: OperatorStatus.PENDING, + activeEpochCount: 0, bundleCount: 0, lastBundleAt: 0, ...props, diff --git a/indexers/staking-squid/src/utils/cache.ts b/indexers/staking-squid/src/utils/cache.ts index 8dcd7dbd3..7352087a2 100644 --- a/indexers/staking-squid/src/utils/cache.ts +++ b/indexers/staking-squid/src/utils/cache.ts @@ -1,6 +1,8 @@ -import type { Store } from "@subsquid/typeorm-store"; +import { Store } from "@subsquid/typeorm-store"; +import { Entity } from "@subsquid/typeorm-store/src/store"; import { Account, + Bundle, Deposit, Domain, Nominator, @@ -13,21 +15,25 @@ import { } from "../model"; import type { Ctx } from "../processor"; -export type Cache = { +export type PermanentCache = { domains: Map; accounts: Map; operators: Map; nominators: Map; +}; + +export type TemporaryCache = { deposits: Map; withdrawals: Map; - + bundles: Map; operatorRewardedEvents: Map; - stats: Map; statsPerDomain: Map; statsPerOperator: Map; }; +export type Cache = PermanentCache & TemporaryCache; + export const initCache: Cache = { domains: new Map(), accounts: new Map(), @@ -35,21 +41,25 @@ export const initCache: Cache = { nominators: new Map(), deposits: new Map(), withdrawals: new Map(), - + bundles: new Map(), operatorRewardedEvents: new Map(), - stats: new Map(), statsPerDomain: new Map(), statsPerOperator: new Map(), }; export const load = async (ctx: Ctx): Promise => { - const domains = await ctx.store.find(Domain); - const accounts = await ctx.store.find(Account); - const operators = await ctx.store.find(Operator); - const nominators = await ctx.store.find(Nominator); - const deposits = await ctx.store.find(Deposit); - const withdrawals = await ctx.store.find(Withdrawal); + const [domains, accounts, operators, nominators] = await Promise.all([ + ctx.store.find(Domain), + ctx.store.find(Account), + ctx.store.find(Operator), + ctx.store.find(Nominator), + ]); + + console.log("Loaded domains:", domains.length); + console.log("Loaded accounts:", accounts.length); + console.log("Loaded operators:", operators.length); + console.log("Loaded nominators:", nominators.length); return { ...initCache, @@ -57,22 +67,37 @@ export const load = async (ctx: Ctx): Promise => { accounts: new Map(accounts.map((a) => [a.id, a])), operators: new Map(operators.map((o) => [o.id, o])), nominators: new Map(nominators.map((n) => [n.id, n])), - deposits: new Map(deposits.map((d) => [d.id, d])), - withdrawals: new Map(withdrawals.map((w) => [w.id, w])), }; }; -export const save = async (ctx: Ctx, cache: Cache) => { - await ctx.store.save(Array.from(cache.domains.values())); - await ctx.store.save(Array.from(cache.accounts.values())); - await ctx.store.save(Array.from(cache.operators.values())); - await ctx.store.save(Array.from(cache.nominators.values())); - await ctx.store.save(Array.from(cache.deposits.values())); - await ctx.store.save(Array.from(cache.withdrawals.values())); +const saveEntry = async ( + ctx: Ctx, + cache: Cache, + name: keyof Cache +) => { + try { + const entity = cache[name] as unknown as Map; + if (entity.size === 0) return; + + console.log(`Saving ${entity.size} ${name} entries to the database.`); - await ctx.store.save(Array.from(cache.operatorRewardedEvents.values())); + await ctx.store.save(Array.from(entity.values())); + } catch (e) { + console.error(`Failed to save ${name} with error:`, e); + } +}; + +export const save = async (ctx: Ctx, cache: Cache) => { + await Promise.all( + Object.keys(cache).map((k) => saveEntry(ctx, cache, k as keyof Cache)) + ); - await ctx.store.save(Array.from(cache.stats.values())); - await ctx.store.save(Array.from(cache.statsPerDomain.values())); - await ctx.store.save(Array.from(cache.statsPerOperator.values())); + // Clear the cache for entries not needed for reference + cache.deposits.clear(); + cache.withdrawals.clear(); + cache.bundles.clear(); + cache.operatorRewardedEvents.clear(); + cache.stats.clear(); + cache.statsPerDomain.clear(); + cache.statsPerOperator.clear(); }; diff --git a/indexers/staking-squid/src/utils/index.ts b/indexers/staking-squid/src/utils/index.ts index d4babbeca..10cf27a49 100644 --- a/indexers/staking-squid/src/utils/index.ts +++ b/indexers/staking-squid/src/utils/index.ts @@ -3,20 +3,35 @@ import type { Store } from "@subsquid/typeorm-store"; import { decodeHex } from "@subsquid/util-internal-hex"; import type { CtxBlock, ProcessorContext } from "../processor"; -export const hexToAccount = (hex: string): string => - codec(2254).encode(decodeHex(hex)); +const CODEC = 2254; + +export const hexToAccount = (hex: string): string => { + try { + return codec(CODEC).encode(decodeHex(hex)); + } catch (error) { + console.error("Failed to convert hex to account:", error); + return ""; + } +}; export const getCallSigner = ( call: ProcessorContext["blocks"][0]["extrinsics"][0]["call"] -): string => hexToAccount((call as any).origin.value.value); +): string => { + try { + return hexToAccount((call as any).origin.value.value); + } catch (error) { + console.error("Failed to get call signer:", error); + return ""; + } +}; export const appendOrArray = (arr: T[] | undefined, item: T): T[] => - !arr ? [item] : [...arr, item]; + arr ? [...arr, item] : [item]; export const getBlockNumber = (block: CtxBlock): number => block.header.height; export const getTimestamp = (block: CtxBlock): Date => - new Date(block.header.timestamp || 0); + new Date(block.header.timestamp ?? 0); export const domainUID = (domainId: number): string => `${domainId}`;