From 83cd12c926204151c0e65cfbd075aec4ad001a16 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Aug 2025 15:34:52 +0200 Subject: [PATCH 01/10] chore(deps): bump ethers from 6.13.7 to 6.15.0 (#3448) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Francisco López --- packages/apps/dashboard/server/package.json | 2 +- .../exchange-oracle/client/package.json | 2 +- .../exchange-oracle/server/package.json | 2 +- packages/apps/human-app/frontend/package.json | 2 +- packages/apps/human-app/server/package.json | 2 +- .../apps/job-launcher/client/package.json | 2 +- .../reputation-oracle/server/package.json | 2 +- .../server/typeorm-migrations-datasource.ts | 3 +- packages/apps/staking/package.json | 2 +- packages/core/package.json | 6 +- .../human-protocol-sdk/package.json | 2 +- packages/sdk/typescript/subgraph/package.json | 2 +- yarn.lock | 57 ++++++++++++------- 13 files changed, 50 insertions(+), 36 deletions(-) diff --git a/packages/apps/dashboard/server/package.json b/packages/apps/dashboard/server/package.json index d68596f927..51a334dfca 100644 --- a/packages/apps/dashboard/server/package.json +++ b/packages/apps/dashboard/server/package.json @@ -34,7 +34,7 @@ "cache-manager": "^5.4.0", "cache-manager-redis-yet": "^5.1.5", "dayjs": "^1.11.12", - "ethers": "~6.13.5", + "ethers": "~6.15.0", "lodash": "^4.17.21", "reflect-metadata": "^0.2.2", "rxjs": "^7.2.0" diff --git a/packages/apps/fortune/exchange-oracle/client/package.json b/packages/apps/fortune/exchange-oracle/client/package.json index 31f5326e90..5d852d6df7 100644 --- a/packages/apps/fortune/exchange-oracle/client/package.json +++ b/packages/apps/fortune/exchange-oracle/client/package.json @@ -36,7 +36,7 @@ "@tanstack/react-query": "^5.67.2", "@tanstack/react-query-persist-client": "^5.80.7", "axios": "^1.7.2", - "ethers": "^6.13.5", + "ethers": "^6.15.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.24.1", diff --git a/packages/apps/fortune/exchange-oracle/server/package.json b/packages/apps/fortune/exchange-oracle/server/package.json index 093d3b942c..338765e4cb 100644 --- a/packages/apps/fortune/exchange-oracle/server/package.json +++ b/packages/apps/fortune/exchange-oracle/server/package.json @@ -47,7 +47,7 @@ "class-transformer": "^0.5.1", "class-validator": "0.14.1", "dotenv": "^17.0.0", - "ethers": "~6.13.5", + "ethers": "~6.15.0", "joi": "^17.13.3", "jsonwebtoken": "^9.0.2", "minio": "7.1.3", diff --git a/packages/apps/human-app/frontend/package.json b/packages/apps/human-app/frontend/package.json index b8912e8602..f5b5b6a3af 100644 --- a/packages/apps/human-app/frontend/package.json +++ b/packages/apps/human-app/frontend/package.json @@ -34,7 +34,7 @@ "@tanstack/react-query": "^5.75.5", "@wagmi/core": "^2.17.1", "date-fns": "^4.1.0", - "ethers": "^6.13.5", + "ethers": "^6.15.0", "i18next": "^23.8.2", "jwt-decode": "^4.0.0", "lodash": "^4.17.21", diff --git a/packages/apps/human-app/server/package.json b/packages/apps/human-app/server/package.json index a747aefe53..233e79c157 100644 --- a/packages/apps/human-app/server/package.json +++ b/packages/apps/human-app/server/package.json @@ -43,7 +43,7 @@ "cache-manager-redis-yet": "^5.1.5", "class-transformer": "^0.5.1", "class-validator": "0.14.1", - "ethers": "~6.13.5", + "ethers": "^6.15.0", "joi": "^17.13.3", "jsonwebtoken": "^9.0.2", "jwt-decode": "^4.0.0", diff --git a/packages/apps/job-launcher/client/package.json b/packages/apps/job-launcher/client/package.json index 77bd257b64..58aad979ef 100644 --- a/packages/apps/job-launcher/client/package.json +++ b/packages/apps/job-launcher/client/package.json @@ -23,7 +23,7 @@ "copy-to-clipboard": "^3.3.3", "dayjs": "^1.11.12", "decimal.js": "^10.6.0", - "ethers": "^6.13.5", + "ethers": "^6.15.0", "file-saver": "^2.0.5", "formik": "^2.4.2", "jwt-decode": "^4.0.0", diff --git a/packages/apps/reputation-oracle/server/package.json b/packages/apps/reputation-oracle/server/package.json index d326c19c1a..80264fda85 100644 --- a/packages/apps/reputation-oracle/server/package.json +++ b/packages/apps/reputation-oracle/server/package.json @@ -54,7 +54,7 @@ "class-transformer": "^0.5.1", "class-validator": "^0.14.1", "dotenv": "^17.0.0", - "ethers": "~6.13.5", + "ethers": "~6.15.0", "helmet": "^7.1.0", "joi": "^17.13.3", "json-stable-stringify": "^1.2.1", diff --git a/packages/apps/reputation-oracle/server/typeorm-migrations-datasource.ts b/packages/apps/reputation-oracle/server/typeorm-migrations-datasource.ts index 2b079f0d57..c82b8552b9 100644 --- a/packages/apps/reputation-oracle/server/typeorm-migrations-datasource.ts +++ b/packages/apps/reputation-oracle/server/typeorm-migrations-datasource.ts @@ -1,6 +1,7 @@ +import * as dotenv from 'dotenv'; import { DataSource } from 'typeorm'; import { SnakeNamingStrategy } from 'typeorm-naming-strategies'; -import * as dotenv from 'dotenv'; + import Environment from './src/utils/environment'; dotenv.config({ diff --git a/packages/apps/staking/package.json b/packages/apps/staking/package.json index 0da86f96be..e5d57eec72 100644 --- a/packages/apps/staking/package.json +++ b/packages/apps/staking/package.json @@ -36,7 +36,7 @@ "@tanstack/react-query": "^5.67.2", "@tanstack/react-query-persist-client": "^5.80.7", "axios": "^1.7.2", - "ethers": "^6.13.5", + "ethers": "^6.15.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.24.1", diff --git a/packages/core/package.json b/packages/core/package.json index 54041e1310..4e91829196 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -54,7 +54,7 @@ "license": "MIT", "devDependencies": { "@nomicfoundation/hardhat-chai-matchers": "^2.0.7", - "@nomicfoundation/hardhat-ethers": "^3.0.5", + "@nomicfoundation/hardhat-ethers": "^3.1.0", "@nomicfoundation/hardhat-network-helpers": "^1.0.12", "@nomicfoundation/hardhat-toolbox": "^4.0.0", "@nomicfoundation/hardhat-verify": "^2.0.11", @@ -70,7 +70,7 @@ "chai": "^4.5.0", "concurrently": "^9.1.2", "eslint": "^8.55.0", - "ethers": "~6.13.5", + "ethers": "~6.15.0", "hardhat": "^2.26.0", "hardhat-abi-exporter": "^2.10.1", "hardhat-contract-sizer": "^2.6.1", @@ -87,7 +87,7 @@ "xdeployer": "3.1.6" }, "peerDependencies": { - "ethers": "~6.13.5" + "ethers": "~6.15.0" }, "lint-staged": { "*.sol": [ diff --git a/packages/sdk/typescript/human-protocol-sdk/package.json b/packages/sdk/typescript/human-protocol-sdk/package.json index 8d91dedc23..234a374f0d 100644 --- a/packages/sdk/typescript/human-protocol-sdk/package.json +++ b/packages/sdk/typescript/human-protocol-sdk/package.json @@ -40,7 +40,7 @@ "dependencies": { "@human-protocol/core": "workspace:x", "axios": "^1.4.0", - "ethers": "~6.13.5", + "ethers": "~6.15.0", "graphql": "^16.8.1", "graphql-request": "^6.1.0", "graphql-tag": "^2.12.6", diff --git a/packages/sdk/typescript/subgraph/package.json b/packages/sdk/typescript/subgraph/package.json index 9978300489..db9f7cb9a1 100644 --- a/packages/sdk/typescript/subgraph/package.json +++ b/packages/sdk/typescript/subgraph/package.json @@ -42,7 +42,7 @@ "@graphql-eslint/eslint-plugin": "^3.19.1", "@human-protocol/core": "workspace:*", "eslint": "^8.55.0", - "ethers": "~6.13.5", + "ethers": "~6.15.0", "graphql": "^16.6.0", "matchstick-as": "^0.6.0", "mustache": "^4.2.0", diff --git a/yarn.lock b/yarn.lock index 03a1bc3c5b..05ce456b83 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3942,7 +3942,7 @@ __metadata: resolution: "@human-protocol/core@workspace:packages/core" dependencies: "@nomicfoundation/hardhat-chai-matchers": "npm:^2.0.7" - "@nomicfoundation/hardhat-ethers": "npm:^3.0.5" + "@nomicfoundation/hardhat-ethers": "npm:^3.1.0" "@nomicfoundation/hardhat-network-helpers": "npm:^1.0.12" "@nomicfoundation/hardhat-toolbox": "npm:^4.0.0" "@nomicfoundation/hardhat-verify": "npm:^2.0.11" @@ -3958,7 +3958,7 @@ __metadata: chai: "npm:^4.5.0" concurrently: "npm:^9.1.2" eslint: "npm:^8.55.0" - ethers: "npm:~6.13.5" + ethers: "npm:~6.15.0" hardhat: "npm:^2.26.0" hardhat-abi-exporter: "npm:^2.10.1" hardhat-contract-sizer: "npm:^2.6.1" @@ -3974,7 +3974,7 @@ __metadata: typescript: "npm:^5.8.3" xdeployer: "npm:3.1.6" peerDependencies: - ethers: ~6.13.5 + ethers: ~6.15.0 languageName: unknown linkType: soft @@ -4059,7 +4059,7 @@ __metadata: eslint: "npm:^8.0.1" eslint-config-prettier: "npm:^9.1.0" eslint-plugin-prettier: "npm:^5.2.1" - ethers: "npm:~6.13.5" + ethers: "npm:~6.15.0" jest: "npm:29.5.0" lodash: "npm:^4.17.21" prettier: "npm:^3.4.2" @@ -4147,7 +4147,7 @@ __metadata: eslint: "npm:^8.57.0" eslint-plugin-react-hooks: "npm:^5.1.0" eslint-plugin-react-refresh: "npm:^0.4.11" - ethers: "npm:^6.13.5" + ethers: "npm:^6.15.0" prettier: "npm:^3.4.2" react: "npm:^18.3.1" react-dom: "npm:^18.3.1" @@ -4199,7 +4199,7 @@ __metadata: eslint: "npm:^8.55.0" eslint-config-prettier: "npm:^9.1.0" eslint-plugin-prettier: "npm:^5.2.1" - ethers: "npm:~6.13.5" + ethers: "npm:~6.15.0" jest: "npm:29.7.0" joi: "npm:^17.13.3" jsonwebtoken: "npm:^9.0.2" @@ -4293,7 +4293,7 @@ __metadata: eslint-config-prettier: "npm:^9.1.0" eslint-plugin-prettier: "npm:^5.2.1" eslint-plugin-react-refresh: "npm:^0.4.11" - ethers: "npm:^6.13.5" + ethers: "npm:^6.15.0" husky: "npm:^9.1.6" i18next: "npm:^23.8.2" jsdom: "npm:^25.0.1" @@ -4365,7 +4365,7 @@ __metadata: eslint: "npm:^8.55.0" eslint-config-prettier: "npm:^9.1.0" eslint-plugin-prettier: "npm:^5.2.1" - ethers: "npm:~6.13.5" + ethers: "npm:^6.15.0" jest: "npm:29.7.0" joi: "npm:^17.13.3" jsonwebtoken: "npm:^9.0.2" @@ -4419,7 +4419,7 @@ __metadata: eslint-plugin-import: "npm:^2.29.0" eslint-plugin-react: "npm:^7.34.3" eslint-plugin-react-hooks: "npm:^5.1.0" - ethers: "npm:^6.13.5" + ethers: "npm:^6.15.0" file-saver: "npm:^2.0.5" formik: "npm:^2.4.2" jwt-decode: "npm:^4.0.0" @@ -4583,7 +4583,7 @@ __metadata: eslint-import-resolver-typescript: "npm:^4.4.4" eslint-plugin-import: "npm:^2.32.0" eslint-plugin-prettier: "npm:^5.5.4" - ethers: "npm:~6.13.5" + ethers: "npm:~6.15.0" globals: "npm:^16.3.0" helmet: "npm:^7.1.0" jest: "npm:29.7.0" @@ -4618,7 +4618,7 @@ __metadata: "@human-protocol/core": "workspace:x" axios: "npm:^1.4.0" eslint: "npm:^8.55.0" - ethers: "npm:~6.13.5" + ethers: "npm:~6.15.0" graphql: "npm:^16.8.1" graphql-request: "npm:^6.1.0" graphql-tag: "npm:^2.12.6" @@ -4655,7 +4655,7 @@ __metadata: eslint: "npm:^8.57.0" eslint-plugin-react-hooks: "npm:^5.1.0" eslint-plugin-react-refresh: "npm:^0.4.11" - ethers: "npm:^6.13.5" + ethers: "npm:^6.15.0" prettier: "npm:^3.4.2" react: "npm:^18.3.1" react-dom: "npm:^18.3.1" @@ -4679,7 +4679,7 @@ __metadata: "@graphql-eslint/eslint-plugin": "npm:^3.19.1" "@human-protocol/core": "workspace:*" eslint: "npm:^8.55.0" - ethers: "npm:~6.13.5" + ethers: "npm:~6.15.0" graphql: "npm:^16.6.0" matchstick-as: "npm:^0.6.0" mustache: "npm:^4.2.0" @@ -7028,7 +7028,7 @@ __metadata: languageName: node linkType: hard -"@nomicfoundation/hardhat-ethers@npm:^3.0.0, @nomicfoundation/hardhat-ethers@npm:^3.0.5": +"@nomicfoundation/hardhat-ethers@npm:^3.0.0": version: 3.0.8 resolution: "@nomicfoundation/hardhat-ethers@npm:3.0.8" dependencies: @@ -7041,6 +7041,19 @@ __metadata: languageName: node linkType: hard +"@nomicfoundation/hardhat-ethers@npm:^3.1.0": + version: 3.1.0 + resolution: "@nomicfoundation/hardhat-ethers@npm:3.1.0" + dependencies: + debug: "npm:^4.1.1" + lodash.isequal: "npm:^4.5.0" + peerDependencies: + ethers: ^6.14.0 + hardhat: ^2.26.0 + checksum: 10c0/66c5c9f9dd56f1cea25f5c9890f99251fd3179090bb0ad3f73fc1184e4a8ceaba544b5165d3f67e500cf0047d1550350e80df10e28cbabae3d61a659826163ae + languageName: node + linkType: hard + "@nomicfoundation/hardhat-ignition@npm:^0.15.5": version: 0.15.11 resolution: "@nomicfoundation/hardhat-ignition@npm:0.15.11" @@ -18457,9 +18470,9 @@ __metadata: languageName: node linkType: hard -"ethers@npm:^6.13.5, ethers@npm:^6.7.0, ethers@npm:^6.8.1": - version: 6.14.0 - resolution: "ethers@npm:6.14.0" +"ethers@npm:^6.15.0, ethers@npm:~6.15.0": + version: 6.15.0 + resolution: "ethers@npm:6.15.0" dependencies: "@adraffy/ens-normalize": "npm:1.10.1" "@noble/curves": "npm:1.2.0" @@ -18468,13 +18481,13 @@ __metadata: aes-js: "npm:4.0.0-beta.5" tslib: "npm:2.7.0" ws: "npm:8.17.1" - checksum: 10c0/7da443719a21c0b071dbd7d47e50faa8fcacc746e2b5487a394992215457903b1d187279e7134b48414d2626d0dbbf58b20c3bb8d246c359aa0a7453b608d582 + checksum: 10c0/0a4581b662fe46a889a524d3aba43dc6f0ac59b3ae08dce678ee4b5799aab4906109ab24684c9644deedfc9d6e79b59faccecbeda9b6b7ceb085724d596a49e9 languageName: node linkType: hard -"ethers@npm:~6.13.5": - version: 6.13.7 - resolution: "ethers@npm:6.13.7" +"ethers@npm:^6.7.0, ethers@npm:^6.8.1": + version: 6.14.0 + resolution: "ethers@npm:6.14.0" dependencies: "@adraffy/ens-normalize": "npm:1.10.1" "@noble/curves": "npm:1.2.0" @@ -18483,7 +18496,7 @@ __metadata: aes-js: "npm:4.0.0-beta.5" tslib: "npm:2.7.0" ws: "npm:8.17.1" - checksum: 10c0/bb693e8714c6082ee2c1d2af8a209bbc10d83ca4a686de368e169bf1dc804e926be58280c4bdaf0c2e9a4d021317f0db694ecfa4040381728ca46d45427db2a7 + checksum: 10c0/7da443719a21c0b071dbd7d47e50faa8fcacc746e2b5487a394992215457903b1d187279e7134b48414d2626d0dbbf58b20c3bb8d246c359aa0a7453b608d582 languageName: node linkType: hard From 8c6afbe01e352b593635124b575731df11c509c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20L=C3=B3pez?= <50665615+flopez7@users.noreply.github.com> Date: Thu, 28 Aug 2025 10:33:41 +0200 Subject: [PATCH 02/10] [Human App] Add caching for proposals endpoint and increase cache duration (#3524) --- .../apps/human-app/server/src/app.module.ts | 1 + .../config/environment-config.service.ts | 14 ++ .../governance/governance.controller.ts | 2 +- .../modules/governance/governance.service.ts | 121 +++++++++--------- 4 files changed, 75 insertions(+), 63 deletions(-) diff --git a/packages/apps/human-app/server/src/app.module.ts b/packages/apps/human-app/server/src/app.module.ts index 078f1101bf..529154b0ed 100644 --- a/packages/apps/human-app/server/src/app.module.ts +++ b/packages/apps/human-app/server/src/app.module.ts @@ -110,6 +110,7 @@ const JOI_BOOLEAN_STRING_SCHEMA = Joi.string().valid('true', 'false'); CACHE_TTL_ORACLE_AVAILABLE_JOBS: Joi.number(), JOB_ASSIGNMENTS_DATA_RETENTION_DAYS: Joi.number(), CACHE_TTL_EXCHANGE_ORACLE_URL: Joi.number(), + CACHE_TTL_PROPOSALS: Joi.number(), CACHE_TTL_EXCHANGE_ORACLE_REGISTRATION_NEEDED: Joi.number(), MAX_EXECUTIONS_TO_SKIP: Joi.number(), FEATURE_FLAG_JOBS_DISCOVERY: JOI_BOOLEAN_STRING_SCHEMA, diff --git a/packages/apps/human-app/server/src/common/config/environment-config.service.ts b/packages/apps/human-app/server/src/common/config/environment-config.service.ts index 447e21e7e3..4b227f75a9 100644 --- a/packages/apps/human-app/server/src/common/config/environment-config.service.ts +++ b/packages/apps/human-app/server/src/common/config/environment-config.service.ts @@ -16,6 +16,7 @@ const DEFAULT_CACHE_TTL_EXCHANGE_ORACLE_URL = 24 * 60 * 60; const DEFAULT_MAX_EXECUTIONS_TO_SKIP = 32; const DEFAULT_CACHE_TTL_JOB_TYPES = 24 * 60 * 60; const DEFAULT_CACHE_TTL_EXCHANGE_ORACLE_REGISTRATION_NEEDED = 24 * 60 * 60; +const DEFAULT_CACHE_TTL_PROPOSALS = 5 * 60; @Injectable() export class EnvironmentConfigService { @@ -208,6 +209,19 @@ export class EnvironmentConfigService { return this.configService.getOrThrow('GOVERNOR_ADDRESS'); } + /** + * The cache time-to-live (TTL) for proposals data. + * Default: 5 minutes + */ + get cacheTtlProposals(): number { + return ( + this.configService.get( + 'CACHE_TTL_PROPOSALS', + DEFAULT_CACHE_TTL_PROPOSALS, + ) * 1000 + ); + } + /** * Flag indicating if CORS is enabled. * Default: false diff --git a/packages/apps/human-app/server/src/modules/governance/governance.controller.ts b/packages/apps/human-app/server/src/modules/governance/governance.controller.ts index 13f93948e0..44362203c5 100644 --- a/packages/apps/human-app/server/src/modules/governance/governance.controller.ts +++ b/packages/apps/human-app/server/src/modules/governance/governance.controller.ts @@ -17,7 +17,7 @@ export class GovernanceController { @ApiOperation({ summary: 'Get pending and active governance proposals' }) @ApiOkResponse({ type: ProposalResponse, isArray: true }) @HttpCode(200) - @Header('Cache-Control', 'private, max-age=60') + @Header('Cache-Control', 'private, max-age=300') @Get('/proposals') public async getProposals(): Promise { return this.governanceService.getProposals(); diff --git a/packages/apps/human-app/server/src/modules/governance/governance.service.ts b/packages/apps/human-app/server/src/modules/governance/governance.service.ts index 5eb8b0c64b..a5618bbca7 100644 --- a/packages/apps/human-app/server/src/modules/governance/governance.service.ts +++ b/packages/apps/human-app/server/src/modules/governance/governance.service.ts @@ -1,24 +1,23 @@ import { MetaHumanGovernor__factory } from '@human-protocol/core/typechain-types'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; -import { Inject, Injectable, Logger } from '@nestjs/common'; +import { Inject, Injectable } from '@nestjs/common'; import { Cache } from 'cache-manager'; import { ethers } from 'ethers'; import _ from 'lodash'; import { EnvironmentConfigService } from '../../common/config/environment-config.service'; import { ProposalState } from '../../common/enums/proposal'; +import logger from '../../logger'; import { ProposalResponse } from './model/governance.model'; const N_BLOCKS_LOOKBACK = 100000; -type Proposal = { - proposalId: string; - voteStart: number; - voteEnd: number; +type Block = { + timestamp: number; + number: number; }; - @Injectable() export class GovernanceService { - private readonly logger = new Logger(GovernanceService.name); + private readonly logger = logger.child({ context: GovernanceService.name }); constructor( private readonly configService: EnvironmentConfigService, @@ -26,6 +25,21 @@ export class GovernanceService { ) {} public async getProposals(): Promise { + const lastScannedBlockKey = this.generateCacheKey('last-scanned-block'); + const proposalListKey = this.generateCacheKey('proposal', 'list'); + const cachedLastScannedBlock = + await this.cacheManager.get(lastScannedBlockKey); + const cachedProposals = + (await this.cacheManager.get(proposalListKey)) ?? []; + + if ( + cachedLastScannedBlock && + Date.now() - cachedLastScannedBlock.timestamp * 1000 < + this.configService.cacheTtlProposals + ) { + return cachedProposals; + } + const provider = new ethers.JsonRpcProvider( this.configService.governanceRpcUrl, ); @@ -34,48 +48,37 @@ export class GovernanceService { provider, ); - const currentBlock = await provider.getBlockNumber(); - const lastScannedBlockKey = this.generateCacheKey('last-scanned-block'); - const proposalListKey = this.generateCacheKey('proposal', 'list'); - - const cachedLastScannedBlock = - (await this.cacheManager.get(lastScannedBlockKey)) ?? 0; - + const currentBlock = await provider.getBlock('latest'); + if (!currentBlock) { + this.logger.error('No latest block from RPC'); + throw new Error('Blockchain node unavailable'); + } + const cachedLastScannedBlockNumber = cachedLastScannedBlock?.number ?? 0; const fromBlock = - cachedLastScannedBlock > 0 - ? cachedLastScannedBlock + 1 - : Math.max(0, currentBlock - N_BLOCKS_LOOKBACK); + cachedLastScannedBlockNumber > 0 + ? cachedLastScannedBlockNumber + 1 + : currentBlock.number - N_BLOCKS_LOOKBACK; - let proposalList: Proposal[] = []; + let allProposals: ProposalResponse[] = []; try { const newProposals = await this.getProposalCreatedEvents( contract, fromBlock, - currentBlock, + currentBlock.number, ); - const cachedList = - (await this.cacheManager.get(proposalListKey)) || []; - - proposalList = _.uniqBy([...cachedList, ...newProposals], 'proposalId'); - } catch (err) { - this.logger.warn( - 'getProposalCreatedEvents failed, falling back to cached list', - { - error: err, - }, + allProposals = _.uniqBy( + [...cachedProposals, ...newProposals], + 'proposalId', ); - proposalList = - (await this.cacheManager.get(proposalListKey)) || []; + } catch (err) { + this.logger.warn('getProposalCreatedEvents failed; using previous kept', { + error: err, + }); } - const proposals: ProposalResponse[] = []; - const keptProposalList: typeof proposalList = []; - - for (const proposal of proposalList) { - const voteStartMs = (proposal.voteStart ?? 0) * 1000; - const voteEndMs = (proposal.voteEnd ?? 0) * 1000; - + const finalProposals: ProposalResponse[] = []; + for (const proposal of allProposals) { let state: ProposalState; try { state = Number( @@ -93,11 +96,6 @@ export class GovernanceService { continue; } - keptProposalList.push(proposal); - - let forVotes = 0; - let againstVotes = 0; - let abstainVotes = 0; if (state === ProposalState.ACTIVE) { try { const votes = (await contract.proposalVotes(proposal.proposalId)) as [ @@ -106,9 +104,9 @@ export class GovernanceService { ethers.BigNumberish, ]; const [againstBn, forBn, abstainBn] = votes; - forVotes = Number(ethers.formatEther(forBn)); - againstVotes = Number(ethers.formatEther(againstBn)); - abstainVotes = Number(ethers.formatEther(abstainBn)); + proposal.forVotes = Number(ethers.formatEther(forBn)); + proposal.againstVotes = Number(ethers.formatEther(againstBn)); + proposal.abstainVotes = Number(ethers.formatEther(abstainBn)); } catch (err) { this.logger.warn('Failed to fetch votes for proposal', { error: err, @@ -118,31 +116,27 @@ export class GovernanceService { } } - proposals.push({ - proposalId: proposal.proposalId, - forVotes, - againstVotes, - abstainVotes, - voteStart: voteStartMs, - voteEnd: voteEndMs, - }); + finalProposals.push(proposal); } - await this.cacheManager.set(proposalListKey, keptProposalList); - await this.cacheManager.set(lastScannedBlockKey, currentBlock); + await this.cacheManager.set(proposalListKey, finalProposals); + await this.cacheManager.set(lastScannedBlockKey, { + number: currentBlock.number, + timestamp: currentBlock.timestamp, + }); - return proposals; + return finalProposals; } private async getProposalCreatedEvents( contract: ReturnType, fromBlock: number, toBlock: number, - ): Promise { + ): Promise { const filter = contract.filters.ProposalCreated(); const logs = await contract.queryFilter(filter, fromBlock, toBlock); - const proposals: Proposal[] = []; + const proposals: ProposalResponse[] = []; for (const log of logs) { try { const parsed = contract.interface.parseLog(log); @@ -150,10 +144,13 @@ export class GovernanceService { parsed?.args.proposalId as ethers.BigNumberish ).toString(); - const proposal: Proposal = { + const proposal: ProposalResponse = { proposalId, - voteStart: Number(parsed?.args?.voteStart), - voteEnd: Number(parsed?.args?.voteEnd), + voteStart: Number(parsed?.args?.voteStart) * 1000, + voteEnd: Number(parsed?.args?.voteEnd) * 1000, + forVotes: 0, + againstVotes: 0, + abstainVotes: 0, }; proposals.push(proposal); } catch (err) { From 3ba39abdf72c84d99452a314de35286296bcc06a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 29 Aug 2025 10:12:40 +0200 Subject: [PATCH 03/10] chore(deps): bump actions/dependency-review-action from 4.7.1 to 4.7.2 (#3521) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci-dependency-review.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-dependency-review.yaml b/.github/workflows/ci-dependency-review.yaml index 79a3c2feeb..edbb0d5aef 100644 --- a/.github/workflows/ci-dependency-review.yaml +++ b/.github/workflows/ci-dependency-review.yaml @@ -14,6 +14,6 @@ jobs: steps: - uses: actions/checkout@v4.1.1 - name: Dependency Review - uses: actions/dependency-review-action@v4.7.1 + uses: actions/dependency-review-action@v4.7.2 with: show-openssf-scorecard: false From 7b16aa735b6f8fd87bf0870d2137b022519697bb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 29 Aug 2025 10:13:30 +0200 Subject: [PATCH 04/10] chore(deps): bump react-hook-form from 7.56.2 to 7.62.0 (#3519) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- packages/apps/human-app/frontend/package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/apps/human-app/frontend/package.json b/packages/apps/human-app/frontend/package.json index f5b5b6a3af..d9b609658c 100644 --- a/packages/apps/human-app/frontend/package.json +++ b/packages/apps/human-app/frontend/package.json @@ -45,7 +45,7 @@ "query-string": "^9.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-hook-form": "^7.55.0", + "react-hook-form": "^7.62.0", "react-i18next": "^15.1.0", "react-imask": "^7.4.0", "react-number-format": "^5.4.3", diff --git a/yarn.lock b/yarn.lock index 05ce456b83..213246deca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4308,7 +4308,7 @@ __metadata: query-string: "npm:^9.0.0" react: "npm:^18.2.0" react-dom: "npm:^18.2.0" - react-hook-form: "npm:^7.55.0" + react-hook-form: "npm:^7.62.0" react-i18next: "npm:^15.1.0" react-imask: "npm:^7.4.0" react-number-format: "npm:^5.4.3" @@ -26115,12 +26115,12 @@ __metadata: languageName: node linkType: hard -"react-hook-form@npm:^7.55.0": - version: 7.56.2 - resolution: "react-hook-form@npm:7.56.2" +"react-hook-form@npm:^7.62.0": + version: 7.62.0 + resolution: "react-hook-form@npm:7.62.0" peerDependencies: react: ^16.8.0 || ^17 || ^18 || ^19 - checksum: 10c0/9eeca7525c5c0b1356670e7cc77687298c7280a31c99bca396596124371467c310cad22f2c10efd9c75b4570d07740ee1c6ad481d59c1b725c7600767f0a35af + checksum: 10c0/451a25a2ddf07be14f690d2ad3f2f970e0b933fd059ef141c6da9e19d0566d739e9d5cc9c482e3533f3fd01d97e658b896a01ce45a1259ad7b0d4638ba0112c6 languageName: node linkType: hard From e26b1c2bbefcaf85e65454871f659d8403a1e0cf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 29 Aug 2025 10:23:58 +0200 Subject: [PATCH 05/10] chore(deps): bump react-dom and @types/react-dom (#3520) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- packages/apps/dashboard/client/package.json | 4 +- packages/apps/faucet/client/package.json | 2 +- .../exchange-oracle/client/package.json | 4 +- packages/apps/human-app/frontend/package.json | 4 +- .../apps/job-launcher/client/package.json | 4 +- packages/apps/staking/package.json | 4 +- yarn.lock | 55 +++++++++---------- 7 files changed, 37 insertions(+), 40 deletions(-) diff --git a/packages/apps/dashboard/client/package.json b/packages/apps/dashboard/client/package.json index 5458c1869e..84572ae7fe 100644 --- a/packages/apps/dashboard/client/package.json +++ b/packages/apps/dashboard/client/package.json @@ -33,7 +33,7 @@ "clsx": "^2.1.1", "dayjs": "^1.11.11", "react": "^18.2.0", - "react-dom": "^18.2.0", + "react-dom": "^19.1.1", "react-number-format": "^5.4.3", "react-router-dom": "^6.23.1", "recharts": "^2.13.0-alpha.4", @@ -48,7 +48,7 @@ "devDependencies": { "@eslint/js": "^9.27.0", "@types/react": "^18.2.66", - "@types/react-dom": "^18.2.22", + "@types/react-dom": "^19.1.9", "@typescript-eslint/eslint-plugin": "^7.2.0", "@typescript-eslint/parser": "^7.2.0", "@vitejs/plugin-react": "^4.2.1", diff --git a/packages/apps/faucet/client/package.json b/packages/apps/faucet/client/package.json index 9d0b0142d2..40d61b1005 100644 --- a/packages/apps/faucet/client/package.json +++ b/packages/apps/faucet/client/package.json @@ -9,7 +9,7 @@ "@mui/icons-material": "^7.0.1", "@mui/material": "^5.16.7", "react": "^18.2.0", - "react-dom": "^18.2.0", + "react-dom": "^19.1.1", "react-loading-skeleton": "^3.3.1", "react-router-dom": "^6.4.3", "serve": "^14.2.4", diff --git a/packages/apps/fortune/exchange-oracle/client/package.json b/packages/apps/fortune/exchange-oracle/client/package.json index 5d852d6df7..d645839729 100644 --- a/packages/apps/fortune/exchange-oracle/client/package.json +++ b/packages/apps/fortune/exchange-oracle/client/package.json @@ -38,7 +38,7 @@ "axios": "^1.7.2", "ethers": "^6.15.0", "react": "^18.3.1", - "react-dom": "^18.3.1", + "react-dom": "^19.1.1", "react-router-dom": "^6.24.1", "serve": "^14.2.4", "viem": "2.x", @@ -46,7 +46,7 @@ }, "devDependencies": { "@types/react": "^18.3.12", - "@types/react-dom": "^18.3.0", + "@types/react-dom": "^19.1.9", "@types/react-router-dom": "^5.3.3", "@typescript-eslint/eslint-plugin": "^7.13.1", "@typescript-eslint/parser": "^7.13.1", diff --git a/packages/apps/human-app/frontend/package.json b/packages/apps/human-app/frontend/package.json index d9b609658c..39b38c8498 100644 --- a/packages/apps/human-app/frontend/package.json +++ b/packages/apps/human-app/frontend/package.json @@ -44,7 +44,7 @@ "prop-types": "^15.8.1", "query-string": "^9.0.0", "react": "^18.2.0", - "react-dom": "^18.2.0", + "react-dom": "^19.1.1", "react-hook-form": "^7.62.0", "react-i18next": "^15.1.0", "react-imask": "^7.4.0", @@ -65,7 +65,7 @@ "@types/node": "^22.10.5", "@types/prop-types": "^15", "@types/react": "^18.3.12", - "@types/react-dom": "^18.2.17", + "@types/react-dom": "^19.1.9", "@typescript-eslint/eslint-plugin": "^6.20.0", "@typescript-eslint/parser": "^6.20.0", "@vercel/style-guide": "^6.0.0", diff --git a/packages/apps/job-launcher/client/package.json b/packages/apps/job-launcher/client/package.json index 58aad979ef..ce1d1f9b1f 100644 --- a/packages/apps/job-launcher/client/package.json +++ b/packages/apps/job-launcher/client/package.json @@ -28,7 +28,7 @@ "formik": "^2.4.2", "jwt-decode": "^4.0.0", "react": "^18.2.0", - "react-dom": "^18.2.0", + "react-dom": "^19.1.1", "react-redux": "^9.1.0", "react-router-dom": "^6.14.1", "recharts": "^2.7.2", @@ -67,7 +67,7 @@ "devDependencies": { "@types/file-saver": "^2.0.7", "@types/react": "^18.3.12", - "@types/react-dom": "^18.2.25", + "@types/react-dom": "^19.1.9", "@types/xml2js": "^0.4.14", "@vitejs/plugin-react": "^4.2.1", "eslint": "^8.55.0", diff --git a/packages/apps/staking/package.json b/packages/apps/staking/package.json index e5d57eec72..8a45ec6211 100644 --- a/packages/apps/staking/package.json +++ b/packages/apps/staking/package.json @@ -38,7 +38,7 @@ "axios": "^1.7.2", "ethers": "^6.15.0", "react": "^18.3.1", - "react-dom": "^18.3.1", + "react-dom": "^19.1.1", "react-router-dom": "^6.24.1", "serve": "^14.2.4", "viem": "2.x", @@ -46,7 +46,7 @@ }, "devDependencies": { "@types/react": "^18.3.12", - "@types/react-dom": "^18.3.0", + "@types/react-dom": "^19.1.9", "@types/react-router-dom": "^5.3.3", "@typescript-eslint/eslint-plugin": "^7.13.1", "@typescript-eslint/parser": "^7.13.1", diff --git a/yarn.lock b/yarn.lock index 213246deca..161ec6f452 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3994,7 +3994,7 @@ __metadata: "@mui/x-date-pickers": "npm:^8.7.0" "@tanstack/react-query": "npm:^5.67.2" "@types/react": "npm:^18.2.66" - "@types/react-dom": "npm:^18.2.22" + "@types/react-dom": "npm:^19.1.9" "@types/react-router-dom": "npm:^5.3.3" "@types/recharts": "npm:^1.8.29" "@typescript-eslint/eslint-plugin": "npm:^7.2.0" @@ -4012,7 +4012,7 @@ __metadata: globals: "npm:^16.2.0" prettier: "npm:^3.4.2" react: "npm:^18.2.0" - react-dom: "npm:^18.2.0" + react-dom: "npm:^19.1.1" react-number-format: "npm:^5.4.3" react-router-dom: "npm:^6.23.1" recharts: "npm:^2.13.0-alpha.4" @@ -4090,7 +4090,7 @@ __metadata: eslint-plugin-react-hooks: "npm:^5.1.0" prettier: "npm:^3.4.2" react: "npm:^18.2.0" - react-dom: "npm:^18.2.0" + react-dom: "npm:^19.1.1" react-loading-skeleton: "npm:^3.3.1" react-router-dom: "npm:^6.4.3" serve: "npm:^14.2.4" @@ -4138,7 +4138,7 @@ __metadata: "@tanstack/react-query": "npm:^5.67.2" "@tanstack/react-query-persist-client": "npm:^5.80.7" "@types/react": "npm:^18.3.12" - "@types/react-dom": "npm:^18.3.0" + "@types/react-dom": "npm:^19.1.9" "@types/react-router-dom": "npm:^5.3.3" "@typescript-eslint/eslint-plugin": "npm:^7.13.1" "@typescript-eslint/parser": "npm:^7.13.1" @@ -4150,7 +4150,7 @@ __metadata: ethers: "npm:^6.15.0" prettier: "npm:^3.4.2" react: "npm:^18.3.1" - react-dom: "npm:^18.3.1" + react-dom: "npm:^19.1.1" react-router-dom: "npm:^6.24.1" serve: "npm:^14.2.4" typescript: "npm:^5.6.3" @@ -4282,7 +4282,7 @@ __metadata: "@types/node": "npm:^22.10.5" "@types/prop-types": "npm:^15" "@types/react": "npm:^18.3.12" - "@types/react-dom": "npm:^18.2.17" + "@types/react-dom": "npm:^19.1.9" "@typescript-eslint/eslint-plugin": "npm:^6.20.0" "@typescript-eslint/parser": "npm:^6.20.0" "@vercel/style-guide": "npm:^6.0.0" @@ -4307,7 +4307,7 @@ __metadata: prop-types: "npm:^15.8.1" query-string: "npm:^9.0.0" react: "npm:^18.2.0" - react-dom: "npm:^18.2.0" + react-dom: "npm:^19.1.1" react-hook-form: "npm:^7.62.0" react-i18next: "npm:^15.1.0" react-imask: "npm:^7.4.0" @@ -4406,7 +4406,7 @@ __metadata: "@tanstack/react-query-persist-client": "npm:^5.80.7" "@types/file-saver": "npm:^2.0.7" "@types/react": "npm:^18.3.12" - "@types/react-dom": "npm:^18.2.25" + "@types/react-dom": "npm:^19.1.9" "@types/xml2js": "npm:^0.4.14" "@vitejs/plugin-react": "npm:^4.2.1" axios: "npm:^1.1.3" @@ -4425,7 +4425,7 @@ __metadata: jwt-decode: "npm:^4.0.0" prettier: "npm:^3.4.2" react: "npm:^18.2.0" - react-dom: "npm:^18.2.0" + react-dom: "npm:^19.1.1" react-redux: "npm:^9.1.0" react-router-dom: "npm:^6.14.1" recharts: "npm:^2.7.2" @@ -4646,7 +4646,7 @@ __metadata: "@tanstack/react-query": "npm:^5.67.2" "@tanstack/react-query-persist-client": "npm:^5.80.7" "@types/react": "npm:^18.3.12" - "@types/react-dom": "npm:^18.3.0" + "@types/react-dom": "npm:^19.1.9" "@types/react-router-dom": "npm:^5.3.3" "@typescript-eslint/eslint-plugin": "npm:^7.13.1" "@typescript-eslint/parser": "npm:^7.13.1" @@ -4658,7 +4658,7 @@ __metadata: ethers: "npm:^6.15.0" prettier: "npm:^3.4.2" react: "npm:^18.3.1" - react-dom: "npm:^18.3.1" + react-dom: "npm:^19.1.1" react-router-dom: "npm:^6.24.1" sass: "npm:^1.89.2" serve: "npm:^14.2.4" @@ -10621,12 +10621,12 @@ __metadata: languageName: node linkType: hard -"@types/react-dom@npm:^18.2.17, @types/react-dom@npm:^18.2.22, @types/react-dom@npm:^18.2.25, @types/react-dom@npm:^18.3.0": - version: 18.3.7 - resolution: "@types/react-dom@npm:18.3.7" +"@types/react-dom@npm:^19.1.9": + version: 19.1.9 + resolution: "@types/react-dom@npm:19.1.9" peerDependencies: - "@types/react": ^18.0.0 - checksum: 10c0/8bd309e2c3d1604a28a736a24f96cbadf6c05d5288cfef8883b74f4054c961b6b3a5e997fd5686e492be903c8f3380dba5ec017eff3906b1256529cd2d39603e + "@types/react": ^19.0.0 + checksum: 10c0/34c8dda86c1590b3ef0e7ecd38f9663a66ba2dd69113ba74fb0adc36b83bbfb8c94c1487a2505282a5f7e5e000d2ebf36f4c0fd41b3b672f5178fd1d4f1f8f58 languageName: node linkType: hard @@ -26096,15 +26096,14 @@ __metadata: languageName: node linkType: hard -"react-dom@npm:^18.2.0, react-dom@npm:^18.3.1": - version: 18.3.1 - resolution: "react-dom@npm:18.3.1" +"react-dom@npm:^19.1.1": + version: 19.1.1 + resolution: "react-dom@npm:19.1.1" dependencies: - loose-envify: "npm:^1.1.0" - scheduler: "npm:^0.23.2" + scheduler: "npm:^0.26.0" peerDependencies: - react: ^18.3.1 - checksum: 10c0/a752496c1941f958f2e8ac56239172296fcddce1365ce45222d04a1947e0cc5547df3e8447f855a81d6d39f008d7c32eab43db3712077f09e3f67c4874973e85 + react: ^19.1.1 + checksum: 10c0/8c91198510521299c56e4e8d5e3a4508b2734fb5e52f29eeac33811de64e76fe586ad32c32182e2e84e070d98df67125da346c3360013357228172dbcd20bcdd languageName: node linkType: hard @@ -27177,12 +27176,10 @@ __metadata: languageName: node linkType: hard -"scheduler@npm:^0.23.2": - version: 0.23.2 - resolution: "scheduler@npm:0.23.2" - dependencies: - loose-envify: "npm:^1.1.0" - checksum: 10c0/26383305e249651d4c58e6705d5f8425f153211aef95f15161c151f7b8de885f24751b377e4a0b3dd42cce09aad3f87a61dab7636859c0d89b7daf1a1e2a5c78 +"scheduler@npm:^0.26.0": + version: 0.26.0 + resolution: "scheduler@npm:0.26.0" + checksum: 10c0/5b8d5bfddaae3513410eda54f2268e98a376a429931921a81b5c3a2873aab7ca4d775a8caac5498f8cbc7d0daeab947cf923dbd8e215d61671f9f4e392d34356 languageName: node linkType: hard From 1b3d77eda876cdd762c6f92d6b6f459d3d2e1ef9 Mon Sep 17 00:00:00 2001 From: Dmitry Nechay Date: Fri, 29 Aug 2025 16:21:45 +0300 Subject: [PATCH 06/10] [HUMAN App] fix: proxied requests errors handling (#3527) --- .../src/common/filter/exceptions.filter.ts | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/packages/apps/human-app/server/src/common/filter/exceptions.filter.ts b/packages/apps/human-app/server/src/common/filter/exceptions.filter.ts index 47f17a8953..61eb3a6c4d 100644 --- a/packages/apps/human-app/server/src/common/filter/exceptions.filter.ts +++ b/packages/apps/human-app/server/src/common/filter/exceptions.filter.ts @@ -24,18 +24,27 @@ export class ExceptionFilter implements IExceptionFilter { if (exception instanceof HttpException) { status = exception.getStatus(); message = exception.getResponse(); - } else if (exception.response) { - status = exception.response.status; - message = - exception.response.data?.message || exception.response.statusText; - } else { - let formattedError = exception; - if (exception instanceof AxiosError) { - formattedError = errorUtils.formatError(exception); - formattedError.outgoingRequestUrl = exception.config?.url; + } else if (exception instanceof AxiosError) { + /** + * All requests we made to external services are supposed to be made by axios, + * and we either proxy response code or act as a "bad gateway" if sending fails. + */ + status = exception.response?.status || HttpStatus.BAD_GATEWAY; + if (status >= 200 && status < 400) { + /** + * In case some 3rd party that we are using (e.g. graphql client) + * throws with such status code (their API schema can be literally anything) + */ + this.logger.warn('Unexpected http status in exception response', { + status, + error: errorUtils.formatError(exception), + path: request.url, + }); } + message = exception.response?.data?.message || 'Bad gateway'; + } else { this.logger.error('Unhandled exception', { - error: formattedError, + error: errorUtils.formatError(exception), path: request.url, }); } From 83712c7fce7d0e0f9166e080fa6ec7478db86b2d Mon Sep 17 00:00:00 2001 From: Dmitry Nechay Date: Fri, 29 Aug 2025 17:43:33 +0300 Subject: [PATCH 07/10] fix: react & react-dome versions compatibility (#3528) --- packages/apps/dashboard/client/package.json | 8 +-- packages/apps/faucet/client/package.json | 5 +- .../exchange-oracle/client/package.json | 4 +- packages/apps/human-app/frontend/package.json | 6 +- .../apps/job-launcher/client/package.json | 6 +- packages/apps/staking/package.json | 4 +- yarn.lock | 70 ++++++++++--------- 7 files changed, 54 insertions(+), 49 deletions(-) diff --git a/packages/apps/dashboard/client/package.json b/packages/apps/dashboard/client/package.json index 84572ae7fe..db9a58ea33 100644 --- a/packages/apps/dashboard/client/package.json +++ b/packages/apps/dashboard/client/package.json @@ -32,8 +32,8 @@ "axios": "^1.7.2", "clsx": "^2.1.1", "dayjs": "^1.11.11", - "react": "^18.2.0", - "react-dom": "^19.1.1", + "react": "^18.3.1", + "react-dom": "^18.3.1", "react-number-format": "^5.4.3", "react-router-dom": "^6.23.1", "recharts": "^2.13.0-alpha.4", @@ -47,8 +47,8 @@ }, "devDependencies": { "@eslint/js": "^9.27.0", - "@types/react": "^18.2.66", - "@types/react-dom": "^19.1.9", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", "@typescript-eslint/eslint-plugin": "^7.2.0", "@typescript-eslint/parser": "^7.2.0", "@vitejs/plugin-react": "^4.2.1", diff --git a/packages/apps/faucet/client/package.json b/packages/apps/faucet/client/package.json index 40d61b1005..519f1c2dc9 100644 --- a/packages/apps/faucet/client/package.json +++ b/packages/apps/faucet/client/package.json @@ -8,8 +8,8 @@ "@human-protocol/sdk": "workspace:*", "@mui/icons-material": "^7.0.1", "@mui/material": "^5.16.7", - "react": "^18.2.0", - "react-dom": "^19.1.1", + "react": "^18.3.1", + "react-dom": "^18.3.1", "react-loading-skeleton": "^3.3.1", "react-router-dom": "^6.4.3", "serve": "^14.2.4", @@ -17,6 +17,7 @@ }, "devDependencies": { "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", "dotenv": "^17.0.0", "eslint": "^8.55.0", "eslint-config-react-app": "^7.0.1", diff --git a/packages/apps/fortune/exchange-oracle/client/package.json b/packages/apps/fortune/exchange-oracle/client/package.json index d645839729..6c53fd1633 100644 --- a/packages/apps/fortune/exchange-oracle/client/package.json +++ b/packages/apps/fortune/exchange-oracle/client/package.json @@ -38,7 +38,7 @@ "axios": "^1.7.2", "ethers": "^6.15.0", "react": "^18.3.1", - "react-dom": "^19.1.1", + "react-dom": "^18.3.1", "react-router-dom": "^6.24.1", "serve": "^14.2.4", "viem": "2.x", @@ -46,7 +46,7 @@ }, "devDependencies": { "@types/react": "^18.3.12", - "@types/react-dom": "^19.1.9", + "@types/react-dom": "^18.3.1", "@types/react-router-dom": "^5.3.3", "@typescript-eslint/eslint-plugin": "^7.13.1", "@typescript-eslint/parser": "^7.13.1", diff --git a/packages/apps/human-app/frontend/package.json b/packages/apps/human-app/frontend/package.json index 39b38c8498..367858acdd 100644 --- a/packages/apps/human-app/frontend/package.json +++ b/packages/apps/human-app/frontend/package.json @@ -43,8 +43,8 @@ "notistack": "^3.0.1", "prop-types": "^15.8.1", "query-string": "^9.0.0", - "react": "^18.2.0", - "react-dom": "^19.1.1", + "react": "^18.3.1", + "react-dom": "^18.3.1", "react-hook-form": "^7.62.0", "react-i18next": "^15.1.0", "react-imask": "^7.4.0", @@ -65,7 +65,7 @@ "@types/node": "^22.10.5", "@types/prop-types": "^15", "@types/react": "^18.3.12", - "@types/react-dom": "^19.1.9", + "@types/react-dom": "^18.3.1", "@typescript-eslint/eslint-plugin": "^6.20.0", "@typescript-eslint/parser": "^6.20.0", "@vercel/style-guide": "^6.0.0", diff --git a/packages/apps/job-launcher/client/package.json b/packages/apps/job-launcher/client/package.json index ce1d1f9b1f..52199b62c1 100644 --- a/packages/apps/job-launcher/client/package.json +++ b/packages/apps/job-launcher/client/package.json @@ -27,8 +27,8 @@ "file-saver": "^2.0.5", "formik": "^2.4.2", "jwt-decode": "^4.0.0", - "react": "^18.2.0", - "react-dom": "^19.1.1", + "react": "^18.3.1", + "react-dom": "^18.3.1", "react-redux": "^9.1.0", "react-router-dom": "^6.14.1", "recharts": "^2.7.2", @@ -67,7 +67,7 @@ "devDependencies": { "@types/file-saver": "^2.0.7", "@types/react": "^18.3.12", - "@types/react-dom": "^19.1.9", + "@types/react-dom": "^18.3.1", "@types/xml2js": "^0.4.14", "@vitejs/plugin-react": "^4.2.1", "eslint": "^8.55.0", diff --git a/packages/apps/staking/package.json b/packages/apps/staking/package.json index 8a45ec6211..da54e0f22a 100644 --- a/packages/apps/staking/package.json +++ b/packages/apps/staking/package.json @@ -38,7 +38,7 @@ "axios": "^1.7.2", "ethers": "^6.15.0", "react": "^18.3.1", - "react-dom": "^19.1.1", + "react-dom": "^18.3.1", "react-router-dom": "^6.24.1", "serve": "^14.2.4", "viem": "2.x", @@ -46,7 +46,7 @@ }, "devDependencies": { "@types/react": "^18.3.12", - "@types/react-dom": "^19.1.9", + "@types/react-dom": "^18.3.1", "@types/react-router-dom": "^5.3.3", "@typescript-eslint/eslint-plugin": "^7.13.1", "@typescript-eslint/parser": "^7.13.1", diff --git a/yarn.lock b/yarn.lock index 161ec6f452..cd57d60e0b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3993,8 +3993,8 @@ __metadata: "@mui/x-data-grid": "npm:^8.7.0" "@mui/x-date-pickers": "npm:^8.7.0" "@tanstack/react-query": "npm:^5.67.2" - "@types/react": "npm:^18.2.66" - "@types/react-dom": "npm:^19.1.9" + "@types/react": "npm:^18.3.12" + "@types/react-dom": "npm:^18.3.1" "@types/react-router-dom": "npm:^5.3.3" "@types/recharts": "npm:^1.8.29" "@typescript-eslint/eslint-plugin": "npm:^7.2.0" @@ -4011,8 +4011,8 @@ __metadata: eslint-plugin-react-refresh: "npm:^0.4.11" globals: "npm:^16.2.0" prettier: "npm:^3.4.2" - react: "npm:^18.2.0" - react-dom: "npm:^19.1.1" + react: "npm:^18.3.1" + react-dom: "npm:^18.3.1" react-number-format: "npm:^5.4.3" react-router-dom: "npm:^6.23.1" recharts: "npm:^2.13.0-alpha.4" @@ -4081,6 +4081,7 @@ __metadata: "@mui/icons-material": "npm:^7.0.1" "@mui/material": "npm:^5.16.7" "@types/react": "npm:^18.3.12" + "@types/react-dom": "npm:^18.3.1" dotenv: "npm:^17.0.0" eslint: "npm:^8.55.0" eslint-config-react-app: "npm:^7.0.1" @@ -4089,8 +4090,8 @@ __metadata: eslint-plugin-react: "npm:^7.34.3" eslint-plugin-react-hooks: "npm:^5.1.0" prettier: "npm:^3.4.2" - react: "npm:^18.2.0" - react-dom: "npm:^19.1.1" + react: "npm:^18.3.1" + react-dom: "npm:^18.3.1" react-loading-skeleton: "npm:^3.3.1" react-router-dom: "npm:^6.4.3" serve: "npm:^14.2.4" @@ -4138,7 +4139,7 @@ __metadata: "@tanstack/react-query": "npm:^5.67.2" "@tanstack/react-query-persist-client": "npm:^5.80.7" "@types/react": "npm:^18.3.12" - "@types/react-dom": "npm:^19.1.9" + "@types/react-dom": "npm:^18.3.1" "@types/react-router-dom": "npm:^5.3.3" "@typescript-eslint/eslint-plugin": "npm:^7.13.1" "@typescript-eslint/parser": "npm:^7.13.1" @@ -4150,7 +4151,7 @@ __metadata: ethers: "npm:^6.15.0" prettier: "npm:^3.4.2" react: "npm:^18.3.1" - react-dom: "npm:^19.1.1" + react-dom: "npm:^18.3.1" react-router-dom: "npm:^6.24.1" serve: "npm:^14.2.4" typescript: "npm:^5.6.3" @@ -4282,7 +4283,7 @@ __metadata: "@types/node": "npm:^22.10.5" "@types/prop-types": "npm:^15" "@types/react": "npm:^18.3.12" - "@types/react-dom": "npm:^19.1.9" + "@types/react-dom": "npm:^18.3.1" "@typescript-eslint/eslint-plugin": "npm:^6.20.0" "@typescript-eslint/parser": "npm:^6.20.0" "@vercel/style-guide": "npm:^6.0.0" @@ -4306,8 +4307,8 @@ __metadata: prettier: "npm:^3.4.2" prop-types: "npm:^15.8.1" query-string: "npm:^9.0.0" - react: "npm:^18.2.0" - react-dom: "npm:^19.1.1" + react: "npm:^18.3.1" + react-dom: "npm:^18.3.1" react-hook-form: "npm:^7.62.0" react-i18next: "npm:^15.1.0" react-imask: "npm:^7.4.0" @@ -4406,7 +4407,7 @@ __metadata: "@tanstack/react-query-persist-client": "npm:^5.80.7" "@types/file-saver": "npm:^2.0.7" "@types/react": "npm:^18.3.12" - "@types/react-dom": "npm:^19.1.9" + "@types/react-dom": "npm:^18.3.1" "@types/xml2js": "npm:^0.4.14" "@vitejs/plugin-react": "npm:^4.2.1" axios: "npm:^1.1.3" @@ -4424,8 +4425,8 @@ __metadata: formik: "npm:^2.4.2" jwt-decode: "npm:^4.0.0" prettier: "npm:^3.4.2" - react: "npm:^18.2.0" - react-dom: "npm:^19.1.1" + react: "npm:^18.3.1" + react-dom: "npm:^18.3.1" react-redux: "npm:^9.1.0" react-router-dom: "npm:^6.14.1" recharts: "npm:^2.7.2" @@ -4646,7 +4647,7 @@ __metadata: "@tanstack/react-query": "npm:^5.67.2" "@tanstack/react-query-persist-client": "npm:^5.80.7" "@types/react": "npm:^18.3.12" - "@types/react-dom": "npm:^19.1.9" + "@types/react-dom": "npm:^18.3.1" "@types/react-router-dom": "npm:^5.3.3" "@typescript-eslint/eslint-plugin": "npm:^7.13.1" "@typescript-eslint/parser": "npm:^7.13.1" @@ -4658,7 +4659,7 @@ __metadata: ethers: "npm:^6.15.0" prettier: "npm:^3.4.2" react: "npm:^18.3.1" - react-dom: "npm:^19.1.1" + react-dom: "npm:^18.3.1" react-router-dom: "npm:^6.24.1" sass: "npm:^1.89.2" serve: "npm:^14.2.4" @@ -10621,12 +10622,12 @@ __metadata: languageName: node linkType: hard -"@types/react-dom@npm:^19.1.9": - version: 19.1.9 - resolution: "@types/react-dom@npm:19.1.9" +"@types/react-dom@npm:^18.3.1": + version: 18.3.7 + resolution: "@types/react-dom@npm:18.3.7" peerDependencies: - "@types/react": ^19.0.0 - checksum: 10c0/34c8dda86c1590b3ef0e7ecd38f9663a66ba2dd69113ba74fb0adc36b83bbfb8c94c1487a2505282a5f7e5e000d2ebf36f4c0fd41b3b672f5178fd1d4f1f8f58 + "@types/react": ^18.0.0 + checksum: 10c0/8bd309e2c3d1604a28a736a24f96cbadf6c05d5288cfef8883b74f4054c961b6b3a5e997fd5686e492be903c8f3380dba5ec017eff3906b1256529cd2d39603e languageName: node linkType: hard @@ -10669,7 +10670,7 @@ __metadata: languageName: node linkType: hard -"@types/react@npm:^18.2.66, @types/react@npm:^18.3.12": +"@types/react@npm:^18.3.12": version: 18.3.21 resolution: "@types/react@npm:18.3.21" dependencies: @@ -26096,14 +26097,15 @@ __metadata: languageName: node linkType: hard -"react-dom@npm:^19.1.1": - version: 19.1.1 - resolution: "react-dom@npm:19.1.1" +"react-dom@npm:^18.3.1": + version: 18.3.1 + resolution: "react-dom@npm:18.3.1" dependencies: - scheduler: "npm:^0.26.0" + loose-envify: "npm:^1.1.0" + scheduler: "npm:^0.23.2" peerDependencies: - react: ^19.1.1 - checksum: 10c0/8c91198510521299c56e4e8d5e3a4508b2734fb5e52f29eeac33811de64e76fe586ad32c32182e2e84e070d98df67125da346c3360013357228172dbcd20bcdd + react: ^18.3.1 + checksum: 10c0/a752496c1941f958f2e8ac56239172296fcddce1365ce45222d04a1947e0cc5547df3e8447f855a81d6d39f008d7c32eab43db3712077f09e3f67c4874973e85 languageName: node linkType: hard @@ -26284,7 +26286,7 @@ __metadata: languageName: node linkType: hard -"react@npm:^18.2.0, react@npm:^18.3.1": +"react@npm:^18.3.1": version: 18.3.1 resolution: "react@npm:18.3.1" dependencies: @@ -27176,10 +27178,12 @@ __metadata: languageName: node linkType: hard -"scheduler@npm:^0.26.0": - version: 0.26.0 - resolution: "scheduler@npm:0.26.0" - checksum: 10c0/5b8d5bfddaae3513410eda54f2268e98a376a429931921a81b5c3a2873aab7ca4d775a8caac5498f8cbc7d0daeab947cf923dbd8e215d61671f9f4e392d34356 +"scheduler@npm:^0.23.2": + version: 0.23.2 + resolution: "scheduler@npm:0.23.2" + dependencies: + loose-envify: "npm:^1.1.0" + checksum: 10c0/26383305e249651d4c58e6705d5f8425f153211aef95f15161c151f7b8de885f24751b377e4a0b3dd42cce09aad3f87a61dab7636859c0d89b7daf1a1e2a5c78 languageName: node linkType: hard From a55f0d37885b4bd36164c9ebc46bb78ebd987d1b Mon Sep 17 00:00:00 2001 From: Dmitry Nechay Date: Mon, 1 Sep 2025 15:47:02 +0300 Subject: [PATCH 08/10] [Reputation Oracle] feat: validate restore password token as uuid (#3529) --- .../server/src/common/validators/index.ts | 21 ++++++++++++- .../src/modules/auth/dto/password.dto.ts | 18 +++-------- .../src/modules/auth/dto/sign-in.dto.ts | 8 +++-- .../src/modules/auth/dto/sign-up.dto.ts | 5 ++- .../src/modules/user/user.controller.ts | 6 ++-- .../server/src/modules/user/user.dto.ts | 31 ++++++++----------- 6 files changed, 48 insertions(+), 41 deletions(-) diff --git a/packages/apps/reputation-oracle/server/src/common/validators/index.ts b/packages/apps/reputation-oracle/server/src/common/validators/index.ts index 459430a8bb..3b123b8843 100644 --- a/packages/apps/reputation-oracle/server/src/common/validators/index.ts +++ b/packages/apps/reputation-oracle/server/src/common/validators/index.ts @@ -1,6 +1,13 @@ import { applyDecorators } from '@nestjs/common'; import { Transform } from 'class-transformer'; -import { IsEmail, IsEnum, ValidationOptions } from 'class-validator'; +import { + IsEmail, + IsEnum, + IsString, + MaxLength, + MinLength, + ValidationOptions, +} from 'class-validator'; export * from './password'; export * from './web3'; @@ -31,3 +38,15 @@ export function IsLowercasedEnum( }), ); } + +export function IsValidPassword() { + return applyDecorators( + IsString(), + MinLength(8, { + message: 'Password must be at least 8 characters long.', + }), + MaxLength(128, { + message: 'Password must be at most 128 characters', + }), + ); +} diff --git a/packages/apps/reputation-oracle/server/src/modules/auth/dto/password.dto.ts b/packages/apps/reputation-oracle/server/src/modules/auth/dto/password.dto.ts index a1a61c3652..8c40888c4e 100644 --- a/packages/apps/reputation-oracle/server/src/modules/auth/dto/password.dto.ts +++ b/packages/apps/reputation-oracle/server/src/modules/auth/dto/password.dto.ts @@ -1,17 +1,7 @@ -import { applyDecorators } from '@nestjs/common'; import { ApiProperty } from '@nestjs/swagger'; -import { IsString, MinLength } from 'class-validator'; +import { IsString, IsUUID } from 'class-validator'; -import { IsLowercasedEmail } from '@/common/validators'; - -export function ValidPassword() { - return applyDecorators( - IsString(), - MinLength(8, { - message: 'Password must be at least 8 characters long.', - }), - ); -} +import { IsLowercasedEmail, IsValidPassword } from '@/common/validators'; export class ForgotPasswordDto { @ApiProperty() @@ -25,11 +15,11 @@ export class ForgotPasswordDto { export class RestorePasswordDto { @ApiProperty() - @ValidPassword() + @IsValidPassword() password: string; @ApiProperty() - @IsString() + @IsUUID() token: string; @ApiProperty({ name: 'h_captcha_token' }) diff --git a/packages/apps/reputation-oracle/server/src/modules/auth/dto/sign-in.dto.ts b/packages/apps/reputation-oracle/server/src/modules/auth/dto/sign-in.dto.ts index feeeddf728..5c6877abc7 100644 --- a/packages/apps/reputation-oracle/server/src/modules/auth/dto/sign-in.dto.ts +++ b/packages/apps/reputation-oracle/server/src/modules/auth/dto/sign-in.dto.ts @@ -1,7 +1,11 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { IsEthereumAddress, IsOptional, IsString } from 'class-validator'; -import { IsLowercasedEmail, IsValidWeb3Signature } from '@/common/validators'; +import { + IsLowercasedEmail, + IsValidPassword, + IsValidWeb3Signature, +} from '@/common/validators'; export class Web2SignInDto { @ApiProperty() @@ -9,7 +13,7 @@ export class Web2SignInDto { email: string; @ApiProperty() - @IsString() + @IsValidPassword() password: string; @ApiPropertyOptional({ name: 'h_captcha_token' }) diff --git a/packages/apps/reputation-oracle/server/src/modules/auth/dto/sign-up.dto.ts b/packages/apps/reputation-oracle/server/src/modules/auth/dto/sign-up.dto.ts index a09abf7419..b58e06d822 100644 --- a/packages/apps/reputation-oracle/server/src/modules/auth/dto/sign-up.dto.ts +++ b/packages/apps/reputation-oracle/server/src/modules/auth/dto/sign-up.dto.ts @@ -5,18 +5,17 @@ import { UserRole } from '@/common/enums'; import { IsLowercasedEmail, IsLowercasedEnum, + IsValidPassword, IsValidWeb3Signature, } from '@/common/validators'; -import { ValidPassword } from './password.dto'; - export class Web2SignUpDto { @ApiProperty() @IsLowercasedEmail() email: string; @ApiProperty() - @ValidPassword() + @IsValidPassword() password: string; @ApiProperty({ name: 'h_captcha_token' }) diff --git a/packages/apps/reputation-oracle/server/src/modules/user/user.controller.ts b/packages/apps/reputation-oracle/server/src/modules/user/user.controller.ts index f32a2837ad..f544296108 100644 --- a/packages/apps/reputation-oracle/server/src/modules/user/user.controller.ts +++ b/packages/apps/reputation-oracle/server/src/modules/user/user.controller.ts @@ -28,7 +28,7 @@ import { DisableOperatorDto, PrepareSignatureDto, RegisterAddressRequestDto, - SignatureBodyDto, + PreparedSignatureResponseDto, RegisterLabelerResponseDto, EnableOperatorDto, RegistrationInExchangeOracleDto, @@ -146,14 +146,14 @@ export class UserController { @ApiResponse({ status: 200, description: 'Typed structured data object generated successfully', - type: SignatureBodyDto, + type: PreparedSignatureResponseDto, }) @Public() @Post('/prepare-signature') @HttpCode(200) async prepareSignature( @Body() data: PrepareSignatureDto, - ): Promise { + ): Promise { let nonce: string | undefined; if (data.type === SignatureType.SIGNIN) { const user = await this.userService.findOperatorUser(data.address); diff --git a/packages/apps/reputation-oracle/server/src/modules/user/user.dto.ts b/packages/apps/reputation-oracle/server/src/modules/user/user.dto.ts index 1686800868..0efc024c39 100644 --- a/packages/apps/reputation-oracle/server/src/modules/user/user.dto.ts +++ b/packages/apps/reputation-oracle/server/src/modules/user/user.dto.ts @@ -1,5 +1,5 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsEthereumAddress, IsOptional, IsString } from 'class-validator'; +import { IsEthereumAddress, IsString } from 'class-validator'; import { SignatureType } from '@/common/enums'; import { IsLowercasedEnum, IsValidWeb3Signature } from '@/common/validators'; @@ -31,37 +31,32 @@ export class DisableOperatorDto { signature: string; } -export class SignatureBodyDto { +export class PrepareSignatureDto { @ApiProperty() @IsEthereumAddress() + address: string; + + @ApiProperty({ + enum: SignatureType, + }) + @IsLowercasedEnum(SignatureType) + type: SignatureType; +} + +export class PreparedSignatureResponseDto { + @ApiProperty() from: string; @ApiProperty() - @IsEthereumAddress() to: string; @ApiProperty() - @IsString() contents: string; @ApiProperty() - @IsOptional() - @IsString() nonce?: string | undefined; } -export class PrepareSignatureDto { - @ApiProperty() - @IsEthereumAddress() - address: string; - - @ApiProperty({ - enum: SignatureType, - }) - @IsLowercasedEnum(SignatureType) - type: SignatureType; -} - export class RegistrationInExchangeOracleDto { @ApiProperty({ name: 'oracle_address', From cbe51dfbc2c129ce2bd650ee64959667733abaf6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Sep 2025 16:30:50 +0200 Subject: [PATCH 09/10] chore(deps-dev): bump @openzeppelin/hardhat-upgrades from 3.9.0 to 3.9.1 (#3534) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- packages/core/package.json | 2 +- yarn.lock | 29 ++++++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index 4e91829196..2b898f5bc1 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -60,7 +60,7 @@ "@nomicfoundation/hardhat-verify": "^2.0.11", "@openzeppelin/contracts": "5.0.2", "@openzeppelin/contracts-upgradeable": "^4.9.2", - "@openzeppelin/hardhat-upgrades": "^3.3.0", + "@openzeppelin/hardhat-upgrades": "^3.9.1", "@tenderly/hardhat-tenderly": "^2.0.1", "@typechain/ethers-v6": "^0.5.1", "@typechain/hardhat": "^9.1.0", diff --git a/yarn.lock b/yarn.lock index cd57d60e0b..7cfb07d651 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3948,7 +3948,7 @@ __metadata: "@nomicfoundation/hardhat-verify": "npm:^2.0.11" "@openzeppelin/contracts": "npm:5.0.2" "@openzeppelin/contracts-upgradeable": "npm:^4.9.2" - "@openzeppelin/hardhat-upgrades": "npm:^3.3.0" + "@openzeppelin/hardhat-upgrades": "npm:^3.9.1" "@tenderly/hardhat-tenderly": "npm:^2.0.1" "@typechain/ethers-v6": "npm:^0.5.1" "@typechain/hardhat": "npm:^9.1.0" @@ -7492,6 +7492,33 @@ __metadata: languageName: node linkType: hard +"@openzeppelin/hardhat-upgrades@npm:^3.9.1": + version: 3.9.1 + resolution: "@openzeppelin/hardhat-upgrades@npm:3.9.1" + dependencies: + "@openzeppelin/defender-sdk-base-client": "npm:^2.1.0" + "@openzeppelin/defender-sdk-deploy-client": "npm:^2.1.0" + "@openzeppelin/defender-sdk-network-client": "npm:^2.1.0" + "@openzeppelin/upgrades-core": "npm:^1.41.0" + chalk: "npm:^4.1.0" + debug: "npm:^4.1.1" + ethereumjs-util: "npm:^7.1.5" + proper-lockfile: "npm:^4.1.1" + undici: "npm:^6.11.1" + peerDependencies: + "@nomicfoundation/hardhat-ethers": ^3.0.6 + "@nomicfoundation/hardhat-verify": ^2.0.14 + ethers: ^6.6.0 + hardhat: ^2.24.1 + peerDependenciesMeta: + "@nomicfoundation/hardhat-verify": + optional: true + bin: + migrate-oz-cli-project: dist/scripts/migrate-oz-cli-project.js + checksum: 10c0/6b453bd1af116bc785f040661fe4e51403a9ab82d76e894346336b66f85dbd0d46751639bf325cef6ce030d41442551e92b8925429fff86fce900cd3d655028d + languageName: node + linkType: hard + "@openzeppelin/upgrades-core@npm:^1.32.2, @openzeppelin/upgrades-core@npm:^1.41.0": version: 1.44.0 resolution: "@openzeppelin/upgrades-core@npm:1.44.0" From bb1325c1411458e78cc41ffaa6b61254194c6ab9 Mon Sep 17 00:00:00 2001 From: portuu3 <61605646+portuu3@users.noreply.github.com> Date: Wed, 3 Sep 2025 17:24:23 +0200 Subject: [PATCH 10/10] [Job Launcher] Job type audio attribute annotation (#3540) --- .../Jobs/Create/AudinoJobRequestForm.tsx | 3 + .../src/components/Jobs/Create/helpers.ts | 3 +- .../job-launcher/client/src/types/index.ts | 1 + .../server/src/common/enums/job.ts | 1 + .../server/src/common/utils/storage.ts | 1 + ...56902190591-addAudioAttributeAnnotation.ts | 60 +++++++++++++++++++ .../modules/cron-job/cron-job.service.spec.ts | 2 + .../src/modules/job/job.service.spec.ts | 7 +++ .../server/src/modules/job/job.service.ts | 29 ++++++++- .../src/modules/manifest/manifest.dto.ts | 5 +- .../modules/manifest/manifest.service.spec.ts | 38 +++++++++++- .../src/modules/manifest/manifest.service.ts | 5 +- .../routing-protocol.service.ts | 4 +- 13 files changed, 148 insertions(+), 11 deletions(-) create mode 100644 packages/apps/job-launcher/server/src/database/migrations/1756902190591-addAudioAttributeAnnotation.ts diff --git a/packages/apps/job-launcher/client/src/components/Jobs/Create/AudinoJobRequestForm.tsx b/packages/apps/job-launcher/client/src/components/Jobs/Create/AudinoJobRequestForm.tsx index df5705cff3..5d7101a397 100644 --- a/packages/apps/job-launcher/client/src/components/Jobs/Create/AudinoJobRequestForm.tsx +++ b/packages/apps/job-launcher/client/src/components/Jobs/Create/AudinoJobRequestForm.tsx @@ -178,6 +178,9 @@ export const AudinoJobRequestForm = () => { Audio transcription + + Audio attribute annotation + diff --git a/packages/apps/job-launcher/client/src/components/Jobs/Create/helpers.ts b/packages/apps/job-launcher/client/src/components/Jobs/Create/helpers.ts index 6f79f33075..cbea2eaf54 100644 --- a/packages/apps/job-launcher/client/src/components/Jobs/Create/helpers.ts +++ b/packages/apps/job-launcher/client/src/components/Jobs/Create/helpers.ts @@ -5,7 +5,6 @@ import { StorageProviders, GCSRegions, AWSRegions, - AudinoJobType, } from '../../../types'; export const mapCvatFormValues = ( @@ -76,7 +75,7 @@ export const mapAudinoFormValues = ( const { audinoRequest } = jobRequest; return { - type: audinoRequest?.type || AudinoJobType.AUDIO_TRANSCRIPTION, + type: audinoRequest?.type, labels: audinoRequest?.labels?.map((label) => label.name) || [], description: audinoRequest?.description || '', qualifications: audinoRequest?.qualifications diff --git a/packages/apps/job-launcher/client/src/types/index.ts b/packages/apps/job-launcher/client/src/types/index.ts index db19cc899d..a0813f42a0 100644 --- a/packages/apps/job-launcher/client/src/types/index.ts +++ b/packages/apps/job-launcher/client/src/types/index.ts @@ -125,6 +125,7 @@ export enum HCaptchaJobType { export enum AudinoJobType { AUDIO_TRANSCRIPTION = 'audio_transcription', + AUDIO_ATTRIBUTE_ANNOTATION = 'audio_attribute_annotation', } export type FortuneRequest = { diff --git a/packages/apps/job-launcher/server/src/common/enums/job.ts b/packages/apps/job-launcher/server/src/common/enums/job.ts index 10b9138107..f7168e9fd8 100644 --- a/packages/apps/job-launcher/server/src/common/enums/job.ts +++ b/packages/apps/job-launcher/server/src/common/enums/job.ts @@ -45,6 +45,7 @@ export enum CvatJobType { export enum AudinoJobType { AUDIO_TRANSCRIPTION = 'audio_transcription', + AUDIO_ATTRIBUTE_ANNOTATION = 'audio_attribute_annotation', } export const JobType = [ diff --git a/packages/apps/job-launcher/server/src/common/utils/storage.ts b/packages/apps/job-launcher/server/src/common/utils/storage.ts index d9f897ca24..b5278915ef 100644 --- a/packages/apps/job-launcher/server/src/common/utils/storage.ts +++ b/packages/apps/job-launcher/server/src/common/utils/storage.ts @@ -24,6 +24,7 @@ export function generateBucketUrl( CvatJobType.IMAGE_BOXES_FROM_POINTS, CvatJobType.IMAGE_SKELETONS_FROM_BOXES, AudinoJobType.AUDIO_TRANSCRIPTION, + AudinoJobType.AUDIO_ATTRIBUTE_ANNOTATION, ] as JobRequestType[] ).includes(jobType) && storageData.provider != StorageProviders.AWS && diff --git a/packages/apps/job-launcher/server/src/database/migrations/1756902190591-addAudioAttributeAnnotation.ts b/packages/apps/job-launcher/server/src/database/migrations/1756902190591-addAudioAttributeAnnotation.ts new file mode 100644 index 0000000000..bcdf41aca0 --- /dev/null +++ b/packages/apps/job-launcher/server/src/database/migrations/1756902190591-addAudioAttributeAnnotation.ts @@ -0,0 +1,60 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddAudioAttributeAnnotation1756902190591 + implements MigrationInterface +{ + name = 'AddAudioAttributeAnnotation1756902190591'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TYPE "hmt"."jobs_request_type_enum" + RENAME TO "jobs_request_type_enum_old" + `); + await queryRunner.query(` + CREATE TYPE "hmt"."jobs_request_type_enum" AS ENUM( + 'image_points', + 'image_polygons', + 'image_boxes', + 'image_boxes_from_points', + 'image_skeletons_from_boxes', + 'fortune', + 'hcaptcha', + 'audio_transcription', + 'audio_attribute_annotation' + ) + `); + await queryRunner.query(` + ALTER TABLE "hmt"."jobs" + ALTER COLUMN "request_type" TYPE "hmt"."jobs_request_type_enum" USING "request_type"::"text"::"hmt"."jobs_request_type_enum" + `); + await queryRunner.query(` + DROP TYPE "hmt"."jobs_request_type_enum_old" + `); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE TYPE "hmt"."jobs_request_type_enum_old" AS ENUM( + 'audio_transcription', + 'fortune', + 'hcaptcha', + 'image_boxes', + 'image_boxes_from_points', + 'image_points', + 'image_polygons', + 'image_skeletons_from_boxes' + ) + `); + await queryRunner.query(` + ALTER TABLE "hmt"."jobs" + ALTER COLUMN "request_type" TYPE "hmt"."jobs_request_type_enum_old" USING "request_type"::"text"::"hmt"."jobs_request_type_enum_old" + `); + await queryRunner.query(` + DROP TYPE "hmt"."jobs_request_type_enum" + `); + await queryRunner.query(` + ALTER TYPE "hmt"."jobs_request_type_enum_old" + RENAME TO "jobs_request_type_enum" + `); + } +} diff --git a/packages/apps/job-launcher/server/src/modules/cron-job/cron-job.service.spec.ts b/packages/apps/job-launcher/server/src/modules/cron-job/cron-job.service.spec.ts index 82643408eb..9890a2505f 100644 --- a/packages/apps/job-launcher/server/src/modules/cron-job/cron-job.service.spec.ts +++ b/packages/apps/job-launcher/server/src/modules/cron-job/cron-job.service.spec.ts @@ -706,6 +706,7 @@ describe('CronJobService', () => { escrowAddress: MOCK_ADDRESS, chainId: ChainId.LOCALHOST, retriesCount: 0, + requestType: FortuneJobType.FORTUNE, }; jobEntityMock2 = { @@ -717,6 +718,7 @@ describe('CronJobService', () => { escrowAddress: MOCK_ADDRESS, chainId: ChainId.LOCALHOST, retriesCount: 0, + requestType: CvatJobType.IMAGE_POINTS, }; findJobMock = jest diff --git a/packages/apps/job-launcher/server/src/modules/job/job.service.spec.ts b/packages/apps/job-launcher/server/src/modules/job/job.service.spec.ts index 85b530fd0f..65012b5dc2 100644 --- a/packages/apps/job-launcher/server/src/modules/job/job.service.spec.ts +++ b/packages/apps/job-launcher/server/src/modules/job/job.service.spec.ts @@ -1392,6 +1392,13 @@ describe('JobService', () => { expect(jobService.getOracleType(jobType)).toBe(OracleType.HCAPTCHA); }, ); + + it('should throw an error for unsupported job type', () => { + const unsupportedJobType = 'UNSUPPORTED_JOB_TYPE' as any; + expect(() => jobService.getOracleType(unsupportedJobType)).toThrow( + new ConflictError(ErrorJob.InvalidRequestType), + ); + }); }); describe('processEscrowCancellation', () => { diff --git a/packages/apps/job-launcher/server/src/modules/job/job.service.ts b/packages/apps/job-launcher/server/src/modules/job/job.service.ts index 867ce88a90..574e58c01d 100644 --- a/packages/apps/job-launcher/server/src/modules/job/job.service.ts +++ b/packages/apps/job-launcher/server/src/modules/job/job.service.ts @@ -48,6 +48,7 @@ import { add, div, max, mul } from '../../common/utils/decimal'; import { getTokenDecimals } from '../../common/utils/tokens'; import { CronJobRepository } from '../cron-job/cron-job.repository'; import { + AudinoManifestDto, CvatManifestDto, FortuneManifestDto, HCaptchaManifestDto, @@ -132,6 +133,9 @@ export class JobService { [AudinoJobType.AUDIO_TRANSCRIPTION]: { getTrustedHandlers: () => [], }, + [AudinoJobType.AUDIO_ATTRIBUTE_ANNOTATION]: { + getTrustedHandlers: () => [], + }, }; public async createJob( @@ -303,6 +307,7 @@ export class JobService { ( [ AudinoJobType.AUDIO_TRANSCRIPTION, + AudinoJobType.AUDIO_ATTRIBUTE_ANNOTATION, FortuneJobType.FORTUNE, HCaptchaJobType.HCAPTCHA, ] as JobRequestType[] @@ -655,11 +660,16 @@ export class JobService { return OracleType.FORTUNE; } else if (requestType === HCaptchaJobType.HCAPTCHA) { return OracleType.HCAPTCHA; - } else if (requestType === AudinoJobType.AUDIO_TRANSCRIPTION) { + } else if ( + Object.values(AudinoJobType).includes(requestType as AudinoJobType) + ) { return OracleType.AUDINO; - } else { + } else if ( + Object.values(CvatJobType).includes(requestType as CvatJobType) + ) { return OracleType.CVAT; } + throw new ConflictError(ErrorJob.InvalidRequestType); } public async processEscrowCancellation( @@ -790,6 +800,21 @@ export class JobService { qualifications: manifest.qualifications, }), }; + } else if ( + Object.values(AudinoJobType).includes( + jobEntity.requestType as AudinoJobType, + ) + ) { + const manifest = manifestData as AudinoManifestDto; + specificManifestDetails = { + requestType: manifest.annotation?.type, + submissionsRequired: manifest.annotation?.segment_duration, + description: manifest.annotation?.description, + ...(manifest.annotation?.qualifications && + manifest.annotation?.qualifications?.length > 0 && { + qualifications: manifest.annotation?.qualifications, + }), + }; } else { const manifest = manifestData as CvatManifestDto; specificManifestDetails = { diff --git a/packages/apps/job-launcher/server/src/modules/manifest/manifest.dto.ts b/packages/apps/job-launcher/server/src/modules/manifest/manifest.dto.ts index 096d21fcc0..737c9995b9 100644 --- a/packages/apps/job-launcher/server/src/modules/manifest/manifest.dto.ts +++ b/packages/apps/job-launcher/server/src/modules/manifest/manifest.dto.ts @@ -1,6 +1,5 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { - Equals, IsArray, IsEthereumAddress, IsNotEmpty, @@ -160,8 +159,8 @@ class AudinoAnnotation { @IsUrl() public user_guide: string; - @Equals(AudinoJobType.AUDIO_TRANSCRIPTION) - public type: AudinoJobType.AUDIO_TRANSCRIPTION; + @IsEnumCaseInsensitive(AudinoJobType) + public type: AudinoJobType; @IsNumber() @IsPositive() diff --git a/packages/apps/job-launcher/server/src/modules/manifest/manifest.service.spec.ts b/packages/apps/job-launcher/server/src/modules/manifest/manifest.service.spec.ts index 0ae790de59..9ceb072d16 100644 --- a/packages/apps/job-launcher/server/src/modules/manifest/manifest.service.spec.ts +++ b/packages/apps/job-launcher/server/src/modules/manifest/manifest.service.spec.ts @@ -302,8 +302,8 @@ describe('ManifestService', () => { }); describe('createAudinoManifest', () => { - it('should create an Audino manifest successfully', async () => { - const mockDto = createJobAudinoDto(); // Use the helper function + it('should create an Audino manifest for audio transcription successfully', async () => { + const mockDto = createJobAudinoDto(); const mockRequestType = AudinoJobType.AUDIO_TRANSCRIPTION; const mockTokenFundAmount = faker.number.int({ min: 1, max: 1000 }); const mockTokenFundDecimals = faker.number.int({ min: 1, max: 18 }); @@ -335,6 +335,40 @@ describe('ManifestService', () => { }, }); }); + + it('should create an Audino manifest for audio attribute annotation successfully', async () => { + const mockDto = createJobAudinoDto(); + const mockRequestType = AudinoJobType.AUDIO_ATTRIBUTE_ANNOTATION; + const mockTokenFundAmount = faker.number.int({ min: 1, max: 1000 }); + const mockTokenFundDecimals = faker.number.int({ min: 1, max: 18 }); + + const result = await manifestService.createManifest( + mockDto as any, + mockRequestType, + mockTokenFundAmount, + mockTokenFundDecimals, + ); + + expect(result).toEqual({ + annotation: { + description: mockDto.requesterDescription, + labels: mockDto.labels, + qualifications: mockDto.qualifications || [], + type: mockRequestType, + user_guide: mockDto.userGuide, + segment_duration: mockDto.segmentDuration, + }, + data: { + data_url: generateBucketUrl(mockDto.data.dataset, mockRequestType) + .href, + }, + validation: { + gt_url: generateBucketUrl(mockDto.groundTruth, mockRequestType) + .href, + min_quality: mockDto.minQuality, + }, + }); + }); }); describe('createHCaptchaManifest', () => { diff --git a/packages/apps/job-launcher/server/src/modules/manifest/manifest.service.ts b/packages/apps/job-launcher/server/src/modules/manifest/manifest.service.ts index d497c898f2..29a56dbf2e 100644 --- a/packages/apps/job-launcher/server/src/modules/manifest/manifest.service.ts +++ b/packages/apps/job-launcher/server/src/modules/manifest/manifest.service.ts @@ -116,6 +116,7 @@ export class ManifestService { ); case AudinoJobType.AUDIO_TRANSCRIPTION: + case AudinoJobType.AUDIO_ATTRIBUTE_ANNOTATION: return this.createAudinoManifest(dto as JobAudinoDto, requestType); default: @@ -612,7 +613,9 @@ export class ManifestService { } else if (requestType === HCaptchaJobType.HCAPTCHA) { return; dtoCheck = new HCaptchaManifestDto(); - } else if (requestType === AudinoJobType.AUDIO_TRANSCRIPTION) { + } else if ( + Object.values(AudinoJobType).includes(requestType as AudinoJobType) + ) { dtoCheck = new AudinoManifestDto(); } else { dtoCheck = new CvatManifestDto(); diff --git a/packages/apps/job-launcher/server/src/modules/routing-protocol/routing-protocol.service.ts b/packages/apps/job-launcher/server/src/modules/routing-protocol/routing-protocol.service.ts index f32b1f2160..c18ea42f55 100644 --- a/packages/apps/job-launcher/server/src/modules/routing-protocol/routing-protocol.service.ts +++ b/packages/apps/job-launcher/server/src/modules/routing-protocol/routing-protocol.service.ts @@ -171,7 +171,9 @@ export class RoutingProtocolService { exchangeOracle: this.web3ConfigService.cvatExchangeOracleAddress, recordingOracle: this.web3ConfigService.cvatRecordingOracleAddress, }; - } else if (jobType === AudinoJobType.AUDIO_TRANSCRIPTION) { + } else if ( + Object.values(AudinoJobType).includes(jobType as AudinoJobType) + ) { return { reputationOracle: this.web3ConfigService.reputationOracleAddress, exchangeOracle: this.web3ConfigService.audinoExchangeOracleAddress,