From 645f766397db5f7b7d820f9459baf939ae4e2f52 Mon Sep 17 00:00:00 2001 From: rrozek Date: Mon, 12 Aug 2024 16:16:30 +0200 Subject: [PATCH 1/2] fix dependencies --- .github/workflows/build-and-push-ecr-dev.yml | 2 +- .github/workflows/build-and-push-ecr-prod.yml | 2 +- Dev.Dockerfile | 2 +- Dockerfile | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-and-push-ecr-dev.yml b/.github/workflows/build-and-push-ecr-dev.yml index 7eb4f05..eef0cbf 100644 --- a/.github/workflows/build-and-push-ecr-dev.yml +++ b/.github/workflows/build-and-push-ecr-dev.yml @@ -19,7 +19,7 @@ jobs: # - name: Check out code # uses: actions/checkout@v3 # - name: Run tests - # run: docker-compose -f docker-compose.test.yml run server npm test + # run: docker compose -f docker-compose.test.yml run server npm test build-push: name: Build image and push to ECR(tokenguard-dev) diff --git a/.github/workflows/build-and-push-ecr-prod.yml b/.github/workflows/build-and-push-ecr-prod.yml index 3773a67..b7fbf63 100644 --- a/.github/workflows/build-and-push-ecr-prod.yml +++ b/.github/workflows/build-and-push-ecr-prod.yml @@ -19,7 +19,7 @@ jobs: # - name: Check out code # uses: actions/checkout@v3 # - name: Run tests - # run: docker-compose -f docker-compose.test.yml run server npm test + # run: docker compose -f docker-compose.test.yml run server npm test build-push: name: Build and push to ECR(tokenguard-prod) diff --git a/Dev.Dockerfile b/Dev.Dockerfile index a37b005..3913ba5 100644 --- a/Dev.Dockerfile +++ b/Dev.Dockerfile @@ -3,8 +3,8 @@ FROM node:18-alpine WORKDIR /app COPY package*.json ./ -RUN npm install RUN npm install -g ts-node-dev +RUN npm install --prefer-dedupe COPY ./ ./ RUN npm run build diff --git a/Dockerfile b/Dockerfile index 8b9d650..a1890a8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ USER node COPY --chown=node:node package.json ./ -RUN npm install +RUN npm install --prefer-dedupe COPY --chown=node:node . . RUN npm run build EXPOSE 8080 From 7f8f098654666213401fe2b7000316a97a456ae2 Mon Sep 17 00:00:00 2001 From: rrozek Date: Wed, 14 Aug 2024 04:18:42 +0200 Subject: [PATCH 2/2] add support for moonbeam evm in dapp analytics --- package-lock.json | 489 +++++++++++++++++- package.json | 1 + .../dapp-analytics/abi.interface.ts | 99 +++- .../dapp-analytics.controller.ts | 212 +++++++- .../dapp-analytics.validation.ts | 74 ++- .../dapp-analytics/helpers/utils.ts | 30 ++ src/components/node-api/chainstate.ts | 1 + src/config/dapp-analytics-networks.ts | 35 ++ 8 files changed, 878 insertions(+), 63 deletions(-) create mode 100644 src/components/dapp-analytics/helpers/utils.ts diff --git a/package-lock.json b/package-lock.json index 79630dd..e84516f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,16 @@ { "name": "dashboard-creator-server", - "version": "3.0.1", + "version": "3.1.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "dashboard-creator-server", - "version": "3.0.1", + "version": "3.1.2", "license": "MIT", "dependencies": { "@kubernetes/client-node": "^0.21.0", + "@polkadot/api": "^10.11.2", "@polkadot/keyring": "^12.6.2", "@polkadot/util": "^12.6.2", "@subsquid/ink-abi": "^3.0.3", @@ -24,6 +25,7 @@ "deepmerge": "^4.3.1", "dockerode": "^4.0.2", "dotenv": "^16.4.5", + "ethers": "^6.13.1", "express": "^4.17.1", "express-http-context": "^1.2.4", "express-mongo-sanitize": "^2.2.0", @@ -31,6 +33,7 @@ "http-status": "^1.5.0", "jest-mock-axios": "^4.7.3", "joi": "^17.5.0", + "lodash": "^4.17.21", "lusca": "^1.7.0", "md5": "^2.3.0", "mongodb-memory-server": "^9.1.6", @@ -82,6 +85,11 @@ "node": ">=0.10.0" } }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz", + "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==" + }, "node_modules/@ampproject/remapping": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", @@ -1494,6 +1502,146 @@ "node": ">=14" } }, + "node_modules/@polkadot-api/client": { + "version": "0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/client/-/client-0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0.tgz", + "integrity": "sha512-0fqK6pUKcGHSG2pBvY+gfSS+1mMdjd/qRygAcKI5d05tKsnZLRnmhb9laDguKmGEIB0Bz9vQqNK3gIN/cfvVwg==", + "optional": true, + "dependencies": { + "@polkadot-api/metadata-builders": "0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0", + "@polkadot-api/substrate-bindings": "0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0", + "@polkadot-api/substrate-client": "0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0", + "@polkadot-api/utils": "0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" + }, + "peerDependencies": { + "rxjs": ">=7.8.0" + } + }, + "node_modules/@polkadot-api/json-rpc-provider": { + "version": "0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider/-/json-rpc-provider-0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0.tgz", + "integrity": "sha512-EaUS9Fc3wsiUr6ZS43PQqaRScW7kM6DYbuM/ou0aYjm8N9MBqgDbGm2oL6RE1vAVmOfEuHcXZuZkhzWtyvQUtA==", + "optional": true + }, + "node_modules/@polkadot-api/json-rpc-provider-proxy": { + "version": "0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider-proxy/-/json-rpc-provider-proxy-0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0.tgz", + "integrity": "sha512-0hZ8vtjcsyCX8AyqP2sqUHa1TFFfxGWmlXJkit0Nqp9b32MwZqn5eaUAiV2rNuEpoglKOdKnkGtUF8t5MoodKw==", + "optional": true + }, + "node_modules/@polkadot-api/metadata-builders": { + "version": "0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/metadata-builders/-/metadata-builders-0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0.tgz", + "integrity": "sha512-BD7rruxChL1VXt0icC2gD45OtT9ofJlql0qIllHSRYgama1CR2Owt+ApInQxB+lWqM+xNOznZRpj8CXNDvKIMg==", + "optional": true, + "dependencies": { + "@polkadot-api/substrate-bindings": "0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0", + "@polkadot-api/utils": "0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" + } + }, + "node_modules/@polkadot-api/substrate-bindings": { + "version": "0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-bindings/-/substrate-bindings-0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0.tgz", + "integrity": "sha512-N4vdrZopbsw8k57uG58ofO7nLXM4Ai7835XqakN27MkjXMp5H830A1KJE0L9sGQR7ukOCDEIHHcwXVrzmJ/PBg==", + "optional": true, + "dependencies": { + "@noble/hashes": "^1.3.1", + "@polkadot-api/utils": "0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0", + "@scure/base": "^1.1.1", + "scale-ts": "^1.6.0" + } + }, + "node_modules/@polkadot-api/substrate-client": { + "version": "0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-client/-/substrate-client-0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0.tgz", + "integrity": "sha512-lcdvd2ssUmB1CPzF8s2dnNOqbrDa+nxaaGbuts+Vo8yjgSKwds2Lo7Oq+imZN4VKW7t9+uaVcKFLMF7PdH0RWw==", + "optional": true + }, + "node_modules/@polkadot-api/utils": { + "version": "0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/utils/-/utils-0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0.tgz", + "integrity": "sha512-0CYaCjfLQJTCRCiYvZ81OncHXEKPzAexCMoVloR+v2nl/O2JRya/361MtPkeNLC6XBoaEgLAG9pWQpH3WePzsw==", + "optional": true + }, + "node_modules/@polkadot/api": { + "version": "10.13.1", + "resolved": "https://registry.npmjs.org/@polkadot/api/-/api-10.13.1.tgz", + "integrity": "sha512-YrKWR4TQR5CDyGkF0mloEUo7OsUA+bdtENpJGOtNavzOQUDEbxFE0PVzokzZfVfHhHX2CojPVmtzmmLxztyJkg==", + "dependencies": { + "@polkadot/api-augment": "10.13.1", + "@polkadot/api-base": "10.13.1", + "@polkadot/api-derive": "10.13.1", + "@polkadot/keyring": "^12.6.2", + "@polkadot/rpc-augment": "10.13.1", + "@polkadot/rpc-core": "10.13.1", + "@polkadot/rpc-provider": "10.13.1", + "@polkadot/types": "10.13.1", + "@polkadot/types-augment": "10.13.1", + "@polkadot/types-codec": "10.13.1", + "@polkadot/types-create": "10.13.1", + "@polkadot/types-known": "10.13.1", + "@polkadot/util": "^12.6.2", + "@polkadot/util-crypto": "^12.6.2", + "eventemitter3": "^5.0.1", + "rxjs": "^7.8.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/api-augment": { + "version": "10.13.1", + "resolved": "https://registry.npmjs.org/@polkadot/api-augment/-/api-augment-10.13.1.tgz", + "integrity": "sha512-IAKaCp19QxgOG4HKk9RAgUgC/VNVqymZ2GXfMNOZWImZhxRIbrK+raH5vN2MbWwtVHpjxyXvGsd1RRhnohI33A==", + "dependencies": { + "@polkadot/api-base": "10.13.1", + "@polkadot/rpc-augment": "10.13.1", + "@polkadot/types": "10.13.1", + "@polkadot/types-augment": "10.13.1", + "@polkadot/types-codec": "10.13.1", + "@polkadot/util": "^12.6.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/api-base": { + "version": "10.13.1", + "resolved": "https://registry.npmjs.org/@polkadot/api-base/-/api-base-10.13.1.tgz", + "integrity": "sha512-Okrw5hjtEjqSMOG08J6qqEwlUQujTVClvY1/eZkzKwNzPelWrtV6vqfyJklB7zVhenlxfxqhZKKcY7zWSW/q5Q==", + "dependencies": { + "@polkadot/rpc-core": "10.13.1", + "@polkadot/types": "10.13.1", + "@polkadot/util": "^12.6.2", + "rxjs": "^7.8.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/api-derive": { + "version": "10.13.1", + "resolved": "https://registry.npmjs.org/@polkadot/api-derive/-/api-derive-10.13.1.tgz", + "integrity": "sha512-ef0H0GeCZ4q5Om+c61eLLLL29UxFC2/u/k8V1K2JOIU+2wD5LF7sjAoV09CBMKKHfkLenRckVk2ukm4rBqFRpg==", + "dependencies": { + "@polkadot/api": "10.13.1", + "@polkadot/api-augment": "10.13.1", + "@polkadot/api-base": "10.13.1", + "@polkadot/rpc-core": "10.13.1", + "@polkadot/types": "10.13.1", + "@polkadot/types-codec": "10.13.1", + "@polkadot/util": "^12.6.2", + "@polkadot/util-crypto": "^12.6.2", + "rxjs": "^7.8.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@polkadot/keyring": { "version": "12.6.2", "resolved": "https://registry.npmjs.org/@polkadot/keyring/-/keyring-12.6.2.tgz", @@ -1524,6 +1672,148 @@ "node": ">=18" } }, + "node_modules/@polkadot/rpc-augment": { + "version": "10.13.1", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-augment/-/rpc-augment-10.13.1.tgz", + "integrity": "sha512-iLsWUW4Jcx3DOdVrSHtN0biwxlHuTs4QN2hjJV0gd0jo7W08SXhWabZIf9mDmvUJIbR7Vk+9amzvegjRyIf5+A==", + "dependencies": { + "@polkadot/rpc-core": "10.13.1", + "@polkadot/types": "10.13.1", + "@polkadot/types-codec": "10.13.1", + "@polkadot/util": "^12.6.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/rpc-core": { + "version": "10.13.1", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-core/-/rpc-core-10.13.1.tgz", + "integrity": "sha512-eoejSHa+/tzHm0vwic62/aptTGbph8vaBpbvLIK7gd00+rT813ROz5ckB1CqQBFB23nHRLuzzX/toY8ID3xrKw==", + "dependencies": { + "@polkadot/rpc-augment": "10.13.1", + "@polkadot/rpc-provider": "10.13.1", + "@polkadot/types": "10.13.1", + "@polkadot/util": "^12.6.2", + "rxjs": "^7.8.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/rpc-provider": { + "version": "10.13.1", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-provider/-/rpc-provider-10.13.1.tgz", + "integrity": "sha512-oJ7tatVXYJ0L7NpNiGd69D558HG5y5ZDmH2Bp9Dd4kFTQIiV8A39SlWwWUPCjSsen9lqSvvprNLnG/VHTpenbw==", + "dependencies": { + "@polkadot/keyring": "^12.6.2", + "@polkadot/types": "10.13.1", + "@polkadot/types-support": "10.13.1", + "@polkadot/util": "^12.6.2", + "@polkadot/util-crypto": "^12.6.2", + "@polkadot/x-fetch": "^12.6.2", + "@polkadot/x-global": "^12.6.2", + "@polkadot/x-ws": "^12.6.2", + "eventemitter3": "^5.0.1", + "mock-socket": "^9.3.1", + "nock": "^13.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@substrate/connect": "0.8.8" + } + }, + "node_modules/@polkadot/types": { + "version": "10.13.1", + "resolved": "https://registry.npmjs.org/@polkadot/types/-/types-10.13.1.tgz", + "integrity": "sha512-Hfvg1ZgJlYyzGSAVrDIpp3vullgxrjOlh/CSThd/PI4TTN1qHoPSFm2hs77k3mKkOzg+LrWsLE0P/LP2XddYcw==", + "dependencies": { + "@polkadot/keyring": "^12.6.2", + "@polkadot/types-augment": "10.13.1", + "@polkadot/types-codec": "10.13.1", + "@polkadot/types-create": "10.13.1", + "@polkadot/util": "^12.6.2", + "@polkadot/util-crypto": "^12.6.2", + "rxjs": "^7.8.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-augment": { + "version": "10.13.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-augment/-/types-augment-10.13.1.tgz", + "integrity": "sha512-TcrLhf95FNFin61qmVgOgayzQB/RqVsSg9thAso1Fh6pX4HSbvI35aGPBAn3SkA6R+9/TmtECirpSNLtIGFn0g==", + "dependencies": { + "@polkadot/types": "10.13.1", + "@polkadot/types-codec": "10.13.1", + "@polkadot/util": "^12.6.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-codec": { + "version": "10.13.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-codec/-/types-codec-10.13.1.tgz", + "integrity": "sha512-AiQ2Vv2lbZVxEdRCN8XSERiWlOWa2cTDLnpAId78EnCtx4HLKYQSd+Jk9Y4BgO35R79mchK4iG+w6gZ+ukG2bg==", + "dependencies": { + "@polkadot/util": "^12.6.2", + "@polkadot/x-bigint": "^12.6.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-create": { + "version": "10.13.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-create/-/types-create-10.13.1.tgz", + "integrity": "sha512-Usn1jqrz35SXgCDAqSXy7mnD6j4RvB4wyzTAZipFA6DGmhwyxxIgOzlWQWDb+1PtPKo9vtMzen5IJ+7w5chIeA==", + "dependencies": { + "@polkadot/types-codec": "10.13.1", + "@polkadot/util": "^12.6.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-known": { + "version": "10.13.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-known/-/types-known-10.13.1.tgz", + "integrity": "sha512-uHjDW05EavOT5JeU8RbiFWTgPilZ+odsCcuEYIJGmK+es3lk/Qsdns9Zb7U7NJl7eJ6OWmRtyrWsLs+bU+jjIQ==", + "dependencies": { + "@polkadot/networks": "^12.6.2", + "@polkadot/types": "10.13.1", + "@polkadot/types-codec": "10.13.1", + "@polkadot/types-create": "10.13.1", + "@polkadot/util": "^12.6.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-support": { + "version": "10.13.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-support/-/types-support-10.13.1.tgz", + "integrity": "sha512-4gEPfz36XRQIY7inKq0HXNVVhR6HvXtm7yrEmuBuhM86LE0lQQBkISUSgR358bdn2OFSLMxMoRNoh3kcDvdGDQ==", + "dependencies": { + "@polkadot/util": "^12.6.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@polkadot/util": { "version": "12.6.2", "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-12.6.2.tgz", @@ -1674,6 +1964,19 @@ "node": ">=18" } }, + "node_modules/@polkadot/x-fetch": { + "version": "12.6.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-fetch/-/x-fetch-12.6.2.tgz", + "integrity": "sha512-8wM/Z9JJPWN1pzSpU7XxTI1ldj/AfC8hKioBlUahZ8gUiJaOF7K9XEFCrCDLis/A1BoOu7Ne6WMx/vsJJIbDWw==", + "dependencies": { + "@polkadot/x-global": "12.6.2", + "node-fetch": "^3.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@polkadot/x-global": { "version": "12.6.2", "resolved": "https://registry.npmjs.org/@polkadot/x-global/-/x-global-12.6.2.tgz", @@ -1725,6 +2028,19 @@ "node": ">=18" } }, + "node_modules/@polkadot/x-ws": { + "version": "12.6.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-ws/-/x-ws-12.6.2.tgz", + "integrity": "sha512-cGZWo7K5eRRQCRl2LrcyCYsrc3lRbTlixZh3AzgU8uX4wASVGRlNWi/Hf4TtHNe1ExCDmxabJzdIsABIfrr7xw==", + "dependencies": { + "@polkadot/x-global": "12.6.2", + "tslib": "^2.6.2", + "ws": "^8.15.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@scure/base": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.6.tgz", @@ -1892,6 +2208,49 @@ "xxhashjs": "^0.2.2" } }, + "node_modules/@substrate/connect": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/@substrate/connect/-/connect-0.8.8.tgz", + "integrity": "sha512-zwaxuNEVI9bGt0rT8PEJiXOyebLIo6QN1SyiAHRPBOl6g3Sy0KKdSN8Jmyn++oXhVRD8aIe75/V8ZkS81T+BPQ==", + "deprecated": "versions below 1.x are no longer maintained", + "optional": true, + "dependencies": { + "@substrate/connect-extension-protocol": "^2.0.0", + "@substrate/connect-known-chains": "^1.1.1", + "@substrate/light-client-extension-helpers": "^0.0.4", + "smoldot": "2.0.22" + } + }, + "node_modules/@substrate/connect-extension-protocol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@substrate/connect-extension-protocol/-/connect-extension-protocol-2.0.0.tgz", + "integrity": "sha512-nKu8pDrE3LNCEgJjZe1iGXzaD6OSIDD4Xzz/yo4KO9mQ6LBvf49BVrt4qxBFGL6++NneLiWUZGoh+VSd4PyVIg==", + "optional": true + }, + "node_modules/@substrate/connect-known-chains": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@substrate/connect-known-chains/-/connect-known-chains-1.2.2.tgz", + "integrity": "sha512-gOGrXSWA2d/3kf8Yco00VlHZl48smzAGW5Z9MDxMht98hRpT2yEEN4N5QdoEKMI4ihDW8goXGzmp79D0hFPpuA==", + "optional": true + }, + "node_modules/@substrate/light-client-extension-helpers": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@substrate/light-client-extension-helpers/-/light-client-extension-helpers-0.0.4.tgz", + "integrity": "sha512-vfKcigzL0SpiK+u9sX6dq2lQSDtuFLOxIJx2CKPouPEHIs8C+fpsufn52r19GQn+qDhU8POMPHOVoqLktj8UEA==", + "optional": true, + "dependencies": { + "@polkadot-api/client": "0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0", + "@polkadot-api/json-rpc-provider": "0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0", + "@polkadot-api/json-rpc-provider-proxy": "0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0", + "@polkadot-api/substrate-client": "0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0", + "@substrate/connect-extension-protocol": "^2.0.0", + "@substrate/connect-known-chains": "^1.1.1", + "rxjs": "^7.8.1" + }, + "peerDependencies": { + "smoldot": "2.x" + } + }, "node_modules/@substrate/ss58-registry": { "version": "1.48.0", "resolved": "https://registry.npmjs.org/@substrate/ss58-registry/-/ss58-registry-1.48.0.tgz", @@ -2513,6 +2872,11 @@ "node": ">=0.4.0" } }, + "node_modules/aes-js": { + "version": "4.0.0-beta.5", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", + "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==" + }, "node_modules/agent-base": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", @@ -4535,6 +4899,70 @@ "node": ">= 0.6" } }, + "node_modules/ethers": { + "version": "6.13.2", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.13.2.tgz", + "integrity": "sha512-9VkriTTed+/27BGuY1s0hf441kqwHJ1wtN2edksEtiRvXx+soxRX3iSXTfFqq2+YwrOqbDoTHjIhQnjJRlzKmg==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/ethers-io/" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@adraffy/ens-normalize": "1.10.1", + "@noble/curves": "1.2.0", + "@noble/hashes": "1.3.2", + "@types/node": "18.15.13", + "aes-js": "4.0.0-beta.5", + "tslib": "2.4.0", + "ws": "8.17.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/ethers/node_modules/@noble/curves": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "dependencies": { + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ethers/node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ethers/node_modules/@types/node": { + "version": "18.15.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.13.tgz", + "integrity": "sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==" + }, + "node_modules/ethers/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -6678,6 +7106,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, "node_modules/lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", @@ -6985,6 +7418,14 @@ "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" }, + "node_modules/mock-socket": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/mock-socket/-/mock-socket-9.3.1.tgz", + "integrity": "sha512-qxBgB7Qa2sEQgHFjj0dSigq7fX4k6Saisd5Nelwp2q8mlbAFh5dHV9JTTlF8viYJLSSWgMCZFUom8PJcMNBoJw==", + "engines": { + "node": ">= 8" + } + }, "node_modules/mongodb": { "version": "5.9.2", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.9.2.tgz", @@ -7264,6 +7705,19 @@ "node": ">=12.22.0" } }, + "node_modules/nock": { + "version": "13.5.4", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.4.tgz", + "integrity": "sha512-yAyTfdeNJGGBFxWdzSKCBYxs5FxLbCg5X5Q4ets974hcQzG1+qCxvIyOo4j2Ry6MUlhWVMX4OoYDefAIIwupjw==", + "dependencies": { + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "propagate": "^2.0.0" + }, + "engines": { + "node": ">= 10.13" + } + }, "node_modules/node-domexception": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", @@ -7939,6 +8393,14 @@ "node": ">= 6" } }, + "node_modules/propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "engines": { + "node": ">= 8" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -8321,6 +8783,14 @@ "runtime-node-refresh": "bin/rnr.js" } }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/safe-array-concat": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.0.tgz", @@ -8397,6 +8867,12 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "node_modules/scale-ts": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/scale-ts/-/scale-ts-1.6.0.tgz", + "integrity": "sha512-Ja5VCjNZR8TGKhUumy9clVVxcDpM+YFjAnkMuwQy68Hixio3VRRvWdE3g8T/yC+HXA0ZDQl2TGyUmtmbcVl40Q==", + "optional": true + }, "node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -8598,6 +9074,15 @@ "npm": ">= 3.0.0" } }, + "node_modules/smoldot": { + "version": "2.0.22", + "resolved": "https://registry.npmjs.org/smoldot/-/smoldot-2.0.22.tgz", + "integrity": "sha512-B50vRgTY6v3baYH6uCgL15tfaag5tcS2o/P5q1OiXcKGv1axZDfz2dzzMuIkVpyMR2ug11F6EAtQlmYBQd292g==", + "optional": true, + "dependencies": { + "ws": "^8.8.1" + } + }, "node_modules/socks": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", diff --git a/package.json b/package.json index 73c1709..0a18139 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "deepmerge": "^4.3.1", "dockerode": "^4.0.2", "dotenv": "^16.4.5", + "ethers": "^6.13.1", "express": "^4.17.1", "express-http-context": "^1.2.4", "express-mongo-sanitize": "^2.2.0", diff --git a/src/components/dapp-analytics/abi.interface.ts b/src/components/dapp-analytics/abi.interface.ts index 321e5d4..6c13e41 100644 --- a/src/components/dapp-analytics/abi.interface.ts +++ b/src/components/dapp-analytics/abi.interface.ts @@ -1,4 +1,4 @@ -interface IAbiTypeDef { +interface IInkAbiTypeDef { id: number; type: { def: { @@ -37,45 +37,45 @@ interface IAbiTypeDef { }; } -interface IAbiType { +interface IInkAbiType { type: number; displayName: string[]; } -interface IAbiArg { +interface IInkAbiArg { docs: string[]; - type: IAbiType; + type: IInkAbiType; label: string; indexed?: boolean; } -interface IAbiEvent { - args: IAbiArg[]; +interface IInkAbiEvent { + args: IInkAbiArg[]; docs: string[]; label: string; } -interface IAbiMessage { - args: IAbiArg[]; +interface IInkAbiMessage { + args: IInkAbiArg[]; docs: string[]; label: string; default: boolean; mutates: boolean; payable: boolean; selector: string; - returnType: IAbiType; + returnType: IInkAbiType; } -interface IAbiSpec { +interface IInkAbiSpec { docs: string[]; - events: IAbiEvent[]; - messages: IAbiMessage[]; - types: IAbiTypeDef[]; + events: IInkAbiEvent[]; + messages: IInkAbiMessage[]; + types: IInkAbiTypeDef[]; } -interface IAbi { - spec: IAbiSpec; - types: IAbiTypeDef[]; +interface IInkAbi { + spec: IInkAbiSpec; + types: IInkAbiTypeDef[]; } interface IAbiEventsOutput { @@ -119,14 +119,60 @@ interface IAbiCallsOutputContractCallArg { type: string; } +interface IEvmAbi extends Array {} + +interface IEvmAbiItem { + type: 'function' | 'constructor' | 'event' | 'fallback' | 'receive'; + name?: string; // Only functions and events have names + inputs?: IEvmAbiInputOutput[]; + outputs?: IEvmAbiInputOutput[]; + stateMutability?: 'pure' | 'view' | 'nonpayable' | 'payable'; + anonymous?: boolean; // Only events can be anonymous + payable?: boolean; // Deprecated in favor of stateMutability + constant?: boolean; // Deprecated in favor of stateMutability +} + +interface IEvmAbiInputOutput { + name: string; + type: string; // Solidity types like 'uint256', 'address', 'bytes32', etc. + internalType?: string; // Optionally provides the internal Solidity type + indexed?: boolean; // Only events' inputs can be indexed +} + +interface IEvmEventAbiItem extends IEvmAbiItem { + type: 'event'; + anonymous?: boolean; + inputs: IEvmAbiInputOutput[]; +} + +interface IEvmFunctionAbiItem extends IEvmAbiItem { + type: 'function'; + constant?: boolean; + payable?: boolean; + stateMutability: 'pure' | 'view' | 'nonpayable' | 'payable'; + inputs: IEvmAbiInputOutput[]; + outputs: IEvmAbiInputOutput[]; +} + +interface IEvmConstructorAbiItem extends IEvmAbiItem { + type: 'constructor'; + stateMutability: 'nonpayable' | 'payable'; + inputs: IEvmAbiInputOutput[]; +} + +interface IEvmFallbackAbiItem extends IEvmAbiItem { + type: 'fallback' | 'receive'; + stateMutability: 'nonpayable' | 'payable'; +} + export { - IAbi, - IAbiArg, - IAbiEvent, - IAbiMessage, - IAbiSpec, - IAbiType, - IAbiTypeDef, + IInkAbi, + IInkAbiArg, + IInkAbiEvent, + IInkAbiMessage, + IInkAbiSpec, + IInkAbiType, + IInkAbiTypeDef, IAbiEventsOutput, IAbiEventsOutputContract, IAbiEventsOutputContractEvent, @@ -135,4 +181,11 @@ export { IAbiCallsOutputContract, IAbiCallsOutputContractCall, IAbiCallsOutputContractCallArg, + IEvmAbi, + IEvmAbiItem, + IEvmAbiInputOutput, + IEvmConstructorAbiItem, + IEvmEventAbiItem, + IEvmFallbackAbiItem, + IEvmFunctionAbiItem, }; diff --git a/src/components/dapp-analytics/dapp-analytics.controller.ts b/src/components/dapp-analytics/dapp-analytics.controller.ts index 12a1689..95f00c6 100644 --- a/src/components/dapp-analytics/dapp-analytics.controller.ts +++ b/src/components/dapp-analytics/dapp-analytics.controller.ts @@ -4,21 +4,25 @@ import axios from 'axios'; import logger from '@core/utils/logger'; import { IDAppData } from './dapp-analytics.interface'; import { - IAbi, - IAbiEvent, - IAbiMessage, - IAbiArg, + IInkAbi, + IInkAbiEvent, + IInkAbiMessage, + IInkAbiArg, IAbiEventsOutput, IAbiEventsOutputContract, IAbiEventsOutputContractEvent, IAbiCallsOutput, IAbiCallsOutputContract, IAbiCallsOutputContractCall, + IEvmAbi, + IEvmEventAbiItem, + IEvmFunctionAbiItem, } from './abi.interface'; import { resolveType } from './substrate-types.mapping'; import { docker, k8sApi } from 'server'; import { getCurrentBlock } from '@components/node-api/chainstate'; import { convertAndFormatNumbers } from './helpers/formatting'; +import { isSubstrateAbi, isEvmAbi } from './helpers/utils'; import DAPP_ANALYTICS_NETWORKS from './../../config/dapp-analytics-networks'; import config from '@config/config'; @@ -125,6 +129,9 @@ export const startDappIndexerDocker = async ( `GATEWAY_URL=${networkConfig.gatewayUrl}`, `RPC_INGESTION_DISABLED=${networkConfig.rpcIngestionDisabled}`, `CREATED_TIMESTAMP=${timestamp}`, + `DAPP_ADDRESSES=${dappData.abis + .map((abi) => abi.address) + .join(',')}`, ], }; container = await docker.createContainer(containerOptions); @@ -247,6 +254,10 @@ export const startDappIndexerPod = async ( value: networkConfig.rpcIngestionDisabled.toString(), }, { name: 'CREATED_TIMESTAMP', value: timestamp.toString() }, + { + name: 'DAPP_ADDRESSES', + value: dappData.abis.map((abi) => abi.address).join(','), + }, ], }, ], @@ -640,14 +651,14 @@ export const getDappUnits = async ( }); }; -const extractAbiEvents = function ( - contractAbi: IAbi, +const extractInkAbiEvents = function ( + contractAbi: IInkAbi, ): IAbiEventsOutputContractEvent[] { // Extract events const events: IAbiEventsOutputContractEvent[] = contractAbi.spec.events.map( - (event: IAbiEvent) => ({ + (event: IInkAbiEvent) => ({ name: event.label, - args: event.args.map((arg: IAbiArg) => ({ + args: event.args.map((arg: IInkAbiArg) => ({ name: arg.label, type: resolveType(arg.type.type, contractAbi.types), })), @@ -656,16 +667,32 @@ const extractAbiEvents = function ( return events; }; -const extractAbiFunctions = function ( - contractAbi: IAbi, +const extractEvmAbiEvents = function ( + abi: IEvmAbi, +): IAbiEventsOutputContractEvent[] { + const events: IAbiEventsOutputContractEvent[] = abi + .filter((item) => item.type === 'event') + .map((event: IEvmEventAbiItem) => ({ + name: event.name!, + args: event.inputs!.map((input) => ({ + name: input.name, + type: input.type, + })), + })); + + return events; +}; + +const extractInkAbiFunctions = function ( + contractAbi: IInkAbi, ): IAbiCallsOutputContractCall[] { // Extract messages (calls) that mutate the blockchain state const calls: IAbiCallsOutputContractCall[] = contractAbi.spec.messages - .filter((message: IAbiMessage) => message.mutates) - .map((message: IAbiMessage) => ({ + .filter((message: IInkAbiMessage) => message.mutates) + .map((message: IInkAbiMessage) => ({ name: message.label, selector: message.selector, - args: message.args.map((arg: IAbiArg) => ({ + args: message.args.map((arg: IInkAbiArg) => ({ name: arg.label, type: resolveType(arg.type.type, contractAbi.types), })), @@ -673,6 +700,28 @@ const extractAbiFunctions = function ( return calls; }; +const extractEvmAbiFunctions = function ( + abi: IEvmAbi, +): IAbiCallsOutputContractCall[] { + const calls: IAbiCallsOutputContractCall[] = abi + .filter( + (item) => + item.type === 'function' && + item.stateMutability !== 'view' && + item.stateMutability !== 'pure', + ) + .map((func: IEvmFunctionAbiItem) => ({ + name: func.name!, + selector: '0x00000000', // jrojek TODO: calculate selector + args: func.inputs!.map((input) => ({ + name: input.name, + type: input.type, + })), + })); + + return calls; +}; + export const getDappAbiEvents = async ( req: Request, res: Response, @@ -683,18 +732,35 @@ export const getDappAbiEvents = async ( ); if (response.status === 200) { - let dappEventsOutput: IAbiEventsOutput = { contracts: [] }; const dapp = response.data; + let dappEventsOutput: IAbiEventsOutput = { contracts: [] }; + for (const contract of dapp.abis) { - const dAppContract: IAbiEventsOutputContract = { - name: contract.name, - address: contract.address, - events: extractAbiEvents(contract.abi), - }; - dappEventsOutput.contracts.push(dAppContract); + logger.debug(`Contract ABI: ${JSON.stringify(contract.abi)}`); + if (isSubstrateAbi(contract.abi)) { + const dAppContract: IAbiEventsOutputContract = { + name: contract.name, + address: contract.address, + events: extractInkAbiEvents(contract.abi), + }; + dappEventsOutput.contracts.push(dAppContract); + } else if (isEvmAbi(contract.abi)) { + const dAppContract: IAbiEventsOutputContract = { + name: contract.name, + address: contract.address, + events: extractEvmAbiEvents(contract.abi), + }; + dappEventsOutput.contracts.push(dAppContract); + } else { + return res + .status(httpStatus.BAD_REQUEST) + .json({ message: 'Unsupported ABI format' }); + } } + return res.status(httpStatus.OK).json(dappEventsOutput); } + return res.status(response.status).json({ message: response.data.message || 'No dApps found', }); @@ -711,6 +777,44 @@ export const getDappAbiEvents = async ( } }; +// export const getDappAbiEvents = async ( +// req: Request, +// res: Response, +// ): Promise => { +// try { +// const response = await axios.get( +// `${API_BASE_URL}/dapp-analytics/dapp/${req.params.id}`, +// ); + +// if (response.status === 200) { +// let dappEventsOutput: IAbiEventsOutput = { contracts: [] }; +// const dapp = response.data; +// for (const contract of dapp.abis) { +// const dAppContract: IAbiEventsOutputContract = { +// name: contract.name, +// address: contract.address, +// events: extractInkAbiEvents(contract.abi), +// }; +// dappEventsOutput.contracts.push(dAppContract); +// } +// return res.status(httpStatus.OK).json(dappEventsOutput); +// } +// return res.status(response.status).json({ +// message: response.data.message || 'No dApps found', +// }); +// } catch (error) { +// logger.error('Error retrieving dApps ABIs:', error); +// if (error.response) { +// return res.status(error.response.status).json({ +// message: error.response.data.message || 'Error retrieving dApps ABIs', +// }); +// } +// return res.status(httpStatus.INTERNAL_SERVER_ERROR).json({ +// message: 'Failed to connect to backend service', +// }); +// } +// }; + export const getDappAbiCalls = async ( req: Request, res: Response, @@ -721,19 +825,34 @@ export const getDappAbiCalls = async ( ); if (response.status === 200) { - let dappCallsOutput: IAbiCallsOutput = { contracts: [] }; const dapp = response.data; + let dappCallsOutput: IAbiCallsOutput = { contracts: [] }; for (const contract of dapp.abis) { - const dAppContract: IAbiCallsOutputContract = { - name: contract.name, - address: contract.address, - calls: extractAbiFunctions(contract.abi), - }; - dappCallsOutput.contracts.push(dAppContract); + if (isSubstrateAbi(contract.abi)) { + const dAppContract: IAbiCallsOutputContract = { + name: contract.name, + address: contract.address, + calls: extractInkAbiFunctions(contract.abi), + }; + dappCallsOutput.contracts.push(dAppContract); + } else if (isEvmAbi(contract.abi)) { + const dAppContract: IAbiCallsOutputContract = { + name: contract.name, + address: contract.address, + calls: extractEvmAbiFunctions(contract.abi), + }; + dappCallsOutput.contracts.push(dAppContract); + } else { + return res + .status(httpStatus.BAD_REQUEST) + .json({ message: 'Unsupported ABI format' }); + } } + return res.status(httpStatus.OK).json(dappCallsOutput); } + return res.status(response.status).json({ message: response.data.message || 'No dApps found', }); @@ -750,6 +869,45 @@ export const getDappAbiCalls = async ( } }; +// export const getDappAbiCalls = async ( +// req: Request, +// res: Response, +// ): Promise => { +// try { +// const response = await axios.get( +// `${API_BASE_URL}/dapp-analytics/dapp/${req.params.id}`, +// ); + +// if (response.status === 200) { +// let dappCallsOutput: IAbiCallsOutput = { contracts: [] }; +// const dapp = response.data; + +// for (const contract of dapp.abis) { +// const dAppContract: IAbiCallsOutputContract = { +// name: contract.name, +// address: contract.address, +// calls: extractInkAbiFunctions(contract.abi), +// }; +// dappCallsOutput.contracts.push(dAppContract); +// } +// return res.status(httpStatus.OK).json(dappCallsOutput); +// } +// return res.status(response.status).json({ +// message: response.data.message || 'No dApps found', +// }); +// } catch (error) { +// logger.error('Error retrieving dApps ABIs:', error); +// if (error.response) { +// return res.status(error.response.status).json({ +// message: error.response.data.message || 'Error retrieving dApps ABIs', +// }); +// } +// return res.status(httpStatus.INTERNAL_SERVER_ERROR).json({ +// message: 'Failed to connect to backend service', +// }); +// } +// }; + export const getDappDataMetrics = async ( req: Request, res: Response, diff --git a/src/components/dapp-analytics/dapp-analytics.validation.ts b/src/components/dapp-analytics/dapp-analytics.validation.ts index 26a94ea..b4cc7ea 100644 --- a/src/components/dapp-analytics/dapp-analytics.validation.ts +++ b/src/components/dapp-analytics/dapp-analytics.validation.ts @@ -1,7 +1,9 @@ import Joi from 'joi'; -import { Abi as SubsquidAbi } from '@subsquid/ink-abi'; -import { decodeAddress } from '@polkadot/keyring'; +import { Abi as SubsquidInkAbi } from '@subsquid/ink-abi'; +import { Interface as SubsquidEvmAbi, isAddress as isEvmAddress } from 'ethers'; +import { decodeAddress as decodeSubstrateAddress } from '@polkadot/keyring'; +// Validator for Substrate addresses const substrateAddressValidator = Joi.extend((joi) => ({ type: 'substrateAddress', base: joi.string(), @@ -10,7 +12,7 @@ const substrateAddressValidator = Joi.extend((joi) => ({ }, validate(value, helpers) { try { - decodeAddress(value); + decodeSubstrateAddress(value); return { value }; } catch (error) { return { errors: helpers.error('substrateAddress.base') }; @@ -18,28 +20,78 @@ const substrateAddressValidator = Joi.extend((joi) => ({ }, })); -const subsquidAbiValidator = Joi.extend((joi) => ({ - type: 'subsquidAbi', +// Validator for EVM addresses +const evmAddressValidator = Joi.extend((joi) => ({ + type: 'evmAddress', + base: joi.string(), + messages: { + 'evmAddress.base': '{{#label}} must be a valid EVM address', + }, + validate(value, helpers) { + try { + if (isEvmAddress(value)) { + return { value }; + } else { + return { errors: helpers.error('evmAddress.base') }; + } + } catch (error) { + return { errors: helpers.error('evmAddress.base') }; + } + }, +})); + +// Validator for Subsquid Ink ABI (object) +const subsquidInkAbiValidator = Joi.extend((joi) => ({ + type: 'subsquidInkAbi', base: joi.object(), messages: { - 'subsquidAbi.validate': - '{{#label}} does not conform to Subsquid ABI format', + 'subsquidInkAbi.validate': + '{{#label}} does not conform to Subsquid Ink ABI format', + }, + validate(value, helpers) { + try { + new SubsquidInkAbi(value); + return { value }; + } catch (error) { + return { errors: helpers.error('subsquidInkAbi.validate') }; + } + }, +})); + +// Validator for EVM ABI (array) +const subsquidEvmAbiValidator = Joi.extend((joi) => ({ + type: 'subsquidEvmAbi', + base: joi.array(), + messages: { + 'subsquidEvmAbi.validate': + '{{#label}} does not conform to Subsquid EVM ABI format', }, validate(value, helpers) { try { - new SubsquidAbi(value); + new SubsquidEvmAbi(value); // This line checks the validity of the ABI return { value }; } catch (error) { - return { errors: helpers.error('subsquidAbi.validate') }; + return { errors: helpers.error('subsquidEvmAbi.validate') }; } }, })); +// Combined ABI Validator const abiSchema = Joi.array().items( Joi.object({ name: Joi.string(), - address: substrateAddressValidator.substrateAddress().required(), - abi: subsquidAbiValidator.subsquidAbi().required(), + address: Joi.alternatives() + .try( + substrateAddressValidator.substrateAddress(), + evmAddressValidator.evmAddress(), + ) + .required(), + abi: Joi.alternatives() + .try( + subsquidInkAbiValidator.subsquidInkAbi(), + subsquidEvmAbiValidator.subsquidEvmAbi(), + ) + .required(), }), ); diff --git a/src/components/dapp-analytics/helpers/utils.ts b/src/components/dapp-analytics/helpers/utils.ts new file mode 100644 index 0000000..85351c8 --- /dev/null +++ b/src/components/dapp-analytics/helpers/utils.ts @@ -0,0 +1,30 @@ +import { IInkAbi, IEvmAbi } from '../abi.interface'; +import logger from '@core/utils/logger'; + +const isSubstrateAbi = (abi: any): boolean => { + return ( + abi && + abi.spec && + Array.isArray(abi.spec.events) && + Array.isArray(abi.spec.messages) + ); +}; + +const isEvmAbi = (abi: any): boolean => { + logger.debug(`isEvmAbi abi: ${JSON.stringify(abi)}`); + abi.forEach((item) => { + logger.debug(`isEvmAbi abi item.type: ${JSON.stringify(item.type)}`); + }); + const isEvmAbi = + abi && + Array.isArray(abi) && + abi.every((item) => + ['function', 'event', 'constructor', 'fallback', 'receive'].includes( + item.type, + ), + ); + logger.debug(`isEvmAbi isEvmAbi: ${isEvmAbi}`); + return isEvmAbi; +}; + +export { isSubstrateAbi, isEvmAbi }; diff --git a/src/components/node-api/chainstate.ts b/src/components/node-api/chainstate.ts index 72d07f5..b4aecf0 100644 --- a/src/components/node-api/chainstate.ts +++ b/src/components/node-api/chainstate.ts @@ -4,6 +4,7 @@ const rpcEndpoints: { [key: string]: string } = { polkadot: 'wss://rpc.polkadot.io', kusama: 'wss://kusama-rpc.polkadot.io', 'aleph-zero': 'wss://rpc.azero.dev', + moonbeam: 'wss://moonbeam.api.onfinality.io/public-ws', }; export async function getCurrentBlock(chain: string) { diff --git a/src/config/dapp-analytics-networks.ts b/src/config/dapp-analytics-networks.ts index a32f034..da0a1fb 100644 --- a/src/config/dapp-analytics-networks.ts +++ b/src/config/dapp-analytics-networks.ts @@ -6,6 +6,20 @@ const DAPP_ANALYTICS_NETWORKS = { gatewayUrl: 'https://v2.archive.subsquid.io/network/aleph-zero', rpcIngestionDisabled: 'true', }, + 'arbitrum-one': { + type: 'evm', + rpcEndpoint: 'not found yet', + ss58Network: 'irrelevant', + gatewayUrl: 'https://v2.archive.subsquid.io/network/arbitrum-one', + rpcIngestionDisabled: 'true', + }, + 'arbitrum-nova': { + type: 'evm', + rpcEndpoint: 'not found yet', + ss58Network: 'irrelevant', + gatewayUrl: 'https://v2.archive.subsquid.io/network/arbitrum-nova', + rpcIngestionDisabled: 'true', + }, astar: { type: 'substrate', rpcEndpoint: 'https://astar.api.onfinality.io/public', @@ -13,6 +27,27 @@ const DAPP_ANALYTICS_NETWORKS = { gatewayUrl: 'https://v2.archive.subsquid.io/network/astar-substrate', rpcIngestionDisabled: 'true', }, + avalanche: { + type: 'evm', + rpcEndpoint: 'not found yet', + ss58Network: 'irrelevant', + gatewayUrl: 'https://v2.archive.subsquid.io/network/avalanche-mainnet', + rpcIngestionDisabled: 'true', + }, + moonbeam: { + type: 'evm', + rpcEndpoint: 'https://moonbeam.api.onfinality.io/public', + ss58Network: 'irrelevant', + gatewayUrl: 'https://v2.archive.subsquid.io/network/moonbeam-mainnet', + rpcIngestionDisabled: 'true', + }, + optimism: { + type: 'evm', + rpcEndpoint: 'not found yet', + ss58Network: 'irrelevant', + gatewayUrl: 'https://v2.archive.subsquid.io/network/optimism-mainnet', + rpcIngestionDisabled: 'true', + }, }; export default DAPP_ANALYTICS_NETWORKS;