From 0ce47b2494f6c103f5948f1f5b2a81e01a4fb0e1 Mon Sep 17 00:00:00 2001 From: Dmitry Nechay Date: Mon, 23 Jun 2025 13:41:13 +0300 Subject: [PATCH 1/4] fix: calculate job bounty for audino from fund amount --- .../server/src/common/types/manifest.ts | 1 - .../server/src/modules/abuse/abuse.service.ts | 2 +- .../audino-payouts-calculator.ts | 19 ++++++++----------- .../cvat-payouts-calculator.ts | 2 +- .../fortune-payouts-calculator.ts | 2 +- 5 files changed, 11 insertions(+), 15 deletions(-) diff --git a/packages/apps/reputation-oracle/server/src/common/types/manifest.ts b/packages/apps/reputation-oracle/server/src/common/types/manifest.ts index 11f12b4d8e..bcc7d3964e 100644 --- a/packages/apps/reputation-oracle/server/src/common/types/manifest.ts +++ b/packages/apps/reputation-oracle/server/src/common/types/manifest.ts @@ -23,7 +23,6 @@ export type AudinoManifest = { validation: { min_quality: number; }; - job_bounty: string; }; export type JobManifest = FortuneManifest | CvatManifest | AudinoManifest; diff --git a/packages/apps/reputation-oracle/server/src/modules/abuse/abuse.service.ts b/packages/apps/reputation-oracle/server/src/modules/abuse/abuse.service.ts index 04c3b30015..80853720eb 100644 --- a/packages/apps/reputation-oracle/server/src/modules/abuse/abuse.service.ts +++ b/packages/apps/reputation-oracle/server/src/modules/abuse/abuse.service.ts @@ -65,7 +65,7 @@ export class AbuseService { data.slasher, data.staker, data.escrowAddress, - BigInt(ethers.parseUnits(data.amount.toString(), 'ether')), + BigInt(ethers.parseUnits(data.amount.toString(), 18)), ); } diff --git a/packages/apps/reputation-oracle/server/src/modules/escrow-completion/payouts-calculation/audino-payouts-calculator.ts b/packages/apps/reputation-oracle/server/src/modules/escrow-completion/payouts-calculation/audino-payouts-calculator.ts index 0c6921ed16..d1f276a572 100644 --- a/packages/apps/reputation-oracle/server/src/modules/escrow-completion/payouts-calculation/audino-payouts-calculator.ts +++ b/packages/apps/reputation-oracle/server/src/modules/escrow-completion/payouts-calculation/audino-payouts-calculator.ts @@ -1,9 +1,8 @@ -import { EscrowClient } from '@human-protocol/sdk'; +import { EscrowClient, EscrowUtils } from '@human-protocol/sdk'; import { Injectable } from '@nestjs/common'; -import { ethers } from 'ethers'; import type { OverrideProperties } from 'type-fest'; -import { AUDINO_RESULTS_ANNOTATIONS_FILENAME } from '../../../common/constants'; +import { AUDINO_VALIDATION_META_FILENAME } from '../../../common/constants'; import { AudinoAnnotationMeta, AudinoManifest } from '../../../common/types'; import { StorageService } from '../../storage'; @@ -28,7 +27,6 @@ export class AudinoPayoutsCalculator implements EscrowPayoutsCalculator { ) {} async calculate({ - manifest, chainId, escrowAddress, }: CalculateAudinoPayoutsInput): Promise { @@ -40,24 +38,23 @@ export class AudinoPayoutsCalculator implements EscrowPayoutsCalculator { const annotations = await this.storageService.downloadJsonLikeData( - `${intermediateResultsUrl}/${AUDINO_RESULTS_ANNOTATIONS_FILENAME}`, + `${intermediateResultsUrl}/${AUDINO_VALIDATION_META_FILENAME}`, ); if (annotations.jobs.length === 0 || annotations.results.length === 0) { throw new Error('Invalid annotation meta'); } - const jobBountyValue = ethers.parseUnits(manifest.job_bounty, 18); - const workersBounties = new Map(); + const escrowData = await EscrowUtils.getEscrow(chainId, escrowAddress); + const jobBountyValue = + BigInt(escrowData.totalFundedAmount) / BigInt(annotations.jobs.length); + const workersBounties = new Map(); for (const job of annotations.jobs) { const jobFinalResult = annotations.results.find( (result) => result.id === job.final_result_id, ); - if ( - jobFinalResult - // && jobFinalResult.annotation_quality >= manifest.validation.min_quality - ) { + if (jobFinalResult) { const workerAddress = jobFinalResult.annotator_wallet_address; const currentWorkerBounty = workersBounties.get(workerAddress) || 0n; diff --git a/packages/apps/reputation-oracle/server/src/modules/escrow-completion/payouts-calculation/cvat-payouts-calculator.ts b/packages/apps/reputation-oracle/server/src/modules/escrow-completion/payouts-calculation/cvat-payouts-calculator.ts index 3131c5a147..87f3c1f36b 100644 --- a/packages/apps/reputation-oracle/server/src/modules/escrow-completion/payouts-calculation/cvat-payouts-calculator.ts +++ b/packages/apps/reputation-oracle/server/src/modules/escrow-completion/payouts-calculation/cvat-payouts-calculator.ts @@ -48,7 +48,7 @@ export class CvatPayoutsCalculator implements EscrowPayoutsCalculator { } const jobBountyValue = ethers.parseUnits(manifest.job_bounty, 18); - const workersBounties = new Map(); + const workersBounties = new Map(); for (const job of annotations.jobs) { const jobFinalResult = annotations.results.find( diff --git a/packages/apps/reputation-oracle/server/src/modules/escrow-completion/payouts-calculation/fortune-payouts-calculator.ts b/packages/apps/reputation-oracle/server/src/modules/escrow-completion/payouts-calculation/fortune-payouts-calculator.ts index ffeb96261c..e94e823dab 100644 --- a/packages/apps/reputation-oracle/server/src/modules/escrow-completion/payouts-calculation/fortune-payouts-calculator.ts +++ b/packages/apps/reputation-oracle/server/src/modules/escrow-completion/payouts-calculation/fortune-payouts-calculator.ts @@ -35,7 +35,7 @@ export class FortunePayoutsCalculator implements EscrowPayoutsCalculator { .map((item) => item.workerAddress); const payoutAmount = - BigInt(ethers.parseUnits(manifest.fundAmount.toString(), 'ether')) / + ethers.parseUnits(manifest.fundAmount.toString(), 18) / BigInt(recipients.length); return recipients.map((recipient) => ({ From ce5f63b1416a77dfef65e87a04ee40a309c7991a Mon Sep 17 00:00:00 2001 From: Dmitry Nechay Date: Mon, 23 Jun 2025 14:34:34 +0300 Subject: [PATCH 2/4] feat: show audino oracle only if enabled --- .../worker/services/oracles.service.ts | 18 ++----------- .../src/common/utils/jwt-token.model.ts | 2 +- .../h-captcha/spec/h-captcha.fixtures.ts | 1 + .../model/oracle-discovery.model.ts | 10 +++---- .../oracle-discovery.controller.ts | 27 +++++++++++++++++-- .../oracle-discovery.service.ts | 3 ++- .../spec/oracle-discovery.controller.spec.ts | 10 ++++--- .../spec/oracle-discovery.fixture.ts | 4 +++ 8 files changed, 47 insertions(+), 28 deletions(-) diff --git a/packages/apps/human-app/frontend/src/modules/worker/services/oracles.service.ts b/packages/apps/human-app/frontend/src/modules/worker/services/oracles.service.ts index bbc7632a36..807a76950e 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/services/oracles.service.ts +++ b/packages/apps/human-app/frontend/src/modules/worker/services/oracles.service.ts @@ -11,6 +11,7 @@ const OracleSchema = z.object({ address: z.string(), chainId: z.number(), role: z.string(), + name: z.string(), url: z.string(), jobTypes: z.array(z.string()), registrationNeeded: z.boolean().optional().nullable(), @@ -23,21 +24,6 @@ export type Oracle = OracleBase & { name: string; }; -const OracleNameToUrls = { - CVAT: [ - 'https://stg-exchange-oracle.humanprotocol.org', - 'https://exchange-oracle.humanprotocol.org', - ], - Fortune: ['https://stg-fortune-exchange-oracle-server.humanprotocol.org'], -} as const; - -const oracleUrlToNameMap = new Map(); -for (const [oracleName, oracleUrls] of Object.entries(OracleNameToUrls)) { - for (const oracleUrl of oracleUrls) { - oracleUrlToNameMap.set(oracleUrl, oracleName); - } -} - const isTestnet = env.VITE_NETWORK === 'testnet'; const H_CAPTCHA_ORACLE: Oracle = { @@ -80,7 +66,7 @@ async function getOracles(selectedJobTypes: string[]) { oracles = oracles.concat( results.map((oracle: OracleBase) => ({ ...oracle, - name: oracleUrlToNameMap.get(oracle.url) ?? '', + name: oracle.name ? oracle.name.split(' ')[0] : '', })) ); } diff --git a/packages/apps/human-app/server/src/common/utils/jwt-token.model.ts b/packages/apps/human-app/server/src/common/utils/jwt-token.model.ts index c330d54fd6..bad884b4b6 100644 --- a/packages/apps/human-app/server/src/common/utils/jwt-token.model.ts +++ b/packages/apps/human-app/server/src/common/utils/jwt-token.model.ts @@ -12,7 +12,7 @@ export class JwtUserData { @AutoMap() reputation_network: string; @AutoMap() - qualifications?: string[]; + qualifications: string[]; @AutoMap() site_key: string; @AutoMap() diff --git a/packages/apps/human-app/server/src/modules/h-captcha/spec/h-captcha.fixtures.ts b/packages/apps/human-app/server/src/modules/h-captcha/spec/h-captcha.fixtures.ts index 5476814b38..5696a6d2cd 100644 --- a/packages/apps/human-app/server/src/modules/h-captcha/spec/h-captcha.fixtures.ts +++ b/packages/apps/human-app/server/src/modules/h-captcha/spec/h-captcha.fixtures.ts @@ -74,6 +74,7 @@ export const jwtUserDataFixture: JwtUserData = { wallet_address: POLYGON_WALLET_ADDR, email: EMAIL, kyc_status: 'approved', + qualifications: [], site_key: H_CAPTCHA_SITE_KEY, reputation_network: REPUTATION_NETWORK, iat: IAT, diff --git a/packages/apps/human-app/server/src/modules/oracle-discovery/model/oracle-discovery.model.ts b/packages/apps/human-app/server/src/modules/oracle-discovery/model/oracle-discovery.model.ts index a306dc0b3c..e7269b49f0 100644 --- a/packages/apps/human-app/server/src/modules/oracle-discovery/model/oracle-discovery.model.ts +++ b/packages/apps/human-app/server/src/modules/oracle-discovery/model/oracle-discovery.model.ts @@ -72,11 +72,11 @@ export class DiscoveredOracle implements IOperator { @ApiPropertyOptional({ description: 'Website of the operator' }) website?: string; - @ApiPropertyOptional({ description: 'URL of the oracle operator' }) + @ApiProperty({ description: 'URL of the oracle operator' }) url: string; - @ApiPropertyOptional({ description: 'Role of the oracle operator' }) - role?: string; + @ApiProperty({ description: 'Role of the oracle operator' }) + role: string; @ApiPropertyOptional({ type: [String], @@ -98,8 +98,8 @@ export class DiscoveredOracle implements IOperator { }) reputationNetworks?: string[]; - @ApiPropertyOptional({ description: 'Name of the operator' }) - name?: string; + @ApiProperty({ description: 'Name of the operator' }) + name: string; @ApiPropertyOptional({ description: 'Category of the operator' }) category?: string; diff --git a/packages/apps/human-app/server/src/modules/oracle-discovery/oracle-discovery.controller.ts b/packages/apps/human-app/server/src/modules/oracle-discovery/oracle-discovery.controller.ts index 9e442a620d..69ff5d4eb3 100644 --- a/packages/apps/human-app/server/src/modules/oracle-discovery/oracle-discovery.controller.ts +++ b/packages/apps/human-app/server/src/modules/oracle-discovery/oracle-discovery.controller.ts @@ -7,7 +7,12 @@ import { UsePipes, ValidationPipe, } from '@nestjs/common'; -import { ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; +import { + ApiBearerAuth, + ApiOkResponse, + ApiOperation, + ApiTags, +} from '@nestjs/swagger'; import { OracleDiscoveryService } from './oracle-discovery.service'; import { GetOraclesCommand, @@ -17,6 +22,8 @@ import { import { InjectMapper } from '@automapper/nestjs'; import { Mapper } from '@automapper/core'; import { EnvironmentConfigService } from '../../common/config/environment-config.service'; +import { JwtPayload } from '../../common/config/params-decorators'; +import { JwtUserData } from '../../common/utils/jwt-token.model'; @Controller() export class OracleDiscoveryController { @@ -25,7 +32,9 @@ export class OracleDiscoveryController { private readonly environmentConfigService: EnvironmentConfigService, @InjectMapper() private readonly mapper: Mapper, ) {} + @ApiTags('Oracle-Discovery') + @ApiBearerAuth() @Get('/oracles') @ApiOperation({ summary: 'Oracles discovery' }) @ApiOkResponse({ @@ -34,6 +43,7 @@ export class OracleDiscoveryController { }) @UsePipes(new ValidationPipe()) public async getOracles( + @JwtPayload() jwtPayload: JwtUserData, @Query() query: GetOraclesQuery, ): Promise { if (!this.environmentConfigService.jobsDiscoveryFlag) { @@ -43,6 +53,19 @@ export class OracleDiscoveryController { ); } const command = this.mapper.map(query, GetOraclesQuery, GetOraclesCommand); - return await this.oracleDiscoveryService.getOracles(command); + const oracles = await this.oracleDiscoveryService.getOracles(command); + + const isAudinoAvailableForUser = + jwtPayload.qualifications.includes('audino'); + + return oracles.filter((oracle) => { + const isAudinoOracle = oracle.jobTypes.includes('audio_transcription'); + + if (isAudinoOracle) { + return isAudinoAvailableForUser; + } else { + return true; + } + }); } } diff --git a/packages/apps/human-app/server/src/modules/oracle-discovery/oracle-discovery.service.ts b/packages/apps/human-app/server/src/modules/oracle-discovery/oracle-discovery.service.ts index 3508b1ceea..898acc2612 100644 --- a/packages/apps/human-app/server/src/modules/oracle-discovery/oracle-discovery.service.ts +++ b/packages/apps/human-app/server/src/modules/oracle-discovery/oracle-discovery.service.ts @@ -90,6 +90,7 @@ export class OracleDiscoveryService { new DiscoveredOracle({ id: exchangeOracle.id, address: exchangeOracle.address, + name: exchangeOracle.name, role: exchangeOracle.role, url: exchangeOracle.url, jobTypes: exchangeOracle.jobTypes, @@ -128,7 +129,7 @@ export class OracleDiscoveryService { operator: IOperator, possibleJobTypes: string[], ): operator is DiscoveredOracle { - if (!operator.url) { + if (!operator.url || !operator.name || !operator.role) { return false; } diff --git a/packages/apps/human-app/server/src/modules/oracle-discovery/spec/oracle-discovery.controller.spec.ts b/packages/apps/human-app/server/src/modules/oracle-discovery/spec/oracle-discovery.controller.spec.ts index 58df22e460..973ad32b76 100644 --- a/packages/apps/human-app/server/src/modules/oracle-discovery/spec/oracle-discovery.controller.spec.ts +++ b/packages/apps/human-app/server/src/modules/oracle-discovery/spec/oracle-discovery.controller.spec.ts @@ -63,8 +63,10 @@ describe('OracleDiscoveryController', () => { const dtoFixture = { selected_job_types: ['job-type-1', 'job-type-2'], } as GetOraclesQuery; - const result: DiscoveredOracle[] = - await controller.getOracles(dtoFixture); + const result: DiscoveredOracle[] = await controller.getOracles( + { qualifications: [] } as any, + dtoFixture, + ); const expectedResponse = generateOracleDiscoveryResponseBody(); expect(serviceMock.getOracles).toHaveBeenCalled(); expect(result).toEqual(expectedResponse); @@ -77,7 +79,9 @@ describe('OracleDiscoveryController', () => { (configServiceMock as any).jobsDiscoveryFlag = false; - await expect(controller.getOracles(dtoFixture)).rejects.toThrow( + await expect( + controller.getOracles({ qualifications: [] } as any, dtoFixture), + ).rejects.toThrow( new HttpException( 'Oracles discovery is disabled', HttpStatus.FORBIDDEN, diff --git a/packages/apps/human-app/server/src/modules/oracle-discovery/spec/oracle-discovery.fixture.ts b/packages/apps/human-app/server/src/modules/oracle-discovery/spec/oracle-discovery.fixture.ts index 3a621629a3..61aa375ee6 100644 --- a/packages/apps/human-app/server/src/modules/oracle-discovery/spec/oracle-discovery.fixture.ts +++ b/packages/apps/human-app/server/src/modules/oracle-discovery/spec/oracle-discovery.fixture.ts @@ -9,6 +9,7 @@ export const response1: DiscoveredOracle = { address: '0xd06eac24a0c47c776Ce6826A93162c4AfC029047', chainId: ChainId.POLYGON_AMOY, role: 'role1', + name: 'oracle1', url: 'common-url', jobTypes: ['job-type-3'], retriesCount: 0, @@ -29,6 +30,7 @@ export const response2: DiscoveredOracle = { address: '0xd10c3402155c058D78e4D5fB5f50E125F06eb39d', chainId: ChainId.POLYGON_AMOY, role: 'role2', + name: 'oracle2', url: '', jobTypes: ['job-type-1', 'job-type-3', 'job-type-4'], retriesCount: 0, @@ -49,6 +51,7 @@ export const response3: DiscoveredOracle = { address: '0xd83422155c058D78e4D5fB5f50E125F06eb39d', chainId: ChainId.POLYGON_AMOY, role: 'role3', + name: 'oracle3', url: 'common-url', jobTypes: ['job-type-2'], retriesCount: 0, @@ -69,6 +72,7 @@ export const response4: DiscoveredOracle = { address: '0xd83422155c058D78e4D5fB5f50E125F06eb39d', chainId: ChainId.BSC_TESTNET, role: 'role3', + name: 'oracle4', url: 'common-url', jobTypes: ['job-type-1', 'job-type-3', 'job-type-4'], retriesCount: 0, From 872314d30a95afd46e9a9da3cd69e8de6e305789 Mon Sep 17 00:00:00 2001 From: Dmitry Nechay Date: Mon, 23 Jun 2025 14:40:28 +0300 Subject: [PATCH 3/4] feat: enable audino on prd jl --- .../client/src/components/Jobs/Create/CreateJob.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/apps/job-launcher/client/src/components/Jobs/Create/CreateJob.tsx b/packages/apps/job-launcher/client/src/components/Jobs/Create/CreateJob.tsx index a019830c0b..9907c08ee1 100644 --- a/packages/apps/job-launcher/client/src/components/Jobs/Create/CreateJob.tsx +++ b/packages/apps/job-launcher/client/src/components/Jobs/Create/CreateJob.tsx @@ -69,7 +69,7 @@ export const CreateJob = () => { {/* {!IS_MAINNET && ( hCaptcha )} */} - {!IS_MAINNET && Audino} + Audino Date: Mon, 23 Jun 2025 16:51:12 +0300 Subject: [PATCH 4/4] chore: add todo for audino --- .../modules/oracle-discovery/oracle-discovery.controller.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/apps/human-app/server/src/modules/oracle-discovery/oracle-discovery.controller.ts b/packages/apps/human-app/server/src/modules/oracle-discovery/oracle-discovery.controller.ts index 69ff5d4eb3..2e315ad0b1 100644 --- a/packages/apps/human-app/server/src/modules/oracle-discovery/oracle-discovery.controller.ts +++ b/packages/apps/human-app/server/src/modules/oracle-discovery/oracle-discovery.controller.ts @@ -58,6 +58,9 @@ export class OracleDiscoveryController { const isAudinoAvailableForUser = jwtPayload.qualifications.includes('audino'); + /** + * TODO: remove filtering logic when Audino available for everyone + */ return oracles.filter((oracle) => { const isAudinoOracle = oracle.jobTypes.includes('audio_transcription');